Skip to content

Commit

Permalink
Merge pull request #109 from thiagofigueiro/fix-policy-nexus-3.19
Browse files Browse the repository at this point in the history
fix: ``cleanup-policy`` commands for Nexus 3.19.x
  • Loading branch information
thiagofigueiro authored Feb 22, 2020
2 parents a93bf45 + 8ee8533 commit 33fb005
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 50 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
dist: bionic
sudo: required
env:
# TODO: use latest 4 images; e.g.:
# TODO: automatically use last patch version of 3 latest minor versions
# curl -s 'https://hub.docker.com/v2/repositories/sonatype/nexus3/tags/?page_size=25&page=1' -H 'Accept: application/json' --compressed -H 'Pragma: no-cache' -H 'Cache-Control: no-cache' | jq '.results[:4] | .[] | .name'
#"latest"
#"3.21.1"
#"3.21.0"
#"3.20.1"
- NEXUS_VERSION=3.20.0
- NEXUS_VERSION=3.19.1
- NEXUS_VERSION=3.20.1
- NEXUS_VERSION=3.21.1
services:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from setuptools import find_packages, setup

package_name = 'nexus3-cli'
package_version = '3.0.2'
package_version = '3.0.3'

requires = [
'click>=7.0.0,<8',
Expand Down
25 changes: 19 additions & 6 deletions src/nexuscli/api/cleanup_policy/collection.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import json
import logging
import semver

from nexuscli import exception, nexus_util
from nexuscli.api.cleanup_policy import CleanupPolicy

LOG = logging.getLogger(__name__)
GROOVY_SCRIPT_VERSIONS = [semver.VersionInfo(3, 20, 0)]


class CleanupPolicyCollection(object):
Expand All @@ -18,13 +20,24 @@ class CleanupPolicyCollection(object):
methods that require connectivity to Nexus.
"""
GROOVY_SCRIPT_NAME = 'nexus3-cli-cleanup-policy'
"""Groovy script used by this class"""
"""Default Groovy script used by this class"""

def __init__(self, client=None):
self._client = client
script_content = nexus_util.groovy_script(self.GROOVY_SCRIPT_NAME)
self._setup_script()

@property
def _script_name(self):
return nexus_util.script_for_version(
self.GROOVY_SCRIPT_NAME,
self._client.server_version,
GROOVY_SCRIPT_VERSIONS)

def _setup_script(self):

script_content = nexus_util.groovy_script(self._script_name)
self._client.scripts.create_if_missing(
self.GROOVY_SCRIPT_NAME, script_content)
self._script_name, script_content)

def create_or_update(self, cleanup_policy):
"""
Expand All @@ -45,7 +58,7 @@ def create_or_update(self, cleanup_policy):
try:
LOG.debug('Create/update cleanup policy: %s', script_args)
response = self._client.scripts.run(
self.GROOVY_SCRIPT_NAME, data=script_args)
self._script_name, data=script_args)
except exception.NexusClientAPIError:
raise exception.NexusClientCreateCleanupPolicyError(
cleanup_policy.configuration['name'])
Expand All @@ -69,7 +82,7 @@ def get_by_name(self, name):

try:
response = self._client.scripts.run(
self.GROOVY_SCRIPT_NAME, data=script_args)
self._script_name, data=script_args)
except exception.NexusClientAPIError:
raise exception.NexusClientInvalidCleanupPolicy(name)

