Skip to content

Commit

Permalink
Only show artifacts when they are complete building (#113)
Browse files Browse the repository at this point in the history
* Fixing docker registry protocol

* Black formatting

* Trying to use hybrid atributes

* Fixing regex for environment names allowing numbers

* Black and flake8 formatting
  • Loading branch information
costrouc authored Aug 24, 2021
1 parent 2f98e25 commit 690e8f8
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 20 deletions.
40 changes: 39 additions & 1 deletion conda-store-server/conda_store_server/orm.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
)
from sqlalchemy.orm import sessionmaker, relationship, scoped_session
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy import create_engine

from conda_store_server import utils
Expand Down Expand Up @@ -109,6 +110,8 @@ class Build(Base):
ended_on = Column(DateTime, default=None)
deleted_on = Column(DateTime, default=None)

build_artifacts = relationship("BuildArtifact", back_populates="build")

def build_path(self, store_directory):
store_path = os.path.abspath(store_directory)
return os.path.join(store_path, self.build_key)
Expand All @@ -131,6 +134,13 @@ def build_key(self):
datetime_format = "%Y%m%d-%H%M%S-%f"
return f"{self.specification.sha256}-{self.scheduled_on.strftime(datetime_format)}-{self.id}-{self.specification.name}"

@staticmethod
def parse_build_key(key):
parts = key.split("-")
if len(parts) < 5:
return None
return int(parts[4]) # build_id

@property
def log_key(self):
return f"logs/{self.build_key}.log"
Expand All @@ -150,6 +160,34 @@ def docker_manifest_key(self):
def docker_blob_key(self, blob_hash):
return f"docker/blobs/{blob_hash}"

@hybrid_property
def has_lockfile(self):
return any(
artifact.artifact_type == BuildArtifactType.LOCKFILE
for artifact in self.build_artifacts
)

@hybrid_property
def has_yaml(self):
return any(
artifact.artifact_type == BuildArtifactType.YAML
for artifact in self.build_artifacts
)

@hybrid_property
def has_conda_pack(self):
return any(
artifact.artifact_type == BuildArtifactType.CONDA_PACK
for artifact in self.build_artifacts
)

@hybrid_property
def has_docker_manifest(self):
return any(
artifact.artifact_type == BuildArtifactType.DOCKER_MANIFEST
for artifact in self.build_artifacts
)


class BuildArtifact(Base):
"""Artifacts of a given build"""
Expand All @@ -159,7 +197,7 @@ class BuildArtifact(Base):
id = Column(Integer, primary_key=True)

build_id = Column(Integer, ForeignKey("build.id"))
build = relationship(Build)
build = relationship(Build, back_populates="build_artifacts")

artifact_type = Column(Enum(BuildArtifactType), nullable=False)

Expand Down
6 changes: 4 additions & 2 deletions conda-store-server/conda_store_server/server/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
from conda_store_server import schema, orm


ARN_ALLOWED_REGEX = re.compile(r"^([A-Za-z\_\-\*]+)/([A-Za-z\_\-\*]+)$")
ARN_ALLOWED_REGEX = re.compile(
r"^([A-Za-z0-9|<>=\.\_\-\*]+)/([A-Za-z0-9|<>=\.\_\-\*]+)$"
)


class Permissions(enum.Enum):
Expand Down Expand Up @@ -104,7 +106,7 @@ def compile_arn_regex(arn):
if not ARN_ALLOWED_REGEX.match(arn):
raise ValueError(f"invalid arn={arn}")

regex_arn = "^" + re.sub(r"\*", r"[A-Za-z_\-]*", arn) + "$"
regex_arn = "^" + re.sub(r"\*", r"[A-Za-z0-9_\-\.|<>=]*", arn) + "$"
return re.compile(regex_arn)

@staticmethod
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,16 @@ <h3 class="card-title">Conda Packages
<h3 class="card-title">Conda Environment Artifacts</h3>
</div>
<ul class="list-group list-group-flush">
{% if 'YAML' in build_artifact_types %}
{% if build.has_yaml %}
<li class="list-group-item">YAML: <a href="{{ url_for('ui.api_get_build_yaml', build_id=build.id) }}">environment.yaml</a></li>
{% endif %}
{% if 'LOCKFILE' in build_artifact_types %}
{% if build.has_lockfile %}
<li class="list-group-item">Lockfile: <a href="{{ url_for('ui.api_get_build_lockfile', build_id=build.id) }}">conda-{{ platform }}.lock</a></li>
{% endif %}
{% if 'CONDA_PACK' in build_artifact_types %}
{% if build.has_conda_pack %}
<li class="list-group-item">Archive: <a href="{{ url_for('ui.api_get_build_archive', build_id=build.id) }}">environment.tar.gz</a></li>
{% endif %}
{% if 'DOCKER_MANIFEST' in build_artifact_types %}
{% if build.has_docker_manifest %}
<li class="list-group-item">Docker: {{ registry_external_url }}/{{ build.namespace.name }}/{{ build.specification.name }}:{{ build.build_key }}</a></li>
{% endif %}
</ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,16 @@ <h5 class="card-title">
<a href="{{ url_for('ui.ui_get_environment', namespace=environment.namespace.name, name=environment.name) }}">{{ environment.namespace.name }}/{{ environment.name }}</a>
<span class="badge badge-light">{{ (environment.build.size or 0) | filesizeformat(true) }}</span>
</h5>
{% if environment.build.status.value == 'COMPLETED' %}
{% if environment.build.has_yaml %}
<a class="card-link" href="{{ url_for('ui.api_get_build_yaml', build_id=environment.build_id) }}"><ion-icon name="code-download"></ion-icon> YAML</a>
{% endif %}
{% if environment.build.has_lockfile %}
<a class="card-link" href="{{ url_for('ui.api_get_build_lockfile', build_id=environment.build_id) }}"><ion-icon name="lock-closed-outline"></ion-icon> Lockfile</a>
{% endif %}
{% if environment.build.has_conda_pack %}
<a class="card-link" href="{{ url_for('ui.api_get_build_archive', build_id=environment.build_id) }}"><ion-icon name="archive-outline"></ion-icon> Archive</a>
{% endif %}
{% if environment.build.has_docker_manifest %}
<a class="card-link" onclick="setClipboard('{{ registry_external_url }}/{{ environment.namespace.name }}/{{ environment.name }}:{{ environment.build.build_key }}')" role="button" data-toggle="popover" data-trigger="hover focus" data-content="Click to copy!"><ion-icon name="logo-docker"></ion-icon> Docker</a>
{% endif %}
</div>
Expand Down
21 changes: 14 additions & 7 deletions conda-store-server/conda_store_server/server/views/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,18 +88,25 @@ def get_docker_image_manifest(conda_store, image, tag, timeout=10 * 60):

if tag == "latest":
build_key = environment.build.build_key
elif tag.startswith("sha256:"):
# looking for sha256 of docker manifest
manifests_key = f"docker/manifest/{tag}"
return redirect(conda_store.storage.get_url(manifests_key))
else:
build_key = tag

build_id = orm.Build.parse_build_key(build_key)
if build_id is None:
return docker_error_message(schema.DockerRegistryError.MANIFEST_UNKNOWN)

build = api.get_build(conda_store.db, build_id)
if build is None:
return docker_error_message(schema.DockerRegistryError.MANIFEST_UNKNOWN)

# waiting for image to be built by conda-store
start_time = time.time()
while orm.BuildArtifactType.DOCKER_MANIFEST not in {
_.artifact_type
for _ in api.get_build_artifact_types(
conda_store.db, environment.build.id
).all()
}:
conda_store.db.refresh(environment)
while not build.has_docker_manifest:
conda_store.db.refresh(build)
time.sleep(10)
if time.time() - start_time > timeout:
return docker_error_message(schema.DockerRegistryError.MANIFEST_UNKNOWN)
Expand Down
5 changes: 0 additions & 5 deletions conda-store-server/conda_store_server/server/views/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,8 @@ def ui_get_build(build_id):
require=True,
)

build_artifact_types = api.get_build_artifact_types(conda_store.db, build.id)

context = {
"build": build,
"build_artifact_types": [
_.artifact_type.value for _ in build_artifact_types.all()
],
"registry_external_url": server.registry_external_url,
"entity": auth.authenticate_request(),
"platform": conda_platform(),
Expand Down

0 comments on commit 690e8f8

Please sign in to comment.