def Run(self, args): """Run 'operations wait'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Raises: HttpException: An http error response was received while executing api request. Raises: DeploymentManagerError: Operation finished with error(s) or timed out. """ project = properties.VALUES.core.project.Get(required=True) failed_ops = [] for operation_name in args.operation_name: try: dm_v2_util.WaitForOperation(operation_name, project, self.context, '', OPERATION_TIMEOUT) except DeploymentManagerError: failed_ops.append(operation_name) if failed_ops: if len(failed_ops) == 1: raise DeploymentManagerError( 'Operation %s failed to complete or has errors.' % failed_ops[0]) else: raise DeploymentManagerError( 'Some operations failed to complete without errors:\n' + '\n'.join(failed_ops)) else: log.status.Print('All operations completed successfully.')
def WaitForOperation(operation_name, project, context, operation_description, timeout=None): """Wait for an operation to complete. Polls the operation requested approximately every second, showing a progress indicator. Returns when the operation has completed. Args: operation_name: The name of the operation to wait on, as returned by operations.list. project: The name of the project that this operation belongs to. context: Context object with messages and client to access the deploymentmanager service. operation_description: A short description of the operation to wait on, such as 'create' or 'delete'. Will be displayed to the user. timeout: Optional (approximate) timeout in seconds, after which wait will return failure. Raises: HttpException: A http error response was received while executing api request. Will be raised if the operation cannot be found. DeploymentManagerError: The operation finished with error(s) or exceeded the timeout without completing. """ client = context['deploymentmanager-client'] messages = context['deploymentmanager-messages'] ticks = 0 message = ('Waiting for ' + ('{0} '.format(operation_description) if operation_description else '') + operation_name) with console_io.ProgressTracker(message, autotick=False) as ticker: while timeout is None or ticks < timeout: ticks += 1 try: operation = client.operations.Get( messages.DeploymentmanagerOperationsGetRequest( project=project, operation=operation_name, )) except apitools_exceptions.HttpError as error: raise HttpException(GetError(error)) ticker.Tick() # Operation status will be one of PENDING, RUNNING, DONE if operation.status == 'DONE': if operation.error: raise DeploymentManagerError('Error in Operation ' + operation_name + ': ' + str(operation.error)) else: # Operation succeeded return time.sleep(1) # wait one second and try again # Timeout exceeded raise DeploymentManagerError('Wait for Operation ' + operation_name + ' exceeded timeout.')
def BuildTargetConfigFromManifest(client, messages, project_id, deployment_id, manifest_id, properties=None): """Construct a TargetConfig from a manifest of a previous deployment. Args: client: Deployment Manager v2 API client. messages: Object with v2 API messages. project_id: Project for this deployment. This is used when pulling the the existing manifest. deployment_id: Deployment used to pull retrieve the manifest. manifest_id: The manifest to pull down for constructing the target. properties: Dictionary of properties, only used if the manifest has a single resource. Properties will override only. If the manifest has properties which do not exist in the properties hash will remain unchanged. Returns: TargetConfig containing the contents of the config file and the names and contents of any imports. Raises: HttpException: in the event that there is a failure to pull the manifest from deployment manager DeploymentManagerError: When the manifest being revived has more than one resource """ try: manifest = client.manifests.Get( messages.DeploymentmanagerManifestsGetRequest( project=project_id, deployment=deployment_id, manifest=manifest_id, )) config_file = manifest.config imports = manifest.imports except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error) # If properties were specified, then we need to ensure that the # configuration in the manifest retrieved has only a single resource. if properties: config_yaml = yaml.load(config_file.content) if len(config_yaml['resources']) != 1: raise DeploymentManagerError( 'Manifest reuse with properties requires ' 'there only be a single resource.') single_resource = config_yaml['resources'][0] if not single_resource.has_key('properties'): single_resource['properties'] = {} existing_properties = single_resource['properties'] for key, value in properties.iteritems(): existing_properties[key] = value config_file.content = yaml.dump(config_yaml, default_flow_style=False) return messages.TargetConfiguration(config=config_file, imports=imports)
def _BuildConfig(full_path, properties): """Takes the argument from the --config flag, and returns a processed config. Args: full_path: Path to the config yaml file, with an optional list of imports. properties: Dictionary of properties, only used if the file is a template. Returns: A tuple of base_path, config_contents, and a list of import objects. Raises: DeploymentManagerError: If using the properties flag for a config file instead of a template. """ config_obj = _BuildImportObject(full_path) if not config_obj.IsTemplate(): if properties: raise DeploymentManagerError( 'The properties flag should only be used ' 'when passing in a template as your config file.') return config_obj # Otherwise we should build the config from scratch. base_name = config_obj.GetBaseName() # Build the single template resource. custom_resource = {'type': base_name, 'name': _SanitizeBaseName(base_name)} # Attach properties if we were given any. if properties: custom_resource['properties'] = properties # Add the import and single resource together into a config file. custom_dict = {'imports': [{'path': base_name},], 'resources': [custom_resource,]} # Dump using default_flow_style=False to use spacing instead of '{ }' custom_content = yaml.dump(custom_dict, default_flow_style=False) # Override the template_object with it's new config_content return config_obj.SetContent(custom_content)
def _SanitizeLimitFlag(limit): """Sanitizes and returns a limit flag value. Args: limit: the limit flag value to sanitize. Returns: Sanitized limit flag value. Raises: DeploymentManagerError: if the provided limit flag value is not a positive integer. """ if limit is None: limit = sys.maxint else: if limit > sys.maxint: limit = sys.maxint elif limit <= 0: raise DeploymentManagerError( '--limit must be a positive integer; received: {0}'.format( limit)) return limit
def _BuildConfig(full_path, properties): """Takes the argument from the --config flag, and returns a processed config. Args: full_path: Path to the config yaml file, with an optional list of imports. properties: Dictionary of properties, only used if the file is a template. Returns: A tuple of base_path, config_contents, and a list of import objects. Raises: DeploymentManagerError: If using the properties flag for a config file instead of a template. """ config_obj = _BuildImportObject(full_path) if not config_obj.IsTemplate(): if properties: raise DeploymentManagerError( 'The properties flag should only be used ' 'when passing in a template as your config file.') return config_obj # Otherwise we should build the config from scratch. base_name = config_obj.GetBaseName() # Build the single template resource. custom_resource = {'type': base_name, 'name': _SanitizeBaseName(base_name)} # Attach properties if we were given any. if properties: custom_resource['properties'] = properties # Add the import and single resource together into a config file. custom_dict = { 'imports': [ { 'path': base_name }, ], 'resources': [ custom_resource, ] } custom_outputs = [] # Import the schema file and attach the outputs to config if there is any schema_path = config_obj.GetFullPath() + '.schema' schema_name = config_obj.GetName() + '.schema' schema_object = _BuildImportObject(schema_path, schema_name) if schema_object.Exists(): schema_content = schema_object.GetContent() config_name = custom_resource['name'] try: yaml_schema = yaml.safe_load(schema_content) if yaml_schema and OUTPUTS in yaml_schema: for output_name in yaml_schema[OUTPUTS].keys(): custom_outputs.append({ 'name': output_name, 'value': '$(ref.' + config_name + '.' + output_name + ')' }) except yaml.YAMLError as e: raise exceptions.BadFileException('Invalid schema file %s. %s' % (schema_path, str(e))) if custom_outputs: custom_dict['outputs'] = custom_outputs # Dump using default_flow_style=False to use spacing instead of '{ }' custom_content = yaml.dump(custom_dict, default_flow_style=False) # Override the template_object with it's new config_content return config_obj.SetContent(custom_content)