Expand All @@ -86,7 +99,7 @@ def list(self):
instances.
:rtype: list[CleanupPolicy]
"""
response = self._client.scripts.run(self.GROOVY_SCRIPT_NAME, data={})
response = self._client.scripts.run(self._script_name, data={})

cleanup_policies = json.loads(response['result'])

Expand Down
28 changes: 2 additions & 26 deletions src/nexuscli/api/repository/collection.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import json
import pathlib
import semver

from nexuscli import exception
from nexuscli import exception, nexus_util
from nexuscli.api.repository import model

SCRIPT_CREATE_VERSIONS = [semver.VersionInfo(3, 21, 0)]
Expand Down Expand Up @@ -158,29 +157,6 @@ def _repository_args_kwargs(raw_configuration):
return args, kwargs


def _script_for_version(script_name, server_version, versions):
"""
Determine if a certain nexus server version requires a different version of
the given groovy script.
:param script_name: original name of the script.
:param server_version: VersionInfo for the Nexus server.
:param versions: list of VersionInfo. Each element represents an existing
groovy script that must be used with server_version or greater.
:return: the version-specific name of script_name.
"""
if server_version is None:
return script_name

for breaking_version in sorted(versions, reverse=True):
if server_version >= breaking_version:
script_path = pathlib.Path(script_name)
# e.g.: nexus3-cli-repository-create_3.21.0.groovy
return f'{script_path.stem}_{breaking_version}{script_path.suffix}'

return script_name


class RepositoryCollection:
"""
A class to manage Nexus 3 repositories.
Expand Down Expand Up @@ -274,7 +250,7 @@ def create(self, repository):
:type repository: Repository
:raises NexusClientCreateRepositoryError: error creating repository.
"""
script_name = _script_for_version(
script_name = nexus_util.script_for_version(
SCRIPT_NAME_CREATE,
self._client.server_version,
SCRIPT_CREATE_VERSIONS)
Expand Down
22 changes: 9 additions & 13 deletions src/nexuscli/api/script/groovy/nexus3-cli-cleanup-policy.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import groovy.json.JsonSlurper
import groovy.json.JsonBuilder
import java.util.concurrent.TimeUnit

import org.sonatype.nexus.cleanup.storage.CleanupPolicy
import org.sonatype.nexus.cleanup.storage.CleanupPolicyStorage
import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.IS_PRERELEASE_KEY
import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.LAST_BLOB_UPDATED_KEY
Expand All @@ -16,7 +17,7 @@ def cleanupPolicyStorage = container.lookup(CleanupPolicyStorage.class.getName()
try {
parsed_args = new JsonSlurper().parseText(args)
} catch(Exception ex) {
log.debug("list")
// "list" operation
def policies = []
cleanupPolicyStorage.getAll().each {
policies << toJsonString(it)
Expand All @@ -34,7 +35,6 @@ if (parsed_args.name == null) {

// "get" operation
if (parsed_args.size() == 1) {
log.debug("get")
existingPolicy = cleanupPolicyStorage.get(parsed_args.name)
return toJsonString(existingPolicy)
}
Expand All @@ -54,18 +54,14 @@ if (cleanupPolicyStorage.exists(parsed_args.name)) {

// "create" operation
format = parsed_args.format == "all" ? "ALL_FORMATS" : parsed_args.format

log.debug("Creating Cleanup Policy <name=${parsed_args.name}>")
cleanupPolicy = cleanupPolicyStorage.newCleanupPolicy()

log.debug("Configuring Cleanup Policy <policy=${cleanupPolicy}>")
cleanupPolicy.setName(parsed_args.name)
cleanupPolicy.setNotes(parsed_args.notes)
cleanupPolicy.setFormat(format)
cleanupPolicy.setMode('deletion')
cleanupPolicy.setCriteria(criteriaMap)

log.debug("Adding Cleanup Policy <policy=${cleanupPolicy}>")
cleanupPolicy = new CleanupPolicy(
name: parsed_args.name,
notes: parsed_args.notes,
format: format,
mode: 'deletion',
criteria: criteriaMap
)
cleanupPolicyStorage.add(cleanupPolicy)
return toJsonString(cleanupPolicy)

Expand Down
113 changes: 113 additions & 0 deletions src/nexuscli/api/script/groovy/nexus3-cli-cleanup-policy_3.20.0.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Original from:
// https://github.com/idealista/nexus-role/blob/master/files/scripts/cleanup_policy.groovy
import com.google.common.collect.Maps
import groovy.json.JsonSlurper
import groovy.json.JsonBuilder
import java.util.concurrent.TimeUnit

import org.sonatype.nexus.cleanup.storage.CleanupPolicyStorage
import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.IS_PRERELEASE_KEY
import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.LAST_BLOB_UPDATED_KEY
import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.LAST_DOWNLOADED_KEY


def cleanupPolicyStorage = container.lookup(CleanupPolicyStorage.class.getName())

try {
parsed_args = new JsonSlurper().parseText(args)
} catch(Exception ex) {
log.debug("list")
def policies = []
cleanupPolicyStorage.getAll().each {
policies << toJsonString(it)
}
return policies
}

parsed_args.each {
log.debug("Received arguments: <${it.key}=${it.value}> (${it.value.getClass()})")
}

if (parsed_args.name == null) {
throw new Exception("Missing mandatory argument: name")
}

// "get" operation
if (parsed_args.size() == 1) {
log.debug("get")
existingPolicy = cleanupPolicyStorage.get(parsed_args.name)
return toJsonString(existingPolicy)
}

// create and update use this
Map<String, String> criteriaMap = createCriteria(parsed_args)

// "update" operation
if (cleanupPolicyStorage.exists(parsed_args.name)) {
log.debug("Updating Cleanup Policy <name=${parsed_args.name}>")
existingPolicy = cleanupPolicyStorage.get(parsed_args.name)
existingPolicy.setNotes(parsed_args.notes)
existingPolicy.setCriteria(criteriaMap)
cleanupPolicyStorage.update(existingPolicy)
return toJsonString(existingPolicy)
}

// "create" operation
format = parsed_args.format == "all" ? "ALL_FORMATS" : parsed_args.format

log.debug("Creating Cleanup Policy <name=${parsed_args.name}>")
cleanupPolicy = cleanupPolicyStorage.newCleanupPolicy()

log.debug("Configuring Cleanup Policy <policy=${cleanupPolicy}>")
cleanupPolicy.setName(parsed_args.name)
cleanupPolicy.setNotes(parsed_args.notes)
cleanupPolicy.setFormat(format)
cleanupPolicy.setMode('deletion')
cleanupPolicy.setCriteria(criteriaMap)

log.debug("Adding Cleanup Policy <policy=${cleanupPolicy}>")
cleanupPolicyStorage.add(cleanupPolicy)
return toJsonString(cleanupPolicy)


def Map<String, String> createCriteria(parsed_args) {
Map<String, String> criteriaMap = Maps.newHashMap()
if (parsed_args.criteria.lastBlobUpdated == null) {
criteriaMap.remove(LAST_BLOB_UPDATED_KEY)
} else {
criteriaMap.put(LAST_BLOB_UPDATED_KEY, asStringSeconds(parsed_args.criteria.lastBlobUpdated))
}
if (parsed_args.criteria.lastDownloaded == null) {
criteriaMap.remove(LAST_DOWNLOADED_KEY)
} else {
criteriaMap.put(LAST_DOWNLOADED_KEY, asStringSeconds(parsed_args.criteria.lastDownloaded))
}
if (parsed_args.criteria.preRelease != "") {
criteriaMap.put(IS_PRERELEASE_KEY, String.valueOf(parsed_args.criteria.preRelease))
}
log.debug("Using criteriaMap: ${criteriaMap}")

return criteriaMap
}

def Integer asSeconds(days) {
return days * TimeUnit.DAYS.toSeconds(1)
}

def String asStringSeconds(daysInt) {
return String.valueOf(asSeconds(daysInt))
}

// There's got to be a better way to do this.
// using JsonOutput directly on the object causes a stack overflow
def String toJsonString(cleanup_policy) {
def policyString = new JsonBuilder()
policyString {
name cleanup_policy.getName()
notes cleanup_policy.getNotes()
format cleanup_policy.getFormat()
mode cleanup_policy.getMode()
criteria cleanup_policy.getCriteria()
}
return policyString.toPrettyString()
}
24 changes: 24 additions & 0 deletions src/nexuscli/nexus_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import hashlib
import mmap
import os
import pathlib
import pkg_resources


Expand Down Expand Up @@ -165,3 +166,26 @@ def ensure_exists(path, is_dir=False):
path.mkdir(exist_ok=True)
else:
path.touch()


def script_for_version(script_name, server_version, versions):
"""
Determine if a certain nexus server version requires a different version of
the given groovy script.
:param script_name: original name of the script.
:param server_version: VersionInfo for the Nexus server.
:param versions: list of VersionInfo. Each element represents an existing
groovy script that must be used with server_version or greater.
:return: the version-specific name of script_name.
"""
if server_version is None:
return script_name

for breaking_version in sorted(versions, reverse=True):
if server_version >= breaking_version:
script_path = pathlib.Path(script_name)
# e.g.: nexus3-cli-repository-create_3.21.0.groovy
return f'{script_path.stem}_{breaking_version}{script_path.suffix}'

return script_name
5 changes: 3 additions & 2 deletions tests/api/cleanup_policy/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@


@pytest.fixture
def cleanup_policy_collection(mocker):
def cleanup_policy_collection(mocker, nexus_mock_client):
"""An instance with a magic mock as its client"""
fixture = CleanupPolicyCollection(client=mocker.Mock())
nexus_mock_client._scripts = mocker.Mock()
fixture = CleanupPolicyCollection(client=nexus_mock_client)
return fixture


Expand Down

0 comments on commit 33fb005

Please sign in to comment.