Tuesday, August 04, 2009

Updating a Linux box behind proxy server

Recently I had to update a Linux (Fedora 9 to be precise) box, which was behind a proxy server. To get through the proxy server I configured relevant environment variables as described here. After executing 'sudo yum update' command system seems to be hanged after emitting following line on the console-

Loaded plugins: refresh-packagekit

After a long wait yum failed with following error message on console-

[vinod@localhost ~]# sudo yum update
Loaded plugins: refresh-packagekit
Could not retrieve mirrorlist http://mirrors.fedoraproject.org/mirrorlist?repo=fedora-9&arch=i386 error was
[Errno 4] IOError: <urlopen error (110, 'Connection timed out')>
Error: Cannot retrieve repository metadata (repomd.xml) for repository: fedora. Please verify its path and try again


OK, so yum was not able to get through proxy, but when I tried to access the same URL using wget-

[vinod@localhost ~]# wget http://mirrors.fedoraproject.org/mirrorlist?repo=fedora-9&arch=i386

it was able to access it. Then what could be wrong :-/

After pondering for sometime, I realized that proxy configuration was done using a non-root user while yum was running as root (due to sudo). Environment variables configured for a user are not carried forwarded (to another user) by Linux when user is changed using su or sudo command. Configuring the proxy as root solved the problem and yum successfully updated system.

Monday, August 03, 2009

Getting started with Redmine

For sometime I was looking for a project management software, which can provide an integrated view of most of the project related artifacts/activities. Redmine was the most impressive tool, I came across. It has issue tracking, wiki, forum, documents, integration with third party tools (source repositories, LDAP etc.), plugin to integrate with Hudson and many more features. It has plugin based architecture, so possibilities are almost endless.

Redmine is written in Ruby (Ruby on Rail) and I know nothing about that other than spelling :-) I had to install several packages to get started with it. Redmine stores its data in a database and when it comes to open source DB, PostgreSQL is always my first choice. As a practice I prefer to serve all web applications through Apache and here I found an Apache module called Passenger, which can serve Ruby applications.

Here are my notes, which I took during installing Redmine and supporting applications.

  1. Install PostgreSQL for Linux
          $ ./postgresql-x.x.x-x.bin --mode text

    Follow the installer instructions to complete the installation. Make sure that PostgreSQL accepts connections from other than 'localhost' else you can't connect to DB from any other machine. This can be done by editing 'pg_hba' configuration file.

  2. Install Ruby, Redmine needs ruby 1.8.7-
          $ tar zxvf ruby-1.8.7-p174.zip
          $ cd ruby-1.8.7-p174
          $ ./configure --prefix=/opt/redmine/ruby-1.8.7
          $ make
          $ make install


    Add ruby to your classpath before executing further installation steps.

  3. Install ruby-gems, say rubygems-1.3.4
          $ unzip rubygems-1.3.4.zip
          $ cd rubygems-1.3.4
          $ ruby setup.rb

  4. Install rake
          $ unzip rake-0.8.7.zip
          $ cd rake-0.8.7
          $ ruby install.rb

  5. Install rails (Ruby On Rails) version 2.1.2 (required by Redmine)
          $ gem install rails -v 2.1.2

    The GEM needs internet connectivity to download and install the packages. Though there are ways to install packages locally, after downloading them manually. As I am new to Ruby so preferred the easiest installation method.

  6. Install PostgreSQL adapter for Ruby
    Add PostgreSQL's 'bin' directory in PATH before executing following command-
        $ gem install pg

  7. Install redmine

    • Unzip the downloaded archive
      $ tar zxvf redmine-0.8.4.tar.gz

    • Create 'redmine' database user
      CREATE ROLE redmine LOGIN PASSWORD 'redmine' NOSUPERUSER NOINHERIT CREATEDB NOCREATEROLE;

    • Create 'redmine' database
      CREATE DATABASE redmine WITH ENCODING='UTF8' OWNER=redmine;

    • Define PosgtreSQL DB in '/opt/redmine/redmine-0.8.4/config/database.yml'
             production:
               adapter: postgresql
               database: redmine
               host: localhost
               port: 5432
               username: redmine
               password: "redmine"

    • Create the database structure, by running the following command under the redmine installation directory. It will create tables and an administrator account.
          $ rake db:migrate RAILS_ENV="production"

    • Insert default configuration data in database, by running the following command:
          $ rake redmine:load_default_data RAILS_ENV="production"

  8. Install Passenger module to enable Apache to serve ruby applications
        $ tar zxvf passenger-2.2.4.tar.gz
        $ cd passenger-2.2.4/bin
        $ export APXS2=/opt/redmine/apache-2.2.11/bin/apxs
        $ ./passenger-install-apache2-module

  9. Configure Apache to server redmine or any other ruby application using passenger
    To publish a rubby application under a sub-uri, create a symbolic link to application's public directory under web root directory. Thats the way passenger works, pointing directly to application's public directory will not work here.

        $ ln -s /opt/redmine/redmine-0.8.4/public redmine

    Here is a sample configuration text from Apache's httpd-vhosts.conf file to serve Redmine-

        NameVirtualHost *:80

        LoadModule passenger_module /opt/redmine/passenger-2.2.4/ext/apache2/mod_passenger.so
        PassengerRoot /opt/redmine/passenger-2.2.4
        PassengerRuby /opt/redmine/ruby-1.8.7/bin/ruby

        <VirtualHost *:80>
            ServerAdmin webmaster@mydomain.com
            ServerName redmine

            DocumentRoot "/opt/apache-2.2.11/htdocs"
            RailsEnv production
            RailsBaseURI /redmine

            ErrorLog "logs/error_log"
            CustomLog "logs/access_log" common

            <Directory "/opt/redmine/redmine-0.8.4/public">
                Options FollowSymLinks
                AllowOverride None
                Order deny,allow
                Allow from all
            </Directory>
        </VirtualHost>
