66from pathlib import Path
77from typing import Any , Dict
88
9- from oras .oci import Layer
9+ from oras .defaults import annotation_title as ANNOTATION_TITLE
1010
11+ from ..constants import GL_DISTRIBUTION_NAME , GL_REPOSITORY_URL
1112from ..features import CName
13+ from .layer import Layer
1214from .manifest import Manifest
1315from .platform import NewPlatform
1416from .schemas import EmptyManifestMetadata
@@ -27,6 +29,31 @@ class ImageManifest(Manifest):
2729 Apache License, Version 2.0
2830 """
2931
32+ ANNOTATION_ARCH_KEY = "org.opencontainers.image.architecture"
33+ """
34+ OCI image manifest architecture annotation
35+ """
36+ ANNOTATION_CNAME_KEY = "cname"
37+ """
38+ OCI image manifest GardenLinux canonical name annotation
39+ """
40+ ANNOTATION_DESCRIPTION_KEY = "org.opencontainers.image.description"
41+ """
42+ OCI image manifest description annotation
43+ """
44+ ANNOTATION_FEATURE_SET_KEY = "feature_set"
45+ """
46+ OCI image manifest GardenLinux feature set annotation
47+ """
48+ ANNOTATION_SOURCE_REPO_KEY = "org.opencontainers.image.source"
49+ """
50+ OCI image manifest GardenLinux source repository URL annotation
51+ """
52+ ANNOTATION_TITLE_KEY = ANNOTATION_TITLE
53+ """
54+ OCI image manifest title annotation
55+ """
56+
3057 @property
3158 def arch (self ) -> str :
3259 """
@@ -36,12 +63,12 @@ def arch(self) -> str:
3663 :since: 0.7.0
3764 """
3865
39- if "architecture" not in self .get ("annotations" , {}):
66+ if ImageManifest . ANNOTATION_ARCH_KEY not in self .get ("annotations" , {}):
4067 raise RuntimeError (
41- "Unexpected manifest with missing config annotation 'architecture ' found"
68+ f "Unexpected manifest with missing config annotation '{ ImageManifest . ANNOTATION_ARCH_KEY } ' found"
4269 )
4370
44- return self ["annotations" ]["architecture" ] # type: ignore[no-any-return]
71+ return self ["annotations" ][ImageManifest . ANNOTATION_ARCH_KEY ] # type: ignore[no-any-return]
4572
4673 @arch .setter
4774 def arch (self , value : str ) -> None :
@@ -54,7 +81,7 @@ def arch(self, value: str) -> None:
5481 """
5582
5683 self ._ensure_annotations_dict ()
57- self ["annotations" ]["architecture" ] = value
84+ self ["annotations" ][ImageManifest . ANNOTATION_ARCH_KEY ] = value
5885
5986 @property
6087 def cname (self ) -> str :
@@ -65,12 +92,12 @@ def cname(self) -> str:
6592 :since: 0.7.0
6693 """
6794
68- if "cname" not in self .get ("annotations" , {}):
95+ if ImageManifest . ANNOTATION_CNAME_KEY not in self .get ("annotations" , {}):
6996 raise RuntimeError (
70- "Unexpected manifest with missing config annotation 'cname ' found"
97+ f "Unexpected manifest with missing config annotation '{ ImageManifest . ANNOTATION_CNAME_KEY } ' found"
7198 )
7299
73- return self ["annotations" ]["cname" ] # type: ignore[no-any-return]
100+ return self ["annotations" ][ImageManifest . ANNOTATION_CNAME_KEY ] # type: ignore[no-any-return]
74101
75102 @cname .setter
76103 def cname (self , value : str ) -> None :
@@ -83,7 +110,7 @@ def cname(self, value: str) -> None:
83110 """
84111
85112 self ._ensure_annotations_dict ()
86- self ["annotations" ]["cname" ] = value
113+ self ["annotations" ][ImageManifest . ANNOTATION_CNAME_KEY ] = value
87114
88115 @property
89116 def feature_set (self ) -> str :
@@ -94,12 +121,12 @@ def feature_set(self) -> str:
94121 :since: 0.7.0
95122 """
96123
97- if "feature_set" not in self .get ("annotations" , {}):
124+ if ImageManifest . ANNOTATION_FEATURE_SET_KEY not in self .get ("annotations" , {}):
98125 raise RuntimeError (
99- "Unexpected manifest with missing config annotation 'feature_set ' found"
126+ f "Unexpected manifest with missing config annotation '{ ImageManifest . ANNOTATION_FEATURE_SET_KEY } ' found"
100127 )
101128
102- return self ["annotations" ]["feature_set" ] # type: ignore[no-any-return]
129+ return self ["annotations" ][ImageManifest . ANNOTATION_FEATURE_SET_KEY ] # type: ignore[no-any-return]
103130
104131 @feature_set .setter
105132 def feature_set (self , value : str ) -> None :
@@ -112,7 +139,7 @@ def feature_set(self, value: str) -> None:
112139 """
113140
114141 self ._ensure_annotations_dict ()
115- self ["annotations" ]["feature_set" ] = value
142+ self ["annotations" ][ImageManifest . ANNOTATION_FEATURE_SET_KEY ] = value
116143
117144 @property
118145 def flavor (self ) -> str :
@@ -126,56 +153,75 @@ def flavor(self) -> str:
126153 return CName (self .cname ).flavor
127154
128155 @property
129- def layers_as_dict (self ) -> Dict [str , Any ]:
156+ def extended_dict (self ) -> Dict [str , Any ]:
130157 """
131- Returns the OCI image manifest layers as a dictionary.
158+ Returns the final parsed and extended OCI manifest dictionary
132159
133- :return: (dict) OCI image manifest layers with title as key
134- :since: 0.7 .0
160+ :return: (dict) OCI manifest dictionary
161+ :since: 1.0 .0
135162 """
136163
137- layers = {}
164+ manifest_dict = Manifest (self ).extended_dict
165+ manifest_annotations = manifest_dict ["annotations" ]
138166
139- for layer in self ["layers" ]:
140- if "org.opencontainers.image.title" not in layer .get ("annotations" , {}):
141- raise RuntimeError (
142- "Unexpected layer with missing annotation 'org.opencontainers.image.title' found"
143- )
167+ if ImageManifest .ANNOTATION_TITLE_KEY not in manifest_annotations :
168+ manifest_annotations [ImageManifest .ANNOTATION_TITLE_KEY ] = (
169+ GL_DISTRIBUTION_NAME
170+ )
144171
145- layers [ layer [ "annotations" ][ "org.opencontainers.image.title" ]] = layer
172+ manifest_description = manifest_annotations [ ImageManifest . ANNOTATION_TITLE_KEY ]
146173
147- return layers
174+ if ImageManifest .ANNOTATION_SOURCE_REPO_KEY not in manifest_annotations :
175+ manifest_annotations [ImageManifest .ANNOTATION_SOURCE_REPO_KEY ] = (
176+ GL_REPOSITORY_URL
177+ )
148178
149- @property
150- def version (self ) -> str :
151- """
152- Returns the GardenLinux version of the OCI image manifest.
179+ if ImageManifest .ANNOTATION_ARCH_KEY in manifest_annotations :
180+ manifest_annotations ["architecture" ] = self .arch
181+ manifest_description += f" ({ self .arch } )"
153182
154- :return: (str) OCI image GardenLinux version
155- :since: 0.7.0
156- """
183+ if ImageManifest . ANNOTATION_VERSION_KEY in manifest_annotations :
184+ manifest_annotations [ "org.opencontainers.image.version" ] = self . version
185+ manifest_description += " " + self . version
157186
158- if "version" not in self .get ("annotations" , {}):
159- raise RuntimeError (
160- "Unexpected manifest with missing config annotation 'version' found"
187+ if ImageManifest .ANNOTATION_COMMIT_KEY in manifest_annotations :
188+ manifest_annotations ["org.opencontainers.image.revision" ] = self .commit
189+ manifest_description += f" ({ self .commit } )"
190+
191+ if ImageManifest .ANNOTATION_FEATURE_SET_KEY in manifest_annotations :
192+ manifest_description += (
193+ " - " + manifest_annotations [ImageManifest .ANNOTATION_FEATURE_SET_KEY ]
194+ )
195+
196+ if ImageManifest .ANNOTATION_DESCRIPTION_KEY not in manifest_annotations :
197+ manifest_annotations [ImageManifest .ANNOTATION_DESCRIPTION_KEY ] = (
198+ manifest_description
161199 )
162200
163- return self [ "annotations" ][ "version" ] # type: ignore[no-any-return]
201+ return manifest_dict
164202
165- @version . setter
166- def version (self , value : str ) -> None :
203+ @property
204+ def layers_as_dict (self ) -> Dict [ str , Any ] :
167205 """
168- Sets the GardenLinux version of the OCI image manifest.
169-
170- :param value: OCI image GardenLinux version
206+ Returns the OCI image manifest layers as a dictionary.
171207
172- :since: 0.7.0
208+ :return: (dict) OCI image manifest layers with title as key
209+ :since: 0.7.0
173210 """
174211
175- self ._ensure_annotations_dict ()
176- self ["annotations" ]["version" ] = value
212+ layers = {}
213+
214+ for layer in self ["layers" ]:
215+ if ImageManifest .ANNOTATION_TITLE_KEY not in layer .get ("annotations" , {}):
216+ raise RuntimeError (
217+ f"Unexpected layer with missing annotation '{ ImageManifest .ANNOTATION_TITLE_KEY } ' found"
218+ )
219+
220+ layers [layer ["annotations" ][ImageManifest .ANNOTATION_TITLE_KEY ]] = layer
177221
178- def append_layer (self , layer : Layer ) -> None :
222+ return layers
223+
224+ def append_layer (self , layer : Layer | Dict [str , Any ]) -> None :
179225 """
180226 Appends the given OCI image manifest layer to the manifest
181227
@@ -184,30 +230,30 @@ def append_layer(self, layer: Layer) -> None:
184230 :since: 0.7.0
185231 """
186232
187- if not isinstance (layer , Layer ):
188- raise RuntimeError ( "Unexpected layer type given" )
189-
190- layer_dict = layer . dict
233+ if isinstance (layer , Layer ):
234+ layer_dict = layer . dict
235+ else :
236+ layer_dict = layer
191237
192- if "org.opencontainers.image.title" not in layer_dict .get ("annotations" , {}):
238+ if ImageManifest . ANNOTATION_TITLE_KEY not in layer_dict .get ("annotations" , {}):
193239 raise RuntimeError (
194- "Unexpected layer with missing annotation 'org.opencontainers.image.title ' found"
240+ f "Unexpected layer with missing annotation '{ ImageManifest . ANNOTATION_TITLE_KEY } ' found"
195241 )
196242
197- image_title = layer_dict ["annotations" ]["org.opencontainers.image.title" ]
243+ image_title = layer_dict ["annotations" ][ImageManifest . ANNOTATION_TITLE_KEY ]
198244 existing_layer_index = 0
199245
200246 for existing_layer in self ["layers" ]:
201- if "org.opencontainers.image.title" not in existing_layer .get (
247+ if ImageManifest . ANNOTATION_TITLE_KEY not in existing_layer .get (
202248 "annotations" , {}
203249 ):
204250 raise RuntimeError (
205- "Unexpected layer with missing annotation 'org.opencontainers.image.title ' found"
251+ f "Unexpected layer with missing annotation '{ ImageManifest . ANNOTATION_TITLE_KEY } ' found"
206252 )
207253
208254 if (
209255 image_title
210- == existing_layer ["annotations" ]["org.opencontainers.image.title" ]
256+ == existing_layer ["annotations" ][ImageManifest . ANNOTATION_TITLE_KEY ]
211257 ):
212258 break
213259
0 commit comments