Skip to content

Commit

Permalink
Adding TLS_CERTIFICATE property. (#337)
Browse files Browse the repository at this point in the history
  • Loading branch information
wgrzelak authored Apr 23, 2019
1 parent 51748fd commit 120d776
Show file tree
Hide file tree
Showing 16 changed files with 380 additions and 29 deletions.
65 changes: 64 additions & 1 deletion docs/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ Supports versions starting from `v1beta1`.
Represents the type of the input in the form for that property.

#### Supported types

- `string`
- `integer`
- `boolean`
Expand Down Expand Up @@ -240,7 +241,7 @@ A regex pattern. The value needs to match `pattern`.

### x-google-marketplace

This serves as an annotation to tell gcp to handle that property in a special way, depending on `type`.
This serves as an annotation to tell GCP to handle that property in a special way, depending on `type`.
It has several usages and more will be added based on demand.

### [Examples](https://github.com/GoogleCloudPlatform/marketplace-k8s-app-example/blob/master/wordpress/schema.yaml).
Expand All @@ -253,6 +254,7 @@ It has several usages and more will be added based on demand.
It defines how this object will be handled. Each type has a different set of properties.

#### Supported types

- `NAME`: To be used as the name of the app.
- `NAMESPACE`: To be used as the kubernetes namespace where the app will installed.
- `IMAGE`: Link to a docker image.
Expand All @@ -264,6 +266,7 @@ It defines how this object will be handled. Each type has a different set of pro
- `APPLICATION_UID`: The uuid of the created `Application` object.
- `ISTIO_ENABLED`: Indicates whether Istio is enabled for the deployment.
- `INGRESS_AVAILABLE`: Indicates whether the cluster is detected to have Ingress support.
- `TLS_CERTIFICATE`: To be used to support a custom certificate or generate a self-signed certificate.

---

Expand Down Expand Up @@ -421,6 +424,66 @@ This boolean property receives a True value if the cluster is detected to have I

---

### type: TLS_CERTIFICATE

This property provides an SSL/TLS certificate for the Kubernetes manifest. By default, a self-signed certificate is generated.

Example:

```yaml
properties:
certificate:
type: string
x-google-marketplace:
type: TLS_CERTIFICATE
tlsCertificate:
generatedProperties:
base64EncodedPrivateKey: TLS_CERTIFICATE_KEY
base64EncodedCertificate: TLS_CERTIFICATE_CRT
```

* `base64EncodedPrivateKey`: Denotes the name of a property receive a private key.
* `base64EncodedCertificate`: Denotes the name of a property receive a certificate.

You can provide your custom certificate by overwrite the `certificate` property in the JSON format as following:

```json
{
"private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----",
"certificate": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
}
```

In your helm chart, based on the above example, you can handle the certificate:

```yaml
apiVersion: v1
kind: Secret
metadata:
name: tls-secret
namespace: demo
data:
tls.key: {{ .Values.TLS_CERTIFICATE_KEY }}
tls.crt: {{ .Values.TLS_CERTIFICATE_CRT }}
type: kubernetes.io/tls
```

In your `envsubst` manifest, based on the above example, you can handle the certificate:

```yaml
apiVersion: v1
kind: Secret
metadata:
name: tls-secret
namespace: demo
data:
tls.key: $TLS_CERTIFICATE_KEY
tls.crt: $TLS_CERTIFICATE_CRT
type: kubernetes.io/tls
```

---

## clusterConstraints

Nested under `x-google-marketplace`, this can be used for specifying constraints on the kubernetes cluster. These constraints help determine if the application can be successfully deployed to the cluster. The UI can filter out ineligible clusters following these constraints.
Expand Down
1 change: 1 addition & 0 deletions marketplace/deployer_envsubst_base/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
gettext \
jq \
python \
python-openssl \
python-pip \
python-setuptools \
python-yaml \
Expand Down
1 change: 1 addition & 0 deletions marketplace/deployer_helm_base/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
gettext \
jq \
python \
python-openssl \
python-pip \
python-setuptools \
python-yaml \
Expand Down
1 change: 1 addition & 0 deletions marketplace/deployer_helm_tiller_base/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
jq \
procps \
python \
python-openssl \
python-pip \
python-setuptools \
python-yaml \
Expand Down
33 changes: 33 additions & 0 deletions marketplace/deployer_util/config_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
XTYPE_APPLICATION_UID = 'APPLICATION_UID'
XTYPE_ISTIO_ENABLED = 'ISTIO_ENABLED'
XTYPE_INGRESS_AVAILABLE = 'INGRESS_AVAILABLE'
XTYPE_TLS_CERTIFICATE = 'TLS_CERTIFICATE'

WIDGET_TYPES = ['help']

Expand Down Expand Up @@ -465,9 +466,11 @@ def __init__(self, name, dictionary, required):
self._service_account = None
self._storage_class = None
self._string = None
self._tls_certificate = None

if not NAME_RE.match(name):
raise InvalidSchema('Invalid property name: {}'.format(name))

self._type = _must_get_and_apply(
dictionary, 'type', lambda v: {
'int': int,
Expand All @@ -476,6 +479,7 @@ def __init__(self, name, dictionary, required):
'number': float,
'boolean': bool,
}.get(v, None), 'Property {} has no type'.format(name))

if not self._type:
raise InvalidSchema('Property {} has unsupported type: {}'.format(
name, dictionary['type']))
Expand All @@ -488,6 +492,7 @@ def __init__(self, name, dictionary, required):
if self._x:
xt = _must_get(self._x, 'type',
'Property {} has {} without a type'.format(name, XGOOGLE))

if xt in (XTYPE_NAME, XTYPE_NAMESPACE, XTYPE_DEPLOYER_IMAGE):
_property_must_have_type(self, str)
elif xt in (XTYPE_ISTIO_ENABLED, XTYPE_INGRESS_AVAILABLE):
Expand Down Expand Up @@ -525,6 +530,10 @@ def __init__(self, name, dictionary, required):
_property_must_have_type(self, str)
d = self._x.get('reportingSecret', {})
self._reporting_secret = SchemaXReportingSecret(d)
elif xt == XTYPE_TLS_CERTIFICATE:
_property_must_have_type(self, str)
d = self._x.get('tlsCertificate', {})
self._tls_certificate = SchemaXTlsCertificate(d)
else:
raise InvalidSchema('Property {} has an unknown type: {}'.format(
name, xt))
Expand Down Expand Up @@ -580,6 +589,10 @@ def storage_class(self):
def string(self):
return self._string

@property
def tls_certificate(self):
return self._tls_certificate

def str_to_type(self, str_val):
if self._type == bool:
if str_val in {'true', 'True', 'yes', 'Yes'}:
Expand Down Expand Up @@ -745,6 +758,26 @@ def __init__(self, dictionary):
pass


class SchemaXTlsCertificate:
"""Accesses TLS_CERTIFICATE property."""

def __init__(self, dictionary):
generated_properties = dictionary.get('generatedProperties', {})

self._base64_encoded_private_key = generated_properties.get(
'base64EncodedPrivateKey', None)
self._base64_encoded_certificate = generated_properties.get(
'base64EncodedCertificate', None)

@property
def base64_encoded_private_key(self):
return self._base64_encoded_private_key

@property
def base64_encoded_certificate(self):
return self._base64_encoded_certificate


def _must_get(dictionary, key, error_msg):
"""Gets the value of the key, or raises InvalidSchema."""
if key not in dictionary:
Expand Down
62 changes: 54 additions & 8 deletions marketplace/deployer_util/config_helper_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@
type: boolean
x-google-marketplace:
type: INGRESS_AVAILABLE
certificate:
type: string
x-google-marketplace:
type: TLS_CERTIFICATE
tlsCertificate:
generatedProperties:
base64EncodedPrivateKey: keyEncoded
base64EncodedCertificate: crtEncoded
required:
- propertyString
- propertyPassword
Expand Down Expand Up @@ -113,14 +121,16 @@ def load_and_validate(schema_yaml):

def test_types_and_defaults(self):
schema = config_helper.Schema.load_yaml(SCHEMA)
self.assertEqual({
'propertyString', 'propertyStringWithDefault', 'propertyInt',
'propertyIntWithDefault', 'propertyInteger',
'propertyIntegerWithDefault', 'propertyNumber',
'propertyNumberWithDefault', 'propertyBoolean',
'propertyBooleanWithDefault', 'propertyImage', 'propertyDeployerImage',
'propertyPassword', 'applicationUid', 'istioEnabled', 'ingressAvailable'
}, set(schema.properties))
self.assertEqual(
{
'propertyString', 'propertyStringWithDefault', 'propertyInt',
'propertyIntWithDefault', 'propertyInteger',
'propertyIntegerWithDefault', 'propertyNumber',
'propertyNumberWithDefault', 'propertyBoolean',
'propertyBooleanWithDefault', 'propertyImage',
'propertyDeployerImage', 'propertyPassword', 'applicationUid',
'istioEnabled', 'ingressAvailable', 'certificate'
}, set(schema.properties))
self.assertEqual(str, schema.properties['propertyString'].type)
self.assertIsNone(schema.properties['propertyString'].default)
self.assertEqual(str, schema.properties['propertyStringWithDefault'].type)
Expand Down Expand Up @@ -161,6 +171,8 @@ def test_types_and_defaults(self):
self.assertEqual(bool, schema.properties['ingressAvailable'].type)
self.assertEqual('INGRESS_AVAILABLE',
schema.properties['ingressAvailable'].xtype)
self.assertEqual(str, schema.properties['certificate'].type)
self.assertEqual('TLS_CERTIFICATE', schema.properties['certificate'].xtype)

def test_invalid_names(self):
self.assertRaises(
Expand Down Expand Up @@ -385,6 +397,40 @@ def test_password(self):
self.assertEqual(True, schema.properties['pw'].password.include_symbols)
self.assertEqual(False, schema.properties['pw'].password.base64)

def test_certificate(self):
schema = config_helper.Schema.load_yaml("""
properties:
c1:
type: string
x-google-marketplace:
type: TLS_CERTIFICATE
""")

self.assertIsNotNone(schema.properties['c1'].tls_certificate)
self.assertIsNone(
schema.properties['c1'].tls_certificate.base64_encoded_private_key)
self.assertIsNone(
schema.properties['c1'].tls_certificate.base64_encoded_certificate)

schema = config_helper.Schema.load_yaml("""
properties:
c1:
type: string
x-google-marketplace:
type: TLS_CERTIFICATE
tlsCertificate:
generatedProperties:
base64EncodedPrivateKey: c1.Base64Key
base64EncodedCertificate: c1.Base64Crt
""")
self.assertIsNotNone(schema.properties['c1'].tls_certificate)
self.assertEqual(
'c1.Base64Key',
schema.properties['c1'].tls_certificate.base64_encoded_private_key)
self.assertEqual(
'c1.Base64Crt',
schema.properties['c1'].tls_certificate.base64_encoded_certificate)

def test_int_type(self):
schema = config_helper.Schema.load_yaml("""
properties:
Expand Down
Loading

0 comments on commit 120d776

Please sign in to comment.