Skip to content

Commit 01a4da0

Browse files
Jon DonovanDouglas Greiman
authored andcommitted
Python compat (GoogleCloudPlatform#174)
* Add support for python-compat * Update app.yaml and tests * Add missing .dockerignore file * Updating per review comments * Adding missing files * Update gen_dockerfile.py * Update gen_dockerfile_test.py
1 parent 597a874 commit 01a4da0

8 files changed

Lines changed: 97 additions & 21 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
FROM gcr.io/google_appengine/python-compat-multicore
2+
ADD . /app/
3+
RUN if [ -s requirements.txt ]; then pip install -r requirements.txt; fi
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.dockerignore
2+
Dockerfile
3+
.git
4+
.hg
5+
.svn

scripts/gen_dockerfile.py

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929

3030
import validation_utils
3131

32-
3332
# Validate characters for dockerfile image names.
3433
#
3534
# This roots out obvious mistakes, the full gory details are here:
@@ -63,7 +62,7 @@
6362
# Validated application configuration
6463
AppConfig = collections.namedtuple(
6564
'AppConfig',
66-
'base_image dockerfile_python_version entrypoint has_requirements_txt'
65+
'base_image dockerfile_python_version entrypoint has_requirements_txt is_python_compat'
6766
)
6867

6968

@@ -97,6 +96,16 @@ def get_app_config(raw_config, base_image, config_file, source_dir):
9796
'Expected {} contents to be a Mapping type, but found type "{}"'.
9897
format(config_file, type(raw_config)))
9998

99+
# Short circuit for python compat.
100+
if validation_utils.get_field_value(
101+
raw_config, 'runtime', str) == 'python-compat':
102+
return AppConfig(
103+
base_image=None,
104+
dockerfile_python_version=None,
105+
entrypoint=None,
106+
has_requirements_txt=None,
107+
is_python_compat=True)
108+
100109
entrypoint = validation_utils.get_field_value(
101110
raw_config, 'entrypoint', str)
102111
if not PRINTABLE_REGEX.match(entrypoint):
@@ -133,7 +142,8 @@ def get_app_config(raw_config, base_image, config_file, source_dir):
133142
base_image=base_image,
134143
dockerfile_python_version=dockerfile_python_version,
135144
entrypoint=entrypoint,
136-
has_requirements_txt=has_requirements_txt)
145+
has_requirements_txt=has_requirements_txt,
146+
is_python_compat=False)
137147

138148

139149
def get_data(name):
@@ -175,19 +185,24 @@ def generate_files(app_config):
175185
else:
176186
optional_entrypoint = ''
177187

178-
dockerfile = ''.join([
179-
get_data('Dockerfile.preamble.template').format(
180-
base_image=app_config.base_image),
181-
get_data('Dockerfile.virtualenv.template').format(
182-
python_version=app_config.dockerfile_python_version),
183-
optional_requirements_txt,
184-
get_data('Dockerfile.install_app'),
185-
optional_entrypoint,
186-
])
188+
if app_config.is_python_compat:
189+
dockerfile = get_data('Dockerfile.python_compat')
190+
dockerignore = get_data('dockerignore.python_compat')
191+
else:
192+
dockerfile = ''.join([
193+
get_data('Dockerfile.preamble.template').format(
194+
base_image=app_config.base_image),
195+
get_data('Dockerfile.virtualenv.template').format(
196+
python_version=app_config.dockerfile_python_version),
197+
optional_requirements_txt,
198+
get_data('Dockerfile.install_app'),
199+
optional_entrypoint,
200+
])
201+
dockerignore = get_data('dockerignore')
187202

188203
return {
189204
'Dockerfile': dockerfile,
190-
'.dockerignore': get_data('dockerignore'),
205+
'.dockerignore': dockerignore,
191206
}
192207

193208

@@ -206,7 +221,7 @@ def generate_dockerfile_command(base_image, config_file, source_dir):
206221

207222
# Determine complete configuration
208223
app_config = get_app_config(raw_config, base_image, config_file,
209-
source_dir)
224+
source_dir)
210225

211226
# Generate list of filenames and their textual contents
212227
files = generate_files(app_config)

