From 858e8fc37c321876ff324967cf5ed8ef6a955ca3 Mon Sep 17 00:00:00 2001 From: Tamar Ben-Shachar Date: Tue, 1 Dec 2015 15:33:43 -0800 Subject: [PATCH] dcos package install with universe v2 --- cli/dcoscli/package/main.py | 3 +- cli/tests/data/dcos.toml | 2 +- .../package/json/test_describe_app_cli.json | 4 +- .../json/test_describe_app_options.json | 4 +- .../test_describe_marathon_app_render.json | 4 +- cli/tests/data/ssl/ssl.toml | 2 +- cli/tests/integrations/common.py | 7 ++ cli/tests/integrations/test_config.py | 32 ++++----- .../integrations/test_marathon_groups.py | 27 +++---- cli/tests/integrations/test_package.py | 18 ++--- dcos/http.py | 2 +- dcos/package.py | 71 +++++++++++++++++-- tests/test_package.py | 9 +++ 13 files changed, 127 insertions(+), 58 deletions(-) diff --git a/cli/dcoscli/package/main.py b/cli/dcoscli/package/main.py index fca3df0..002315e 100644 --- a/cli/dcoscli/package/main.py +++ b/cli/dcoscli/package/main.py @@ -364,7 +364,8 @@ def _install(package_name, package_version, options_path, app_id, cli, app, revision_map = pkg.package_revisions_map() package_version = revision_map.get(pkg_revision) - if app and pkg.has_marathon_definition(pkg_revision): + if app and (pkg.has_marathon_definition(pkg_revision) or + pkg.has_marathon_mustache_definition(pkg_revision)): # Install in Marathon msg = 'Installing Marathon app for package [{}] version [{}]'.format( pkg.name(), package_version) diff --git a/cli/tests/data/dcos.toml b/cli/tests/data/dcos.toml index 2187319..27a5a85 100644 --- a/cli/tests/data/dcos.toml +++ b/cli/tests/data/dcos.toml @@ -5,5 +5,5 @@ timeout = 5 ssl_verify = "false" dcos_url = "http://dcos.snakeoil.mesosphere.com" [package] -sources = [ "https://github.com/mesosphere/universe/archive/cli-test-2.zip",] +sources = [ "https://github.com/mesosphere/universe/archive/cli-test-3.zip",] cache = "tmp/cache" diff --git a/cli/tests/data/package/json/test_describe_app_cli.json b/cli/tests/data/package/json/test_describe_app_cli.json index 065e9c0..c1067d6 100644 --- a/cli/tests/data/package/json/test_describe_app_cli.json +++ b/cli/tests/data/package/json/test_describe_app_cli.json @@ -14,8 +14,8 @@ 0 ], "uris": [ - "https://downloads.mesosphere.io/cassandra-mesos/artifacts/0.2.0-1/cassandra-mesos-0.2.0-1.tar.gz", - "https://downloads.mesosphere.io/java/jre-7u76-linux-x64.tar.gz" + "{{resource.assets.uris.cassandra-mesos-tar-gz}}", + "{{resource.assets.uris.jre-7u76-linux-x64}}" ], "healthChecks": [ { diff --git a/cli/tests/data/package/json/test_describe_app_options.json b/cli/tests/data/package/json/test_describe_app_options.json index c856890..e2d7477 100644 --- a/cli/tests/data/package/json/test_describe_app_options.json +++ b/cli/tests/data/package/json/test_describe_app_options.json @@ -35,9 +35,9 @@ "DCOS_PACKAGE_IS_FRAMEWORK": "true", "DCOS_PACKAGE_METADATA": "eyJkZXNjcmlwdGlvbiI6ICJBIGNsdXN0ZXItd2lkZSBpbml0IGFuZCBjb250cm9sIHN5c3RlbSBmb3Igc2VydmljZXMgaW4gY2dyb3VwcyBvciBEb2NrZXIgY29udGFpbmVycy4iLCAiZnJhbWV3b3JrIjogdHJ1ZSwgImltYWdlcyI6IHsiaWNvbi1sYXJnZSI6ICJodHRwczovL2Rvd25sb2Fkcy5tZXNvc3BoZXJlLmlvL21hcmF0aG9uL2Fzc2V0cy9pY29uLXNlcnZpY2UtbWFyYXRob24tbGFyZ2UucG5nIiwgImljb24tbWVkaXVtIjogImh0dHBzOi8vZG93bmxvYWRzLm1lc29zcGhlcmUuaW8vbWFyYXRob24vYXNzZXRzL2ljb24tc2VydmljZS1tYXJhdGhvbi1tZWRpdW0ucG5nIiwgImljb24tc21hbGwiOiAiaHR0cHM6Ly9kb3dubG9hZHMubWVzb3NwaGVyZS5pby9tYXJhdGhvbi9hc3NldHMvaWNvbi1zZXJ2aWNlLW1hcmF0aG9uLXNtYWxsLnBuZyJ9LCAibGljZW5zZXMiOiBbeyJuYW1lIjogIkFwYWNoZSBMaWNlbnNlIFZlcnNpb24gMi4wIiwgInVybCI6ICJodHRwczovL2dpdGh1Yi5jb20vbWVzb3NwaGVyZS9tYXJhdGhvbi9ibG9iL21hc3Rlci9MSUNFTlNFIn1dLCAibWFpbnRhaW5lciI6ICJzdXBwb3J0QG1lc29zcGhlcmUuaW8iLCAibmFtZSI6ICJtYXJhdGhvbiIsICJwb3N0SW5zdGFsbE5vdGVzIjogIk1hcmF0aG9uIERDT1MgU2VydmljZSBoYXMgYmVlbiBzdWNjZXNzZnVsbHkgaW5zdGFsbGVkIVxuXG5cdERvY3VtZW50YXRpb246IGh0dHBzOi8vbWVzb3NwaGVyZS5naXRodWIuaW8vbWFyYXRob25cblx0SXNzdWVzOiBodHRwczovZ2l0aHViLmNvbS9tZXNvc3BoZXJlL21hcmF0aG9uL2lzc3Vlc1xuIiwgInBvc3RVbmluc3RhbGxOb3RlcyI6ICJUaGUgTWFyYXRob24gRENPUyBTZXJ2aWNlIGhhcyBiZWVuIHVuaW5zdGFsbGVkIGFuZCB3aWxsIG5vIGxvbmdlciBydW4uXG5QbGVhc2UgZm9sbG93IHRoZSBpbnN0cnVjdGlvbnMgYXQgaHR0cDovL2RvY3MubWVzb3NwaGVyZS5jb20vc2VydmljZXMvbWFyYXRob24vI3VuaW5zdGFsbCB0byBjbGVhbiB1cCBhbnkgcGVyc2lzdGVkIHN0YXRlIiwgInByZUluc3RhbGxOb3RlcyI6ICJXZSByZWNvbW1lbmQgYSBtaW5pbXVtIG9mIG9uZSBub2RlIHdpdGggYXQgbGVhc3QgMiBDUFUncyBhbmQgMUdCIG9mIFJBTSBhdmFpbGFibGUgZm9yIHRoZSBNYXJhdGhvbiBTZXJ2aWNlLiIsICJzY20iOiAiaHR0cHM6Ly9naXRodWIuY29tL21lc29zcGhlcmUvbWFyYXRob24uZ2l0IiwgInRhZ3MiOiBbImluaXQiLCAibG9uZy1ydW5uaW5nIl0sICJ2ZXJzaW9uIjogIjAuMTEuMSJ9", "DCOS_PACKAGE_NAME": "marathon", - "DCOS_PACKAGE_REGISTRY_VERSION": "1.0.0-rc1", + "DCOS_PACKAGE_REGISTRY_VERSION": "2.0.0-rc1", "DCOS_PACKAGE_RELEASE": "6", - "DCOS_PACKAGE_SOURCE": "https://github.com/mesosphere/universe/archive/cli-test-2.zip", + "DCOS_PACKAGE_SOURCE": "https://github.com/mesosphere/universe/archive/cli-test-3.zip", "DCOS_PACKAGE_VERSION": "0.11.1" }, "mem": 1024.0, diff --git a/cli/tests/data/package/json/test_describe_marathon_app_render.json b/cli/tests/data/package/json/test_describe_marathon_app_render.json index 9146f7a..e8ead20 100644 --- a/cli/tests/data/package/json/test_describe_marathon_app_render.json +++ b/cli/tests/data/package/json/test_describe_marathon_app_render.json @@ -35,9 +35,9 @@ "DCOS_PACKAGE_IS_FRAMEWORK": "true", "DCOS_PACKAGE_METADATA": "eyJkZXNjcmlwdGlvbiI6ICJBIGNsdXN0ZXItd2lkZSBpbml0IGFuZCBjb250cm9sIHN5c3RlbSBmb3Igc2VydmljZXMgaW4gY2dyb3VwcyBvciBEb2NrZXIgY29udGFpbmVycy4iLCAiZnJhbWV3b3JrIjogdHJ1ZSwgImltYWdlcyI6IHsiaWNvbi1sYXJnZSI6ICJodHRwczovL2Rvd25sb2Fkcy5tZXNvc3BoZXJlLmlvL21hcmF0aG9uL2Fzc2V0cy9pY29uLXNlcnZpY2UtbWFyYXRob24tbGFyZ2UucG5nIiwgImljb24tbWVkaXVtIjogImh0dHBzOi8vZG93bmxvYWRzLm1lc29zcGhlcmUuaW8vbWFyYXRob24vYXNzZXRzL2ljb24tc2VydmljZS1tYXJhdGhvbi1tZWRpdW0ucG5nIiwgImljb24tc21hbGwiOiAiaHR0cHM6Ly9kb3dubG9hZHMubWVzb3NwaGVyZS5pby9tYXJhdGhvbi9hc3NldHMvaWNvbi1zZXJ2aWNlLW1hcmF0aG9uLXNtYWxsLnBuZyJ9LCAibGljZW5zZXMiOiBbeyJuYW1lIjogIkFwYWNoZSBMaWNlbnNlIFZlcnNpb24gMi4wIiwgInVybCI6ICJodHRwczovL2dpdGh1Yi5jb20vbWVzb3NwaGVyZS9tYXJhdGhvbi9ibG9iL21hc3Rlci9MSUNFTlNFIn1dLCAibWFpbnRhaW5lciI6ICJzdXBwb3J0QG1lc29zcGhlcmUuaW8iLCAibmFtZSI6ICJtYXJhdGhvbiIsICJwb3N0SW5zdGFsbE5vdGVzIjogIk1hcmF0aG9uIERDT1MgU2VydmljZSBoYXMgYmVlbiBzdWNjZXNzZnVsbHkgaW5zdGFsbGVkIVxuXG5cdERvY3VtZW50YXRpb246IGh0dHBzOi8vbWVzb3NwaGVyZS5naXRodWIuaW8vbWFyYXRob25cblx0SXNzdWVzOiBodHRwczovZ2l0aHViLmNvbS9tZXNvc3BoZXJlL21hcmF0aG9uL2lzc3Vlc1xuIiwgInBvc3RVbmluc3RhbGxOb3RlcyI6ICJUaGUgTWFyYXRob24gRENPUyBTZXJ2aWNlIGhhcyBiZWVuIHVuaW5zdGFsbGVkIGFuZCB3aWxsIG5vIGxvbmdlciBydW4uXG5QbGVhc2UgZm9sbG93IHRoZSBpbnN0cnVjdGlvbnMgYXQgaHR0cDovL2RvY3MubWVzb3NwaGVyZS5jb20vc2VydmljZXMvbWFyYXRob24vI3VuaW5zdGFsbCB0byBjbGVhbiB1cCBhbnkgcGVyc2lzdGVkIHN0YXRlIiwgInByZUluc3RhbGxOb3RlcyI6ICJXZSByZWNvbW1lbmQgYSBtaW5pbXVtIG9mIG9uZSBub2RlIHdpdGggYXQgbGVhc3QgMiBDUFUncyBhbmQgMUdCIG9mIFJBTSBhdmFpbGFibGUgZm9yIHRoZSBNYXJhdGhvbiBTZXJ2aWNlLiIsICJzY20iOiAiaHR0cHM6Ly9naXRodWIuY29tL21lc29zcGhlcmUvbWFyYXRob24uZ2l0IiwgInRhZ3MiOiBbImluaXQiLCAibG9uZy1ydW5uaW5nIl0sICJ2ZXJzaW9uIjogIjAuMTEuMSJ9", "DCOS_PACKAGE_NAME": "marathon", - "DCOS_PACKAGE_REGISTRY_VERSION": "1.0.0-rc1", + "DCOS_PACKAGE_REGISTRY_VERSION": "2.0.0-rc1", "DCOS_PACKAGE_RELEASE": "6", - "DCOS_PACKAGE_SOURCE": "https://github.com/mesosphere/universe/archive/cli-test-2.zip", + "DCOS_PACKAGE_SOURCE": "https://github.com/mesosphere/universe/archive/cli-test-3.zip", "DCOS_PACKAGE_VERSION": "0.11.1" }, "mem": 1024.0, diff --git a/cli/tests/data/ssl/ssl.toml b/cli/tests/data/ssl/ssl.toml index 625cda3..f87c58a 100644 --- a/cli/tests/data/ssl/ssl.toml +++ b/cli/tests/data/ssl/ssl.toml @@ -1,6 +1,6 @@ [package] cache = "tmp/cache" -sources = [ "https://github.com/mesosphere/universe/archive/cli-test-2.zip",] +sources = [ "https://github.com/mesosphere/universe/archive/cli-test-3.zip",] [core] timeout = 5 dcos_url = "https://dcos.snakeoil.mesosphere.com" diff --git a/cli/tests/integrations/common.py b/cli/tests/integrations/common.py index eea9c87..7056e6f 100644 --- a/cli/tests/integrations/common.py +++ b/cli/tests/integrations/common.py @@ -234,6 +234,13 @@ def add_app(app_path, wait=True): watch_all_deployments() +def remove_group(group_id): + assert_command(['dcos', 'marathon', 'group', 'remove', group_id]) + + # Let's make sure that we don't return until the deployment has finished + watch_all_deployments() + + def remove_app(app_id): """ Remove an app diff --git a/cli/tests/integrations/test_config.py b/cli/tests/integrations/test_config.py index c6e943b..d616f59 100644 --- a/cli/tests/integrations/test_config.py +++ b/cli/tests/integrations/test_config.py @@ -59,7 +59,7 @@ core.ssl_verify=false core.timeout=5 package.cache=tmp/cache package.sources=['https://github.com/mesosphere/universe/archive/\ -cli-test-2.zip'] +cli-test-3.zip'] """ assert_command(['dcos', 'config', 'show'], stdout=stdout, @@ -157,11 +157,11 @@ def test_append_empty_list(env): config_set('package.sources', '[]', env) _append_value( 'package.sources', - 'https://github.com/mesosphere/universe/archive/cli-test-2.zip', + 'https://github.com/mesosphere/universe/archive/cli-test-3.zip', env) _get_value( 'package.sources', - ['https://github.com/mesosphere/universe/archive/cli-test-2.zip'], + ['https://github.com/mesosphere/universe/archive/cli-test-3.zip'], env) @@ -169,11 +169,11 @@ def test_prepend_empty_list(env): config_set('package.sources', '[]', env) _prepend_value( 'package.sources', - 'https://github.com/mesosphere/universe/archive/cli-test-2.zip', + 'https://github.com/mesosphere/universe/archive/cli-test-3.zip', env) _get_value( 'package.sources', - ['https://github.com/mesosphere/universe/archive/cli-test-2.zip'], + ['https://github.com/mesosphere/universe/archive/cli-test-3.zip'], env) @@ -184,7 +184,7 @@ def test_append_list(env): env) _get_value( 'package.sources', - ['https://github.com/mesosphere/universe/archive/cli-test-2.zip', + ['https://github.com/mesosphere/universe/archive/cli-test-3.zip', 'https://github.com/mesosphere/universe/archive/version-2.x.zip'], env) config_unset('package.sources', '1', env) @@ -198,7 +198,7 @@ def test_prepend_list(env): _get_value( 'package.sources', ['https://github.com/mesosphere/universe/archive/version-2.x.zip', - 'https://github.com/mesosphere/universe/archive/cli-test-2.zip'], + 'https://github.com/mesosphere/universe/archive/cli-test-3.zip'], env) config_unset('package.sources', '0', env) @@ -249,7 +249,7 @@ def test_unset_output(env): def test_unset_index_output(env): stdout = ( b"[package.sources]: removed element " - b"'https://github.com/mesosphere/universe/archive/cli-test-2.zip' " + b"'https://github.com/mesosphere/universe/archive/cli-test-3.zip' " b"at index '0'\n" ) @@ -259,14 +259,14 @@ def test_unset_index_output(env): _prepend_value( 'package.sources', - 'https://github.com/mesosphere/universe/archive/cli-test-2.zip', + 'https://github.com/mesosphere/universe/archive/cli-test-3.zip', env) def test_set_whole_list(env): config_set( 'package.sources', - '["https://github.com/mesosphere/universe/archive/cli-test-2.zip"]', + '["https://github.com/mesosphere/universe/archive/cli-test-3.zip"]', env) @@ -293,7 +293,7 @@ def test_unset_list_index(env): env) _prepend_value( 'package.sources', - 'https://github.com/mesosphere/universe/archive/cli-test-2.zip', + 'https://github.com/mesosphere/universe/archive/cli-test-3.zip', env) @@ -337,7 +337,7 @@ def test_validate(env): def test_validation_error(env): - source = ["https://github.com/mesosphere/universe/archive/cli-test-2.zip"] + source = ["https://github.com/mesosphere/universe/archive/cli-test-3.zip"] config_unset('package.sources', None, env) stdout = b"Error: missing required property 'sources'.\n" @@ -401,12 +401,12 @@ def test_url_validation(env): def test_append_url_validation(env): default_value = ('["https://github.com/mesosphere/universe/archive/' - 'cli-test-2.zip"]') + 'cli-test-3.zip"]') config_set('package.sources', '[]', env) _append_value( 'package.sources', - 'https://github.com/mesosphere/universe/archive/cli-test-2.zip', + 'https://github.com/mesosphere/universe/archive/cli-test-3.zip', env) _append_value( 'package.sources', @@ -429,12 +429,12 @@ def test_append_url_validation(env): def test_prepend_url_validation(env): default_value = ('["https://github.com/mesosphere/universe/archive/' - 'cli-test-2.zip"]') + 'cli-test-3.zip"]') config_set('package.sources', '[]', env) _prepend_value( 'package.sources', - 'https://github.com/mesosphere/universe/archive/cli-test-2.zip', + 'https://github.com/mesosphere/universe/archive/cli-test-3.zip', env) _prepend_value( 'package.sources', diff --git a/cli/tests/integrations/test_marathon_groups.py b/cli/tests/integrations/test_marathon_groups.py index b529dbb..f5aa805 100644 --- a/cli/tests/integrations/test_marathon_groups.py +++ b/cli/tests/integrations/test_marathon_groups.py @@ -1,15 +1,15 @@ import contextlib import json -from .common import (assert_command, assert_lines, exec_command, show_app, - watch_all_deployments) +from .common import (assert_command, assert_lines, exec_command, remove_group, + show_app, watch_all_deployments) GOOD_GROUP = 'tests/data/marathon/groups/good.json' def test_deploy_group(): _deploy_group(GOOD_GROUP) - _remove_group('test-group') + remove_group('test-group') def test_group_list_table(): @@ -19,12 +19,12 @@ def test_group_list_table(): def test_validate_complicated_group_and_app(): _deploy_group('tests/data/marathon/groups/complicated.json') - _remove_group('test-group') + remove_group('test-group') def test_optional_deploy_group(): _deploy_group(GOOD_GROUP, False) - _remove_group('test-group') + remove_group('test-group') def test_add_existing_group(): @@ -108,7 +108,7 @@ def test_scale_group(): res = json.loads(stdout.decode('utf-8')) assert res['groups'][0]['apps'][0]['instances'] == 2 - _remove_group('scale-group') + remove_group('scale-group') def test_scale_group_not_exist(): @@ -122,7 +122,7 @@ def test_scale_group_not_exist(): res = json.loads(stdout.decode('utf-8')) assert len(res['apps']) == 0 - _remove_group('scale-group') + remove_group('scale-group') def test_scale_group_when_scale_factor_negative(): @@ -132,7 +132,7 @@ def test_scale_group_when_scale_factor_negative(): assert b'Command not recognized' in stdout assert returncode == 1 watch_all_deployments() - _remove_group('scale-group') + remove_group('scale-group') def test_scale_group_when_scale_factor_not_float(): @@ -142,14 +142,7 @@ def test_scale_group_when_scale_factor_not_float(): assert stderr == b'Error parsing string as float\n' assert returncode == 1 watch_all_deployments() - _remove_group('scale-group') - - -def _remove_group(group_id): - assert_command(['dcos', 'marathon', 'group', 'remove', group_id]) - - # Let's make sure that we don't return until the deployment has finished - watch_all_deployments() + remove_group('scale-group') def _deploy_group(file_path, stdin=True): @@ -212,4 +205,4 @@ def _group(path, group_id): try: yield finally: - _remove_group(group_id) + remove_group(group_id) diff --git a/cli/tests/integrations/test_package.py b/cli/tests/integrations/test_package.py index 71c0790..5aee5e9 100644 --- a/cli/tests/integrations/test_package.py +++ b/cli/tests/integrations/test_package.py @@ -51,7 +51,7 @@ def _chronos_description(app_ids): "maintainer": "support@mesosphere.io", "name": "chronos", "packageSource": "https://github.com/mesosphere/universe/archive/\ -cli-test-2.zip", +cli-test-3.zip", "postInstallNotes": "Chronos DCOS Service has been successfully " "installed!\n\n\tDocumentation: http://mesos." "github.io/chronos\n\tIssues: https://github.com/" @@ -97,8 +97,8 @@ def test_version(): def test_sources_list(): - stdout = b"1a9bef0c579dd0692af9c6ba22c3ec910fb03efc " + \ - b"https://github.com/mesosphere/universe/archive/cli-test-2.zip\n" + stdout = b"fd40db7f075490e0c92ec6fcd62ec1caa361b313 " + \ + b"https://github.com/mesosphere/universe/archive/cli-test-3.zip\n" assert_command(['dcos', 'package', 'sources'], stdout=stdout) @@ -324,12 +324,12 @@ b3NwaGVyZS9kY29zLWhlbGxvd29ybGQifQ==""" CJdfQ==""" expected_source = b"""https://github.com/mesosphere/universe/archive/\ -cli-test-2.zip""" +cli-test-3.zip""" expected_labels = { 'DCOS_PACKAGE_METADATA': expected_metadata, 'DCOS_PACKAGE_COMMAND': expected_command, - 'DCOS_PACKAGE_REGISTRY_VERSION': b'1.0.0-rc1', + 'DCOS_PACKAGE_REGISTRY_VERSION': b'2.0.0-rc1', 'DCOS_PACKAGE_NAME': b'helloworld', 'DCOS_PACKAGE_VERSION': b'0.1.0', 'DCOS_PACKAGE_SOURCE': expected_source, @@ -434,7 +434,7 @@ def test_uninstall_cli(): "maintainer": "support@mesosphere.io", "name": "helloworld", "packageSource": "https://github.com/mesosphere/universe/archive/\ -cli-test-2.zip", +cli-test-3.zip", "postInstallNotes": "A sample post-installation message", "preInstallNotes": "A sample pre-installation message", "releaseVersion": "0", @@ -552,7 +552,7 @@ def test_list_cli(): "maintainer": "support@mesosphere.io", "name": "helloworld", "packageSource": "https://github.com/mesosphere/universe/archive/\ -cli-test-2.zip", +cli-test-3.zip", "postInstallNotes": "A sample post-installation message", "preInstallNotes": "A sample pre-installation message", "releaseVersion": "0", @@ -586,7 +586,7 @@ cli-test-2.zip", "maintainer": "support@mesosphere.io", "name": "helloworld", "packageSource": "https://github.com/mesosphere/universe/archive/\ -cli-test-2.zip", +cli-test-3.zip", "postInstallNotes": "A sample post-installation message", "preInstallNotes": "A sample pre-installation message", "releaseVersion": "0", @@ -661,7 +661,7 @@ def test_search(): assert returncode == 0 assert b'"packages": []' in stdout assert b'"source": "https://github.com/mesosphere/universe/archive/\ -cli-test-2.zip"' in stdout +cli-test-3.zip"' in stdout assert stderr == b'' returncode, stdout, stderr = exec_command( diff --git a/dcos/http.py b/dcos/http.py index 19d93d6..f11fb15 100644 --- a/dcos/http.py +++ b/dcos/http.py @@ -87,7 +87,7 @@ def _request(method, logger.info('Received HTTP response [%r]: %r', response.status_code, - response.text) + response.headers) return response diff --git a/dcos/package.py b/dcos/package.py index ef6b31e..c6bb0b9 100644 --- a/dcos/package.py +++ b/dcos/package.py @@ -242,6 +242,7 @@ def uninstall_app(app_name, remove_all, app_id, init_client, dcos_client): package_json = _decode_and_add_context( app['id'], app.get('labels', {})) + # First, remove the app from Marathon init_client.remove_app(app['id'], force=True) @@ -569,7 +570,7 @@ def _extract_default_values(config_schema): return defaults -def _merge_options(first, second): +def _merge_options(first, second, overrides=True): """Merges the :code:`second` dictionary into the :code:`first` dictionary. If both dictionaries have the same key and both values are dictionaries then it recursively merges those two dictionaries. @@ -578,6 +579,8 @@ def _merge_options(first, second): :type first: dict :param second: second dictionary :type second: dict + :param overrides: allow second to override first if both have same key + :type overrides: bool :returns: merged dictionary :rtype: dict """ @@ -590,6 +593,10 @@ def _merge_options(first, second): if (isinstance(first_value, collections.Mapping) and isinstance(second_value, collections.Mapping)): result[key] = _merge_options(first_value, second_value) + elif not overrides and first_value != second_value: + raise DCOSException( + "Trying to override package.json's key {} to {}".format( + key, second_value)) else: result[key] = second_value else: @@ -761,7 +768,7 @@ def update_sources(config, validate=False): # TODO(jsancio): move this to the validation when it is forced Registry(source, stage_dir).check_version( LooseVersion('1.0'), - LooseVersion('2.0')) + LooseVersion('3.0')) # validate content if validate: @@ -1162,7 +1169,7 @@ class Registry(): raise DCOSException('Unable to parse [{}]'.format(index_path)) def get_index(self): - """Retuprns the index of packages in this registry. + """Returns the index of packages in this registry. :rtype: dict """ @@ -1270,7 +1277,7 @@ class Package(): logger.info('Generated default options: %r', default_options) - # Merge option overrides + # Merge option overrides, second argument takes precedence options = _merge_options(default_options, user_options) logger.info('Merged options: %r', options) @@ -1321,6 +1328,16 @@ class Package(): return self.has_definition(revision, 'command.json') + def _has_resource_definition(self, revision): + """Returns true if the package defines a resource; false otherwise. + + :param revision: package revision + :type revision: str + :rtype: bool + """ + + return self.has_definition(revision, 'resource.json') + def has_marathon_definition(self, revision): """Returns true if the package defines a Marathon json. false otherwise. @@ -1331,6 +1348,32 @@ class Package(): return self.has_definition(revision, 'marathon.json') + def has_marathon_mustache_definition(self, revision): + """Returns true if the package defines a Marathon.json.mustache false + otherwise. + + :param revision: package revision + :type revision: str + :rtype: bool + """ + + return self.has_definition(revision, 'marathon.json.mustache') + + def _get_marathon_json_file(self, revision): + """Returns the file name of Marathon json + + :param revision: package revision + :type revision: str + :returns: Marathon file name + :rtype: str + """ + if self.has_marathon_definition(revision): + return 'marathon.json' + elif self.has_marathon_mustache_definition(revision): + return 'marathon.json.mustache' + else: + raise DCOSException("Missing Marathon json definition of package") + def config_json(self, revision): """Returns the JSON content of the config.json file. @@ -1353,6 +1396,17 @@ class Package(): return self._json(revision, 'package.json') + def _resource_json(self, revision): + """Returns the JSON content of the resource.json file. + + :param revision: the package revision + :type revision: str + :returns: Package data + :rtype: dict + """ + + return self._json(revision, 'resource.json') + def marathon_json(self, revision, options): """Returns the JSON content of the marathon.json template, after rendering it with options. @@ -1364,8 +1418,13 @@ class Package(): :rtype: dict """ + marathon_file = self._get_marathon_json_file(revision) + if self.has_marathon_mustache_definition(revision) and \ + self._has_resource_definition(revision): + resources = {"resource": self._resource_json(revision)} + options = _merge_options(options, resources, False) init_desc = self._render_template( - 'marathon.json', + marathon_file, revision, options) @@ -1404,7 +1463,7 @@ class Package(): :returns: raw data from marathon.json :rtype: str """ - return self._data(revision, 'marathon.json') + return self._data(revision, self._get_marathon_json_file(revision)) def command_template(self, revision): """ Returns raw data from command.json diff --git a/tests/test_package.py b/tests/test_package.py index 719eeb5..095d8d5 100644 --- a/tests/test_package.py +++ b/tests/test_package.py @@ -31,11 +31,20 @@ MergeData = collections.namedtuple( first={'b': {'a': 'a'}}, second={'b': {'c': 'c'}}, expected={'b': {'c': 'c', 'a': 'a'}}), + MergeData( + first={'b': 'c'}, + second={'b': 'd'}, + expected={'b': 'd'}), ]) def merge_data(request): return request.param +def test_options_merge_wont_override(): + with pytest.raises(DCOSException): + package._merge_options({'b': 'c'}, {'b': 'd'}, False) + + def test_option_merge(merge_data): assert merge_data.expected == package._merge_options( merge_data.first,