-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathbuild.py
More file actions
136 lines (116 loc) · 4.82 KB
/
build.py
File metadata and controls
136 lines (116 loc) · 4.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
from django.conf import settings
from django.db import models
from jsonfield import JSONField
from api.models import UuidAuditedModel
from api.exceptions import DeisException, Conflict
import logging
logger = logging.getLogger(__name__)
class Build(UuidAuditedModel):
"""
Instance of a software build used by runtime nodes
"""
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
app = models.ForeignKey('App', on_delete=models.CASCADE)
image = models.TextField()
# optional fields populated by builder
sha = models.CharField(max_length=40, blank=True)
procfile = JSONField(default={}, blank=True)
dockerfile = models.TextField(blank=True)
class Meta:
get_latest_by = 'created'
ordering = ['-created']
unique_together = (('app', 'uuid'),)
@property
def type(self):
"""Figures out what kind of build type is being deal it with"""
if self.dockerfile:
return 'dockerfile'
elif self.sha:
return 'buildpack'
else:
# docker image (or any sort of image) used via deis pull
return 'image'
@property
def source_based(self):
"""
Checks if a build is source (has a sha) based or not
If True then the Build is coming from the deis builder or something that
built from git / svn / hg / etc directly
"""
return self.sha != ''
@property
def version(self):
return 'git-{}'.format(self.sha) if self.source_based else 'latest'
def create(self, user, *args, **kwargs):
latest_release = self.app.release_set.filter(failed=False).latest()
latest_version = self.app.release_set.latest().version
try:
new_release = latest_release.new(
user,
build=self,
config=latest_release.config,
source_version=self.version
)
self.app.deploy(new_release)
return new_release
except Exception as e:
# check if the exception is during create or publish
if ('new_release' not in locals() and
self.app.release_set.latest().version == latest_version+1):
new_release = self.app.release_set.latest()
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()
raise DeisException(str(e)) from e
def save(self, **kwargs):
previous_release = self.app.release_set.filter(failed=False).latest()
if (
settings.DEIS_DEPLOY_REJECT_IF_PROCFILE_MISSING is True and
# previous release had a Procfile and the current one does not
(
previous_release.build is not None and
len(previous_release.build.procfile) > 0 and
len(self.procfile) == 0
)
):
# Reject deployment
raise Conflict(
'Last deployment had a Procfile but is missing in this deploy. '
'For a successful deployment provide a Procfile.'
)
# See if processes are permitted to be removed
remove_procs = (
# If set to True then contents of Procfile does not affect the outcome
settings.DEIS_DEPLOY_PROCFILE_MISSING_REMOVE is True or
# previous release had a Procfile and the current one does as well
(
previous_release.build is not None and
len(previous_release.build.procfile) > 0 and
len(self.procfile) > 0
)
)
# spin down any proc type removed between the last procfile and the newest one
if remove_procs and previous_release.build is not None:
removed = {}
for proc in previous_release.build.procfile:
if proc not in self.procfile:
# Scale proc type down to 0
removed[proc] = 0
self.app.scale(self.owner, removed)
# make sure the latest build has procfile if the intent is to
# allow empty Procfile without removals
if (
settings.DEIS_DEPLOY_PROCFILE_MISSING_REMOVE is False and
previous_release.build is not None and
len(previous_release.build.procfile) > 0 and
len(self.procfile) == 0
):
self.procfile = previous_release.build.procfile
return super(Build, self).save(**kwargs)
def __str__(self):
return "{0}-{1}".format(self.app.id, str(self.uuid)[:7])