templates/software_status.py (105 lines of code) (raw):

# Copyright 2016 Google Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """A DM template that generates software status resources and outputs. An example YAML showing how this template can be used: resources: - name: software-status type: software_status.py properties: timeout: 300 waiterDependsOn: - wordpress-vm - name: software-status-script type: software_status_script.py properties: checkType: bitnami - name: wordpress-vm type: vm_instance.py properties: instanceName: wordpress-vm serviceAccounts: - scopes: - 'https://www.googleapis.com/auth/cloudruntimeconfig' metadata: items: - key: status-config value: $(ref.software-status.config) - key: status-endpoint value: $(ref.software-status.endpoint) - key: status-path value: $(ref.software-status.path) - key: startup-script value: $(ref.software-status-script.startup-script) Input properties to this template: - timeout: optional. The time to wait for startup success. 5 minute default. - waiterDependsOn: optional. A list of waiter dependency names (e.g., VMs). """ import types import yaml RTC_ENDPOINT = 'https://runtimeconfig.googleapis.com/v1beta1' STATUS_PATH = 'status' DEFAULT_TIMEOUT = '300' # 5 minutes DEFAULT_SUCCESS_NUMBER = 1 DEFAULT_FAILURE_NUMBER = 1 class PropertyError(Exception): """An exception raised when property values are invalid.""" def _ConfigName(context): """Return the short config name.""" return '{}-config'.format(context.env['deployment']) def _ConfigUrl(context): """Returns the full URL to the config, including hostname.""" return '{endpoint}/projects/{project}/configs/{config}'.format( endpoint=RTC_ENDPOINT, project=context.env['project'], config=_ConfigName(context)) def _WaiterName(context): """Returns the short waiter name.""" # This name is only used for the DM manifest entry. The actual waiter name # within RuntimeConfig is static, as it is scoped to the config resource. return '{}-software'.format(context.env['deployment']) def _Timeout(context): """Returns the timeout property or a default value if unspecified.""" timeout = context.properties.get('timeout', DEFAULT_TIMEOUT) try: return str(int(timeout)) except ValueError: raise PropertyError('Invalid timeout value: {}'.format(timeout)) def _SuccessNumber(context): """Returns the successNumber property or a default value if unspecified.""" number = context.properties.get('successNumber', DEFAULT_SUCCESS_NUMBER) try: number = int(number) if number < 1: raise PropertyError('successNumber value must be greater than 0.') return number except ValueError: raise PropertyError('Invalid successNumber value: {}'.format(number)) def _FailureNumber(context): """Returns the failureNumber property or a default value if unspecified.""" number = context.properties.get('failureNumber', DEFAULT_FAILURE_NUMBER) try: number = int(number) if number < 1: raise PropertyError('failureNumber value must be greater than 0.') return number except ValueError: raise PropertyError('Invalid failureNumber value: {}'.format(number)) def _WaiterDependsOn(context): """Returns the waiterDependsOn property or an empty list if unspecified.""" depends_on = context.properties.get('waiterDependsOn', []) if not isinstance(depends_on, list): raise PropertyError('waiterDependsOn must be a list: {}'.format(depends_on)) for item in depends_on: if not isinstance(item, types.StringTypes): raise PropertyError( 'waiterDependsOn must be a list of strings: {}'.format(depends_on)) return depends_on def _RuntimeConfig(context): """Constructs a RuntimeConfig resource.""" deployment_name = context.env['deployment'] return { 'name': _ConfigName(context), 'type': 'runtimeconfig.v1beta1.config', 'properties': { 'config': _ConfigName(context), 'description': ('Holds software readiness status ' 'for deployment {}').format(deployment_name), }, } def _Waiter(context): """Constructs a waiter resource.""" waiter_timeout = _Timeout(context) return { 'name': _WaiterName(context), 'type': 'runtimeconfig.v1beta1.waiter', 'metadata': { 'dependsOn': _WaiterDependsOn(context), }, 'properties': { 'parent': '$(ref.{}.name)'.format(_ConfigName(context)), 'waiter': 'software', 'timeout': '{}s'.format(waiter_timeout), 'success': { 'cardinality': { 'number': _SuccessNumber(context), 'path': '{}/success'.format(STATUS_PATH), }, }, 'failure': { 'cardinality': { 'number': _FailureNumber(context), 'path': '{}/failure'.format(STATUS_PATH), }, }, }, } def GenerateConfig(context): """Entry function to generate the DM config.""" content = { 'resources': [ _RuntimeConfig(context), _Waiter(context), ], 'outputs': [ { 'name': 'config-url', 'value': _ConfigUrl(context) }, { 'name': 'variable-path', 'value': STATUS_PATH }, ] } return yaml.safe_dump(content)