diff --git a/cli/dcoscli/data/universe-schema/command.json b/cli/dcoscli/data/universe-schema/command.json deleted file mode 100644 index dbb355b..0000000 --- a/cli/dcoscli/data/universe-schema/command.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "$schema": "http://json-schema.org/schema#", - "oneOf": [ - { - "type": "object", - "properties": { - "pip": { - "type": "array", - "items": { - "type": "string" - }, - "title": "Embedded Requirements File", - "description": "An array of strings representing of the requirements file to use for installing the subcommand for Pip. Each item is interpreted as a line in the requirements file." - } - }, - "additionalProperties": false, - "required": ["pip"] - } - ] -} diff --git a/cli/dcoscli/data/universe-schema/config.json b/cli/dcoscli/data/universe-schema/config.json deleted file mode 100644 index 85eb502..0000000 --- a/cli/dcoscli/data/universe-schema/config.json +++ /dev/null @@ -1,150 +0,0 @@ -{ - "id": "http://json-schema.org/draft-04/schema#", - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Core schema meta-schema", - "definitions": { - "schemaArray": { - "type": "array", - "minItems": 1, - "items": { "$ref": "#" } - }, - "positiveInteger": { - "type": "integer", - "minimum": 0 - }, - "positiveIntegerDefault0": { - "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ] - }, - "simpleTypes": { - "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] - }, - "stringArray": { - "type": "array", - "items": { "type": "string" }, - "minItems": 1, - "uniqueItems": true - } - }, - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uri" - }, - "$schema": { - "type": "string", - "format": "uri" - }, - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "default": {}, - "multipleOf": { - "type": "number", - "minimum": 0, - "exclusiveMinimum": true - }, - "maximum": { - "type": "number" - }, - "exclusiveMaximum": { - "type": "boolean", - "default": false - }, - "minimum": { - "type": "number" - }, - "exclusiveMinimum": { - "type": "boolean", - "default": false - }, - "maxLength": { "$ref": "#/definitions/positiveInteger" }, - "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" }, - "pattern": { - "type": "string", - "format": "regex" - }, - "additionalItems": { - "anyOf": [ - { "type": "boolean" }, - { "$ref": "#" } - ], - "default": {} - }, - "items": { - "anyOf": [ - { "$ref": "#" }, - { "$ref": "#/definitions/schemaArray" } - ], - "default": {} - }, - "maxItems": { "$ref": "#/definitions/positiveInteger" }, - "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" }, - "uniqueItems": { - "type": "boolean", - "default": false - }, - "maxProperties": { "$ref": "#/definitions/positiveInteger" }, - "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" }, - "required": { "$ref": "#/definitions/stringArray" }, - "additionalProperties": { - "anyOf": [ - { "type": "boolean" }, - { "$ref": "#" } - ], - "default": {} - }, - "definitions": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "default": {} - }, - "properties": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "default": {} - }, - "patternProperties": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "default": {} - }, - "dependencies": { - "type": "object", - "additionalProperties": { - "anyOf": [ - { "$ref": "#" }, - { "$ref": "#/definitions/stringArray" } - ] - } - }, - "enum": { - "type": "array", - "minItems": 1, - "uniqueItems": true - }, - "type": { - "anyOf": [ - { "$ref": "#/definitions/simpleTypes" }, - { - "type": "array", - "items": { "$ref": "#/definitions/simpleTypes" }, - "minItems": 1, - "uniqueItems": true - } - ] - }, - "allOf": { "$ref": "#/definitions/schemaArray" }, - "anyOf": { "$ref": "#/definitions/schemaArray" }, - "oneOf": { "$ref": "#/definitions/schemaArray" }, - "not": { "$ref": "#" } - }, - "dependencies": { - "exclusiveMaximum": [ "maximum" ], - "exclusiveMinimum": [ "minimum" ] - }, - "default": {} -} diff --git a/cli/dcoscli/data/universe-schema/package.json b/cli/dcoscli/data/universe-schema/package.json deleted file mode 100644 index c364dbe..0000000 --- a/cli/dcoscli/data/universe-schema/package.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "$schema": "http://json-schema.org/schema#", - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "scm": { - "type": "string" - }, - "maintainer": { - "type": "string" - }, - "website": { - "type": "string" - }, - "description": { - "type": "string" - }, - "framework": { - "type": "boolean", - "default": false, - "description": "True if this package installs a new Mesos framework." - }, - "preInstallNotes": { - "type": "string", - "description": "Pre installation notes that would be useful to the user of this package." - }, - "postInstallNotes": { - "type": "string", - "description": "Post installation notes that would be useful to the user of this package." - }, - "postUninstallNotes": { - "type": "string", - "description": "Post uninstallation notes that would be useful to the user of this package." - }, - "tags": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[^\\s]+$" - } - }, - "licenses": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "The name of the license. For example one of [Apache License Version 2.0 | MIT License | BSD License | Proprietary]" - }, - "url": { - "type": "string", - "pattern": "((?<=\\()[A-Za-z][A-Za-z0-9\\+\\.\\-]*:([A-Za-z0-9\\.\\-_~:/\\?#\\[\\]@!\\$&'\\(\\)\\*\\+,;=]|%[A-Fa-f0-9]{2})+(?=\\)))|([A-Za-z][A-Za-z0-9\\+\\.\\-]*:([A-Za-z0-9\\.\\-_~:/\\?#\\[\\]@!\\$&'\\(\\)\\*\\+,;=]|%[A-Fa-f0-9]{2})+)", - "description": "The URL where the license can be accessed" - } - }, - "additionalProperties": false, - "required": [ - "name", - "url" - ] - } - } - }, - "required": [ - "name", - "version", - "maintainer", - "description", - "tags" - ], - "additionalProperties": false -} diff --git a/cli/dcoscli/package/main.py b/cli/dcoscli/package/main.py index 09b41e9..b7f2b98 100644 --- a/cli/dcoscli/package/main.py +++ b/cli/dcoscli/package/main.py @@ -1,21 +1,16 @@ -import hashlib import json import os import sys -import tempfile -import zipfile -from collections import defaultdict import dcoscli import docopt import pkg_resources -from dcos import (cmds, cosmospackage, emitting, errors, http, options, - package, subcommand, util) +from dcos import (cmds, cosmospackage, emitting, http, options, package, + subcommand, util) from dcos.errors import DCOSException from dcoscli import tables from dcoscli.subcommand import default_command_info, default_doc from dcoscli.util import decorate_docopt_usage -from six import iteritems logger = util.get_logger(__name__) emitter = emitting.FlatEmitter() @@ -523,230 +518,6 @@ def _uninstall(package_name, remove_all, app_id, cli, app): return 0 -def _bundle(package_directory, output_directory): - """ - :param package_directory: directory containing the package - :type package_directory: str - :param output_directory: directory where to save the package zip file - :type output_directory: str - :returns: process status - :rtype: int - """ - - if output_directory is None: - output_directory = os.getcwd() - logger.debug('Using [%s] as the ouput directory', output_directory) - - # Find package.json file and parse it - if not os.path.exists(os.path.join(package_directory, 'package.json')): - raise DCOSException( - ('The file package.json is required in the package directory ' - '[{}]').format(package_directory)) - - package_json = _validate_json_file( - os.path.join(package_directory, 'package.json')) - - with tempfile.NamedTemporaryFile() as temp_file: - with zipfile.ZipFile( - temp_file.name, - mode='w', - compression=zipfile.ZIP_DEFLATED, - allowZip64=True) as zip_file: - # list through package directory and add files zip archive - for filename in sorted(os.listdir(package_directory)): - fullpath = os.path.join(package_directory, filename) - if filename == 'marathon.json.mustache': - zip_file.write(fullpath, arcname=filename) - elif filename in ['config.json', 'command.json', - 'package.json']: - # schema check the config and command json file - _validate_json_file(fullpath) - zip_file.write(fullpath, arcname=filename) - elif filename == 'assets' and os.path.isdir(fullpath): - _bundle_assets(fullpath, zip_file) - elif filename == 'images' and os.path.isdir(fullpath): - _bundle_images(fullpath, zip_file) - else: - # anything else is an error - raise DCOSException( - ('Error bundling package. Extra file in package ' - 'directory [{}]').format(fullpath)) - - # Compute the name of the package file - zip_file_name = os.path.join( - output_directory, - '{}-{}-{}.zip'.format( - package_json['name'], - package_json['version'], - _hashfile(temp_file.name))) - - if os.path.exists(zip_file_name): - raise DCOSException( - 'Output file [{}] already exists'.format( - zip_file_name)) - - # rename with digest - util.sh_copy(temp_file.name, zip_file_name) - - # Print the full path to the file - emitter.publish( - errors.DefaultError( - 'Created DCOS Universe package [{}].'.format(zip_file_name))) - - return 0 - - -def _validate_json_file(fullpath): - """Validates the content of the file against its schema. Throws an - exception if the file is not valid. - - :param fullpath: full path to the file. - :type fullpath: str - :return: json object if it is a special file - :rtype: dict - """ - - filename = os.path.basename(fullpath) - if filename in ['command.json', 'config.json', 'package.json']: - schema_path = 'data/universe-schema/{}'.format(filename) - else: - raise DCOSException( - ('Error bundling package. Unknown file in package ' - 'directory [{}]').format(fullpath)) - - special_schema = util.load_jsons( - pkg_resources.resource_string('dcoscli', schema_path).decode('utf-8')) - - with util.open_file(fullpath) as special_file: - special_json = util.load_json(special_file) - - errs = util.validate_json(special_json, special_schema) - if errs: - emitter.publish( - errors.DefaultError( - 'Error validating JSON file [{}]'.format(fullpath))) - raise DCOSException(util.list_to_err(errs)) - - return special_json - - -def _hashfile(filename): - """Calculates the sha256 of a file - - :param filename: path to the file to sum - :type filename: str - :returns: digest in hexadecimal - :rtype: str - """ - - hasher = hashlib.sha256() - with open(filename, 'rb') as f: - for chunk in iter(lambda: f.read(4096), b''): - hasher.update(chunk) - return hasher.hexdigest() - - -def _bundle_assets(assets_directory, zip_file): - """Bundle the assets directory - - :param assets_directory: path to the assets directory - :type assets_directory: str - :param zip_file: zip file object - :type zip_file: zipfile.ZipFile - :rtype: None - """ - - for filename in sorted(os.listdir(assets_directory)): - fullpath = os.path.join(assets_directory, filename) - if filename == 'uris' and os.path.isdir(fullpath): - _bundle_uris(fullpath, zip_file) - else: - # anything else is an error - raise DCOSException( - ('Error bundling package. Extra file in package ' - 'directory [{}]').format(fullpath)) - - -def _bundle_uris(uris_directory, zip_file): - """Bundle the uris directory - - :param uris_directory: path to the uris directory - :type uris_directory: str - :param zip_file: zip file object - :type zip_file: zipfile.ZipFile - :rtype: None - """ - - uris = sorted(os.listdir(uris_directory)) - - # these uris will be found through a mustache template from the property - # name so make sure each name is unique. - uri_properties = defaultdict(list) - for name in uris: - uri_properties[name.replace('.', '-')].append(name) - - collisions = [uri_list for (prop, uri_list) in iteritems(uri_properties) - if len(uri_list) > 1] - - if collisions: - raise DCOSException( - 'Error bundling package. Multiple assets map to the same property ' - 'name (periods [.] are replaced with dashes [-]): {}'.format( - collisions)) - for filename in uris: - fullpath = os.path.join(uris_directory, filename) - - zip_file.write(fullpath, arcname='assets/uris/{}'.format(filename)) - - -def _bundle_images(images_directory, zip_file): - """Bundle the images directory - - :param images_directory: path to the images directory - :type images_directory: str - :param zip_file: zip file object - :type zip_file: zipfile.ZipFile - :rtype: None - """ - - for filename in sorted(os.listdir(images_directory)): - fullpath = os.path.join(images_directory, filename) - if (filename == 'icon-small.png' or - filename == 'icon-medium.png' or - filename == 'icon-large.png'): - - util.validate_png(fullpath) - - zip_file.write(fullpath, arcname='images/{}'.format(filename)) - elif filename == 'screenshots' and os.path.isdir(fullpath): - _bundle_screenshots(fullpath, zip_file) - else: - # anything else is an error - raise DCOSException( - ('Error bundling package. Extra file in package ' - 'directory [{}]').format(fullpath)) - - -def _bundle_screenshots(screenshot_directory, zip_file): - """Bundle the screenshots directory - - :param screenshot_directory: path to the screenshots directory - :type screenshot_directory: str - :param zip_file: zip file object - :type zip_file: zipfile.ZipFile - :rtype: None - """ - - for filename in sorted(os.listdir(screenshot_directory)): - fullpath = os.path.join(screenshot_directory, filename) - - util.validate_png(fullpath) - - zip_file.write( - fullpath, - arcname='images/screenshots/{}'.format(filename)) - - def _get_cosmos_url(): """ :returns: cosmos base url diff --git a/cli/setup.py b/cli/setup.py index 681cc00..d1e9125 100644 --- a/cli/setup.py +++ b/cli/setup.py @@ -82,7 +82,6 @@ setup( 'data/*.json', 'data/help/*.txt', 'data/config-schema/*.json', - 'data/universe-schema/*.json', ], },