Copy sign scripts over to lftools 43/5343/4
authorThanh Ha <thanh.ha@linuxfoundation.org>
Wed, 28 Jun 2017 17:51:41 +0000 (13:51 -0400)
committerThanh Ha <thanh.ha@linuxfoundation.org>
Fri, 30 Jun 2017 03:30:01 +0000 (23:30 -0400)
This is a rewrite of the original tools to make it more generically
reusable by any Nexus server and project but is based on the original
code in OpenDaylight's releng/builder project scripts odlrelease and
odlsign-bulk.

Issue: RELENG-20
Change-Id: I35d4da0e2978f06640ddc11a93c9f86676a2a7e7
Signed-off-by: Thanh Ha <thanh.ha@linuxfoundation.org>
lftools/cli/__init__.py
lftools/cli/sign.py [new file with mode: 0644]
setup.py
shell/sign [new file with mode: 0755]

index 29cc48d..e8a5bfd 100644 (file)
@@ -16,6 +16,7 @@ import click
 from lftools.cli.deploy import deploy
 from lftools.cli.jenkins import jenkins_cli
 from lftools.cli.nexus import nexus
+from lftools.cli.sign import sign
 from lftools.cli.version import version
 from lftools.openstack.cmd import openstack
 
@@ -29,9 +30,10 @@ def cli(ctx):
 
 
 cli.add_command(deploy)
-cli.add_command(openstack)
-cli.add_command(nexus)
 cli.add_command(jenkins_cli, name='jenkins')
+cli.add_command(nexus)
+cli.add_command(openstack)
+cli.add_command(sign)
 cli.add_command(version)
 
 
