From 3d48b4847a7803bfefa709fbd32f8b135cb1c985 Mon Sep 17 00:00:00 2001 From: Huy Huynh Date: Wed, 5 Jun 2019 14:04:16 -0700 Subject: [PATCH] Verify Application version against publishedVersion (#362) --- .../create_manifests.sh | 6 +- .../deployer_util/deploy_with_tests.sh | 9 ++- .../deployer_util/overlay_test_schema.py | 67 +++++++++++-------- .../deployer_util/patch_assembly_phase.sh | 15 +++++ .../deployer_util/print_published_version.py | 8 +++ .../templates/application.yaml | 1 + 6 files changed, 69 insertions(+), 37 deletions(-) diff --git a/marketplace/deployer_envsubst_base/create_manifests.sh b/marketplace/deployer_envsubst_base/create_manifests.sh index cf9fd3da..faeddeca 100755 --- a/marketplace/deployer_envsubst_base/create_manifests.sh +++ b/marketplace/deployer_envsubst_base/create_manifests.sh @@ -43,10 +43,10 @@ mkdir "$manifest_dir" # Overwrite the templates using the test templates if [[ "$mode" = "test" ]]; then - if [[ -e "/data-test" ]]; then - cp -RT "/data-test" "/data" + if [[ -e "/data-test/manifest" ]]; then + cp -RT "/data-test/manifest" "/data/manifest" else - echo "$LOG_SMOKE_TEST ERROR Missing /data-test." + echo "$LOG_SMOKE_TEST INFO No overriding manifests found at /data-test/manifest." fi fi diff --git a/marketplace/deployer_util/deploy_with_tests.sh b/marketplace/deployer_util/deploy_with_tests.sh index dc3b7c44..63aecdae 100755 --- a/marketplace/deployer_util/deploy_with_tests.sh +++ b/marketplace/deployer_util/deploy_with_tests.sh @@ -41,11 +41,10 @@ trap "handle_failure" EXIT LOG_SMOKE_TEST="SMOKE_TEST" test_schema="/data-test/schema.yaml" -if [[ -e "$test_schema" ]]; then - overlay_test_schema.py \ - --orig "$test_schema" \ - --dest "/data/schema.yaml" -fi +overlay_test_schema.py \ + --test_schema "$test_schema" \ + --original_schema "/data/schema.yaml" \ + --output "/data/schema.yaml" NAME="$(/bin/print_config.py \ diff --git a/marketplace/deployer_util/overlay_test_schema.py b/marketplace/deployer_util/overlay_test_schema.py index c6ff982a..9ce28a0e 100755 --- a/marketplace/deployer_util/overlay_test_schema.py +++ b/marketplace/deployer_util/overlay_test_schema.py @@ -16,46 +16,55 @@ import yaml import os.path -import log_util as log - from argparse import ArgumentParser + +import log_util as log from constants import LOG_SMOKE_TEST from dict_util import deep_get from yaml_util import load_yaml +_PROG_HELP = """ +Overlay properties declared in the test schema over the original +schema, dumping the content into a specified target. +""" + def main(): - parser = ArgumentParser() - parser.add_argument('--orig', help='Original schema file') - parser.add_argument('--dest', help='Destination schema file') + parser = ArgumentParser(description=_PROG_HELP) + parser.add_argument('--test_schema', help='Test schema file', required=True) + parser.add_argument( + '--original_schema', help='Original schema file', required=True) + parser.add_argument( + '--output', + action='append', + default=[], + help='Location(s) of the file(s) to output the overlayed schema') args = parser.parse_args() - if not os.path.isfile(args.orig): - print("No test schema.yaml defined") - return - - orig = load_yaml(args.orig) - - if 'properties' not in orig: - print("No properties found in {}. Ignoring test schema merge.".format( - args.orig)) - return - - dest = load_yaml(args.dest) - if 'properties' in dest: - for prop in orig['properties']: - if (deep_get(orig, 'properties', prop, 'x-google-marketplace', 'type') != - deep_get(dest, 'properties', prop, 'x-google-marketplace', 'type')): - log.warn( - "{} Changing x-google-marketplace type is not allowed. Property: {}", - LOG_SMOKE_TEST, prop) - continue - dest['properties'][prop] = orig['properties'][prop] + if os.path.isfile(args.test_schema): + test_schema = load_yaml(args.test_schema) else: - dest['properties'] = orig['properties'] + log.info( + '{} Test schema file {} does not exist. ' + 'Using the original schema.', LOG_SMOKE_TEST, args.test_schema) + test_schema = {} + + output_schema = load_yaml(args.original_schema) + output_schema['properties'] = output_schema.get('properties', {}) + for prop in test_schema.get('properties', {}): + test_type = deep_get(test_schema, 'properties', prop, + 'x-google-marketplace', 'type') + output_type = deep_get(output_schema, 'properties', prop, + 'x-google-marketplace', 'type') + if (test_type != output_type): + raise Exception( + 'Changing x-google-marketplace type is not allowed. Property: {}', + prop) + output_schema['properties'][prop] = test_schema['properties'][prop] - with open(args.dest, 'w') as f: - yaml.dump(dest, f) + for output in args.output: + with open(output, 'w') as f: + yaml.dump(output_schema, f) if __name__ == "__main__": diff --git a/marketplace/deployer_util/patch_assembly_phase.sh b/marketplace/deployer_util/patch_assembly_phase.sh index 3a437e6c..181ab4bd 100755 --- a/marketplace/deployer_util/patch_assembly_phase.sh +++ b/marketplace/deployer_util/patch_assembly_phase.sh @@ -43,6 +43,21 @@ fi echo "Marking deployment of application \"$NAME\" as \"$status\"." +if [[ "$status" == "Success" ]]; then + published_version="$(print_published_version.py --empty_if_not_supported)" + if ! [[ -z "$published_version" ]]; then + # Ensure that the application resource has a version + # matching the declared published version. + app_version="$(kubectl get "applications.app.k8s.io/$NAME" --output=json \ + | jq -r .spec.descriptor.version)" + if [[ "$app_version" != "$published_version" ]]; then + echo "Application's version '$app_version' does not match the declared" \ + "publishedVersion '$published_version' in schema.yaml." + exit 1 + fi + fi +fi + # --output=json is used to force kubectl to succeed even if the patch command # makes not change to the resource. Otherwise, this command exits 1. kubectl patch "applications.app.k8s.io/$NAME" \ diff --git a/marketplace/deployer_util/print_published_version.py b/marketplace/deployer_util/print_published_version.py index 176f87ee..c85d1ca6 100755 --- a/marketplace/deployer_util/print_published_version.py +++ b/marketplace/deployer_util/print_published_version.py @@ -29,12 +29,20 @@ def main(): parser = ArgumentParser(description=_PROG_HELP) schema_values_common.add_to_argument_parser(parser) + parser.add_argument( + '--empty_if_not_supported', + action='store_true', + help='For a v1 schema, do not fail but output empty string instead') args = parser.parse_args() schema = schema_values_common.load_schema(args) schema.validate() if (schema.x_google_marketplace is None or not schema.x_google_marketplace.is_v2()): + if args.empty_if_not_supported: + sys.stderr.write('schema.yaml is not in v2 version. Skipping') + sys.stderr.flush() + return raise Exception('schema.yaml must be in v2 version') sys.stdout.write(schema.x_google_marketplace.published_version) diff --git a/tests/marketplace/deployer_helm_tiller_base/onbuild/standard_v2/chart/minimal-helm-chart/templates/application.yaml b/tests/marketplace/deployer_helm_tiller_base/onbuild/standard_v2/chart/minimal-helm-chart/templates/application.yaml index 8cd0f0b3..7421e7ed 100644 --- a/tests/marketplace/deployer_helm_tiller_base/onbuild/standard_v2/chart/minimal-helm-chart/templates/application.yaml +++ b/tests/marketplace/deployer_helm_tiller_base/onbuild/standard_v2/chart/minimal-helm-chart/templates/application.yaml @@ -9,6 +9,7 @@ spec: descriptor: description: |- A minimal example application. + version: 0.1.1 componentKinds: - group: batch/v1 kind: Job