scripts/gen_dockerfile_test.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ def compare_file(filename, dir1, dir2):
5454
'dockerfile_python_version': '',
5555
'has_requirements_txt': False,
5656
'entrypoint': '',
57+
'is_python_compat': False,
58+
}),
59+
('env: flex\nruntime: python-compat', {
60+
'base_image': None,
61+
'dockerfile_python_version': None,
62+
'has_requirements_txt': None,
63+
'entrypoint': None,
64+
'is_python_compat': True,
5765
}),
5866
# All supported python versions
5967
('runtime_config:\n python_version:', {
@@ -125,7 +133,8 @@ def test_get_app_config_invalid(app_yaml):
125133
base_image='',
126134
dockerfile_python_version='',
127135
entrypoint='',
128-
has_requirements_txt=False
136+
has_requirements_txt=False,
137+
is_python_compat=False,
129138
)
130139

131140

@@ -146,6 +155,9 @@ def test_get_app_config_invalid(app_yaml):
146155
# Python version
147156
(_BASE_APP_CONFIG._replace(dockerfile_python_version='_my_version'), True,
148157
'python_version=python_my_version'),
158+
# python-compat runtime
159+
(_BASE_APP_CONFIG._replace(is_python_compat=True), True,
160+
'FROM gcr.io/google_appengine/python-compat-multicore'),
149161
])
150162
def test_generate_files(app_config, should_find, test_string):
151163
result = gen_dockerfile.generate_files(app_config)
@@ -163,10 +175,13 @@ def compare_against_golden_files(app, config_dir, testdata_dir):
163175
compare_file(filename, config_dir, golden_dir)
164176

165177

166-
def test_generate_dockerfile_command(tmpdir, testdata_dir):
178+
@pytest.mark.parametrize('app', [
179+
# Sampled from https://github.com/GoogleCloudPlatform/python-docs-samples
180+
'hello_world',
181+
# From an internal source.
182+
'hello_world_compat'])
183+
def test_generate_dockerfile_command(tmpdir, testdata_dir, app):
167184
"""Generates output and compares against a set of golden files."""
168-
# Sample from https://github.com/GoogleCloudPlatform/python-docs-samples
169-
app = 'hello_world'
170185
app_dir = os.path.join(testdata_dir, app)
171186

172187
# Copy sample app to writable temp dir, and generate Dockerfile.
@@ -179,12 +194,15 @@ def test_generate_dockerfile_command(tmpdir, testdata_dir):
179194
compare_against_golden_files(app, config_dir, testdata_dir)
180195

181196

197+
@pytest.mark.parametrize('app', [
198+
# Sampled from https://github.com/GoogleCloudPlatform/python-docs-samples
199+
'hello_world',
200+
# From an internal source.
201+
'hello_world_compat'])
182202
@pytest.mark.xfail(not shutil.which('gcloud'),
183203
reason='Google Cloud SDK is not installed')
184-
def test_generate_dockerfile_golden(tmpdir, testdata_dir):
204+
def test_generate_dockerfile_golden(tmpdir, testdata_dir, app):
185205
"""Validate our golden files against gcloud app gen-config"""
186-
# Sample from https://github.com/GoogleCloudPlatform/python-docs-samples
187-
app = 'hello_world'
188206
app_dir = os.path.join(testdata_dir, app)
189207

190208
# Copy sample app to writable temp dir, and generate Dockerfile.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
service: default
2+
runtime: python-compat
3+
env: flex
4+
5+
api_version: 1
6+
threadsafe: true
7+
8+
beta_settings:
9+
enable_app_engine_apis: true # Needed for compat apps.
10+
11+
handlers:
12+
- url: .*
13+
script: main.app
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""The hello world flex app!"""
2+
3+
import webapp2
4+
5+
6+
class HelloHandler(webapp2.RequestHandler):
7+
8+
def get(self):
9+
msg = 'Hello GAE Flex (env: flex) Compat-Runtime App\n'
10+
self.response.headers['Content-Type'] = 'text/plain'
11+
self.response.out.write(msg)
12+
13+
app = webapp2.WSGIApplication([('/', HelloHandler)],
14+
debug=True)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.dockerignore
2+
Dockerfile
3+
.git
4+
.hg
5+
.svn
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
FROM gcr.io/google_appengine/python-compat-multicore
2+
ADD . /app/
3+
RUN if [ -s requirements.txt ]; then pip install -r requirements.txt; fi

0 commit comments

Comments
 (0)