Skip to content

Commit 546e847

Browse files
ryantkingIsh Shahjmrodri
authored
Add support for using image digests instead of tags (#5567)
Co-authored-by: Ish Shah <[email protected]> Co-authored-by: jesus m. rodriguez <[email protected]>
1 parent b883d6a commit 546e847

File tree

10 files changed

+172
-3
lines changed

10 files changed

+172
-3
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
entries:
2+
- description: >
3+
Adds support to bundle operators using image digests instead of tags.
4+
kind: "addition"
5+
breaking: false
6+
migration:
7+
header: Support image digests instead of tags
8+
body: |
9+
Add following variables to your project's `Makefile` below the `BUNDLE_IMG ?=`.
10+
11+
```
12+
# BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command
13+
BUNDLE_GEN_FLAGS ?= -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
14+
15+
# USE_IMAGE_DIGESTS defines if images are resolved via tags or digests
16+
# You can enable this value if you would like to use SHA Based Digests
17+
# To enable set flag to true
18+
USE_IMAGE_DIGESTS ?= false
19+
ifeq ($(USE_IMAGE_DIGESTS), true)
20+
BUNDLE_GEN_FLAGS += --use-image-digests
21+
endif
22+
```
23+
24+
Using the YAML string '|' operator means that newlines in this string will
25+
Then in the `bundle` target we want to replace the flags passed to
26+
`generate bundle` with a reference to the `BUNDLE_GEN_FLAGS` above.
27+
28+
The `generate bundle` line should look like this
29+
30+
```
31+
$(KUSTOMIZE) build config/manifests | operator-sdk generate bundle $(BUNDLE_GEN_FLAGS)
32+
```
33+
34+
For reference the *PREVIOUS* version looked as follows
35+
36+
```
37+
$(KUSTOMIZE) build config/manifests | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
38+
```

internal/cmd/operator-sdk/generate/bundle/bundle.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ import (
2020
"io/ioutil"
2121
"os"
2222
"path/filepath"
23+
"strings"
2324

2425
"github.com/operator-framework/api/pkg/apis/scorecard/v1alpha3"
2526
"github.com/operator-framework/operator-registry/pkg/lib/bundle"
27+
corev1 "k8s.io/api/core/v1"
2628
"sigs.k8s.io/yaml"
2729

2830
metricsannotations "github.com/operator-framework/operator-sdk/internal/annotations/metrics"
@@ -188,6 +190,11 @@ func (c bundleCmd) runManifests() (err error) {
188190
c.println("Building a ClusterServiceVersion without an existing base")
189191
}
190192

193+
relatedImages, err := c.findRelatedImages(col)
194+
if err != nil {
195+
return err
196+
}
197+
191198
var opts []gencsv.Option
192199
stdout := genutil.NewMultiManifestWriter(os.Stdout)
193200
if c.stdout {
@@ -202,6 +209,7 @@ func (c bundleCmd) runManifests() (err error) {
202209
Collector: col,
203210
Annotations: metricsannotations.MakeBundleObjectAnnotations(c.layout),
204211
ExtraServiceAccounts: c.extraServiceAccounts,
212+
RelatedImages: relatedImages,
205213
}
206214
if err := csvGen.Generate(opts...); err != nil {
207215
return fmt.Errorf("error generating ClusterServiceVersion: %v", err)
@@ -287,3 +295,51 @@ func (c bundleCmd) runMetadata() error {
287295

288296
return bundleMetadata.GenerateMetadata()
289297
}
298+
299+
// findRelatedImages looks in the controller manager's environment for images used by the operator.
300+
func (c bundleCmd) findRelatedImages(col *collector.Manifests) (map[string]string, error) {
301+
const relatedImagePrefix = "RELATED_IMAGE_"
302+
env, err := c.findManagerEnvironment(col)
303+
if err != nil {
304+
return nil, err
305+
}
306+
imageNames := make(map[string]string, len(env))
307+
for _, envVar := range env {
308+
if strings.HasPrefix(envVar.Name, relatedImagePrefix) {
309+
if envVar.ValueFrom != nil {
310+
return nil, fmt.Errorf("related images with valueFrom field unsupported, found in %s`", envVar.Name)
311+
}
312+
313+
// transforms RELATED_IMAGE_This_IS_a_cool_image to this-is-a-cool-image
314+
name := strings.ToLower(strings.Replace(strings.TrimPrefix(envVar.Name, relatedImagePrefix), "_", "-", -1))
315+
imageNames[name] = envVar.Value
316+
}
317+
}
318+
319+
return imageNames, nil
320+
}
321+
322+
// findManagerEnvironment returns the environment passed to the controller manager container.
323+
func (c bundleCmd) findManagerEnvironment(col *collector.Manifests) ([]corev1.EnvVar, error) {
324+
const (
325+
managerLabel = "control-plane"
326+
managerLabelValue = "controller-manager"
327+
managerContainerName = "manager"
328+
)
329+
330+
for _, deployment := range col.Deployments {
331+
if val, ok := deployment.GetLabels()[managerLabel]; ok && val == managerLabelValue {
332+
for _, container := range deployment.Spec.Template.Spec.Containers {
333+
if container.Name == managerContainerName {
334+
return container.Env, nil
335+
}
336+
}
337+
338+
return nil, fmt.Errorf("manager deployment does not have container named %q", managerContainerName)
339+
}
340+
}
341+
342+
return nil, fmt.Errorf(
343+
"could not find manager deployment, should have label %s=%s", managerLabel, managerLabelValue,
344+
)
345+
}

internal/cmd/operator-sdk/generate/bundle/cmd.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ type bundleCmd struct {
4949
// These are set if a PROJECT config is not present.
5050
layout string
5151
packageName string
52+
53+
// Use Image Digests flag to toggle using traditional Image tags vs SHA Digests
54+
useImageDigests bool
5255
}
5356

5457
// NewCmd returns the 'bundle' command configured for the new project layout.
@@ -139,6 +142,8 @@ func (c *bundleCmd) addFlagsTo(fs *pflag.FlagSet) {
139142
fs.BoolVar(&c.stdout, "stdout", false, "Write bundle manifest to stdout")
140143

141144
fs.StringVar(&c.packageName, "package", "", "Bundle's package name")
145+
146+
fs.BoolVar(&c.useImageDigests, "use-image-digests", false, "Use SHA Digest for images")
142147
}
143148

144149
func (c bundleCmd) println(a ...interface{}) {

internal/generate/clusterserviceversion/clusterserviceversion.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"fmt"
1919
"io"
2020
"path/filepath"
21+
"sort"
2122
"strings"
2223

2324
"github.com/blang/semver/v4"
@@ -56,6 +57,9 @@ type Generator struct {
5657
// ExtraServiceAccounts are ServiceAccount names to consider when matching
5758
// {Cluster}Roles to include in a CSV via their Bindings.
5859
ExtraServiceAccounts []string
60+
// RelatedImages are additional images used by the operator.
61+
// It is a mapping of an image name to an image URL
62+
RelatedImages map[string]string
5963

6064
// Func that returns the writer the generated CSV's bytes are written to.
6165
getWriter func() (io.Writer, error)
@@ -165,6 +169,16 @@ func (g *Generator) generate() (base *operatorsv1alpha1.ClusterServiceVersion, e
165169
if g.FromVersion != "" {
166170
base.Spec.Replaces = genutil.MakeCSVName(g.OperatorName, g.FromVersion)
167171
}
172+
if len(g.RelatedImages) > 0 {
173+
base.Spec.RelatedImages = make([]operatorsv1alpha1.RelatedImage, 0, len(g.RelatedImages))
174+
for name, image := range g.RelatedImages {
175+
base.Spec.RelatedImages = append(base.Spec.RelatedImages, operatorsv1alpha1.RelatedImage{Name: name, Image: image})
176+
}
177+
// ensure deterministic order
178+
sort.SliceStable(base.Spec.RelatedImages, func(i, j int) bool {
179+
return strings.Compare(base.Spec.RelatedImages[i].Name, base.Spec.RelatedImages[j].Name) > 0
180+
})
181+
}
168182

169183
if err := ApplyTo(g.Collector, base, g.ExtraServiceAccounts); err != nil {
170184
return nil, err

internal/plugins/manifests/v2/init.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,25 @@ IMAGE_TAG_BASE ?= %[1]s/%[2]s
155155
# BUNDLE_IMG defines the image:tag used for the bundle.
156156
# You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=<some-registry>/<project-name-bundle>:<tag>)
157157
BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION)
158+
159+
# BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command
160+
BUNDLE_GEN_FLAGS ?= -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
161+
162+
# USE_IMAGE_DIGESTS defines if images are resolved via tags or digests
163+
# You can enable this value if you would like to use SHA Based Digests
164+
# To enable set flag to true
165+
USE_IMAGE_DIGESTS ?= false
166+
ifeq ($(USE_IMAGE_DIGESTS), true)
167+
BUNDLE_GEN_FLAGS += --use-image-digests
168+
endif
158169
`
159170

160171
makefileBundleFragmentGo = `
161172
.PHONY: bundle
162173
bundle: manifests kustomize ## Generate bundle manifests and metadata, then validate generated files.
163174
operator-sdk generate kustomize manifests -q
164175
cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG)
165-
$(KUSTOMIZE) build config/manifests | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
176+
$(KUSTOMIZE) build config/manifests | operator-sdk generate bundle $(BUNDLE_GEN_FLAGS)
166177
operator-sdk bundle validate ./bundle
167178
`
168179

testdata/ansible/memcached-operator/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ IMAGE_TAG_BASE ?= example.com/memcached-operator
3535
# You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=<some-registry>/<project-name-bundle>:<tag>)
3636
BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION)
3737

38+
# BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command
39+
BUNDLE_GEN_FLAGS ?= -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
40+
41+
# USE_IMAGE_DIGESTS defines if images are resolved via tags or digests
42+
# You can enable this value if you would like to use SHA Based Digests
43+
# To enable set flag to true
44+
USE_IMAGE_DIGESTS ?= false
45+
ifeq ($(USE_IMAGE_DIGESTS), true)
46+
BUNDLE_GEN_FLAGS += --use-image-digests
47+
endif
48+
3849
# Image URL to use all building/pushing image targets
3950
IMG ?= controller:latest
4051

testdata/go/v2/memcached-operator/Makefile

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ IMAGE_TAG_BASE ?= example.com/memcached-operator
3535
# You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=<some-registry>/<project-name-bundle>:<tag>)
3636
BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION)
3737

38+
# BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command
39+
BUNDLE_GEN_FLAGS ?= -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
40+
41+
# USE_IMAGE_DIGESTS defines if images are resolved via tags or digests
42+
# You can enable this value if you would like to use SHA Based Digests
43+
# To enable set flag to true
44+
USE_IMAGE_DIGESTS ?= false
45+
ifeq ($(USE_IMAGE_DIGESTS), true)
46+
BUNDLE_GEN_FLAGS += --use-image-digests
47+
endif
48+
3849
# Image URL to use all building/pushing image targets
3950
IMG ?= controller:latest
4051
# Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
@@ -170,7 +181,7 @@ endif
170181
bundle: manifests kustomize ## Generate bundle manifests and metadata, then validate generated files.
171182
operator-sdk generate kustomize manifests --interactive=false -q
172183
cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG)
173-
$(KUSTOMIZE) build config/manifests | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
184+
$(KUSTOMIZE) build config/manifests | operator-sdk generate bundle $(BUNDLE_GEN_FLAGS)
174185
operator-sdk bundle validate ./bundle
175186

176187
.PHONY: bundle-build

testdata/go/v3/memcached-operator/Makefile

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ IMAGE_TAG_BASE ?= example.com/memcached-operator
3535
# You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=<some-registry>/<project-name-bundle>:<tag>)
3636
BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION)
3737

38+
# BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command
39+
BUNDLE_GEN_FLAGS ?= -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
40+
41+
# USE_IMAGE_DIGESTS defines if images are resolved via tags or digests
42+
# You can enable this value if you would like to use SHA Based Digests
43+
# To enable set flag to true
44+
USE_IMAGE_DIGESTS ?= false
45+
ifeq ($(USE_IMAGE_DIGESTS), true)
46+
BUNDLE_GEN_FLAGS += --use-image-digests
47+
endif
48+
3849
# Image URL to use all building/pushing image targets
3950
IMG ?= controller:latest
4051
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
@@ -169,7 +180,7 @@ endef
169180
bundle: manifests kustomize ## Generate bundle manifests and metadata, then validate generated files.
170181
operator-sdk generate kustomize manifests --interactive=false -q
171182
cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG)
172-
$(KUSTOMIZE) build config/manifests | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
183+
$(KUSTOMIZE) build config/manifests | operator-sdk generate bundle $(BUNDLE_GEN_FLAGS)
173184
operator-sdk bundle validate ./bundle
174185

175186
.PHONY: bundle-build

testdata/helm/memcached-operator/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ IMAGE_TAG_BASE ?= example.com/memcached-operator
3535
# You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=<some-registry>/<project-name-bundle>:<tag>)
3636
BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION)
3737

38+
# BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command
39+
BUNDLE_GEN_FLAGS ?= -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
40+
41+
# USE_IMAGE_DIGESTS defines if images are resolved via tags or digests
42+
# You can enable this value if you would like to use SHA Based Digests
43+
# To enable set flag to true
44+
USE_IMAGE_DIGESTS ?= false
45+
ifeq ($(USE_IMAGE_DIGESTS), true)
46+
BUNDLE_GEN_FLAGS += --use-image-digests
47+
endif
48+
3849
# Image URL to use all building/pushing image targets
3950
IMG ?= controller:latest
4051

website/content/en/docs/cli/operator-sdk_generate_bundle.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ operator-sdk generate bundle [flags]
103103
--package string Bundle's package name
104104
-q, --quiet Run in quiet mode
105105
--stdout Write bundle manifest to stdout
106+
--use-image-digests Use SHA Digest for images
106107
-v, --version string Semantic version of the operator in the generated bundle. Only set if creating a new bundle or upgrading your operator
107108
```
108109

0 commit comments

Comments
 (0)