@@ -4,9 +4,16 @@ import (
4
4
"context"
5
5
"encoding/json"
6
6
"fmt"
7
+ "io"
8
+ "path"
7
9
"strings"
8
10
"time"
9
11
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"
10
17
"github.com/operator-framework/api/pkg/operators/v1alpha1"
11
18
"github.com/operator-framework/operator-registry/pkg/image"
12
19
"github.com/operator-framework/operator-registry/pkg/image/containerdregistry"
@@ -23,7 +30,13 @@ import (
23
30
"github.com/operator-framework/kubectl-operator/internal/pkg/catalog"
24
31
)
25
32
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
+ )
27
40
28
41
type CatalogAdd struct {
29
42
config * Configuration
@@ -81,7 +94,7 @@ func (a *CatalogAdd) Run(ctx context.Context) (*v1alpha1.CatalogSource, error) {
81
94
82
95
labels , err := a .labelsFor (ctx , a .IndexImage )
83
96
if err != nil {
84
- return nil , err
97
+ return nil , fmt . Errorf ( "get image labels: %v" , err )
85
98
}
86
99
87
100
a .setDefaults (labels )
@@ -96,20 +109,33 @@ func (a *CatalogAdd) Run(ctx context.Context) (*v1alpha1.CatalogSource, error) {
96
109
}
97
110
98
111
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 )
101
114
}
102
115
103
116
var registryPod * corev1.Pod
104
117
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 {
106
132
defer a .cleanup (cs )
107
133
return nil , err
108
134
}
109
135
110
136
if err := a .updateCatalogSource (ctx , cs , registryPod ); err != nil {
111
137
defer a .cleanup (cs )
112
- return nil , err
138
+ return nil , fmt . Errorf ( "update catalog source: %v" , err )
113
139
}
114
140
}
115
141
@@ -122,51 +148,61 @@ func (a *CatalogAdd) Run(ctx context.Context) (*v1alpha1.CatalogSource, error) {
122
148
}
123
149
124
150
func (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 {
127
153
return nil , fmt .Errorf ("pull image: %v" , err )
128
154
}
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 ())
130
158
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 )
132
160
}
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
134
183
}
135
184
136
185
func (a * CatalogAdd ) setDefaults (labels map [string ]string ) {
137
186
if a .DisplayName == "" {
138
- if v , ok := labels ["operators.operatorframework.io.index.display-name" ]; ok {
187
+ if v , ok := labels [alphaDisplayNameLabel ]; ok {
139
188
a .DisplayName = v
140
189
}
141
190
}
142
191
if a .Publisher == "" {
143
- if v , ok := labels ["operators.operatorframework.io.index.publisher" ]; ok {
192
+ if v , ok := labels [alphaPublisherLabel ]; ok {
144
193
a .Publisher = v
145
194
}
146
195
}
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
161
196
}
162
197
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 )
164
200
command := []string {
165
201
"/bin/sh" ,
166
202
"-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 ),
170
206
}
171
207
172
208
pod := & corev1.Pod {
@@ -184,26 +220,15 @@ func (a *CatalogAdd) createRegistryPod(ctx context.Context, cs *v1alpha1.Catalog
184
220
},
185
221
},
186
222
}
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
+ }
187
227
if err := a .config .Client .Create (ctx , pod ); err != nil {
188
228
return nil , fmt .Errorf ("create registry pod: %v" , err )
189
229
}
190
230
191
231
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
-
207
232
if err := wait .PollImmediateUntil (time .Millisecond * 250 , func () (bool , error ) {
208
233
if err := a .config .Client .Get (ctx , podKey , pod ); err != nil {
209
234
return false , err
@@ -219,26 +244,25 @@ func (a *CatalogAdd) createRegistryPod(ctx context.Context, cs *v1alpha1.Catalog
219
244
}
220
245
221
246
func (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
-
224
247
injectedBundlesJSON , err := json .Marshal (a .InjectBundles )
225
248
if err != nil {
226
249
return fmt .Errorf ("json marshal injected bundles: %v" , err )
227
250
}
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
+
233
252
csKey := objectKeyForObject (cs )
234
253
if err := retry .RetryOnConflict (retry .DefaultBackoff , func () error {
235
254
if err := a .config .Client .Get (ctx , csKey , cs ); err != nil {
236
255
return fmt .Errorf ("get catalog source: %v" , err )
237
256
}
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 ),
240
263
}
241
- return nil
264
+
265
+ return a .config .Client .Update (ctx , cs )
242
266
}); err != nil {
243
267
return err
244
268
}
0 commit comments