diff --git a/lftools/cli/sign.py b/lftools/cli/sign.py
new file mode 100644 (file)
index 0000000..740f82d
--- /dev/null
@@ -0,0 +1,79 @@
+# @License EPL-1.0 <http://spdx.org/licenses/EPL-1.0>
+##############################################################################
+# Copyright (c) 2017 The Linux Foundation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+##############################################################################
+"""Script to GPG sign files."""
+
+__author__ = 'Thanh Ha'
+
+
+import subprocess
+import sys
+import tempfile
+
+import click
+
+
+@click.group()
+@click.pass_context
+def sign(ctx):
+    """GPG sign files."""
+    pass
+
+
+@click.command(name='dir')
+@click.argument('directory')
+@click.pass_context
+def directory(ctx, directory):
+    """GPG signs all of the files in a directory."""
+    status = subprocess.call(['sign', 'dir', directory])
+    sys.exit(status)
+
+
+@click.command(name='nexus')
+@click.argument('nexus-repo-url')
+@click.pass_context
+def nexus(ctx, nexus_repo_url):
+    """Fetch and GPG sign a Nexus repo."""
+    status = subprocess.call(['sign', 'nexus', nexus_repo_url])
+    sys.exit(status)
+
+
+@click.command(name='deploy-nexus')
+@click.argument('nexus-url', envvar='NEXUS_URL')
+@click.argument('nexus-repo', envvar='NEXUS_REPO')
+@click.argument('staging-profile-id', envvar='STAGING_PROFILE_ID')
+@click.option(
+    '-r', '--root-domain', type=str, default='org',
+    help='Root download path of staging repo. (default org)')
+@click.pass_context
+def deploy_nexus(ctx, nexus_url, nexus_repo, staging_profile_id, root_domain):
+    """Sign artifacts from a Nexus repo then upload to a staging repo.
+
+    This is a porcelain command that ties the lftools sign and deploy tools
+    together for easier use. It calls the sign-nexus command and then the
+    deploy-nexus-stage command to create a signed staging repository in Nexus.
+    """
+    # wget does not appear to like to fully clone the root of a staging repo
+    # as a workaround we have to at least give it 1 directory deep. Since most
+    # LF projects are 'org' domain default is org but can be override with the
+    # -r option.
+    nexus_repo_url = "{}/content/repositories/{}/{}".format(nexus_url, nexus_repo, root_domain)
+    sign_dir = tempfile.mkdtemp(prefix='gpg-signatures.')
+
+    status = subprocess.call(['sign', 'nexus', '-d', sign_dir, nexus_repo_url])
+    if status:
+        sys.exit(status)
+
+    status = subprocess.call(['deploy', 'nexus-stage', nexus_url, staging_profile_id, sign_dir])
+    sys.exit(status)
+
+
+sign.add_command(directory)
+sign.add_command(nexus)
+sign.add_command(deploy_nexus)
index 2778c41..bf6bd58 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -53,6 +53,7 @@ setup(
     ''',
     scripts=[
         'shell/deploy',
+        'shell/sign',
         'shell/version',
     ],
 )
diff --git a/shell/sign b/shell/sign
new file mode 100755 (executable)
index 0000000..3ee44e1
--- /dev/null
@@ -0,0 +1,187 @@
+#!/bin/bash
+
+# @License EPL-1.0 <http://spdx.org/licenses/EPL-1.0>
+##############################################################################
+# Copyright (c) 2017 The Linux Foundation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+##############################################################################
+
+# Signs artifacts with gpg
+
+
+sign() {
+    # Entry point for the deploy command.
+    subcommand=$1; shift
+
+    case "$subcommand" in
+        dir )
+            echo "Signing Directory..."
+            sign_dir "$@"
+            exit 0
+            ;;
+        nexus )
+            echo "Signing Nexus repo..."
+            sign_nexus "$@"
+            exit 0
+            ;;
+        * )
+            echo "Invalid command: $subcommand" 1>&2
+            exit 1
+            ;;
+    esac
+}
+
+
+sign_dir() {
+    # GPG signs all of the files in a directory
+    #
+    # Parameters:
+    #
+    #     <directory>: The directory to find and sign artifacts.
+    local dir="$1"
+
+    if [ -z "$1" ]; then
+        echo "Missing required arguments."
+        echo "Usage: $0 sign-dir <directory>"
+        exit 1
+    fi
+
+    test_gpg_key
+
+    set -e  # Fail immediately if any if signing fails
+    cd "$dir"
+    files_to_sign=($(find . -type f ! -name "*.asc" \
+                                    ! -name "*.md5" \
+                                    ! -name "*.sha1" \
+                                    ! -name "_maven.repositories" \
+                                    ! -name "_remote.repositories" \
+                                    ! -name "*.lastUpdated" \
+                                    ! -name "maven-metadata-local.xml" \
+                                    ! -name "maven-metadata.xml"))
+
+    for f in "${files_to_sign[@]}"
+    do
+        echo "Signing $f"
+        gpg --batch -abq "$f"
+    done
+    set +e
+}
+
+
+sign_nexus_usage () {
+    echo "Usage: $0 nexus <nexus_repo_url>"
+    echo ""
+    echo "    nexus_repo_url:   The URL to the Nexus repository to be signed."
+    echo "        Ex: https://nexus.opendaylight.org/content/repositories/autorelease-1888"
+    echo ""
+    echo "Options:"
+    echo "    -d /path/to/store/signatures"
+}
+
+
+sign_nexus() {
+    # Fetch and GPG sign a Nexus repo
+    #
+    # The resultant output of this command is stored in
+    # /tmp/gpg-signatures.XXXXXXXXXX unless the -d parameter is passed to
+    # override.
+    while getopts d: o; do
+      case "$o" in
+        h)
+            sign_nexus_usage
+            exit 0
+            ;;
+
+        d)
+            local sign_dir="$OPTARG"
+            mkdir -p "$sign_dir"
+            ;;
+
+        [?])
+            sign_nexus_usage
+            exit 1
+            ;;
+      esac
+    done
+    shift $((OPTIND-1))
+
+    local nexus_repo_url="$1"
+
+    if [ -z "$1" ]; then
+        echo "Missing required arguments."
+        sign_nexus_usage
+        exit 1
+    fi
+
+    # Ensure that the repo_url has a trailing slash as wget needs it to work
+    case "$nexus_repo_url" in
+        */)
+            ;;
+        *)
+            nexus_repo_url="$nexus_repo_url/"
+            ;;
+    esac
+
+    sign_dir=${sign_dir:-$(mktemp -d /tmp/gpg-signatures.XXXXXXXXXX)}
+    cd "$sign_dir" || exit 1
+
+    echo "Fetching artifacts from $nexus_repo_url to $sign_dir..."
+    wget --recursive --execute robots=off --no-parent --quiet \
+        --no-host-directories --cut-dirs=3 \
+        "$nexus_repo_url"
+
+    echo "Removing files that do not need to be cloned..."
+    remove_files=($(find . -type f -name "index.html" \
+        -o -name "*.asc" \
+        -o -name "*.md5" \
+        -o -name "*.sha1" \
+        -o -name "_maven.repositories*" \
+        -o -name "_remote.repositories*" \
+        -o -name "maven-metadata-local.xml*" \
+        -o -name "maven-metadata.xml*" \
+        -o -name "archetype-catalog.xml"))
+    for f in "${remove_files[@]}"; do
+        rm "$f"
+    done
+
+    echo "Signing artifacts..."
+    sign_dir "$sign_dir"
+
+    echo "Removing non-signature files..."
+    remove_files=($(find . -type f -not -name '*.asc'))
+    for f in "${remove_files[@]}"; do
+        rm "$f"
+    done
+
+    echo "Printing signatures generated"
+    find . -type f
+
+    echo "Signing complete. Signatures can be found in $sign_dir"
+}
+
+
+test_gpg_key() {
+  # Test that our GPG key works by signing a test file.
+
+  local test_file
+  test_file=$(mktemp)
+  echo "Test Signature" > "$test_file"
+  gpg --batch -abq "$test_file"
+  if ! gpg --verify "$test_file".asc "$test_file" > /dev/null 2>&1; then
+      echo "ERROR: Failed to validate signature."
+      rm "$test_file" "$test_file".asc  # Cleanup before we exit
+      exit 1
+  fi
+  rm "$test_file" "$test_file".asc  # Cleanup tmp files
+}
+
+
+# Only run the script if it is being called directly and not sourced.
+if [[ "${BASH_SOURCE[0]}" == "${0}" ]]
+then
+    sign "$@"
+fi