class RequirementSpecification(solum_plan.Requirement): """CAMP v1.1 RequirementSpecification.""" fulfillment = api_types.MultiType(wtypes.text, ServiceSpecification) """Either a ServiceSpecification or a reference to a ServiceSpecification.""" def __init__(self, **kwargs): if 'fulfillment' in kwargs: fulfill = kwargs.get('fulfillment') if type(fulfill) is not str: kwargs['fulfillment'] = ServiceSpecification(**fulfill) super(RequirementSpecification, self).__init__(**kwargs)
class Artifact(wtypes.Base): """Artifact.""" name = wtypes.text "Name of the artifact." artifact_type = wtypes.text "Type of artifact." content = {wtypes.text: api_types.MultiType(wtypes.text, bool)} "Type specific content as a flat dict." ports = api_types.MultiType(api_types.PortType, [api_types.PortType], {wtypes.text: api_types.PortType}) "Type of ports which an app listens on" language_pack = wtypes.text "The Language pack required for this artifact." requirements = [Requirement] "List of requirements for the artifact." unittest_cmd = wtypes.text "Optional unit test command for the artifact." run_cmd = wtypes.text "The command for starting up user code when a DU is deployed." repo_token = wtypes.text "The OAuth token to access repository" def __init__(self, **kwargs): if 'requirements' in kwargs: kwargs['requirements'] = [ Requirement(**re) for re in kwargs.get('requirements', []) ] super(Artifact, self).__init__(**kwargs)
class Plan(api_types.Base): """Representation of an Plan file. The Plan resource is a representation of a Plan file. Plans are used to create Assembly resources. A Plan resource may be used to create an arbitrary number of Assembly instances. They use artifacts and services to indicate what will be used to generate the plan, and what services Solum can use to satisfy them. Note: Plan files are YAML and Plan resources are the REST representation of the Plan file after services have been matched to ones offered by Solum. """ artifacts = [Artifact] """List of artifacts defining the plan.""" services = [ServiceReference] """List of services needed by the plan.""" trigger_uri = wtypes.text """The trigger uri used to trigger the build of the plan""" parameters = { wtypes.text: api_types.MultiType(wtypes.text, six.integer_types, bool, float) } """User defined parameters""" def __init__(self, **kwargs): if 'artifacts' in kwargs: kwargs['artifacts'] = [ Artifact(**art) for art in kwargs.get('artifacts', []) ] if 'services' in kwargs: kwargs['services'] = [ ServiceReference(**sr) for sr in kwargs.get('services', []) ] super(Plan, self).__init__(**kwargs) @classmethod def sample(cls): return cls(uri='http://example.com/v1/plans/x1', name='Example-plan', type='plan', tags=['small'], artifacts=[{ 'name': 'My-python-app', 'artifact_type': 'git_pull', 'content': { 'href': 'git://example.com/project.git', 'private': False }, 'language_pack': uuidutils.generate_uuid(), 'requirements': [{ 'requirement_type': 'git_pull', 'fulfillment': 'id:build' }] }], services=[{ 'name': 'Build-Service', 'id': 'build', 'characteristics': ['python_build_service'] }], project_id='1dae5a09ef2b4d8cbf3594b0eb4f6b94', user_id='55f41cf46df74320b9486a35f5d28a11', trigger_uri='http://example.com/v1/triggers/1abc234', description='A plan with no services or artifacts shown') @classmethod def from_db_model(cls, m, host_url): json = m.as_dict() json['type'] = m.__tablename__ json['uri'] = '%s/v1/%s/%s' % (host_url, m.__resource__, m.uuid) if m.raw_content is not None: json.update(m.raw_content) del json['id'] return cls(**(json)) def as_dict(self, db_model): base = super(Plan, self).as_dict(db_model) if self.artifacts is not wsme.Unset: base.update({ 'artifacts': [wjson.tojson(Artifact, art) for art in self.artifacts] }) if self.services is not wsme.Unset: base.update({ 'services': [wjson.tojson(ServiceReference, ref) for ref in self.services] }) if self.parameters is not wsme.Unset: base.update({'parameters': self.parameters}) return base
def test_invalid_values(self): vt = api_types.MultiType(wtypes.text, bool) self.assertRaises(ValueError, vt.validate, 10) self.assertRaises(ValueError, vt.validate, 0.10) self.assertRaises(ValueError, vt.validate, object())
def test_valid_values(self): vt = api_types.MultiType(wtypes.text, bool) value = vt.validate("somestring") self.assertEqual("somestring", value) value = vt.validate(True) self.assertEqual(True, value)
def test_multitype_tostring(self): vt = api_types.MultiType(wtypes.text, bool) vts = str(vt) self.assertIn(str(wtypes.text), vts) self.assertIn(str(bool), vts) self.assertNotIn(str(six.integer_types[0]), vts)
class LanguagePack(api_types.Base): """Representation of a language pack. When a user creates an application, he specifies the language pack to be used. The language pack is responsible for building the application and producing an artifact for deployment. For a complete list of language pack attributes please refer: https://etherpad.openstack.org/p/Solum-Language-pack-json-format """ def __init__(self, **kwds): self.__name = wsme.Unset super(LanguagePack, self).__init__(**kwds) def get_name(self): return self.__name def set_name(self, value): if len(value) > 100: raise ValueError( _('Names must not be longer than 100 ' 'characters')) allowed_chars = string.ascii_lowercase + string.digits + '-_' for ch in value: if ch not in allowed_chars: raise ValueError(_('Names must only contain a-z,0-9,-,_')) self.__name = value name = wtypes.wsproperty(str, get_name, set_name, mandatory=True) language_pack_type = wtypes.text """Type of the language pack. Identifies the language supported by the language pack. This attribute value will use the org.openstack.solum namespace. """ compiler_versions = [wtypes.text] """List of all the compiler versions supported by the language pack. Example: For a java language pack supporting Java versions 1.4 to 1.7, version = ['1.4', '1.6', '1.7'] """ runtime_versions = [wtypes.text] """List of all runtime versions supported by the language pack. Runtime version can be different than compiler version. Example: An application can be compiled with java 1.7 but it should run in java 1.6 as it is backward compatible. """ language_implementation = wtypes.text """Actual language implementation supported by the language pack. Example: In case of java it might be 'Sun' or 'openJava' In case of C++ it can be 'gcc' or 'icc' or 'microsoft'. """ build_tool_chain = [BuildTool] """Toolchain available in the language pack. Example: For a java language pack which supports Ant and Maven, build_tool_chain = ["{type:ant,version:1.7}","{type:maven,version:1.2}"] """ os_platform = {str: wtypes.text} """OS and its version used by the language pack. This attribute identifies the base image of the language pack. """ attributes = {str: wtypes.text} """Additional section attributes will be used to expose custom attributes designed by language pack creator. """ source_uri = wtypes.text """The URI of the app/element.""" source_format = SOURCE_KIND """The source repository format.""" status = STATE_KIND """The state of the image. """ base_image_id = wtypes.text """The id (in glance) of the image to customize.""" image_format = IMAGE_KIND """The image format.""" created_image_id = wtypes.text """The id of the created image in glance.""" lp_metadata = wtypes.text """The languagepack meta data.""" """Parameters that can be used as part of lp building process.""" lp_params = wtypes.DictType( wtypes.text, wtypes.DictType( wtypes.text, api_types.MultiType(wtypes.text, six.integer_types, bool, float))) @classmethod def from_image(cls, image, host_url): as_dict = {} image_id = image['id'] as_dict['uuid'] = image_id as_dict['name'] = image['name'] as_dict['type'] = 'language_pack' as_dict['uri'] = '%s/v1/%s/%s' % (host_url, 'language_packs', image_id) image_tags = image['tags'] comp_versions = [] run_versions = [] build_tools = [] attrs = {} for tag in image_tags: if tag.startswith(DESCRIPTION): as_dict['description'] = tag[len(DESCRIPTION):] if tag.startswith(TYPE): as_dict['language_pack_type'] = tag[len(TYPE):] if tag.startswith(COMPILER_VERSION): comp_versions.append(tag[len(COMPILER_VERSION):]) if tag.startswith(RUNTIME_VERSION): run_versions.append(tag[len(RUNTIME_VERSION):]) if tag.startswith(IMPLEMENTATION): as_dict['language_implementation'] = tag[len(IMPLEMENTATION):] if tag.startswith(BUILD_TOOL): bt_type, bt_version = tag[len(BUILD_TOOL):].split('::') build_tool = BuildTool(type=bt_type, version=bt_version) build_tools.append(build_tool) if tag.startswith(OS_PLATFORM): osp_type, osp_version = tag[len(OS_PLATFORM):].split('::') os_platform = {'OS': osp_type, 'version': osp_version} as_dict['os_platform'] = os_platform if tag.startswith(ATTRIBUTE): key, value = tag[len(ATTRIBUTE):].split('::') attrs[key] = value as_dict['attributes'] = attrs as_dict['compiler_versions'] = comp_versions as_dict['runtime_versions'] = run_versions as_dict['build_tool_chain'] = build_tools return cls(**(as_dict)) def as_image_dict(self): tags = ['solum::lp'] if self.description is not wsme.Unset: tags.append(DESCRIPTION + self.description) if self.language_pack_type is not wsme.Unset: tags.append(TYPE + self.language_pack_type) if self.compiler_versions is not wsme.Unset: for cv in self.compiler_versions: tags.append(COMPILER_VERSION + cv) if self.runtime_versions is not wsme.Unset: for rv in self.runtime_versions: tags.append(RUNTIME_VERSION + rv) if self.language_implementation is not wsme.Unset: tags.append(IMPLEMENTATION + self.language_implementation) if self.build_tool_chain is not wsme.Unset: for bt in self.build_tool_chain: tags.append(BUILD_TOOL + bt.type + '::' + bt.version) ptfm = self.os_platform if ptfm is not wsme.Unset and 'OS' in ptfm and 'version' in ptfm: tags.append(OS_PLATFORM + ptfm['OS'] + '::' + ptfm['version']) if self.build_tool_chain is not wsme.Unset: for key, value in self.attributes.items(): tags.append(ATTRIBUTE + key + '::' + value) # TODO(julienvey) parse specific attributes for image creation from # self.attributes, such as image_format... return {'name': self.name, 'tags': tags} @classmethod def sample(cls): return cls( uri='http://example.com/v1/images/b3e0d79', source_uri='git://example.com/project/app.git', source_format='heroku', name='php-web-app', type='languagepack', description='A php web application', tags=['group_xyz'], project_id='1dae5a09ef2b4d8cbf3594b0eb4f6b94', user_id='55f41cf46df74320b9486a35f5d28a11', base_image_id='4dae5a09ef2b4d8cbf3594b0eb4f6b94', created_image_id='4afasa09ef2b4d8cbf3594b0ec4f6b94', image_format='docker', language_pack_name='java-1.4-1.7', language_pack_type='org.openstack.solum.Java', language_pack_id='123456789abcdef', compiler_versions=['1.4', '1.6', '1.7'], runtime_versions=['1.4', '1.6', '1.7'], language_implementation='Sun', build_tool_chain=[ BuildTool(type='ant', version='1.7'), BuildTool(type='maven', version='1.2') ], os_platform={ 'OS': 'Ubuntu', 'version': '12.04' }, attributes={ 'optional_attr1': 'value', 'admin_email': '*****@*****.**' }, )
class App(api_types.Base): """Representation of an App. The App is the primary resource managed by Solum. """ # Inherited fields: # uri # uuid # name is overridden. # type # description # tags # project_id # user_id version = wtypes.wsattr(int) id = wtypes.text name = wtypes.text deleted = bool languagepack = wtypes.text stack_id = wtypes.text ports = api_types.MultiType(api_types.PortType, [api_types.PortType], {wtypes.text: api_types.PortType}) source = {wtypes.text: wtypes.text} workflow_config = {wtypes.text: wtypes.text} trigger_uuid = wtypes.text trigger_actions = [wtypes.text] trigger_uri = wtypes.text trust_id = wtypes.text trust_user = wtypes.text app_url = wtypes.text status = wtypes.text repo_token = wtypes.text created_at = datetime.datetime parameters = { wtypes.text: api_types.MultiType(wtypes.text, six.integer_types, bool, float) } """User defined parameters""" def __init__(self, *args, **kwargs): super(App, self).__init__(*args, **kwargs) @classmethod def sample(cls): return cls( name="sampleapp", description="sample app", ) @classmethod def from_db_model(cls, m, host_url): json = m.as_dict() json['type'] = m.__tablename__ json['uri'] = '%s/v1/apps/%s' % (host_url, m.id) json['trigger_uri'] = ('%s/v1/triggers/%s' % (host_url, m.trigger_uuid)) return cls(**(json)) def as_dict(self, db_model): attrs = [ 'name', 'id', 'description', 'languagepack', 'project_id', 'user_id', 'deleted', 'source', 'ports', 'trigger_actions', 'workflow_config', 'stack_id', ] base = super(App, self).as_dict(db_model) if self.parameters is not wsme.Unset: base.update({'parameters': self.parameters}) for a in attrs: if getattr(self, a) is wsme.Unset: continue if getattr(self, a) is None: continue base[a] = getattr(self, a) return base
class Workflow(wtypes.Base): """Representation of a Workflow. A workflow maintains a living creation and deployment of an App. """ # (devkulkarni) Added base_url to get around strict validation # checking of WSME 0.8.0 # https://bugs.launchpad.net/solum/+bug/1491504 # https://bugs.launchpad.net/solum/+bug/1491499 base_url = common_types.Uri "URI of the base resource." uri = common_types.Uri "URI to the resource." uuid = wtypes.text "Unique Identifier of the resource" type = wtypes.text "The resource type." id = wtypes.text updated_at = datetime.datetime created_at = datetime.datetime app_id = wtypes.text wf_id = int source = wtypes.DictType( wtypes.text, api_types.MultiType(wtypes.text, six.integer_types, bool, float)) config = {wtypes.text: wtypes.text} actions = [wtypes.text] du_id = wtypes.text status = wtypes.text result = wtypes.text scale_target = int def __init__(self, *args, **kwargs): super(Workflow, self).__init__(*args, **kwargs) @classmethod def sample(cls): return cls(wf_id=1, config={}, actions={}, source={}, status='') @classmethod def from_db_model(cls, m, host_url): json = m.as_dict() json['type'] = m.__tablename__ json['uri'] = '' json['uri'] = ('%s/v1/apps/%s/workflows/%s' % (host_url, m.app_id, m.wf_id)) return cls(**(json)) def as_dict_from_keys(self, keys): return dict((k, getattr(self, k)) for k in keys if hasattr(self, k) and getattr(self, k) != wsme.Unset) def as_dict(self, db_model): valid_keys = [ attr for attr in db_model.__dict__.keys() if attr[:2] != '__' and attr != 'as_dict' ] base = self.as_dict_from_keys(valid_keys) attrs = [ 'id', 'app_id', 'wf_id', 'source', 'config', 'actions', 'status', 'result', 'scale_target' ] for a in attrs: if getattr(self, a) is wsme.Unset: continue if getattr(self, a) is None: continue base[a] = getattr(self, a) return base