Now Redmine is available for action. Next steps could be configuring to integrate with LDAP and few more things or installing some plugins to enhance the functionality. Next I am looking for ways to migrate issue tracker from Bugzilla to Redmine, to have everything under one roof.

Friday, July 17, 2009

Managing Maven repository with Artifactory

It is close to one year, since we started using Maven as build tool for new projects. Overall experience with Maven has been very good. We are managing a simple disk based directory structure, which is served by Apache as local Maven repository. All artifacts in the local repository are populated manually by copying from a local machine. As more and more projects started using Maven, managing repository became a headache.

Need of a repository manager was felt to minimize the overhead of manually managing local repository. This wiki article has a good comparison of Archiva, Artifactory and Nexus. After a glance at comparison the Artifactory and Nexus seems to have more less similar feature set. The out of the box LDAP integration in Artifactory (Nexus has this feature in paid version) is a big differentiator, this feature was the main reason of going with Artifactory instead of Nexus.

Artifactory keeps its data in a database using Jackrabbit, a Java Content Repository (JSR 170) implementation and Jackrabbit supports almost all popular databases see the list here (these are the DDL scripts for all supported databases and executing when Artifactory is started first time). By default Artifactory uses Derby and other than that they have documented just about using MySQL. Personally I always prefer PostgreSQL over MySQL, so decided to go with PostgreSQL along with WAR based deployment in Tomcat.

Artifactory works out of the box just by dropping the war file in Tomcat and on first run it creates a Derby database in ${user.home}/.artifactory/data directory. To change the database to PostgreSQL or something else stop the Tomcat and follow the steps given below-

1. Delete ${user.home}/.artifactory/data directory.

2. Uncomment and change following line in ${user.home}/.artifactory/etc/artifactory.system.properties file-
    artifactory.jcr.configPath=repo/postgresql

3. Create ${user.home}/.artifactory/etc/repo/postgresql/repo.xml with following contents (this is based on MySQL configuration file available in Artifactory code base)-

<?xml version="1.0" encoding="ISO-8859-1"?>
<!--<!DOCTYPE Repository SYSTEM "config.dtd">-->

