Skip to content
This repository has been archived by the owner on May 6, 2020. It is now read-only.

Adding lifecycle hooks to deis-controller api #1311

Open
wants to merge 29 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
eecc757
Adding initial changes to the api
Cryptophobia Jun 22, 2017
ad405a1
Adding the django migration for lifecycle hooks
Cryptophobia Jun 22, 2017
5c1b736
Final logic for the lifecycle hooks
Cryptophobia Jun 29, 2017
df2cd6b
Fix spacing
Jul 25, 2017
4ac2d66
Merge pull request #1 from madisonhope/master
Cryptophobia Jul 25, 2017
94d6c66
Fixing the broken config api tests
Cryptophobia Jul 25, 2017
7698eb9
Merge remote-tracking branch 'upstream/master'
Cryptophobia Aug 14, 2017
68ee0af
Adding procfile_structure to app api
Cryptophobia Aug 16, 2017
d93fb51
Fixing the test_app.py failure
Cryptophobia Aug 17, 2017
c449566
Adding exception msg to the release api with tests
Cryptophobia Aug 28, 2017
13f463e
Remove EOL warnings
Sep 7, 2017
f42af9b
Update URLs
Sep 7, 2017
c60680f
Fixes #1326 deis run need pods-create perms rbac
Oct 30, 2017
bf1618c
Adding a travis file for testing
Cryptophobia Nov 13, 2017
8a49bdf
Merge remote-tracking branch 'teamhephy/master'
Cryptophobia Nov 13, 2017
7010764
Rename file to .travis.yml
Cryptophobia Nov 13, 2017
ae3740f
Merge branch 'master' into feature/procfile-structure-on-app
Cryptophobia Nov 13, 2017
8a76272
Adding language as go to travis
Cryptophobia Nov 13, 2017
8fe90cf
Fixing an api test failure
Cryptophobia Nov 13, 2017
44684d5
Merge branch 'feature/procfile-structure-on-app' into feature/excepti…
Cryptophobia Nov 13, 2017
73f7e9e
Merge pull request #2 from teamhephy/feature/procfile-structure-on-app
Cryptophobia Nov 13, 2017
aef1f6b
Merge pull request #1 from teamhephy/feature/exceptions-on-release-api
Cryptophobia Nov 13, 2017
2134456
Merge pull request #5 from dmcnaught/master
Cryptophobia Nov 13, 2017
c0d3d35
Trying out the travis notifications to Slack
Cryptophobia Nov 14, 2017
ddbd8ac
add support for rbac v1 API endpoint
Dec 15, 2017
2bf9a23
Merge pull request #6 from n0n0x/add-rbac-v1-support
Cryptophobia Jan 31, 2018
7fadd87
fix(charts): int cast in version comparison
Jun 30, 2018
b924b33
Merge pull request #72 from kingdonb/patch-integer-cast
Cryptophobia Jul 11, 2018
877a7d9
fix(charts): set rbac apiVersion without casting
Cryptophobia Aug 8, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/PULL_REQUEST_TEMPLATE
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ requires deis/workflow#1234
requires deis/workflow-e2e#5678


[docs]: https://github.com/deis/workflow
[e2e]: https://github.com/deis/workflow-e2e
[docs]: https://github.com/deisthree/workflow
[e2e]: https://github.com/deisthree/workflow-e2e
8 changes: 8 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
sudo: required
language: go
services:
- docker
script: make test
notifications:
slack:
secure: pWDCV3od8gxvzxh9DrOTvBL54XoCfWYhZZlwd2ZbyyOz6SS12Psg/ZuCT2253p4yMfF/LPlsz76mr7NgcCrMI0ReveTa/rnt3XBZtyY+1rlsQsy2oxgdAzbO587ENCQeMw2F/OWHaixMT8NDqxEqQd6xafK9Zmg6BeBjwgs7XfXKcR3WzNIuCO0ZG05+Yd0FIxmd/8Xm5tGiFEYr05+Ix6MLdF9MSCXZUPeu1EsYXhDljokLq49w63W1UMU10tm4t7VCEdaO+X9w6EJ5Ov8HDxb6L6IviUYY6+IGTZ01nwIoM6OrGQqfEAytYqgTKdehgQzQnAbLI6TW2wJ0twqEsLrlbTa4NW4j0KkazQJkN5kqcKYQvaeKJJhvJIG44Gi/u78pW3S6W7NU5DhrlE6bbxdIBHJW1vJBimkqu2oBNrO5ZoBB9MS9zflBsU5g/pQpVeHWMnWE8fcYDGa1PqAcr7q6wtdPsrVZhnHmmARN3PwZzIVVVsXbaIQG8VLC5grLGnwMf1Y1fz2nK3sVpCftvrYZT3G6CNAASo+eLOwYdZdiJ9jIS7WNLN1GtpIEvjeDt3QRqsDyH8YoAKUvY5h/v8IWPP/BaSwQbJwep4+Dj7xkpXX5/4wm4jEnVFV1p4xE0lD1AXvEMAVHtPhhggvscNhF9j6oeoPju6eTPcxG+5o=
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ test-functional:
@echo "Implement functional tests in _tests directory"

