Commit bc9eb668 authored by BAIRE Anthony's avatar BAIRE Anthony
Browse files

allow selecting the webapp version to be started

parent d484704b
......@@ -428,17 +428,6 @@ class SandboxManager(Manager):
except docker.errors.NotFound:
return None
@staticmethod
def filter_sandbox_version(query, webapp_id):
"""Narrow a WebappVersion query to filter the version that must be started in the sandbox"""
return (query.
# filter the right webapp
filter_by(webapp_id=webapp_id)
# keep only versions whose state is 'ready' or 'committed'
.filter(WebappVersion.state.in_((int(VersionState.committed), int(VersionState.ready))))
# take the latest one
.order_by(desc(WebappVersion.id)).limit(1))
@staticmethod
def filter_commit_version(query, webapp_id):
......@@ -728,7 +717,7 @@ EOF
webapp.docker_os
# version to be started
sandbox_version = self.filter_sandbox_version(ses.query(WebappVersion), webapp_id).first()
sandbox_version = webapp.sandbox_version
# requested commits
commit_versions = self.filter_commit_version(ses.query(WebappVersion), webapp_id).all()
......@@ -751,8 +740,13 @@ EOF
# commit (if a sandbox exists)
yield from self._manage_commit(webapp, commit_versions, force=True)
# pull requested image
if sandbox_version is not None:
# ensure version belongs to this application
if sandbox_version.webapp_id != webapp.id:
raise Error("invalid version id %d (belongs to webapp %d)" % (
sandbox_version.id, sandbox_version.webapp_id))
# pull requested image
yield from ctrl.image_manager.pull(sandbox_version.id)
# start sandbox
......
......@@ -42,11 +42,13 @@ class Webapp(Base):
id = Column(Integer, primary_key=True)
docker_name = Column(String)
sandbox_state = Column(Integer)
sandbox_version_id = Column(Integer, ForeignKey('webapp_versions.id'))
docker_os_id = Column(Integer, ForeignKey('docker_os.id'))
exec_time = Column(Integer)
entrypoint = Column(String)
versions = relationship("WebappVersion")
versions = relationship("WebappVersion", foreign_keys="[WebappVersion.webapp_id]")
sandbox_version = relationship("WebappVersion", foreign_keys=[sandbox_version_id])
docker_os = relationship("DockerOs")
def __repr__(self):
......@@ -71,7 +73,7 @@ class WebappVersion(Base):
published = Column(Boolean)
state = Column(Integer)
webapp = relationship("Webapp")
webapp = relationship("Webapp", foreign_keys=[webapp_id])
def __setattr__(self, key, value):
if key == "state" and isinstance(value, VersionState):
......
......@@ -429,10 +429,14 @@ class ControllerTestCase(unittest.TestCase):
return ver
def start_sandbox(self, app):
def start_sandbox(self, app, version=None):
with self.session.begin():
app.sandbox_state = S.starting
app.sandbox_state = S.starting
if isinstance(version, str):
version = self.session.query(WebappVersion).filter_by(
webapp_id=app.id, number=version, state=int(V.ready)).one()
app.sandbox_version = version
with self.check_sandbox_transition(app, S.starting, S.running):
self.notify()
......@@ -646,16 +650,20 @@ class ControllerTestCase(unittest.TestCase):
self.add_dummy_version(app, "1.0", state=V.error)
self.add_dummy_version(app, "1.1", state=V.error)
self.start_sandbox(app)
self.start_sandbox(app, ver)
self.check_sandbox_running({app: "webapp/test-app:1.0"})
self.kill_sandbox(app)
# multiple versions
with preamble():
self.add_dummy_version(app, "1.1")
ver = self.add_dummy_version(app, "0.9")
v1 = self.add_dummy_version(app, "1.1")
v2 = self.add_dummy_version(app, "0.9")
self.start_sandbox(app, v1)
self.check_sandbox_running({app: "webapp/test-app:1.1"})
self.kill_sandbox(app)
self.start_sandbox(app)
self.start_sandbox(app, v2)
self.check_sandbox_running({app: "webapp/test-app:0.9"})
self.kill_sandbox(app)
......@@ -665,7 +673,7 @@ class ControllerTestCase(unittest.TestCase):
ver = self.add_dummy_version(app, "0.8", state=V.committed)
with mock.patch.object(self.ctrl.sandbox, "pull", mock.Mock()) as m:
self.start_sandbox(app)
self.start_sandbox(app, ver)
self.check_sandbox_running({app: "webapp/test-app:0.8"})
self.assertFalse(m.called)
......@@ -673,28 +681,56 @@ class ControllerTestCase(unittest.TestCase):
@with_db
def test_sandbox_start_bad_parameters(self, ses, app):
# bad docker_name
for docker_name in BAD_SANDBOX_NAMES:
with part(docker_name):
with ses.begin():
w = Webapp(docker_name=docker_name, sandbox_state = S.starting, docker_os=app.docker_os)
ses.add(w)
with part("case 1: bad docker_name"):
for docker_name in BAD_SANDBOX_NAMES:
with part(docker_name):
with ses.begin():
w = Webapp(docker_name=docker_name, sandbox_state = S.starting, docker_os=app.docker_os)
ses.add(w)
with self.check_log("error", "malformatted docker_name"), \
self.check_sandbox_transition(w, S.starting, S.start_error):
self.notify()
with self.check_log("error", "start error .* malformatted docker_name"), \
self.check_sandbox_transition(w, S.starting, S.start_error):
self.notify()
self.check_sandbox_running({w: None})
self.check_sandbox_running({w: None})
# bad id
with ses.begin():
w = Webapp(id=-1, docker_name="test-bad-id", sandbox_state = S.starting, docker_os=app.docker_os)
ses.add(w)
with self.check_log("error", "bad webapp id"), \
self.check_sandbox_transition(w, S.starting, S.start_error):
self.notify()
with part("case 2: bad webapp id"):
with ses.begin():
w = Webapp(id=-1, docker_name="test-bad-id", sandbox_state = S.starting, docker_os=app.docker_os)
ses.add(w)
with self.check_log("error", "start error .* bad webapp id"), \
self.check_sandbox_transition(w, S.starting, S.start_error):
self.notify()
self.check_sandbox_running({w: None})
self.check_sandbox_running({w: None})
# bad version
with part("case 3: bad version state"):
with preamble():
ver = self.add_dummy_version(app, "0.8", state=V.error)
with ses.begin():
app.sandbox_state = S.starting
app.sandbox_version = ver
with self.check_log("error", "start error .* bad version state"), \
self.check_sandbox_transition(app, S.starting, S.start_error):
self.notify()
# bad version app id
with part("case 4: bad version webapp id"):
with preamble():
with ses.begin():
other = Webapp(docker_name=ENV+"-other-app", docker_os=app.docker_os, sandbox_state=S.idle)
ver = self.add_dummy_version(other, "0.9")
with ses.begin():
app.sandbox_state = S.starting
app.sandbox_version = ver
with self.check_log("error", "start error .* invalid version id %d .*belongs to webapp %d"
% (ver.id, ver.webapp_id)), \
self.check_sandbox_transition(app, S.starting, S.start_error):
self.notify()
@with_db
def test_sandbox_start_prepare_fail(self, ses, app):
......@@ -1071,7 +1107,7 @@ class ControllerTestCase(unittest.TestCase):
self.check_sandbox_running({app: None})
# start the sandbox (again)
self.start_sandbox(app)
self.start_sandbox(app, "1.0")
# ensure the entrypoint is not reset
code, out = self.dk_exec(ctr, [entrypoint, "foo", "bar", "1", "2", "3"])
......@@ -1080,10 +1116,11 @@ class ControllerTestCase(unittest.TestCase):
@with_db
def test_sandbox_process_shutdown(self, ses, app):
with preamble():
self.add_dummy_version(app, "1.0")
ver = self.add_dummy_version(app, "1.0")
with ses.begin():
app.sandbox_state = S.starting
app.sandbox_version = ver
with mock.patch("controller.ImageManager.pull") as m, \
self.check_log("info", "sandbox 'test-app' start aborted \(controller shutdown\)"):
......@@ -1226,7 +1263,7 @@ class ControllerTestCase(unittest.TestCase):
append = """
RUN echo "echo foo" > /bin/entrypoint && chmod 755 /bin/entrypoint
""")
self.start_sandbox(app)
self.start_sandbox(app, "1.0")
self.check_sandbox_running({app: "webapp/test-app:1.0"})
# modify entrypoint
......@@ -1311,7 +1348,7 @@ class ControllerTestCase(unittest.TestCase):
append = """
RUN echo "echo %sfoo" > /bin/entrypoint && chmod 755 /bin/entrypoint
""" % rnd)
self.start_sandbox(app)
self.start_sandbox(app, "1.0")
self.check_sandbox_running({app: "webapp/test-app:1.0"})
def update_entrypoint(txt):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment