diff --git a/deckhand/engine/schema/v1_0/default_schema.py b/deckhand/engine/schema/v1_0/default_schema.py index fa403bc1..b508df5d 100644 --- a/deckhand/engine/schema/v1_0/default_schema.py +++ b/deckhand/engine/schema/v1_0/default_schema.py @@ -15,19 +15,29 @@ substitution_schema = { 'type': 'object', 'properties': { - 'dest': {'type': 'string'}, + 'dest': { + 'type': 'object', + 'properties': { + 'path': {'type': 'string'}, + 'replacePattern': {'type': 'string'} + }, + 'additionalProperties': False, + # 'replacePattern' is not required. + 'required': ['path'] + }, 'src': { 'type': 'object', 'properties': { 'apiVersion': { 'type': 'string', - 'choices': ['deckhand/v1'] + 'pattern': '^([A-Za-z]+\/v[0-9]{1})$' }, 'kind': {'type': 'string'}, - 'name': {'type': 'string'} + 'name': {'type': 'string'}, + 'path': {'type': 'string'} }, 'additionalProperties': False, - 'required': ['apiVersion', 'kind', 'name'] + 'required': ['apiVersion', 'kind', 'name', 'path'] } }, 'additionalProperties': False, @@ -37,31 +47,58 @@ substitution_schema = { schema = { 'type': 'object', 'properties': { - 'apiVersion': { + 'schemaVersion': { 'type': 'string', 'pattern': '^([A-Za-z]+\/v[0-9]{1})$' }, - 'kind': { - 'type': 'string', - 'pattern': '^([A-Za-z]+)$' - }, + 'kind': {'type': 'string'}, 'metadata': { 'type': 'object', 'properties': { + 'metadataVersion': { + 'type': 'string', + 'pattern': '^([A-Za-z]+\/v[0-9]{1})$' + }, 'name': {'type': 'string'}, - 'storage': {'type': 'string'}, + 'labels': { + 'type': 'object', + 'properties': { + 'component': {'type': 'string'}, + 'hostname': {'type': 'string'} + }, + 'additionalProperties': False, + 'required': ['component', 'hostname'] + }, + 'layerDefinition': { + 'type': 'object', + 'properties': { + 'layer': {'enum': ['global', 'region', 'local']}, + 'abstract': {'type': 'boolean'}, + 'childSelector': { + 'type': 'object', + 'properties': { + 'label': {'type': 'string'} + }, + 'additionalProperties': False, + 'required': ['label'] + } + }, + 'additionalProperties': False, + 'required': ['layer', 'abstract', 'childSelector'] + }, 'substitutions': { 'type': 'array', 'items': substitution_schema } }, 'additionalProperties': False, - 'required': ['name', 'storage', 'substitutions'] + 'required': ['metadataVersion', 'name', 'labels', + 'layerDefinition', 'substitutions'] }, 'data': { 'type': 'object' } }, 'additionalProperties': False, - 'required': ['apiVersion', 'kind', 'metadata', 'data'] + 'required': ['schemaVersion', 'kind', 'metadata', 'data'] } diff --git a/deckhand/engine/secret_substitution.py b/deckhand/engine/secret_substitution.py index b959dcf7..b288f291 100644 --- a/deckhand/engine/secret_substitution.py +++ b/deckhand/engine/secret_substitution.py @@ -61,33 +61,7 @@ class SecretSubstitution(object): if v['version'] == self.schema_version][0].schema def validate_data(self): - """Pre-validate that the YAML file is correctly formatted. - - The YAML file must adhere to the following bare minimum format: - - .. code-block:: yaml - - --- - apiVersion: service/v1 - kind: ConsumerOfCertificateData - metadata: - substitutions: - - dest: .tls_endpoint.certificate - src: - apiVersion: deckhand/v1 - kind: Certificate - name: some-certificate-asdf-1234 - # Forward-reference to specific section under "data" below. - - dest: .tls_endpoint.certificateKey - src: - apiVersion: deckhand/v1 - kind: CertificateKey - name: some-certificate-key-asdf-1234 - data: - tls_endpoint: - certificate: null # Data to be substituted. - certificateKey: null # Data to be substituted. - """ + """Pre-validate that the YAML file is correctly formatted.""" self._validate_with_schema() # Validate that each "dest" field exists in the YAML data. @@ -96,7 +70,7 @@ class SecretSubstitution(object): sub_data = self.data['data'] for dest in destinations: - result, missing_attr = self._multi_getattr(dest, sub_data) + result, missing_attr = self._multi_getattr(dest['path'], sub_data) if not result: raise errors.InvalidFormat( 'The attribute "%s" included in the "dest" field "%s" is ' @@ -109,7 +83,7 @@ class SecretSubstitution(object): # Validate that a schema with "apiVersion" version number exists, then # use that schema to validate the YAML data. try: - schema_version = self.data['apiVersion'].split('/')[-1] + schema_version = self.data['schemaVersion'].split('/')[-1] data_schema_version = self.SchemaVersion(schema_version) except (AttributeError, IndexError, KeyError) as e: raise errors.InvalidFormat( diff --git a/deckhand/tests/unit/engine/test_secret_substitution.py b/deckhand/tests/unit/engine/test_secret_substitution.py index 3e3eccbb..df017948 100644 --- a/deckhand/tests/unit/engine/test_secret_substitution.py +++ b/deckhand/tests/unit/engine/test_secret_substitution.py @@ -85,8 +85,8 @@ class TestSecretSubtitution(testtools.TestCase): invalid_data = [ (self._corrupt_data('data'), 'data'), (self._corrupt_data('metadata'), 'metadata'), + (self._corrupt_data('metadata.metadataVersion'), 'metadataVersion'), (self._corrupt_data('metadata.name'), 'name'), - (self._corrupt_data('metadata.storage'), 'storage'), (self._corrupt_data('metadata.substitutions'), 'substitutions'), (self._corrupt_data('metadata.substitutions.0.dest'), 'dest'), (self._corrupt_data('metadata.substitutions.0.src'), 'src') @@ -103,11 +103,12 @@ class TestSecretSubtitution(testtools.TestCase): invalid_data = [] data = copy.deepcopy(self.data) - data['metadata']['substitutions'][0]['dest'] = 'foo' + data['metadata']['substitutions'][0]['dest'] = {'path': 'foo'} invalid_data.append(self._format_data(data)) data = copy.deepcopy(self.data) - data['metadata']['substitutions'][0]['dest'] = 'tls_endpoint.bar' + data['metadata']['substitutions'][0]['dest'] = { + 'path': 'tls_endpoint.bar'} invalid_data.append(self._format_data(data)) def _test(invalid_entry, field, dest): @@ -117,6 +118,6 @@ class TestSecretSubtitution(testtools.TestCase): secret_substitution.SecretSubstitution(invalid_entry) # Verify that invalid body dest reference is invalid. - _test(invalid_data[0], "foo", "foo") + _test(invalid_data[0], "foo", {'path': 'foo'}) # Verify that nested invalid body dest reference is invalid. - _test(invalid_data[1], "bar", "tls_endpoint.bar") + _test(invalid_data[1], "bar", {'path': 'tls_endpoint.bar'}) diff --git a/deckhand/tests/unit/resources/sample.yaml b/deckhand/tests/unit/resources/sample.yaml index 0e921f63..6f2489ae 100644 --- a/deckhand/tests/unit/resources/sample.yaml +++ b/deckhand/tests/unit/resources/sample.yaml @@ -1,23 +1,35 @@ # Sample YAML file for testing forward replacement. --- -apiVersion: service/v1 -kind: ConsumerOfCertificateData +schemaVersion: promenade/v1 +kind: SomeConfigType metadata: - name: asdf-1234 - storage: cleartext + metadataVersion: deckhand/v1 + name: a-unique-config-name-12345 + labels: + component: apiserver + hostname: server0 + layerDefinition: + layer: global + abstract: True + childSelector: + label: value substitutions: - - dest: .tls_endpoint.certificate + - dest: + path: .tls_endpoint.certificate + replacePattern: 'test.pattern' src: apiVersion: deckhand/v1 kind: Certificate name: some-certificate-asdf-1234 - - dest: .tls_endpoint.certificateKey + path: .cert + - dest: + path: .tls_endpoint.key src: apiVersion: deckhand/v1 kind: CertificateKey - name: some-certificate-key-asdf-1234 + name: some-certificate-asdf-1234 + path: .key data: tls_endpoint: - uri: http://localhost:443 - certificate: null - certificateKey: null \ No newline at end of file + certificate: '.cert' + key: deckhand/v1:some-certificate-asdf-1234