exit if errors are found in a nexus repo 13/16513/12
authorAric Gardner <agardner@linuxfoundation.org>
Fri, 9 Aug 2019 21:04:45 +0000 (17:04 -0400)
committerAric Gardner <agardner@linuxfoundation.org>
Tue, 13 Aug 2019 19:54:01 +0000 (15:54 -0400)
nexus release now checks "{}/staging/repository/{}/activity"

Ensures that Repository is in closed state
Checks if Repository is already released (exit 0)
Check for failures, if found (exit 1)

Added
click.option('-v', '--verify-only', is_flag=True, required=False)
if -v is passed, only checks for errors, skips release.

This could be better, but I have officially spent too much time
fiddling with it.
Its a bit of a brute force, but as

curl -v -u <lfid:pass>
'https://nexus.opendaylight.org/\
service/local/staging/repository/honeycomb-vbd-1629/activity'

Actually returns two xml pages.
not being too familliar with soup I used
soup.find_all("value")
and then used a re.search
to check for needed values

Removing the pytests, they don't make sense imo.

ISSUE: RELENG-2291
Signed-off-by: Aric Gardner <agardner@linuxfoundation.org>
Change-Id: I7ae6c487b2aaf0e1df01effa7da15a1b28b624ad

.coafile
lftools/cli/nexus.py
lftools/nexus/cmd.py
releasenotes/notes/nexus-release-2b0ca5f0051c703c.yaml [new file with mode: 0644]
requirements.txt
tests/test_nexus.py

index 07cc80b..be307c4 100644 (file)
--- a/.coafile
+++ b/.coafile
@@ -49,7 +49,8 @@ known_third_party_imports =
     xdg,
     yaml,
     pygerrit2,
-    pygithub
+    pygithub,
+    bs4
 
 pydocstyle_ignore = D203, D213, D301
 max_line_length = 120
index f4bde7c..945eee3 100644 (file)
@@ -122,16 +122,17 @@ def delete_images(ctx, settings, server, repo, pattern, yes):
 @nexus.command()
 @click.pass_context
 @click.argument('REPOS', type=str, nargs=-1)
+@click.option('-v', '--verify-only', 'verify', is_flag=True, required=False)
 @click.option(
     '-s', '--server', type=str,
     help=('Nexus server URL. Can also be set as {} in the environment. '
           'This will override any URL set in settings.yaml.').format(
               NEXUS_URL_ENV))
-def release(ctx, repos, server):
+def release(ctx, repos, verify, server):
     """Release one or more staging repositories."""
     if not server and NEXUS_URL_ENV in environ:
         server = environ[NEXUS_URL_ENV]
-    nexuscmd.release_staging_repos(repos, server)
+    nexuscmd.release_staging_repos(repos, verify, server)
 
 
 @docker.command(name="releasedockerhub")
index 9bc043b..6c72b47 100644 (file)
 
 import csv
 import logging
+import re
 import sys
 
+import bs4
 import requests
 from six.moves import configparser
 import yaml
@@ -302,26 +304,85 @@ def delete_images(settings_file, url, images):
         _nexus.delete_image(image)
 
 
-def release_staging_repos(repos, nexus_url=""):
+def release_staging_repos(repos, verify, nexus_url=""):
     """Release one or more staging repos.
 
     :arg tuple repos: A tuple containing one or more repo name strings.
     :arg str nexus_url: Optional URL of target Nexus server.
+    :arg flag --verify-only: Only verify repo and exit.
     """
     credentials = get_credentials(None, nexus_url)
     _nexus = Nexus(credentials['nexus'], credentials['user'],
                    credentials['password'])
 
     for repo in repos:
