1 | ### this works on linux only |
---|
2 | |
---|
3 | import re |
---|
4 | try: |
---|
5 | import fcntl |
---|
6 | import subprocess |
---|
7 | import signal |
---|
8 | import os |
---|
9 | import shutil |
---|
10 | from gluon.fileutils import read_file, write_file |
---|
11 | except: |
---|
12 | session.flash = 'sorry, only on Unix systems' |
---|
13 | redirect(URL(request.application, 'default', 'site')) |
---|
14 | |
---|
15 | if MULTI_USER_MODE and not is_manager(): |
---|
16 | session.flash = 'Not Authorized' |
---|
17 | redirect(URL('default', 'site')) |
---|
18 | |
---|
19 | from gluon.settings import settings |
---|
20 | if not settings.is_source: |
---|
21 | session.flash = 'Requires running web2py from source' |
---|
22 | redirect(URL(request.application, 'default', 'site')) |
---|
23 | |
---|
24 | forever = 10 ** 8 |
---|
25 | |
---|
26 | |
---|
27 | def kill(): |
---|
28 | p = cache.ram('gae_upload', lambda: None, forever) |
---|
29 | if not p or p.poll() is not None: |
---|
30 | return 'oops' |
---|
31 | os.kill(p.pid, signal.SIGKILL) |
---|
32 | cache.ram('gae_upload', lambda: None, -1) |
---|
33 | |
---|
34 | |
---|
35 | class EXISTS(object): |
---|
36 | def __init__(self, error_message='file not found'): |
---|
37 | self.error_message = error_message |
---|
38 | |
---|
39 | def __call__(self, value): |
---|
40 | if os.path.exists(value): |
---|
41 | return (value, None) |
---|
42 | return (value, self.error_message) |
---|
43 | |
---|
44 | |
---|
45 | def deploy(): |
---|
46 | regex = re.compile('^\w+$') |
---|
47 | apps = sorted( |
---|
48 | file for file in os.listdir(apath(r=request)) if regex.match(file)) |
---|
49 | form = SQLFORM.factory( |
---|
50 | Field('appcfg', default=GAE_APPCFG, label=T('Path to appcfg.py'), |
---|
51 | requires=EXISTS(error_message=T('file not found'))), |
---|
52 | Field('google_application_id', requires=IS_MATCH( |
---|
53 | '[\w\-]+'), label=T('Google Application Id')), |
---|
54 | Field('applications', 'list:string', |
---|
55 | requires=IS_IN_SET(apps, multiple=True), |
---|
56 | label=T('web2py apps to deploy')), |
---|
57 | Field('email', requires=IS_EMAIL(), label=T('GAE Email')), |
---|
58 | Field('password', 'password', requires=IS_NOT_EMPTY(), label=T('GAE Password'))) |
---|
59 | cmd = output = errors = "" |
---|
60 | if form.accepts(request, session): |
---|
61 | try: |
---|
62 | kill() |
---|
63 | except: |
---|
64 | pass |
---|
65 | ignore_apps = [item for item in apps |
---|
66 | if not item in form.vars.applications] |
---|
67 | regex = re.compile('\(applications/\(.*') |
---|
68 | yaml = apath('../app.yaml', r=request) |
---|
69 | if not os.path.exists(yaml): |
---|
70 | example = apath('../app.example.yaml', r=request) |
---|
71 | shutil.copyfile(example, yaml) |
---|
72 | data = read_file(yaml) |
---|
73 | data = re.sub('application:.*', 'application: %s' % |
---|
74 | form.vars.google_application_id, data) |
---|
75 | data = regex.sub( |
---|
76 | '(applications/(%s)/.*)|' % '|'.join(ignore_apps), data) |
---|
77 | write_file(yaml, data) |
---|
78 | |
---|
79 | path = request.env.applications_parent |
---|
80 | cmd = '%s --email=%s --passin update %s' % \ |
---|
81 | (form.vars.appcfg, form.vars.email, path) |
---|
82 | p = cache.ram('gae_upload', |
---|
83 | lambda s=subprocess, c=cmd: s.Popen(c, shell=True, |
---|
84 | stdin=s.PIPE, |
---|
85 | stdout=s.PIPE, |
---|
86 | stderr=s.PIPE, close_fds=True), -1) |
---|
87 | p.stdin.write(form.vars.password + '\n') |
---|
88 | fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK) |
---|
89 | fcntl.fcntl(p.stderr.fileno(), fcntl.F_SETFL, os.O_NONBLOCK) |
---|
90 | return dict(form=form, command=cmd) |
---|
91 | |
---|
92 | |
---|
93 | def callback(): |
---|
94 | p = cache.ram('gae_upload', lambda: None, forever) |
---|
95 | if not p or p.poll() is not None: |
---|
96 | return '<done/>' |
---|
97 | try: |
---|
98 | output = p.stdout.read() |
---|
99 | except: |
---|
100 | output = '' |
---|
101 | try: |
---|
102 | errors = p.stderr.read() |
---|
103 | except: |
---|
104 | errors = '' |
---|
105 | return (output + errors).replace('\n', '<br/>') |
---|