Skip to content

Commit

Permalink
draft/wip
Browse files Browse the repository at this point in the history
  • Loading branch information
git-hyagi committed Apr 3, 2024
1 parent f377a9c commit 63a36bc
Show file tree
Hide file tree
Showing 19 changed files with 497 additions and 114 deletions.
1 change: 1 addition & 0 deletions CHANGES/1296.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Created a new artifactless ConfigBlob model.
2 changes: 1 addition & 1 deletion pulp_container/app/content.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
app.add_routes(
[
web.get(
r"/pulp/container/{path:.+}/{content:(blobs|manifests)}/sha256:{digest:.+}",
r"/pulp/container/{path:.+}/{content:(blobs|manifests|config-blobs)}/sha256:{digest:.+}",
registry.get_by_digest,
)
]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
from json.decoder import JSONDecodeError

from gettext import gettext as _
Expand All @@ -6,8 +7,9 @@

from django.core.exceptions import ObjectDoesNotExist
from django.core.management import BaseCommand
from django.db import IntegrityError, transaction

from pulp_container.app.models import Manifest
from pulp_container.app.models import Manifest, ConfigBlob

from pulp_container.constants import MEDIA_TYPE

Expand Down Expand Up @@ -64,6 +66,9 @@ def update_manifests(self, manifests_qs):
)
manifests_updated_count += len(manifests_to_update)
manifests_to_update.clear()

self.update_config_blob(manifest)

if manifests_to_update:
fields_to_update = ["annotations", "labels", "is_bootable", "is_flatpak"]
manifests_qs.model.objects.bulk_update(
Expand All @@ -73,3 +78,28 @@ def update_manifests(self, manifests_qs):
manifests_updated_count += len(manifests_to_update)

return manifests_updated_count

def update_config_blob(self, manifest):
raw_manifest = manifest.config_blob._artifacts.get().file.read().decode("utf-8")
config_blob = json.loads(raw_manifest)
digest = manifest.config_blob.digest
with transaction.atomic():
try:
blob = ConfigBlob(
data=raw_manifest,
digest=digest,
architecture=config_blob.get("architecture"),
os=config_blob.get("os"),
created=config_blob.get("created"),
author=config_blob.get("author", ""),
os_version=config_blob.get("os_version", ""),
os_features=config_blob.get("os_features", ""),
variant=config_blob.get("variant", ""),
rootfs=config_blob.get("rootfs", {}),
config=config_blob.get("config", {}),
history=config_blob.get("history", {}),
)
blob.save()
except IntegrityError:
blob = ConfigBlob.objects.get(digest=digest)
blob.touch()
57 changes: 57 additions & 0 deletions pulp_container/app/migrations/0039_artifactless_config_blob.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Generated by Django 4.2.10 on 2024-04-03 10:54

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('container', '0038_add_manifest_metadata_fields'),
]