test-integration:
@echo "Check https://github.com/deis/workflow-e2e for the complete integration test suite"
@echo "Check https://github.com/deisthree/workflow-e2e for the complete integration test suite"

upload-coverage:
$(eval CI_ENV := $(shell curl -s https://codecov.io/env | bash))
Expand Down
15 changes: 5 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@

|![](https://upload.wikimedia.org/wikipedia/commons/thumb/1/17/Warning.svg/156px-Warning.svg.png) | Deis Workflow will soon no longer be maintained.<br />Please [read the announcement](https://deis.com/blog/2017/deis-workflow-final-release/) for more detail. |
|---:|---|
| 09/07/2017 | Deis Workflow [v2.18][] final release before entering maintenance mode |
| 03/01/2018 | End of Workflow maintenance: critical patches no longer merged |

# Deis Controller

[![Build Status](https://ci.deis.io/job/controller/badge/icon)](https://ci.deis.io/job/controller)
Expand All @@ -13,7 +8,7 @@

Deis (pronounced DAY-iss) Workflow is an open source Platform as a Service (PaaS) that adds a developer-friendly layer to any [Kubernetes](http://kubernetes.io) cluster, making it easy to deploy and manage applications on your own servers.

For more information about the Deis Workflow, please visit the main project page at https://github.com/deis/workflow.
For more information about the Deis Workflow, please visit the main project page at https://github.com/deisthree/workflow.

We welcome your input! If you have feedback, please [submit an issue][issues]. If you'd like to participate in development, please read the "Development" section below and [submit a pull request][prs].

Expand Down Expand Up @@ -82,8 +77,8 @@ kubectl get pod --namespace=deis -w | grep deis-controller
```

[install-k8s]: https://kubernetes.io/docs/setup/pick-right-solution
[issues]: https://github.com/deis/controller/issues
[prs]: https://github.com/deis/controller/pulls
[workflow]: https://github.com/deis/workflow
[issues]: https://github.com/deisthree/controller/issues
[prs]: https://github.com/deisthree/controller/pulls
[workflow]: https://github.com/deisthree/workflow
[Docker]: https://www.docker.com/
[v2.18]: https://github.com/deis/workflow/releases/tag/v2.18.0
[v2.18]: https://github.com/deisthree/workflow/releases/tag/v2.18.0
2 changes: 1 addition & 1 deletion charts/controller/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: controller
home: https://github.com/deis/controller
home: https://github.com/deisthree/controller
version: <Will be populated by the ci before publishing the chart>
description: Deis Workflow Controller (API).
maintainers:
Expand Down
8 changes: 5 additions & 3 deletions charts/controller/templates/_helpers.tmpl
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
{{/*
Set apiVersion based on Kubernetes version
Set apiVersion based on .Capabilities.APIVersions
*/}}
{{- define "rbacAPIVersion" -}}
{{- if ge .Capabilities.KubeVersion.Minor "6" -}}
{{- if .Capabilities.APIVersions.Has "rbac.authorization.k8s.io/v1beta1" -}}
rbac.authorization.k8s.io/v1beta1
{{- else -}}
{{- else if .Capabilities.APIVersions.Has "rbac.authorization.k8s.io/v1alpha1" -}}
rbac.authorization.k8s.io/v1alpha1
{{- else -}}
rbac.authorization.k8s.io/v1
{{- end -}}
{{- end -}}
2 changes: 1 addition & 1 deletion charts/controller/templates/controller-clusterrole.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ rules:
verbs: ["get"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "delete"]
verbs: ["get", "list", "create", "delete"]
- apiGroups: [""]
resources: ["resourcequotas"]
verbs: ["get", "create"]
Expand Down
26 changes: 26 additions & 0 deletions rootfs/api/migrations/0024_config_lifecycle_hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-06-22 18:42
from __future__ import unicode_literals

from django.db import migrations
import jsonfield.fields


class Migration(migrations.Migration):

dependencies = [
('api', '0023_app_k8s_name_length'),
]

operations = [
migrations.AddField(
model_name='config',
name='lifecycle_post_start',
field=jsonfield.fields.JSONField(blank=True, default={}),
),
migrations.AddField(
model_name='config',
name='lifecycle_pre_stop',
field=jsonfield.fields.JSONField(blank=True, default={}),
),
]
22 changes: 22 additions & 0 deletions rootfs/api/migrations/0025_app_procfile_structure.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-08-14 20:45
from __future__ import unicode_literals

import api.models.app
from django.db import migrations
import jsonfield.fields


class Migration(migrations.Migration):

dependencies = [
('api', '0024_config_lifecycle_hooks'),
]

operations = [
migrations.AddField(
model_name='app',
name='procfile_structure',
field=jsonfield.fields.JSONField(blank=True, default={}, validators=[api.models.app.validate_app_structure]),
),
]
20 changes: 20 additions & 0 deletions rootfs/api/migrations/0026_release_exception.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-08-28 14:59
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api', '0025_app_procfile_structure'),
]

operations = [
migrations.AddField(
model_name='release',
name='exception',
field=models.TextField(blank=True, null=True),
),
]
15 changes: 15 additions & 0 deletions rootfs/api/models/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class App(UuidAuditedModel):
validators=[validate_app_id,
validate_reserved_names])
structure = JSONField(default={}, blank=True, validators=[validate_app_structure])
procfile_structure = JSONField(default={}, blank=True, validators=[validate_app_structure])

class Meta:
verbose_name = 'Application'
Expand Down Expand Up @@ -408,6 +409,7 @@ def scale(self, user, structure): # noqa
if new_structure != self.structure:
# save new structure to the database
self.structure = new_structure
self.procfile_structure = release.build.procfile
self.save()

try:
Expand Down Expand Up @@ -474,6 +476,7 @@ def deploy(self, release, force_deploy=False, rollback_on_failure=True): # noqa
# set processes structure to default if app is new.
if self.structure == {}:
self.structure = self._default_structure(release)
self.procfile_structure = self._default_structure(release)
self.save()
# reset canonical process types if build type has changed.
else:
Expand All @@ -489,8 +492,18 @@ def deploy(self, release, force_deploy=False, rollback_on_failure=True): # noqa
# update with the default process type.
structure.update(self._default_structure(release))
self.structure = structure
# if procfile structure exists then we use it
if release.build.procfile and \
release.build.sha and not \
release.build.dockerfile:
self.procfile_structure = release.build.procfile
self.save()

# always set the procfile structure for any new release
if release.build.procfile:
self.procfile_structure = release.build.procfile
self.save()

# deploy application to k8s. Also handles initial scaling
app_settings = self.appsettings_set.latest()
deploys = {}
Expand Down Expand Up @@ -1086,6 +1099,8 @@ def _gather_app_settings(self, release, app_settings, process_type, replicas):
'app_type': process_type,
'build_type': release.build.type,
'healthcheck': healthcheck,
'lifecycle_post_start': config.lifecycle_post_start,
'lifecycle_pre_stop': config.lifecycle_pre_stop,
'routable': routable,
'deploy_batches': batches,
'deploy_timeout': deploy_timeout,
Expand Down
2 changes: 2 additions & 0 deletions rootfs/api/models/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ def create(self, user, *args, **kwargs):
if 'new_release' in locals():
new_release.failed = True
new_release.summary = "{} deployed {} which failed".format(self.owner, str(self.uuid)[:7]) # noqa
# Get the exception that has occured
new_release.exception = "error: {}".format(str(e))
new_release.save()
else:
self.delete()
Expand Down
5 changes: 4 additions & 1 deletion rootfs/api/models/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class Config(UuidAuditedModel):
app = models.ForeignKey('App', on_delete=models.CASCADE)
values = JSONField(default={}, blank=True)
memory = JSONField(default={}, blank=True)
lifecycle_post_start = JSONField(default={}, blank=True)
lifecycle_pre_stop = JSONField(default={}, blank=True)
cpu = JSONField(default={}, blank=True)
tags = JSONField(default={}, blank=True)
registry = JSONField(default={}, blank=True)
Expand Down Expand Up @@ -162,7 +164,8 @@ def save(self, **kwargs):
# usually means a totally new app
previous_config = self.app.config_set.latest()

for attr in ['cpu', 'memory', 'tags', 'registry', 'values']:
for attr in ['cpu', 'memory', 'tags', 'registry', 'values',
'lifecycle_post_start', 'lifecycle_pre_stop']:
data = getattr(previous_config, attr, {}).copy()
new_data = getattr(self, attr, {}).copy()

Expand Down
37 changes: 37 additions & 0 deletions rootfs/api/models/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class Release(UuidAuditedModel):
version = models.PositiveIntegerField()
summary = models.TextField(blank=True, null=True)
failed = models.BooleanField(default=False)
exception = models.TextField(blank=True, null=True)

config = models.ForeignKey('Config', on_delete=models.CASCADE)
build = models.ForeignKey('Build', null=True, on_delete=models.CASCADE)
Expand Down Expand Up @@ -243,6 +244,8 @@ def rollback(self, user, version=None):
if 'new_release' in locals():
new_release.failed = True
new_release.summary = "{} performed roll back to a release that failed".format(self.owner) # noqa
# Get the exception that has occured
new_release.exception = "error: {}".format(str(e))
new_release.save()
raise DeisException(str(e)) from e

Expand Down Expand Up @@ -424,6 +427,40 @@ def save(self, *args, **kwargs): # noqa
changes = 'changed limits for '+', '.join(changes)
self.summary += "{} {}".format(self.config.owner, changes)

# if the lifecycle_post_start hooks changed, log the dict diff
changes = []
old_lifecycle_post_start = old_config.lifecycle_post_start if old_config else {}
diff = dict_diff(self.config.lifecycle_post_start, old_lifecycle_post_start)
# try to be as succinct as possible
added = ', '.join(k for k in diff.get('added', {}))
added = 'added lifecycle_post_start ' + added if added else ''
changed = ', '.join(k for k in diff.get('changed', {}))
changed = 'changed lifecycle_post_start ' + changed if changed else ''
deleted = ', '.join(k for k in diff.get('deleted', {}))
deleted = 'deleted lifecycle_post_start ' + deleted if deleted else ''
changes = ', '.join(i for i in (added, changed, deleted) if i)
if changes:
if self.summary:
self.summary += ' and '
self.summary += "{} {}".format(self.config.owner, changes)

# if the lifecycle_pre_stop hooks changed, log the dict diff
changes = []
old_lifecycle_pre_stop = old_config.lifecycle_pre_stop if old_config else {}
diff = dict_diff(self.config.lifecycle_pre_stop, old_lifecycle_pre_stop)
# try to be as succinct as possible
added = ', '.join(k for k in diff.get('added', {}))
added = 'added lifecycle_pre_stop ' + added if added else ''
changed = ', '.join(k for k in diff.get('changed', {}))
changed = 'changed lifecycle_pre_stop ' + changed if changed else ''
deleted = ', '.join(k for k in diff.get('deleted', {}))
deleted = 'deleted lifecycle_pre_stop ' + deleted if deleted else ''
changes = ', '.join(i for i in (added, changed, deleted) if i)
if changes:
if self.summary:
self.summary += ' and '
self.summary += "{} {}".format(self.config.owner, changes)

# if the tags changed, log the dict diff
changes = []
old_tags = old_config.tags if old_config else {}
Expand Down
7 changes: 5 additions & 2 deletions rootfs/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,12 @@ class AppSerializer(serializers.ModelSerializer):

owner = serializers.ReadOnlyField(source='owner.username')
structure = serializers.JSONField(required=False)
procfile_structure = serializers.JSONField(required=False)

class Meta:
"""Metadata options for a :class:`AppSerializer`."""
model = models.App
fields = ['uuid', 'id', 'owner', 'structure', 'created', 'updated']
fields = ['uuid', 'id', 'owner', 'structure', 'procfile_structure', 'created', 'updated']


class BuildSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -210,6 +211,8 @@ class ConfigSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
values = JSONFieldSerializer(required=False, binary=True)
memory = JSONFieldSerializer(required=False, binary=True)
lifecycle_post_start = JSONFieldSerializer(required=False, binary=True)
lifecycle_pre_stop = JSONFieldSerializer(required=False, binary=True)
cpu = JSONFieldSerializer(required=False, binary=True)
tags = JSONFieldSerializer(required=False, binary=True)
registry = JSONFieldSerializer(required=False, binary=True)
Expand Down Expand Up @@ -244,7 +247,7 @@ def validate_values(self, data):
if key == 'HEALTHCHECK_URL':
# Only Path information is supported, not query / anchor or anything else
# Path is the only thing Kubernetes supports right now
# See https://github.com/deis/controller/issues/774
# See https://github.com/deisthree/controller/issues/774
uri = urlparse(value)

if not uri.path:
Expand Down
3 changes: 2 additions & 1 deletion rootfs/api/tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ def test_response_data(self, mock_requests):
body = {'id': 'app-{}'.format(random.randrange(1000, 10000))}
response = self.client.post('/v2/apps', body)
for key in response.data:
self.assertIn(key, ['uuid', 'created', 'updated', 'id', 'owner', 'structure'])
self.assertIn(key, ['uuid', 'created', 'updated', 'id', 'owner', 'structure',
'procfile_structure'])
expected = {
'id': body['id'],
'owner': self.user.username,
Expand Down
2 changes: 1 addition & 1 deletion rootfs/api/tests/test_app_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ def test_settings_labels(self, mock_requests):
base_labels = {
'label':
{
'git_repo': 'https://github.com/deis/controller',
'git_repo': 'https://github.com/deisthree/controller',
'team': 'frontend',
'empty': ''
}
Expand Down
3 changes: 2 additions & 1 deletion rootfs/api/tests/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,5 +378,6 @@ def test_regenerate(self):
def test_auth_no_ldap_by_default(self, mock_logger):
"""Ensure that LDAP authentication is disabled by default."""
self.test_auth()
# NOTE(bacongobbler): Using https://github.com/deis/controller/issues/1189 as a test case
# NOTE(bacongobbler): Using https://github.com/deisthree/controller/issues/1189
# as a test case
mock_logger.warning.assert_not_called()
8 changes: 5 additions & 3 deletions rootfs/api/tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@ def test_response_data(self, mock_requests):
response = self.client.post(url, body)
for key in response.data:
self.assertIn(key, ['uuid', 'owner', 'created', 'updated', 'app', 'values', 'memory',
'cpu', 'tags', 'registry', 'healthcheck'])
'cpu', 'tags', 'registry', 'healthcheck', 'lifecycle_post_start',
'lifecycle_pre_stop'])
expected = {
'owner': self.user.username,
'app': app_id,
Expand All @@ -188,7 +189,8 @@ def test_response_data_types_converted(self, mock_requests):
self.assertEqual(response.status_code, 201, response.data)
for key in response.data:
self.assertIn(key, ['uuid', 'owner', 'created', 'updated', 'app', 'values', 'memory',
'cpu', 'tags', 'registry', 'healthcheck'])
'cpu', 'tags', 'registry', 'healthcheck', 'lifecycle_post_start',
'lifecycle_pre_stop'])
expected = {
'owner': self.user.username,
'app': app_id,
Expand Down Expand Up @@ -340,7 +342,7 @@ def test_admin_can_create_config_on_other_apps(self, mock_requests):
def test_config_owner_is_requesting_user(self, mock_requests):
"""
Ensure that setting the config value is owned by the requesting user
See https://github.com/deis/deis/issues/2650
See https://github.com/deisthree/deis/issues/2650
"""
response = self.test_admin_can_create_config_on_other_apps()
self.assertEqual(response.data['owner'], self.user.username)
Expand Down
Loading