<Repository>
    <!--
        virtual file system where the repository stores global state
        (e.g. registered namespaces, custom node types, etc.)
    -->

    <!-- PostgreSQL Filesystem -->
    <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
        <param name="driver" value="org.postgresql.Driver"/>
        <param name="url" value="jdbc:postgresql://localhost:5432/artifactory"/>
        <param name="user" value="artifactory_user"/>
        <param name="password" value="password"/>
        <param name="schema" value="postgresql"/>
    </FileSystem>

    <!-- http://wiki.apache.org/jackrabbit/DataStore -->

    <!-- PostgreSQL Datastore -->
    <DataStore class="org.artifactory.jcr.jackrabbit.ArtifactoryDbDataStoreImpl">
        <param name="url" value="jdbc:postgresql://localhost:5432/artifactory"/>
        <param name="tablePrefix" value=""/>
        <param name="user" value="artifactory_user"/>
        <param name="password" value="password"/>
        <param name="databaseType" value="postgresql"/>
        <param name="driver" value="org.postgresql.Driver"/>
        <param name="minRecordLength" value="512"/>
        <param name="maxConnections" value="15"/>
        <param name="copyWhenReading" value="true"/>
    </DataStore>

    <!--
        security configuration
    -->
    <Security appName="Jackrabbit">
        <!--
            access manager:
            class: FQN of class implementing the AccessManager interface
        -->
        <AccessManager class="org.apache.jackrabbit.core.security.SimpleAccessManager">
            <!-- <param name="config" value="${rep.home}/access.xml"/> -->
        </AccessManager>

        <LoginModule class="org.apache.jackrabbit.core.security.SimpleLoginModule">
            <!-- anonymous user name ('anonymous' is the default value) -->
            <param name="anonymousId" value="anonymous"/>
            <!--
              default user name to be used instead of the anonymous user
              when no login credentials are provided (unset by default)
           -->
            <param name="defaultUserId" value="superuser"/>
        </LoginModule>
    </Security>

    <!--
        location of workspaces root directory and name of default workspace
    -->
    <Workspaces rootPath="${rep.home}/workspaces" defaultWorkspace="default"/>
    <!--
        workspace configuration template:
        used to create the initial workspace if there's no workspace yet
    -->
    <Workspace name="${wsp.name}">
        <!--
            virtual file system of the workspace:
            class: FQN of class implementing the FileSystem interface
        -->
        <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
            <param name="path" value="${wsp.home}"/>
        </FileSystem>
        <!--
            persistence manager of the workspace:
            class: FQN of class implementing the PersistenceManager interface
        -->

        <!-- PostgreSQL Persistance Manager -->
        <PersistenceManager class="org.apache.jackrabbit.core.persistence.bundle.PostgreSQLPersistenceManager">
            <param name="url" value="jdbc:postgresql://localhost:5432/artifactory"/>
            <param name="user" value="artifactory_user"/>
            <param name="password" value="password"/>
            <param name="schemaObjectPrefix" value="${wsp.name}_"/>
        </PersistenceManager>

        <!-- http://issues.apache.org/jira/browse/JCR-314 -->
        <ISMLocking class="org.apache.jackrabbit.core.state.FineGrainedISMLocking"/>

        <!--
            Search index and the file system it uses.
            class: FQN of class implementing the QueryHandler interface

            If required by the QueryHandler implementation, one may configure
            a FileSystem that the handler may use.

            Supported parameters for lucene search index:
            - path: location of the index. This parameter is mandatory!
            - useCompoundFile: advises lucene to use compound files for the index files
            - minMergeDocs: minimum number of nodes in an index until segments are merged
            - volatileIdleTime: idle time in seconds until the volatile index is
              moved to persistent index even though minMergeDocs is not reached.
            - maxMergeDocs: maximum number of nodes in segments that will be merged
            - mergeFactor: determines how often segment indices are merged
            - maxFieldLength: the number of words that are fulltext indexed at most per property.
            - bufferSize: maximum number of documents that are held in a pending
              queue until added to the index
            - cacheSize: size of the document number cache. This cache maps
              uuids to lucene document numbers
            - forceConsistencyCheck: runs a consistency check on every startup. If
              false, a consistency check is only performed when the search index
              detects a prior forced shutdown. This parameter only has an effect
              if 'enableConsistencyCheck' is set to 'true'.
            - enableConsistencyCheck: if set to 'true' a consistency check is
              performed depending on the parameter 'forceConsistencyCheck'. If
              set to 'false' no consistency check is performed on startup, even
              if a redo log had been applied.
            - autoRepair: errors detected by a consistency check are automatically
              repaired. If false, errors are only written to the log.
            - analyzer: class name of a lucene analyzer to use for fulltext indexing of text.
            - queryClass: class name that implements the javax.jcr.query.Query interface.
              this class must extend the class: org.apache.jackrabbit.core.query.AbstractQueryImpl
            - respectDocumentOrder: If true and the query does not contain an 'order by' clause,
              result nodes will be in document order. For better performance when queries return
              a lot of nodes set to 'false'.
            - resultFetchSize: The number of results the query handler should
              initially fetch when a query is executed.
              Default value: Integer.MAX_VALUE (-> all)
            - extractorPoolSize: defines the maximum number of background threads that are
              used to extract text from binary properties. If set to zero (default) no
              background threads are allocated and text extractors run in the current thread.
            - extractorTimeout: a text extractor is executed using a background thread if it
              doesn't finish within this timeout defined in milliseconds. This parameter has
              no effect if extractorPoolSize is zero.
            - extractorBackLogSize: the size of the extractor pool back log. If all threads in
              the pool are busy, incomming work is put into a wait queue. If the wait queue
              reaches the back log size incomming extractor work will not be queued anymore
              but will be executed with the current thread.
            - synonymProviderClass: the name of a class that implements
              org.apache.jackrabbit.core.query.lucene.SynonymProvider. The
              default value is null (-> not set).

            Note: all parameters (except path) in this SearchIndex config are default
            values and can be omitted.
        -->
        <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
            <param name="path" value="${rep.home}/index"/>
            <param name="useCompoundFile" value="true"/>
            <!-- Default is 100 -->
            <param name="minMergeDocs" value="500"/>
            <param name="maxMergeDocs" value="10000"/>
            <param name="volatileIdleTime" value="3"/>
            <!-- Default is 10: more segments quicker the indexing but slower the searching -->
            <param name="mergeFactor" value="10"/>
            <param name="maxFieldLength" value="10000"/>
            <!-- Default is 10 -->
            <param name="bufferSize" value="100"/>
            <param name="cacheSize" value="1000"/>
            <param name="forceConsistencyCheck" value="false"/>
            <param name="enableConsistencyCheck" value="true"/>
            <param name="autoRepair" value="true"/>
            <param name="analyzer" value="org.apache.lucene.analysis.standard.StandardAnalyzer"/>
            <param name="queryClass" value="org.apache.jackrabbit.core.query.QueryImpl"/>
            <param name="respectDocumentOrder" value="false"/>
            <param name="resultFetchSize" value="700"/>
            <param name="supportHighlighting" value="true"/>
            <!--
            Use 5 background threads for text extraction that takes more than 100 milliseconds
            -->
            <param name="extractorPoolSize" value="5"/>
            <param name="extractorTimeout" value="100"/>
            <!-- Default is 100 -->
            <param name="extractorBackLogSize" value="500"/>
            <!-- Indexing configuration -->
            <!--<param name="indexingConfiguration" value="${rep.home}/index/index_config.xml"/>-->
        </SearchIndex>
    </Workspace>

    <!--
        Configures the versioning
    -->
    <Versioning rootPath="${rep.home}/version">
        <!--
            Configures the filesystem to use for versioning for the respective
            persistence manager
        -->
        <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
            <param name="path" value="${rep.home}/version"/>
        </FileSystem>

        <!--
            Configures the perisistence manager to be used for persisting version state.
            Please note that the current versioning implementation is based on
            a 'normal' persistence manager, but this could change in future
            implementations.
        -->
        <!--We do not use versionning-->
        <PersistenceManager class="org.apache.jackrabbit.core.persistence.mem.InMemPersistenceManager">
            <param name="persistent" value="false"/>
        </PersistenceManager>
    </Versioning>

    <!-- Clustering configuration -->
    <!--
    <Cluster id="node1">
        <Journal class="org.apache.jackrabbit.core.journal.DatabaseJournal">
            <param name="revision" value="${rep.home}/revision.log"/>
            <param name="driver" value="org.postgresql.Driver"/>
            <param name="url"
                   value="jdbc:postgresql://localhost:5432/artifactory"/>
            <param name="user" value="artifactory_user"/>
            <param name="password" value="password"/>
        </Journal>
    </Cluster>
    -->