operations = [
migrations.AlterField(
model_name='manifest',
name='config_blob',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='config', to='container.blob'),
),
migrations.CreateModel(
name='ConfigBlob',
fields=[
('content_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.content')),
('data', models.TextField()),
('architecture', models.TextField()),
('os', models.TextField()),
('rootfs', models.JSONField(default=dict)),
('digest', models.TextField(db_index=True)),
('created', models.DateTimeField()),
('author', models.TextField(blank=True, default='')),
('os_version', models.TextField(blank=True, default='')),
('os_features', models.TextField(blank=True, default='')),
('variant', models.TextField(blank=True, default='')),
('config', models.JSONField(default=dict)),
('history', models.JSONField(default=dict)),
],
options={
'default_related_name': '%(app_label)s_%(model_name)s',
'unique_together': {('digest',)},
},
bases=('core.content',),
),
migrations.AddField(
model_name='containerpushrepository',
name='pending_config_blobs',
field=models.ManyToManyField(to='container.configblob'),
),
migrations.AddField(
model_name='containerrepository',
name='pending_config_blobs',
field=models.ManyToManyField(to='container.configblob'),
),
migrations.AddField(
model_name='manifest',
name='config',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='config_blob', to='container.configblob'),
),
]
25 changes: 25 additions & 0 deletions pulp_container/app/modelresource.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from pulp_container.app.models import (
Blob,
ConfigBlob,
ContainerRepository,
ContainerPushRepository,
Manifest,
Expand Down Expand Up @@ -60,6 +61,24 @@ class Meta:
exclude = RepositoryResource.Meta.exclude + ("manifest_signing_service",)


class ConfigBlobResource(BaseContentResource):
"""
Resource for import/export of configblob entities
"""

def set_up_queryset(self):
"""
:return: ConfigBlobs specific to a specified repo-version.
"""
return ConfigBlob.objects.filter(pk__in=self.repo_version.content).order_by(
"content_ptr_id"
)

class Meta:
model = ConfigBlob
import_id_fields = model.natural_key_fields()


class BlobResource(BaseContentResource):
"""
Resource for import/export of blob entities
Expand Down Expand Up @@ -91,6 +110,11 @@ class ManifestResource(BaseContentResource):
attribute="config_blob",
widget=widgets.ForeignKeyWidget(Blob, field="digest"),
)
config = fields.Field(
column_name="config",
attribute="config",
widget=widgets.ForeignKeyWidget(ConfigBlob, field="digest"),
)

def set_up_queryset(self):
"""
Expand Down Expand Up @@ -179,6 +203,7 @@ class Meta:


IMPORT_ORDER = [
ConfigBlobResource,
BlobResource,
ManifestResource,
ManifestListManifestResource,
Expand Down
64 changes: 56 additions & 8 deletions pulp_container/app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,48 @@
logger = getLogger(__name__)


class ConfigBlob(Content):
"""
The manifest for image configuration.
Fields:
created (models.DateTimeField): An combined date and time at which the image was created.
author (models.TextField): Name and/or email of the person or entity which created and is
responsible for maintaining the image.
architecture (models.TextField): The platform architecture.
os (models.TextField): The platform OS name.
os_version (models.TextField): The platform OS version.
os_features (models.TextField): The platform OS features.
variant (models.TextField): The platform variant.
config (models.ForeignKey):
rootfs (models.ForeignKey):
history (models.ForeignKey):
"""

PROTECTED_FROM_RECLAIM = False

TYPE = "config-blob"

data = models.TextField()
architecture = models.TextField()
os = models.TextField()
rootfs = models.JSONField(default=dict)
digest = models.TextField(db_index=True)

created = models.DateTimeField()
author = models.TextField(default="", blank=True)
os_version = models.TextField(default="", blank=True)
os_features = models.TextField(default="", blank=True)
variant = models.TextField(default="", blank=True)

config = models.JSONField(default=dict)
history = models.JSONField(default=dict)

class Meta:
default_related_name = "%(app_label)s_%(model_name)s"
unique_together = ("digest",)


class Blob(Content):
"""
A blob defined within a manifest.
Expand Down Expand Up @@ -107,7 +149,10 @@ class Manifest(Content):

blobs = models.ManyToManyField(Blob, through="BlobManifest")
config_blob = models.ForeignKey(
Blob, related_name="config_blob", null=True, on_delete=models.CASCADE
Blob, related_name="config", null=True, on_delete=models.CASCADE
)
config = models.ForeignKey(
ConfigBlob, related_name="config_blob", null=True, on_delete=models.CASCADE
)

# Order matters for through fields, (source, target)
Expand Down Expand Up @@ -137,11 +182,10 @@ def init_annotations(self, manifest_data=None):
return bool(self.annotations)

def init_labels(self):
if self.config_blob:
config_artifact = self.config_blob._artifacts.get()

config_data, _ = get_content_data(config_artifact)
self.labels = config_data.get("config", {}).get("Labels") or {}
if self.config and "Labels" in self.config.config.keys():
self.labels = self.config.config["Labels"] or {}
else:
self.labels = {}

return bool(self.labels)

Expand Down Expand Up @@ -577,14 +621,15 @@ class ContainerRepository(
"""

TYPE = "container"
CONTENT_TYPES = [Blob, Manifest, Tag, ManifestSignature]
CONTENT_TYPES = [Blob, ConfigBlob, Manifest, Tag, ManifestSignature]
REMOTE_TYPES = [ContainerRemote]
PUSH_ENABLED = False

manifest_signing_service = models.ForeignKey(
ManifestSigningService, on_delete=models.SET_NULL, null=True
)
pending_blobs = models.ManyToManyField(Blob)
pending_config_blobs = models.ManyToManyField(ConfigBlob)
pending_manifests = models.ManyToManyField(Manifest)

class Meta:
Expand Down Expand Up @@ -617,6 +662,7 @@ def remove_pending_content(self, repository_version):
base_version=repository_version.base_version
).values_list("pk")
self.pending_blobs.remove(*Blob.objects.filter(pk__in=added_content))
self.pending_config_blobs.remove(*ConfigBlob.objects.filter(pk__in=added_content))
self.pending_manifests.remove(*Manifest.objects.filter(pk__in=added_content))


Expand All @@ -635,13 +681,14 @@ class ContainerPushRepository(Repository, AutoAddObjPermsMixin):
"""

TYPE = "container-push"
CONTENT_TYPES = [Blob, Manifest, Tag, ManifestSignature]
CONTENT_TYPES = [Blob, ConfigBlob, Manifest, Tag, ManifestSignature]
PUSH_ENABLED = True

manifest_signing_service = models.ForeignKey(
ManifestSigningService, on_delete=models.SET_NULL, null=True
)
pending_blobs = models.ManyToManyField(Blob)
pending_config_blobs = models.ManyToManyField(ConfigBlob)
pending_manifests = models.ManyToManyField(Manifest)

class Meta:
Expand Down Expand Up @@ -671,6 +718,7 @@ def remove_pending_content(self, repository_version):
base_version=repository_version.base_version
).values_list("pk")
self.pending_blobs.remove(*Blob.objects.filter(pk__in=added_content))
self.pending_config_blobs.remove(*ConfigBlob.objects.filter(pk__in=added_content))
self.pending_manifests.remove(*Manifest.objects.filter(pk__in=added_content))


Expand Down
4 changes: 4 additions & 0 deletions pulp_container/app/redirects.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.shortcuts import redirect

from pulp_container.app.exceptions import ManifestNotFound
from pulp_container.app.models import ConfigBlob
from pulp_container.app.utils import get_accepted_media_types
from pulp_container.constants import BLOB_CONTENT_TYPE, MEDIA_TYPE

Expand Down Expand Up @@ -58,6 +59,9 @@ def issue_blob_redirect(self, blob):
"""
Issue a redirect for the passed blob.
"""
if isinstance(blob, ConfigBlob):
return self.redirect_to_content_app("config-blobs", blob.digest)

return self.redirect_to_content_app("blobs", blob.digest)


Expand Down
Loading

0 comments on commit 63a36bc

Please sign in to comment.