-        data = {"data": {"stagedRepositoryIds": [repo]}}
-        log.debug("Sending data: {}".format(data))
-        request_url = "{}/staging/bulk/promote".format(_nexus.baseurl)
-        log.debug("Request URL: {}".format(request_url))
-        response = requests.post(request_url, json=data, auth=_nexus.auth)
-
-        if response.status_code != 201:
-            raise requests.HTTPError("Release failed with the following error:"
-                                     "\n{}: {}".format(response.status_code,
-                                                       response.text))
+        # Verfiy repo before releasing
+        verify_request_url = "{}/staging/repository/{}/activity".format(_nexus.baseurl, repo)
+        log.info("Request URL: {}".format(verify_request_url))
+        response = requests.get(verify_request_url, auth=_nexus.auth)
+
+        if response.status_code != 200:
+            raise requests.HTTPError("Verification of repo failed with the following error:"
+                                     "\n{}: {}".format(response.status_code, response.text))
+            sys.exit(1)
+
+        soup = bs4.BeautifulSoup(response.text, 'xml')
+        values = soup.find_all("value")
+        names = soup.find_all("name")
+        failures = []
+        successes = []
+        isrepoclosed = []
+
+        # Check for failures
+        for message in values:
+            if re.search('StagingRulesFailedException', message.text):
+                failures.append(message)
+            if re.search('Invalid', message.text):
+                failures.append(message)
+
+        # Check if already released
+        for name in names:
+            if re.search('repositoryReleased', name.text):
+                successes.append(name)
+
+        # Ensure Repository is in Closed state
+        for name in names:
+            if re.search('repositoryClosed', name.text):
+                isrepoclosed.append(name)
+
+        if len(failures) != 0:
+            log.info(failures)
+            log.info("One or more rules failed")
+            sys.exit(1)
+        else:
+            log.info("PASS: No rules have failed")
+
+        if len(successes) != 0:
+            log.info(successes)
+            log.info("Nothing to do: Repository already released")
+            sys.exit(0)
+
+        if len(isrepoclosed) != 1:
+            log.info(isrepoclosed)
+            log.info("Repository is not in closed state")
+            sys.exit(1)
         else:
-            log.debug("Successfully released {}".format(str(repo)))
+            log.info("PASS: Repository is in closed state")
+
+        log.info("Successfully verfied {}".format(str(repo)))
+
+    if not verify:
+        print("running release")
+        for repo in repos:
+            data = {"data": {"stagedRepositoryIds": [repo]}}
+            log.debug("Sending data: {}".format(data))
+            request_url = "{}/staging/bulk/promote".format(_nexus.baseurl)
+            log.debug("Request URL: {}".format(request_url))
+            response = requests.post(request_url, json=data, auth=_nexus.auth)
+
+            if response.status_code != 201:
+                raise requests.HTTPError("Release failed with the following error:"
+                                         "\n{}: {}".format(response.status_code,
+                                                           response.text))
+            else:
+                log.debug("Successfully released {}".format(str(repo)))
diff --git a/releasenotes/notes/nexus-release-2b0ca5f0051c703c.yaml b/releasenotes/notes/nexus-release-2b0ca5f0051c703c.yaml
new file mode 100644 (file)
index 0000000..e2a73ec
--- /dev/null
@@ -0,0 +1,10 @@
+---
+features:
+  - |
+    nexus release now checks "{}/staging/repository/{}/activity"
+    Ensures that Repository is in closed state
+    Checks if Repository is already released (exit 0)
+    Check for failures, if found (exit 1)
+    Added
+    click.option('-v', '--verify-only', is_flag=True, required=False)
+    if -v is passed, only checks for errors, skips release
index 4a14546..498db37 100644 (file)
@@ -17,6 +17,7 @@ email_validator
 oauth2client
 pyyaml
 pygerrit2
+bs4
 
 # workarounds to prevent upstream from breaking us
 netifaces==0.10.5
index 38dda2f..a4f883a 100644 (file)
 """Test nexus command."""
 
 import re
-import requests
 
-import pytest
 
-from lftools.nexus import cmd
 from lftools.nexus import util
 
 
@@ -52,33 +49,3 @@ def test_create_repo_target_regex():
     vpp = util.create_repo_target_regex('io.fd.vpp')
     vpp_regex = re.compile(vpp)
     assert vpp_regex.match('/io/fd/vpp/jvpp/16.06/jvpp-16.06.jar')
-
-
-def test_release_staging_repos(responses):
-    """Test release_staging_repos() command."""
-    good_url = "https://nexus.example.org"
-    # Prepare response for Nexus initialization
-    responses.add(responses.GET,
-                  "{}/service/local/repo_targets".format(good_url),
-                  json=None, status=200)
-    # The responses provide implicit assertions.
-    responses.add(responses.POST, "{}/service/local/staging/bulk/promote".format(good_url),
-                  json=None, status=201)
-
-    # Test successful single release.
-    cmd.release_staging_repos(("release-1",), good_url)
-
-    # Test successful multiple release.
-    cmd.release_staging_repos(("release-1", "release-2", "release-3"),
-                              good_url)
-
-    # Test promote failure
-    bad_url1 = "https://nexus-fail1.example.org"
-    responses.add(responses.GET,
-                  "{}/service/local/repo_targets".format(bad_url1),
-                  json=None, status=200)
-    responses.add(responses.POST,
-                  "{}/service/local/staging/bulk/promote".format(bad_url1),
-                  status=401)
-    with pytest.raises(requests.HTTPError):
-        cmd.release_staging_repos(("release-1",), bad_url1)