Skip to content

Commit

Permalink
Support boolean application create property (#304)
Browse files Browse the repository at this point in the history
Some helm charts wish to use global.application.create value
to control whether the Application resource should be included
in the generated manifests. This is the negation of the truthy
value of the existing APPLICATION_UID property.
  • Loading branch information
huyhg authored Jan 29, 2019
1 parent b7a4ceb commit 8b73ef6
Show file tree
Hide file tree
Showing 9 changed files with 127 additions and 45 deletions.
51 changes: 50 additions & 1 deletion docs/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,54 @@ It defines how this object will be handled. Each type has a different set of pro
- `SERVICE_ACCOUNT`: The name of a pre-provisioned k8s `ServiceAccount`. If it does not exist, one is created.
- `STORAGE_CLASS`: The name of a pre-provisioned k8s `StorageClass`. If it does not exist, one is created.
- `STRING`: A string that needs special handling.
- `APPLICATION_UID`: The uuid of the created `Application` object.

---

### type: APPLICATION_UID

When the deployer runs, a placeholder `Application` object has already been created.
The property of this type will receive its uuid.

The template must also be ready handle the following two scenarios:
- It receives an falsy/empty value for this property. In this case, the template must
include an `Application` object in its manifest. This object will be applied
to update the placeholder.
- It receives a real uuid value for this property. In this case, the template must
NOT include an `Application` object in its manifest.

Some tools like helm do not allow an object to pre-exist when the manifests are applied.
Due to the existence of the placeholder, including an `Application` object in the manifest
will result in helm failing to install the chart.

```yaml
properties:
appUid:
type: string
x-google-marketplace:
type: APPLICATION_UID
applicationUid:
generatedProperties:
createApplicationBoolean: global.application.create
```

- `createApplicationBoolean`: Denotes the name of a property to receive a boolean value.
This boolean instructs the template whether it should include an `Application` resource
in its manifest. This is essentially the negated truthy value of the main property.

In your helm chart, based on the above example, you can do one of the following:

```yaml
{{- if not .Values.application_uid }}
# Application object definition
{{- end }}
# or ...
{{- if .Values.global.application.create }}
# Application object definition
{{- end }}
```

---

Expand All @@ -267,7 +315,8 @@ It defines how this object will be handled. Each type has a different set of pro
Example:

```yaml
dbPassword:
properties:
dbPassword:
type: string
x-google-marketplace:
type: GENERATED_PASSWORD
Expand Down
8 changes: 1 addition & 7 deletions marketplace/deployer_helm_tiller_base/bin/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,7 @@ NAMESPACE="$(/bin/print_config.py \
export NAME
export NAMESPACE

app_uid=$(kubectl get "applications/$NAME" \
--namespace="$NAMESPACE" \
--output=jsonpath='{.metadata.uid}')

/bin/expand_config.py --values_mode=raw --app_uid="$app_uid"

bin/deploy_internal.sh
/bin/deploy_internal.sh

patch_assembly_phase.sh --status="Success"

Expand Down
4 changes: 3 additions & 1 deletion marketplace/deployer_helm_tiller_base/bin/deploy_internal.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ app_api_version=$(kubectl get "applications/$NAME" \
# Log information and, at the same time, catch errors early and separately.
# This is a work around for the fact that process and command substitutions
# do not propagate errors.
/bin/expand_config.py --values_mode=raw --app_uid="$app_uid"
echo -n "Application UID: " && print_config.py --xtype APPLICATION_UID --key true && echo ""
echo "=== values.yaml ==="
print_config.py --output=yaml
Expand All @@ -36,11 +37,11 @@ for chart in /data/chart/*; do
# Note: This must be done out-of-band because the Application resource
# is created before the deployer is invoked, but helm does not handle
# pre-existing resources.
/bin/expand_config.py --values_mode=raw --app_uid=""
helm template \
--name="$NAME" \
--namespace="$NAMESPACE" \
--values=<(print_config.py --output=yaml) \
--set "$(print_config.py --xtype APPLICATION_UID --key true)=" \
"$chart" \
| yaml2json \
| jq 'select( .kind == "Application" )' \
Expand All @@ -52,6 +53,7 @@ for chart in /data/chart/*; do
# hook processing.
# Note: The local tiller process is configured with --storage=secret,
# which is recommended but not default behavior.
/bin/expand_config.py --values_mode=raw --app_uid="$app_uid"
helm tiller run "$NAMESPACE" -- \
helm install \
--name="$NAME" \
Expand Down
24 changes: 22 additions & 2 deletions marketplace/deployer_util/config_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ def __init__(self, name, dictionary, required):
self._required = required
self._default = dictionary.get('default', None)
self._x = dictionary.get(XGOOGLE, None)
self._application_uid = None
self._image = None
self._password = None
self._reporting_secret = None
Expand Down Expand Up @@ -197,9 +198,11 @@ def __init__(self, name, dictionary, required):
raise InvalidSchema('Property {} has {} without a type'.format(
name, XGOOGLE))
xt = self._x['type']
if xt in (XTYPE_NAME, XTYPE_NAMESPACE, XTYPE_APPLICATION_UID,
XTYPE_DEPLOYER_IMAGE):
if xt in (XTYPE_NAME, XTYPE_NAMESPACE, XTYPE_DEPLOYER_IMAGE):
pass
elif xt == XTYPE_APPLICATION_UID:
d = self._x.get('applicationUid', {})
self._application_uid = SchemaXApplicationUid(d)
elif xt == XTYPE_IMAGE:
d = self._x.get('image', {})
self._image = SchemaXImage(d)
Expand Down Expand Up @@ -250,6 +253,10 @@ def xtype(self):
return self._x['type']
return None

@property
def application_uid(self):
return self._application_uid

@property
def image(self):
return self._image
Expand Down Expand Up @@ -316,6 +323,19 @@ def __eq__(self, other):
return other._name == self._name and other._d == self._d


class SchemaXApplicationUid:
"""Wrapper class providing convenient access to APPLICATION_UID properties."""

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

@property
def application_create(self):
return self._application_create


class SchemaXImage:
"""Wrapper class providing convenient access to IMAGE and DEPLOYER_IMAGE properties."""

Expand Down
36 changes: 26 additions & 10 deletions marketplace/deployer_util/config_helper_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,32 @@ def test_namespace_type(self):
""")
self.assertIsNotNone(schema.properties['ns'])

def test_application_uid_type(self):
schema = config_helper.Schema.load_yaml("""
properties:
u:
type: string
x-google-marketplace:
type: APPLICATION_UID
""")
self.assertIsNotNone(schema.properties['u'].application_uid)
self.assertIsNone(schema.properties['u'].application_uid.application_create)

def test_application_uid_type_create_application(self):
schema = config_helper.Schema.load_yaml("""
properties:
u:
type: string
x-google-marketplace:
type: APPLICATION_UID
applicationUid:
generatedProperties:
createApplicationBoolean: application.create
""")
self.assertIsNotNone(schema.properties['u'].application_uid)
self.assertEqual('application.create',
schema.properties['u'].application_uid.application_create)

def test_image_type(self):
schema = config_helper.Schema.load_yaml("""
properties:
Expand Down Expand Up @@ -484,16 +510,6 @@ def test_reporting_secret(self):
""")
self.assertIsNotNone(schema.properties['rs'].reporting_secret)

def test_application_uid_type(self):
schema = config_helper.Schema.load_yaml("""
properties:
u:
type: string
x-google-marketplace:
type: APPLICATION_UID
""")
self.assertIsNotNone(schema.properties['u'])

def test_unknown_type(self):
self.assertRaises(
config_helper.InvalidSchema, lambda: config_helper.Schema.load_yaml("""
Expand Down
16 changes: 9 additions & 7 deletions marketplace/deployer_util/expand_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def main():
default='/data/final_values.yaml')
parser.add_argument(
'--app_uid',
help='The application UID for populating into APPLICATION_UID properties.',
help='The application UID for populating into APPLICATION_UID properties',
default='')
args = parser.parse_args()

Expand Down Expand Up @@ -76,12 +76,9 @@ def expand(values_dict, schema, app_uid=''):
result[k] = generate_password(prop.password)
continue

if v is None and prop.xtype == 'APPLICATION_UID':
if not app_uid:
raise InvalidProperty(
'Property {} is of type APPLICATION_UID, but --app_uid was not '
'specified.'.format(k, v))
result[k] = app_uid
if v is None and prop.application_uid:
result[k] = app_uid or ''
generate_properties_for_appuid(prop, app_uid, generated)
continue

if v is None and prop.default is not None:
Expand Down Expand Up @@ -130,6 +127,11 @@ def validate_value_types(values, schema):
k, prop.type, v))


def generate_properties_for_appuid(prop, value, result):
if prop.application_uid.application_create:
result[prop.application_uid.application_create] = False if value else True


def generate_properties_for_image(prop, value, result):
if prop.image.split_by_colon:
before_name, after_name = prop.image.split_by_colon
Expand Down
28 changes: 12 additions & 16 deletions marketplace/deployer_util/expand_config_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,24 +135,20 @@ def test_application_uid(self):
type: string
x-google-marketplace:
type: APPLICATION_UID
applicationUid:
generatedProperties:
createApplicationBoolean: application.create
""")
result = expand_config.expand({}, schema, app_uid='1234-abcd')
self.assertEqual({'application_uid': '1234-abcd'}, result)

def test_application_uid_raises_when_unspecified(self):
schema = config_helper.Schema.load_yaml("""
applicationApiVersion: v1beta1
properties:
application_uid:
type: string
x-google-marketplace:
type: APPLICATION_UID
""")
self.assertRaises(
expand_config.InvalidProperty,
expand_config.expand, {},
schema,
app_uid=None)
self.assertEqual({
'application_uid': '1234-abcd',
'application.create': False
}, result)
result = expand_config.expand({}, schema, app_uid='')
self.assertEqual({
'application_uid': '',
'application.create': True
}, result)

def test_write_values(self):
schema = config_helper.Schema.load_yaml("""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{{- if not .Values.application_uid }}
{{- if .Values.global.application.create }}

apiVersion: app.k8s.io/v1beta1
kind: Application
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ properties:
type: string
x-google-marketplace:
type: APPLICATION_UID
applicationUid:
generatedProperties:
createApplicationBoolean: global.application.create
marketplace-integration.image:
type: string
x-google-marketplace:
Expand Down

0 comments on commit 8b73ef6

Please sign in to comment.