-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathdeis-buildstep-hook.erb
More file actions
148 lines (140 loc) · 5.48 KB
/
Copy pathdeis-buildstep-hook.erb
File metadata and controls
148 lines (140 loc) · 5.48 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
137
138
139
140
141
142
143
144
145
146
147
148
#!/usr/bin/python
from yaml.error import YAMLError
import argparse
import getpass
import json
import os
import socket
import subprocess
import sys
import urllib2 as urllib
import uuid
import yaml
def parse_args():
desc = """
Process a git push by running it through the buildpack process
Note this script must be run as the `git` user.
"""
parser = argparse.ArgumentParser(description=desc,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('src', action='store',
help='path to source repository')
parser.add_argument('user', action='store',
help='name of user who started build',
default='system')
# check execution environment
if getpass.getuser() != 'git':
sys.stderr.write('This script must be run as the "git" user\n')
parser.print_help()
sys.exit(1)
args = parser.parse_args()
args.src = os.path.abspath(args.src)
# store formation ID
args.formation = '/'.join(args.src.split(os.path.sep)[-1:]).replace('.git', '')
return args
def _sizeof_fmt(num):
for x in ['bytes','KB','MB','GB','TB']:
if num < 1024.0:
return "%3.1f %s" % (num, x)
num /= 1024.0
def puts_step(s):
sys.stdout.write("-----> %s\n" % s)
sys.stdout.flush()
def puts_line():
sys.stdout.write("\n")
sys.stdout.flush()
def puts(s):
sys.stdout.write(" " + s)
sys.stdout.flush()
def exit_on_error(error_code, msg):
sys.stderr.write(msg)
sys.stderr.write('\n')
sys.stderr.flush()
sys.exit(error_code)
if __name__ == '__main__':
args = parse_args()
# get sha of master
with open(os.path.join(args.src, 'refs/heads/master')) as f:
sha = f.read().strip('\n')
# prepare for buildpack run
slug_path = os.path.join('<%= @slug_dir %>', '{0}-{1}.tar.gz'.format(args.formation, sha))
tag = uuid.uuid4().hex[:4]
image = '{0}:{1}'.format(args.formation, tag)
try:
# build/compile
cmd = 'git archive master | <%= @buildstep_dir %>/buildstep {0} {1}'.format(args.formation, tag)
p = subprocess.Popen(cmd, cwd=args.src, shell=True)
rc = p.wait()
if rc != 0:
exit_on_error(rc, 'Build failed, leaving current release in place')
# extract slug
cmd = 'docker run {0} tar -C / -cz app > {1}'.format(image, slug_path)
p = subprocess.Popen(cmd, cwd=args.src, shell=True)
rc = p.wait()
if rc != 0:
exit_on_error(rc, 'Could not extract slug from {0}'.format(image))
# extract procfile
cmd = 'docker run {0} cat /app/Procfile'.format(image)
p = subprocess.Popen(cmd, cwd=args.src, shell=True, stdout=subprocess.PIPE)
rc = p.wait()
if rc != 0:
exit_on_error(rc, 'Could not extract Procfile from {0}'.format(image))
try:
procfile = yaml.safe_load(p.stdout.read())
except YAMLError as e:
exit_on_error(1, 'Invalid Procfile format: {0}'.format(e))
# release
cmd = 'docker run {0} /build/release'.format(image)
p = subprocess.Popen(cmd, cwd=args.src, shell=True, stdout=subprocess.PIPE)
rc = p.wait()
if rc != 0:
exit_on_error(rc, 'Release failed, leaving current release in place')
try:
release = yaml.safe_load(p.stdout.read())
except YAMLError as e:
exit_on_error(1, 'Invalid release format: {0}'.format(e))
finally:
p = subprocess.Popen(['docker', 'rmi', image], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
rc = p.wait()
if rc != 0:
exit_on_error(rc, 'Warning: could not clean up image {0}'.format(image))
# calculate checksum
p = subprocess.Popen(['sha256sum', slug_path], stdout=subprocess.PIPE)
rc = p.wait()
if rc != 0:
exit_on_error(rc, 'Could not calculate SHA of slug')
checksum = p.stdout.read().split(' ')[0]
# run
build = {'formation': args.formation, 'sha': sha, 'checksum': checksum}
build['config'] = release.get('config_vars', {})
# TODO: replace with actual user
build['ssh_key'] = args.user
# TODO: figure out better controller fqdn lookup or switch to mandatory S3 hosting
ip = json.loads(urllib.urlopen('http://jsonip.com').read())['ip']
fqdn = socket.gethostbyaddr(ip)[0]
build['url'] = "http://{fqdn}/slugs/{args.formation}-{sha}.tar.gz".format(**locals())
build['procfile'] = procfile
# calculate slug size
build['size'] = os.stat(slug_path).st_size
puts_line()
puts_step("Compiled slug size: %s" % _sizeof_fmt(build['size']))
# run
sys.stdout.write(" " + "Launching... ")
sys.stdout.flush()
p = subprocess.Popen(['sudo', '-u', 'deis', '<%= @controller_dir %>/bin/build-release-run'],
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate(json.dumps(build))
rc = p.wait()
if rc != 0:
raise RuntimeError('Build creation error {0}'.format(stderr))
databag = json.loads(stdout)
sys.stdout.write("done\n")
sys.stdout.flush()
puts_line()
puts_step("{args.formation} deployed to Deis".format(**locals()))
proxies = databag['nodes']['proxies'].values()
if proxies:
for proxy_fqdn in proxies:
puts("http://{proxy_fqdn}\n".format(**locals()))
else:
puts('No proxies found, use `deis scale proxies=N backends=N` to scale your formation\n\n')