def push_command( workspace: Workspace, only: Optional[List[str]] = None, skip: Optional[List[str]] = None, only_workspace: bool = False, ) -> int: """Run the push command on the pushable resources and the workspace. """ if only_workspace: if isinstance(workspace, CentralWorkspaceMixin): raise ConfigurationError( "--only-workspace not valid for central workspace %s" % workspace.name ) resource_list = [] # type: List[LocalStateResourceMixin] else: resource_list = [ cast(LocalStateResourceMixin, workspace.get_resource(rn)) for rn in build_resource_list(workspace, only, skip) ] if isinstance(workspace, CentralWorkspaceMixin): if len(resource_list) == 0: click.echo("No resources to push.") return 0 else: print( "Pushing resources: %s" % ", ".join([cast(Resource, r).name for r in resource_list]) ) workspace.push_resources(resource_list) elif isinstance(workspace, SyncedWorkspaceMixin): if len(resource_list) > 0: click.echo( "Pushing workspace and resources: %s" % ", ".join([cast(Resource, r).name for r in resource_list]) ) elif not only_workspace: click.echo("No resources to push, will still push workspace") else: click.echo("Pushing workspace.") workspace.push(resource_list) workspace.save("push command") return len(resource_list)
def config_command( workspace: Workspace, param_name: Optional[str], param_value: Optional[str], resource: Optional[str], ): if param_name is None and param_value is None: names = [] scopes = [] values = [] isdefaults = [] helps = [] if resource is None: handlers = [ GlobalWorkspaceHandler(workspace), LocalWorkspaceHandler(workspace), ] # type: List[ParamConfigHandler] else: if resource not in workspace.get_resource_names(): raise ConfigurationError( "No resource in this workspace with name '%s'" % resource) resource_obj = workspace.get_resource(resource) handlers = [GlobalResourceHandler(resource_obj, workspace)] if isinstance(resource_obj, LocalStateResourceMixin): handlers.append(LocalResourceHandler(resource_obj, workspace)) for handler in handlers: for name in handler.defs.keys(): names.append(name) scopes.append(handler.get_scope()) helps.append(handler.defs[name].help) values.append(handler.get_value(name)) isdefaults.append("Y" if handler.is_default(name) else "N") print_columns( { "Name": names, "Scope": scopes, "Value": values, "Default?": isdefaults, "Description": helps, }, spec={"Description": ColSpec(width=40)}, paginate=False, ) click.echo() else: assert param_name is not None if resource is None: if param_name in PARAM_DEFS: handler = GlobalWorkspaceHandler(workspace) elif param_name in LOCAL_PARAM_DEFS: handler = LocalWorkspaceHandler(workspace) else: raise ParamNotFoundError("No workspace parameter named '%s'" % param_name) else: # resource-specific if resource not in workspace.get_resource_names(): raise ConfigurationError( "No resource in this workspace with name '%s'" % resource) resource_obj = workspace.get_resource(resource) if isinstance(resource_obj, LocalStateResourceMixin) and ( param_name in resource_obj.get_local_params()): handler = LocalResourceHandler(resource_obj, workspace) elif param_name in resource_obj.get_params().keys(): handler = GlobalResourceHandler(resource_obj, workspace) else: raise ParamNotFoundError( "Resource %s has no parameter named '%s'" % (resource, param_name)) if param_value is None: # just print for the specific param title = "%s parameter '%s'" % (handler.get_what_for().capitalize(), param_name) click.echo(title) click.echo("=" * len(title)) click.echo() print_columns( { "Value": [handler.get_value(param_name)], "Scope": [handler.get_scope()], "Default?": ["Y" if handler.is_default(param_name) else "N"], "Description": [handler.defs[param_name].help], }, spec={"Description": ColSpec(width=60)}, paginate=False, ) click.echo() else: # setting the parameter parsed_value = handler.defs[param_name].parse(param_value) handler.set_value(param_name, handler.defs[param_name].to_json(parsed_value)) param_for = handler.get_what_for() workspace.save("Update of %s parameter %s" % (param_for, param_name)) click.echo("Successfully set %s %s parameter '%s' to %s." % (param_for, handler.get_scope(), param_name, repr(parsed_value)))
def restore_command( workspace: Workspace, tag_or_hash: str, only: Optional[List[str]] = None, leave: Optional[List[str]] = None, strict: bool = False, ) -> int: """Run the restore and return the number of resources affected. """ if not isinstance(workspace, SnapshotWorkspaceMixin): raise ConfigurationError("Workspace %s does not support snapshots" % workspace.name) mixin = cast(SnapshotWorkspaceMixin, workspace) # First, find the history entry md = mixin.get_snapshot_by_tag_or_hash(tag_or_hash) # process the lists of resources current_names = set(workspace.get_resource_names()) # get the non-null resources in snapshot snapshot_names = set([ rn for rn in md.restore_hashes.keys() if md.restore_hashes[rn] is not None ]) all_names = current_names.union(snapshot_names) if (only is not None) and (leave is not None): raise ApiParamError( "Cannot specify both only and leave for restore command.") elif only is not None: # For only, we will be a little stricter, as the user is explicitly # specifying the resources. restore_set = set(only) strict = True elif leave is not None: restore_set = all_names.difference(leave) else: restore_set = all_names # We need to remove result resources from the restore set, as we # do not restore them to their prior state. result_resources = { rname for rname in restore_set if workspace.get_resource_role(rname) == ResourceRoles.RESULTS } result_resources_in_restore_set = result_resources.intersection( restore_set) if len(result_resources_in_restore_set) > 0: if strict: raise ConfigurationError( "Restore set contains result resources, which cannot be restored. The following are result resources: %s" % ", ".join(result_resources_in_restore_set)) else: click.echo( "Skipping the restore of the following result resources, which are left in their latest state: %s" % ", ".join(result_resources_in_restore_set)) restore_set = restore_set.difference(result_resources) # error checking invalid = restore_set.difference(all_names) if len(invalid) > 0: raise ConfigurationError("Resource name(s) not found: %s" % ", ".join(sorted(invalid))) removed_names = restore_set.difference(current_names) if len(removed_names) > 0: if strict: raise ConfigurationError( "Resources have been removed from workspace or have no restore hash and strict mode is enabled." + " Removed resources: %s" % ", ".join(sorted(removed_names))) else: click.echo( "Skipping restore of resources that have been removed from workspace or have no restore hash: %s" % ", ".join(sorted(removed_names)), err=True, ) restore_set = restore_set.difference(removed_names) added_names = restore_set.difference(snapshot_names) if len(added_names) > 0: if strict: raise ConfigurationError( "Resources have been added to workspace since restore, and strict mode enabled." + " Added resources: %s" % ", ".join(sorted(added_names))) else: click.echo( "Resources have been added to workspace since restore, will leave them as-is: %s" % ", ".join(sorted(added_names)), err=True, ) restore_set = restore_set.difference(added_names) # get ordered list of names and resources as well as restore hashes restore_name_list = [ rn for rn in workspace.get_resource_names() if rn in restore_set ] if len(restore_name_list) == 0: click.echo("No resources to restore.") return 0 restore_resource_list = [ workspace.get_resource(rn) for rn in restore_name_list ] for r in restore_resource_list: if not isinstance(r, SnapshotResourceMixin): raise InternalError( "Resource %s was in snapshot, but is not a SnapshotResourceMixin" % r.name) restore_hashes = {rn: md.restore_hashes[rn] for rn in restore_set} tagstr = " (%s)" % ",".join(md.tags) if len(md.tags) > 0 else "" click.echo("Restoring snapshot %s%s" % (md.hashval, tagstr)) def fmt_rlist(rnames): if len(rnames) > 0: return ", ".join(rnames) else: return "None" click.echo(" Resources to restore: %s" % fmt_rlist(restore_name_list)) names_to_leave = sorted(current_names.difference(restore_set)) click.echo(" Resources to leave: %s" % fmt_rlist(names_to_leave)) if not workspace.batch: # Unless in batch mode, we always want to ask for confirmation resp = input("Should I perform this restore? [Y/n]") if resp.lower() != "y" and resp != "": raise UserAbort() # do the work! mixin.restore(md.hashval, restore_hashes, cast(List[SnapshotResourceMixin], restore_resource_list)) workspace.save("Restore to %s" % md.hashval) return len(restore_name_list)