def get_schedule(self, job_id, schedule_id): """Gets the schedules for a given job :param job_id: the ID of the job to remove :type job_id: str :rtype: json of schedules """ job_id = util.normalize_marathon_id_path(job_id) schedule_id = util.normalize_marathon_id_path(schedule_id) path = 'v1/jobs{}/schedules/{}'.format(job_id, schedule_id) response = self._rpc.http_req(http.get, path) return response.json()
def remove_schedule(self, job_id, schedule_id): """Completely removes the requested application. :param job_id: the ID of the job to remove :type job_id: str :param force: whether to override running deployments :type force: bool :rtype: None """ job_id = util.normalize_marathon_id_path(job_id) schedule_id = util.normalize_marathon_id_path(schedule_id) path = 'v1/jobs{}/schedules/{}'.format(job_id, schedule_id) self._rpc.http_req(http.delete, path)
def kill_run(self, job_id, run_id): """Add a new job. :param job_id: the ID of the job :type job_id: str :param run_id: the ID of the job run to remove :type run_id: str :rtype: None """ job_id = util.normalize_marathon_id_path(job_id) run_id = util.normalize_marathon_id_path(run_id) path = '/v1/jobs{}/runs{}/actions/stop'.format(job_id, run_id) self._rpc.http_req(http.post, path)
def get_run(self, job_id, run_id): """Add a new job. :param job_id: the ID of the job :type job_id: str :param run_id: the ID of the job run :type run_id: str :rtype: None """ job_id = util.normalize_marathon_id_path(job_id) run_id = util.normalize_marathon_id_path(run_id) path = '/v1/jobs{}/runs{}'.format(job_id, run_id) response = self._rpc.http_req(http.get, path) return response.json()
def group_add(self, group_resource, id_): """ :param group_resource: optional filename for the group resource :type group_resource: str :returns: process return code :rtype: int """ if id_: group_resource = {'id': id_} else: group_resource = self._resource_reader.get_resource(group_resource) client = self._create_marathon_client() # Check that the group doesn't exist group_id = util.normalize_marathon_id_path(group_resource['id']) try: client.get_group(group_id) except DCOSException as e: logger.exception(e) else: raise DCOSException("Group '{}' already exists".format(group_id)) deployment = client.create_group(group_resource) emitter.publish('Created deployment {}'.format(deployment)) return 0
def _list(json_, app_id, cli_only, package_name): """List installed apps :param json_: output json if True :type json_: bool :param app_id: App ID of app to show :type app_id: str :param cli_only: if True, only show packages with installed subcommands :type cli_only: bool :param package_name: The package to show :type package_name: str :returns: process return code :rtype: int """ package_manager = get_package_manager() if app_id is not None: app_id = util.normalize_marathon_id_path(app_id) results = package.installed_packages(package_manager, app_id, package_name, cli_only) # only emit those packages that match the provided package_name and app_id if results or json_: emitting.publish_table(emitter, results, tables.package_table, json_) else: msg = ("There are currently no installed packages. " "Please use `dcos package install` to install a package.") raise DCOSException(msg) return 0
def restart(self, app_id, force): """ :param app_id: the id of the application :type app_id: str :param force: whether to override running deployments :type force: bool :returns: process return code :rtype: int """ client = self._create_marathon_client() desc = client.get_app(app_id) if desc['instances'] <= 0: app_id = util.normalize_marathon_id_path(app_id) emitter.publish( 'Unable to perform rolling restart of application {!r} ' 'because it has no running tasks'.format( app_id, desc['instances'])) return 1 payload = client.restart_app(app_id, force) message = 'Created deployment {}'.format(payload['deploymentId']) emitter.publish(message) return 0
def group_add(self, group_resource): """ :param group_resource: optional filename for the group resource :type group_resource: str :returns: process return code :rtype: int """ group_resource = self._resource_reader.get_resource(group_resource) client = self._create_marathon_client() # Check that the group doesn't exist group_id = util.normalize_marathon_id_path(group_resource['id']) try: client.get_group(group_id) except DCOSException as e: logger.exception(e) else: raise DCOSException("Group '{}' already exists".format(group_id)) deployment = client.create_group(group_resource) emitter.publish('Created deployment {}'.format(deployment)) return 0
def add(self, app_resource): """ :param app_resource: optional filename for the application resource :type app_resource: str :returns: process return code :rtype: int """ application_resource = self._resource_reader.get_resource(app_resource) # Add application to marathon client = self._create_marathon_client() # Check that the application doesn't exist app_id = util.normalize_marathon_id_path(application_resource['id']) try: client.get_app(app_id) except DCOSException as e: logger.exception(e) else: message = "Application '{}' already exists".format(app_id) raise DCOSException(message) deployment = client.add_app(application_resource) emitter.publish('Created deployment {}'.format(deployment)) return 0
def _list(json_, app_id, cli_only, package_name): """List installed apps :param json_: output json if True :type json_: bool :param app_id: App ID of app to show :type app_id: str :param cli_only: if True, only show packages with installed subcommands :type cli_only: bool :param package_name: The package to show :type package_name: str :returns: process return code :rtype: int """ package_manager = get_package_manager() if app_id is not None: app_id = util.normalize_marathon_id_path(app_id) results = package.installed_packages( package_manager, app_id, package_name, cli_only) # only emit those packages that match the provided package_name and app_id if results or json_: emitting.publish_table(emitter, results, tables.package_table, json_) else: msg = ("There are currently no installed packages. " "Please use `dcos package install` to install a package.") raise DCOSException(msg) return 0
def get_app(self, app_id, version=None): """Returns a representation of the requested application version. If version is None the return the latest version. :param app_id: the ID of the application :type app_id: str :param version: application version as a ISO8601 datetime :type version: str :returns: the requested Marathon application :rtype: dict """ app_id = util.normalize_marathon_id_path(app_id) if version is None: path = 'v2/apps{}'.format(app_id) else: path = 'v2/apps{}/versions/{}'.format(app_id, version) response = self._rpc.http_req(http.get, path) # Looks like Marathon return different JSON for versions if version is None: return response.json().get('app') else: return response.json()
def add(self, app_resource): """ :param app_resource: optional filename for the application resource :type app_resource: str :returns: process return code :rtype: int """ application_resource = self._resource_reader.get_resource(app_resource) # Add application to marathon client = self._create_marathon_client() # Check that the application doesn't exist app_id = util.normalize_marathon_id_path(application_resource['id']) self._check_service_id_length(app_id) try: client.get_app(app_id) except DCOSException as e: logger.exception(e) else: message = "Application '{}' already exists".format(app_id) raise DCOSException(message) deployment = client.add_app(application_resource) emitter.publish('Created deployment {}'.format(deployment)) return 0
def get_app_versions(self, app_id, max_count=None): """Asks Marathon for all the versions of the Application up to a maximum count. :param app_id: the ID of the application or group :type app_id: str :param max_count: the maximum number of version to fetch :type max_count: int :returns: a list of all the version of the application :rtype: [str] """ if max_count is not None and max_count <= 0: raise DCOSException( 'Maximum count must be a positive number: {}'.format( max_count)) app_id = util.normalize_marathon_id_path(app_id) path = 'v2/apps{}/versions'.format(app_id) response = self._rpc.http_req(http.get, path) if max_count is None: return response.json().get('versions') else: return response.json().get('versions')[:max_count]
def get_app_versions(self, app_id, max_count=None): """Asks Marathon for all the versions of the Application up to a maximum count. :param app_id: the ID of the application or group :type app_id: str :param max_count: the maximum number of version to fetch :type max_count: int :returns: a list of all the version of the application :rtype: [str] """ if max_count is not None and max_count <= 0: raise DCOSException( 'Maximum count must be a positive number: {}'.format(max_count) ) app_id = util.normalize_marathon_id_path(app_id) path = 'v2/apps{}/versions'.format(app_id) response = self._rpc.http_req(http.get, path) if max_count is None: return response.json().get('versions') else: return response.json().get('versions')[:max_count]
def kill_run(self, job_id, run_id): """Add a new job. :param job_id: the ID of the job :type job_id: str :param run_id: the ID of the job run to remove :type run_id: str :rtype: None """ if job_id is None or run_id is None: raise DCOSException( "job_id and run_id are required to kill a jobrun.") job_id = util.normalize_marathon_id_path(job_id) run_id = util.normalize_marathon_id_path(run_id) path = '/v1/jobs{}/runs{}/actions/stop'.format(job_id, run_id) self._rpc.http_req(http.post, path)
def run_job(self, job_id): """Add a new job. :param job_id: the ID of the job to remove :type job_id: str :rtype: None """ job_id = util.normalize_marathon_id_path(job_id) path = '/v1/jobs{}/runs'.format(job_id) self._rpc.http_req(http.post, path)
def delay_reset(self, id): """ :param id: The app_id or pod_id which is used to delete the delay :return: The result of reset delay call :rtype: str """ id = util.normalize_marathon_id_path(id) response = self._rpc.http_req( http.delete, 'v2/queue{}/delay'.format(id) ) if response.status_code == 204: return 'Delay on [{}] has been reset'.format(id) raise DCOSHTTPException(response)
def remove_app(self, app_id, force=False): """Completely removes the requested application. :param app_id: the ID of the application to remove :type app_id: str :param force: whether to override running deployments :type force: bool :rtype: None """ app_id = util.normalize_marathon_id_path(app_id) params = self._force_params(force) path = 'v2/apps{}'.format(app_id) self._rpc.http_req(http.delete, path, params=params)
def remove_group(self, group_id, force=False): """Completely removes the requested application. :param group_id: the ID of the application to remove :type group_id: str :param force: whether to override running deployments :type force: bool :rtype: None """ group_id = util.normalize_marathon_id_path(group_id) params = self._force_params(force) path = 'v2/groups{}'.format(group_id) response = self._rpc.http_req(http.delete, path, params=params) return response.json()
def get_job(self, job_id): """Returns a representation of the requested application version. If version is None the return the latest version. :param app_id: the ID of the application :type app_id: str :param version: application version as a ISO8601 datetime :type version: str :returns: the requested Metronome job :rtype: dict """ # refactor util name it isn't marathon specific job_id = util.normalize_marathon_id_path(job_id) path = 'v1/jobs{}'.format(job_id) response = self._rpc.http_req(http.get, path) return response.json()
def restart_app(self, app_id, force=False): """Performs a rolling restart of all of the tasks. :param app_id: the id of the application to restart :type app_id: str :param force: whether to override running deployments :type force: bool :returns: the deployment id and version :rtype: dict """ app_id = util.normalize_marathon_id_path(app_id) params = self._force_params(force) path = 'v2/apps{}/restart'.format(app_id) response = self._rpc.http_req(http.post, path, params=params) return response.json()
def get_job(self, job_id, embed_with=None): """Returns a representation of the requested job. :param job_id: the ID of the application :type job_id: str :param embed_with: list of strings to ?embed=str&embed=str2... :type embed_with: [str] :returns: the requested Metronome job :rtype: dict """ # refactor util name it isn't marathon specific job_id = util.normalize_marathon_id_path(job_id) embeds = _get_embed_query_string(embed_with) if embed_with else None url = ('v1/jobs{}{}'.format(job_id, embeds) if embeds else 'v1/jobs{}'.format(job_id)) response = self._rpc.http_req(http.get, url) return response.json()
def update_schedule(self, job_id, schedule_id, schedule_resource): """Gets the schedules for a given job :param job_id: the ID of the job to remove :type job_id: str :rtype: json of schedules """ job_id = util.normalize_marathon_id_path(job_id) if hasattr(schedule_resource, 'read'): schedule_json = json.load(schedule_resource) else: schedule_json = schedule_resource path = 'v1/jobs{}/schedules/{}'.format(job_id, schedule_id) response = self._rpc.http_req(http.put, path, json=schedule_json) return response.json()
def uninstall(pkg, package_name, remove_all, app_id, cli, app): """Uninstalls a package. :param pkg: package manager to uninstall with :type pkg: PackageManager :param package_name: The package to uninstall :type package_name: str :param remove_all: Whether to remove all instances of the named app :type remove_all: boolean :param app_id: App ID of the app instance to uninstall :type app_id: str :param init_client: The program to use to run the app :type init_client: object :rtype: None """ if cli is False and app is False: cli = app = True uninstalled = False installed = installed_packages(pkg, app_id, package_name) installed_cli = next( (True for installed_pkg in installed if installed_pkg.get("command")), False) installed_app = next( (True for installed_pkg in installed if installed_pkg.get("apps")), False) if cli and installed_cli: if subcommand.uninstall(package_name): uninstalled = True if app and installed_app: if pkg.uninstall_app(package_name, remove_all, app_id): uninstalled = True if uninstalled: return None else: msg = 'Package [{}]'.format(package_name) if app_id is not None: app_id = util.normalize_marathon_id_path(app_id) msg += " with id [{}]".format(app_id) msg += " is not installed" raise DCOSException(msg)
def uninstall(pkg, package_name, remove_all, app_id, cli, app): """Uninstalls a package. :param pkg: package manager to uninstall with :type pkg: PackageManager :param package_name: The package to uninstall :type package_name: str :param remove_all: Whether to remove all instances of the named app :type remove_all: boolean :param app_id: App ID of the app instance to uninstall :type app_id: str :param init_client: The program to use to run the app :type init_client: object :rtype: None """ if cli is False and app is False: cli = app = True uninstalled = False installed = installed_packages( pkg, app_id, package_name, cli_only=False) installed_cli = next((True for installed_pkg in installed if installed_pkg.get("command")), False) installed_app = next((True for installed_pkg in installed if installed_pkg.get("apps")), False) if cli and installed_cli: if subcommand.uninstall(package_name): uninstalled = True if app and installed_app: if pkg.uninstall_app(package_name, remove_all, app_id): uninstalled = True if uninstalled: return None else: msg = 'Package [{}]'.format(package_name) if app_id is not None: app_id = util.normalize_marathon_id_path(app_id) msg += " with id [{}]".format(app_id) msg += " is not installed" raise DCOSException(msg)
def get_group(self, group_id, version=None): """Returns a representation of the requested group version. If version is None the return the latest version. :param group_id: the ID of the application :type group_id: str :param version: application version as a ISO8601 datetime :type version: str :returns: the requested Marathon application :rtype: dict """ group_id = util.normalize_marathon_id_path(group_id) if version is None: path = 'v2/groups{}'.format(group_id) else: path = 'v2/groups{}/versions/{}'.format(group_id, version) response = self._rpc.http_req(http.get, path) return response.json()
def kill_tasks(self, app_id, scale=None, host=None): """Kills the tasks for a given application, and can target a given agent, with a future target scale :param app_id: the id of the application to restart :type app_id: str :param scale: Scale the app down after killing the specified tasks :type scale: bool :param host: host to target restarts on :type host: string """ params = {} app_id = util.normalize_marathon_id_path(app_id) if host: params['host'] = host if scale: params['scale'] = scale path = 'v2/apps{}/tasks'.format(app_id) response = self._rpc.http_req(http.delete, path, params=params) return response.json()
def get_deployments(self, id_filter=None): """Returns a list of deployments, optionally limited to an app or pod. :param id_filter: the id of the application or the pod :type id_filter: str :returns: a list of deployments :rtype: list of dict """ response = self._rpc.http_req(http.get, 'v2/deployments') if id_filter is not None: id_filter = util.normalize_marathon_id_path(id_filter) deployments = [ deployment for deployment in response.json() if id_filter in deployment['affectedApps'] or id_filter in deployment['affectedPods'] ] else: deployments = response.json() return deployments
def get_deployments(self, app_id=None): """Returns a list of deployments, optionally limited to an app. :param app_id: the id of the application :type app_id: str :returns: a list of deployments :rtype: list of dict """ response = self._rpc.http_req(http.get, 'v2/deployments') if app_id is not None: app_id = util.normalize_marathon_id_path(app_id) deployments = [ deployment for deployment in response.json() if app_id in deployment['affectedApps'] ] else: deployments = response.json() return deployments
def get_tasks(self, app_id): """Returns a list of tasks, optionally limited to an app. :param app_id: the id of the application to restart :type app_id: str :returns: a list of tasks :rtype: [dict] """ response = self._rpc.http_req(http.get, 'v2/tasks') if app_id is not None: app_id = util.normalize_marathon_id_path(app_id) tasks = [ task for task in response.json()['tasks'] if app_id == task['appId'] ] else: tasks = response.json()['tasks'] return tasks
def scale_group(self, group_id, scale_factor, force=False): """Scales a group with the requested scale-factor. :param group_id: the ID of the group to scale :type group_id: str :param scale_factor: the requested value of scale-factor :type scale_factor: float :param force: whether to override running deployments :type force: bool :returns: the resulting deployment ID :rtype: bool """ group_id = util.normalize_marathon_id_path(group_id) params = self._force_params(force) path = 'v2/groups{}'.format(group_id) response = self._rpc.http_req(http.put, path, params=params, json={'scaleBy': scale_factor}) deployment = response.json().get('deploymentId') return deployment
def scale_app(self, app_id, instances, force=False): """Scales an application to the requested number of instances. :param app_id: the ID of the application to scale :type app_id: str :param instances: the requested number of instances :type instances: int :param force: whether to override running deployments :type force: bool :returns: the resulting deployment ID :rtype: str """ app_id = util.normalize_marathon_id_path(app_id) params = self._force_params(force) path = 'v2/apps{}'.format(app_id) response = self._rpc.http_req(http.put, path, params=params, json={'instances': int(instances)}) deployment = response.json().get('deploymentId') return deployment
def uninstall(pkg, package_name, remove_all, app_id, cli, app): """Uninstalls a package. :param pkg: package manager to uninstall with :type pkg: PackageManager :param package_name: The package to uninstall :type package_name: str :param remove_all: Whether to remove all instances of the named app :type remove_all: boolean :param app_id: App ID of the app instance to uninstall :type app_id: str :param cli: Whether to remove the CLI only :type cli: boolean :param app: Whether to remove app only :type app: boolean :rtype: None """ installed = installed_packages(pkg, None, package_name, cli_only=False) installed_pkg = next(iter(installed), None) if installed_pkg: installed_cli = installed_pkg.get("command") installed_app = installed_pkg.get("apps") or [] else: msg = 'Package [{}]'.format(package_name) if app_id is not None: app_id = util.normalize_marathon_id_path(app_id) msg += " with id [{}]".format(app_id) msg += " is not installed" raise DCOSException(msg) # Having `app == True` means that the user supplied an explicit `--app` # flag on the command line. Having `cli == True` means that the user # supplied an explicit `--cli` flag on the command line. If either of these # is `True`, run the following. if app or cli: # This forces an unconditional uninstall of the app associated with the # supplied package (with different semantics depending on the values of # `remove_all` and `app_id` as described in the docstring for this # function). if app and installed_app: if not pkg.uninstall_app(package_name, remove_all, app_id): raise DCOSException("Couldn't uninstall package") # This forces an unconditional uninstall of the CLI associated with the # supplied package. if cli and installed_cli: if not subcommand.uninstall(package_name): raise DCOSException("Couldn't uninstall subcommand") return # Having both `app == False` and `cli == False` means that the user didn't # supply either `--app` or `--cli` on the command line. In this situation # we uninstall the app associated with the supplied package (if it exists) # just as if the user had explicitly passed `--app` on the command line. # However, we only uninstall the CLI associated with the package if the app # being uninstalled is the last one remaining on the system. Otherwise, we # leave the CLI in place so other instances of the app can continue to # interact with it. if installed_app: if not pkg.uninstall_app(package_name, remove_all, app_id): raise DCOSException("Couldn't uninstall package") if installed_cli and (remove_all or len(installed_app) <= 1): if not subcommand.uninstall(package_name): raise DCOSException("Couldn't uninstall subcommand")