</Repository>
4. Create Artifactory database using following script-
          CREATE ROLE artifactory_user LOGIN PASSWORD 'password' NOINHERIT VALID UNTIL 'infinity';

    CREATE DATABASE artifactory WITH ENCODING='UTF8' OWNER=artifactory_user;


5. Copy PostgreSQL JDBC driver to $TOMCAT_HOME/lib directory.

6. Restart Tomcat and we are good to go.

Following two steps are optional.

7. Do the LDAP (Active Directory in our case) integration using "Admin > Security > LDAP Settings" screen.

8. Using "Admin > Security > Groups" screen create a group say 'developers' and check the option to automatically join all new users to this group. Now whenever someone tries to login, Artifactory will delegate the task of authentication to LDAP and on successful authentication it will create an account (if not already exist) and include that user in this group.

Now all installation and configuration steps are complete, I proceeded for a project build using Artifactory repository URL http://<server>:<port>/artifactory/repo/. Oops! Maven was not able to get any artifacts :-(, when looked into Artifactory logs, there was a following error message-

2009-07-17 17:17:56,932 [WARN ] (o.a.r.RemoteRepoBase:199) - repo1: Error in getting information for 'org/apache/maven/plugins/maven-compiler-plugin/2.0.2/maven-compiler-plugin-2.0.2.pom' (Failed retrieving resource from http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-compiler-plugin/2.0.2/maven-compiler-plugin-2.0.2.pom: repo1.maven.org).

What could be the reason??? Error message does not give much information. Well server was behind a proxy and Artifactory needs to be told to use the proxy server for internet access. After configuring a proxy, one need to edit the configuration of all remote repositories individually to use the proxy. IMO it would have been better to have an option during proxy configuration to declare that as global proxy to be used by all repositories.

Now artifactory is working fine and taken some workload off from my busy schedule, all thanks to the great work being done by JFrog guys.