@@ -4,9 +4,16 @@ import (
44 "context"
55 "encoding/json"
66 "fmt"
7+ "io"
8+ "path"
79 "strings"
810 "time"
911
12+ "github.com/containerd/containerd/archive/compression"
13+ "github.com/containerd/containerd/images"
14+ "github.com/containerd/containerd/namespaces"
15+ "github.com/containerd/containerd/platforms"
16+ ocispec "github.com/opencontainers/image-spec/specs-go/v1"
1017 "github.com/operator-framework/api/pkg/operators/v1alpha1"
1118 "github.com/operator-framework/operator-registry/pkg/image"
1219 "github.com/operator-framework/operator-registry/pkg/image/containerdregistry"
@@ -23,7 +30,13 @@ import (
2330 "github.com/operator-framework/kubectl-operator/internal/pkg/catalog"
2431)
2532
26- const grpcPort = "50051"
33+ const (
34+ grpcPort = "50051"
35+ dbPathLabel = "operators.operatorframework.io.index.database.v1"
36+ alphaDisplayNameLabel = "alpha.operators.operatorframework.io.index.display-name.v1"
37+ alphaPublisherLabel = "alpha.operators.operatorframework.io.index.publisher.v1"
38+ defaultDatabasePath = "/database/index.db"
39+ )
2740
2841type CatalogAdd struct {
2942 config * Configuration
@@ -81,7 +94,7 @@ func (a *CatalogAdd) Run(ctx context.Context) (*v1alpha1.CatalogSource, error) {
8194
8295 labels , err := a .labelsFor (ctx , a .IndexImage )
8396 if err != nil {
84- return nil , err
97+ return nil , fmt . Errorf ( "get image labels: %v" , err )
8598 }
8699
87100 a .setDefaults (labels )
@@ -96,20 +109,33 @@ func (a *CatalogAdd) Run(ctx context.Context) (*v1alpha1.CatalogSource, error) {
96109 }
97110
98111 cs := catalog .Build (csKey , opts ... )
99- if err := a .createCatalogSource (ctx , cs ); err != nil {
100- return nil , err
112+ if err := a .config . Client . Create (ctx , cs ); err != nil {
113+ return nil , fmt . Errorf ( "create catalogsource: %v" , err )
101114 }
102115
103116 var registryPod * corev1.Pod
104117 if len (a .InjectBundles ) > 0 {
105- if registryPod , err = a .createRegistryPod (ctx , cs ); err != nil {
118+ dbPath , ok := labels [dbPathLabel ]
119+ if ! ok {
120+ // No database path label, so assume this is an index base image.
121+ // Choose "semver" bundle add mode (if not explicitly set) and
122+ // use the default database path.
123+ if a .InjectBundleMode == "" {
124+ a .InjectBundleMode = "semver"
125+ }
126+ dbPath = defaultDatabasePath
127+ }
128+ if a .InjectBundleMode == "" {
129+ a .InjectBundleMode = "replaces"
130+ }
131+ if registryPod , err = a .createRegistryPod (ctx , cs , dbPath ); err != nil {
106132 defer a .cleanup (cs )
107133 return nil , err
108134 }
109135
110136 if err := a .updateCatalogSource (ctx , cs , registryPod ); err != nil {
111137 defer a .cleanup (cs )
112- return nil , err
138+ return nil , fmt . Errorf ( "update catalog source: %v" , err )
113139 }
114140 }
115141
@@ -122,51 +148,61 @@ func (a *CatalogAdd) Run(ctx context.Context) (*v1alpha1.CatalogSource, error) {
122148}
123149
124150func (a * CatalogAdd ) labelsFor (ctx context.Context , indexImage string ) (map [string ]string , error ) {
125- simpleRef := image .SimpleReference (indexImage )
126- if err := a .registry .Pull (ctx , simpleRef ); err != nil {
151+ ref := image .SimpleReference (indexImage )
152+ if err := a .registry .Pull (ctx , ref ); err != nil {
127153 return nil , fmt .Errorf ("pull image: %v" , err )
128154 }
129- labels , err := a .registry .Labels (ctx , simpleRef )
155+
156+ ctx = namespaces .WithNamespace (ctx , namespaces .Default )
157+ img , err := a .registry .Images ().Get (ctx , ref .String ())
130158 if err != nil {
131- return nil , fmt .Errorf ("get image labels : %v" , err )
159+ return nil , fmt .Errorf ("get image from local registry : %v" , err )
132160 }
133- return labels , nil
161+
162+ manifest , err := images .Manifest (ctx , a .registry .Content (), img .Target , platforms .All )
163+ if err != nil {
164+ return nil , fmt .Errorf ("resolve image manifest: %v" , err )
165+ }
166+
167+ ra , err := a .registry .Content ().ReaderAt (ctx , manifest .Config )
168+ if err != nil {
169+ return nil , fmt .Errorf ("get image reader: %v" , err )
170+ }
171+ defer ra .Close ()
172+
173+ decompressed , err := compression .DecompressStream (io .NewSectionReader (ra , 0 , ra .Size ()))
174+ if err != nil {
175+ return nil , fmt .Errorf ("decompress image data: %v" , err )
176+ }
177+ var imageMeta ocispec.Image
178+ dec := json .NewDecoder (decompressed )
179+ if err := dec .Decode (& imageMeta ); err != nil {
180+ return nil , fmt .Errorf ("decode image metadata: %v" , err )
181+ }
182+ return imageMeta .Config .Labels , nil
134183}
135184
136185func (a * CatalogAdd ) setDefaults (labels map [string ]string ) {
137186 if a .DisplayName == "" {
138- if v , ok := labels ["operators.operatorframework.io.index.display-name" ]; ok {
187+ if v , ok := labels [alphaDisplayNameLabel ]; ok {
139188 a .DisplayName = v
140189 }
141190 }
142191 if a .Publisher == "" {
143- if v , ok := labels ["operators.operatorframework.io.index.publisher" ]; ok {
192+ if v , ok := labels [alphaPublisherLabel ]; ok {
144193 a .Publisher = v
145194 }
146195 }
147- if a .InjectBundleMode == "" {
148- if strings .HasPrefix (a .IndexImage , "quay.io/operator-framework/upstream-opm-builder" ) {
149- a .InjectBundleMode = "semver"
150- } else {
151- a .InjectBundleMode = "replaces"
152- }
153- }
154- }
155-
156- func (a * CatalogAdd ) createCatalogSource (ctx context.Context , cs * v1alpha1.CatalogSource ) error {
157- if err := a .config .Client .Create (ctx , cs ); err != nil {
158- return fmt .Errorf ("create catalogsource: %v" , err )
159- }
160- return nil
161196}
162197
163- func (a * CatalogAdd ) createRegistryPod (ctx context.Context , cs * v1alpha1.CatalogSource ) (* corev1.Pod , error ) {
198+ func (a * CatalogAdd ) createRegistryPod (ctx context.Context , cs * v1alpha1.CatalogSource , dbPath string ) (* corev1.Pod , error ) {
199+ dbDir := path .Dir (dbPath )
164200 command := []string {
165201 "/bin/sh" ,
166202 "-c" ,
167- fmt .Sprintf (`mkdir -p /database && \
168- /bin/opm registry add -d /database/index.db --mode=%s -b %s && \
169- /bin/opm registry serve -d /database/index.db -p %s` , a .InjectBundleMode , strings .Join (a .InjectBundles , "," ), grpcPort ),
203+ fmt .Sprintf (`mkdir -p %s && \
204+ /bin/opm registry add -d %s --mode=%s -b %s && \
205+ /bin/opm registry serve -d %s -p %s` , dbDir , dbPath , a .InjectBundleMode , strings .Join (a .InjectBundles , "," ), dbPath , grpcPort ),
170206 }
171207
172208 pod := & corev1.Pod {
@@ -184,26 +220,15 @@ func (a *CatalogAdd) createRegistryPod(ctx context.Context, cs *v1alpha1.Catalog
184220 },
185221 },
186222 }
223+
224+ if err := controllerutil .SetOwnerReference (cs , pod , a .config .Scheme ); err != nil {
225+ return nil , fmt .Errorf ("set registry pod owner reference: %v" , err )
226+ }
187227 if err := a .config .Client .Create (ctx , pod ); err != nil {
188228 return nil , fmt .Errorf ("create registry pod: %v" , err )
189229 }
190230
191231 podKey := objectKeyForObject (pod )
192- if err := retry .RetryOnConflict (retry .DefaultBackoff , func () error {
193- if err := a .config .Client .Get (ctx , podKey , pod ); err != nil {
194- return fmt .Errorf ("get registry pod: %v" , err )
195- }
196- if err := controllerutil .SetOwnerReference (cs , pod , a .config .Scheme ); err != nil {
197- return fmt .Errorf ("set registry pod owner reference: %v" , err )
198- }
199- if err := a .config .Client .Update (ctx , pod ); err != nil {
200- return fmt .Errorf ("update registry pod owner reference: %v" , err )
201- }
202- return nil
203- }); err != nil {
204- return nil , err
205- }
206-
207232 if err := wait .PollImmediateUntil (time .Millisecond * 250 , func () (bool , error ) {
208233 if err := a .config .Client .Get (ctx , podKey , pod ); err != nil {
209234 return false , err
@@ -219,26 +244,25 @@ func (a *CatalogAdd) createRegistryPod(ctx context.Context, cs *v1alpha1.Catalog
219244}
220245
221246func (a * CatalogAdd ) updateCatalogSource (ctx context.Context , cs * v1alpha1.CatalogSource , pod * corev1.Pod ) error {
222- cs .Spec .Address = fmt .Sprintf ("%s:%s" , pod .Status .PodIP , grpcPort )
223-
224247 injectedBundlesJSON , err := json .Marshal (a .InjectBundles )
225248 if err != nil {
226249 return fmt .Errorf ("json marshal injected bundles: %v" , err )
227250 }
228- cs .ObjectMeta .Annotations = map [string ]string {
229- "operators.operatorframework.io/index-image" : a .IndexImage ,
230- "operators.operatorframework.io/inject-bundle-mode" : a .InjectBundleMode ,
231- "operators.operatorframework.io/injected-bundles" : string (injectedBundlesJSON ),
232- }
251+
233252 csKey := objectKeyForObject (cs )
234253 if err := retry .RetryOnConflict (retry .DefaultBackoff , func () error {
235254 if err := a .config .Client .Get (ctx , csKey , cs ); err != nil {
236255 return fmt .Errorf ("get catalog source: %v" , err )
237256 }
238- if err := a .config .Client .Update (ctx , cs ); err != nil {
239- return fmt .Errorf ("update catalog source: %v" , err )
257+
258+ cs .Spec .Address = fmt .Sprintf ("%s:%s" , pod .Status .PodIP , grpcPort )
259+ cs .ObjectMeta .Annotations = map [string ]string {
260+ "operators.operatorframework.io/index-image" : a .IndexImage ,
261+ "operators.operatorframework.io/inject-bundle-mode" : a .InjectBundleMode ,
262+ "operators.operatorframework.io/injected-bundles" : string (injectedBundlesJSON ),
240263 }
241- return nil
264+
265+ return a .config .Client .Update (ctx , cs )
242266 }); err != nil {
243267 return err
244268 }
0 commit comments