From: Thanh Ha Date: Wed, 28 Jun 2017 17:51:41 +0000 (-0400) Subject: Copy sign scripts over to lftools X-Git-Tag: v0.6.0~10 X-Git-Url: https://gerrit.linuxfoundation.org/infra/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F43%2F5343%2F4;p=releng%2Flftools.git Copy sign scripts over to lftools 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 --- diff --git a/lftools/cli/__init__.py b/lftools/cli/__init__.py index 29cc48d0..e8a5bfde 100644 --- a/lftools/cli/__init__.py +++ b/lftools/cli/__init__.py @@ -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 index 00000000..740f82d9 --- /dev/null +++ b/lftools/cli/sign.py @@ -0,0 +1,79 @@ +# @License 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) diff --git a/setup.py b/setup.py index 2778c419..bf6bd581 100644 --- 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 index 00000000..3ee44e12 --- /dev/null +++ b/shell/sign @@ -0,0 +1,187 @@ +#!/bin/bash + +# @License 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: + # + # : The directory to find and sign artifacts. + local dir="$1" + + if [ -z "$1" ]; then + echo "Missing required arguments." + echo "Usage: $0 sign-dir " + 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 " + 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