Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Automated Setup of a Repository Manager

DZone's Guide to

Automated Setup of a Repository Manager

Learn how to use the provisioning REST API that comes with Nexus Repository Manager versions 3+ to make test environments more reproducible.

· DevOps Zone ·
Free Resource

Read why times series is the fastest growing database category.

Previously, I was working on a Nexus Repository Manager set up for different environments, like a test and a production environment, with different privileges, roles, repositories, users, etc.

The problem with such setups is simple: you usually do it manually, which in consequence, is error-prone. If you are doing that more than once, it becomes cumbersome, and you ask yourself: Isn't there a simpler solution to make that setup more reproducible?

Fortunately, there is a solution for that problem.

The Nexus Repository Manager starting with version 3+ provides a Provisioning REST API which allows to upload scripts to Nexus and execute those scripts which makes it possible to do such kind of setup in a scripted manner.

So now we have taken the first barrier. The next one is, how to test such setups?

The simplest way to make experiments related to the Nexus Provisioning API is to use a Docker setup. You can simply get a default installation via a Docker Image.

This can be started by using the following command:

docker run -d -p 8081:8081 --name nexus sonatype/nexus3:3.5.2


Afterwards, you can use the example scripts for provisioning as a starting point.

There is a two-step way to get something configured in Nexus. First, upload the script and afterwards, run it. This can either be done using  curl  manually on the command line, or you can use the  provision.sh  script in example scripting setup, which is much more convenient.

The above example project contains basic examples for different types of setup tasks. The easiest way to start is by cloning the repository and change it based on your own requirements. I started with creating a separate repository for keeping my scripts and changes. By the way, you should use an IDE to write the Groovy scripts (IDEA IntelliJ, for example). This makes implementing scripts easier, cause you get code completion etc.

So now let's start with a real example. A script which will create a repository (I have chosen to create a Maven repository) could look like this:

import org.sonatype.nexus.blobstore.api.BlobStoreManager
import org.sonatype.nexus.repository.maven.LayoutPolicy
import org.sonatype.nexus.repository.storage.WritePolicy
import org.sonatype.nexus.repository.maven.VersionPolicy

def testMavenReleaseRepository = repository.createMavenHosted(
  'test-maven-repository',
  BlobStoreManager.DEFAULT_BLOBSTORE_NAME,
  true,
  VersionPolicy.RELEASE,
  WritePolicy.ALLOW_ONCE
)


This will create a Maven repository named  test-maven-repository  which is configured as a release repository. That means you are not allowed to upload an artifact a second time which is the default for release repositories ( VersionPolicy.RELEASE) in Maven. That's valid for repository managers, too. Furthermore, it will use the BlobStoreManager.DEFAULT_BLOBSTORE_NAME default ). The  true represents the value for the  strictContentTypeValidation parameter.

So now if you have run a script on a running Nexus repository manager instance inside a Docker container, you can check the configuration by looking into the UI of Nexus and see if everything is like you expected it to be. If you find something which is not the way as expected, you simply stop the docker container, fix your script accordingly and start your container and run the script again.

Wait a minute? Running over and over again? Manually checking? Wasn't that the subject at the beginning of this article? Make it reproducible?

So there is a better way to do. Just simply write tests for those things. This means just pick up a testing framework which supports a kind of web testing. I have decided to use Cypress which was easy to setup (unfortunately I had to register via Github. Why?) and with some kind of searching for JQuery issues I had, it took me about two or three hours to have a very good starting point.

So let's take a look at how such a script looks:

describe('test-usage-user Starting', function() {
  beforeEach(function(){
    cy.visit('/')
    // Login as "test-user"
    cy.contains('Sign in').click()
    cy.get('input[name=username]').clear().type('test-user')
    cy.get('input[name=password]').clear().type('test-user')
      .type('{enter}')
  })

  it('Start page', function() {
    cy.get('label').contains('Welcome').end()
    cy.get('label').contains('Welcome to Nexus Repository Manager!').end()
  })

  it('Checking account informations.', function() {
    cy.get('span').contains('test-user').end()
    cy.get('a[data-qtip="Hi, test-user. Manage your user account."]').end()

    // Select the account overview.
    cy.contains('test-user').click()

    // check the "Loading" box, cause it's there only a very short time...
    cy.get('div[role=presentation]').contains('Loading')

    cy.get('span').contains('This is used as your username.')
    cy.contains('Manage your account')

    cy.contains('Sign out').end()
    cy.get('label').contains('Account').end()
    cy.get('span').contains('test-user').end()

  })
  ..
})

I have to admit, there were some challenges to get things working, because it seemed to me that the information on the result pages of Nexus weren't intended for testing, because they contain generated numbers, etc., and unfortunately no unique identifiers for particular parts of the output- for example, an id for a list of repositories. That made the writing of the testing a bit hard and verbose. For example, the following snippet is to extract the information for a single line from the list of repositories: 

cy.get('table>tbody>tr[data-recordid="test-maven-public"]>td>div').then(function ($columns) {
    expect($columns).to.have.length(7)
    // 0 contains the icon
    expect($columns.eq(1)).to.contain('test-maven-public')
    expect($columns.eq(2)).to.contain('group')
    expect($columns.eq(3)).to.contain('maven2')
    expect($columns.eq(4)).to.contain('Online')
    // 5 contains copy URL
    // 6 contains link to get more details
})


So there is no human readable unique id in the  table , something like <table id="repository-list". Only by using the above  data-recordid was it possible to find the correct entries which means you have to know the name of the repositories beforehand. This means if you need to check a list of repositories you have to repeat the above code snippet several times. Improving the output in a way to add better unique id's in a more human-oriented way would make it much easier to get things done based on the idea of infrastructure as code.

In the end, the provisioning API seemed to be not completed yet. So, for example, a configuration for LDAP is a lot more complex than I thought. But maybe I have missed something. The current implementation (as far as it goes) is very simple to use and makes it easy to write such setup scripts which is a great thing.

Learn how to get 20x more performance than Elastic by moving to a Time Series database.

Topics:
docker ,infrastructure as code ,nexus repository ,devops ,testing

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}