Skip to content

Commit 3b389b6

Browse files
jahantechjahandavidcollom
authored
Resolve SHA256 to tags automatically if the images specified in the k8s manifests doesn't contain tag. (#70)
Signed-off-by: Jahan Syed <[email protected]> Co-authored-by: jahan <[email protected]> Co-authored-by: David Collom <[email protected]>
1 parent faa5ded commit 3b389b6

File tree

10 files changed

+82
-6
lines changed

10 files changed

+82
-6
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ enrich version checking on image tags:
107107
is. In this example, the current version of `my-container` will be compared
108108
against the image versions in the `docker.io/bitnami/etcd` registry.
109109

110+
- `resolve-sha-to-tags.version-checker.io/my-container`: is used to
111+
resolve images specified using sha256 in kubernetes manifests to valid semver
112+
tags. To enable this the annotation value must be set to "true".
113+
110114
## Known configurations
111115

112116
From time to time, version-checker may need some of the above options applied to determine the latest version,

go.sum

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,8 @@ github.com/Azure/go-autorest/logger v0.2.2/go.mod h1:I5fg9K52o+iuydlWfa9T5K6WFos
1919
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
2020
github.com/Azure/go-autorest/tracing v0.6.1 h1:YUMSrC/CeD1ZnnXcNYU4a/fzsO35u2Fsful9L/2nyR0=
2121
github.com/Azure/go-autorest/tracing v0.6.1/go.mod h1:/3EgjbsjraOqiicERAeu3m7/z0x1TzjQGAwDrJrXGkc=
22-
github.com/MicahParks/jwkset v0.5.19 h1:XZCsgJv05DBCvxEHYEHlSafqiuVn5ESG0VRB331Fxhw=
23-
github.com/MicahParks/jwkset v0.5.19/go.mod h1:q8ptTGn/Z9c4MwbcfeCDssADeVQb3Pk7PnVxrvi+2QY=
2422
github.com/MicahParks/jwkset v0.8.0 h1:jHtclI38Gibmu17XMI6+6/UB59srp58pQVxePHRK5o8=
2523
github.com/MicahParks/jwkset v0.8.0/go.mod h1:fVrj6TmG1aKlJEeceAz7JsXGTXEn72zP1px3us53JrA=
26-
github.com/MicahParks/keyfunc/v3 v3.3.5 h1:7ceAJLUAldnoueHDNzF8Bx06oVcQ5CfJnYwNt1U3YYo=
27-
github.com/MicahParks/keyfunc/v3 v3.3.5/go.mod h1:SdCCyMJn/bYqWDvARspC6nCT8Sk74MjuAY22C7dCST8=
2824
github.com/MicahParks/keyfunc/v3 v3.3.10 h1:JtEGE8OcNeI297AMrR4gVXivV8fyAawFUMkbwNreJRk=
2925
github.com/MicahParks/keyfunc/v3 v3.3.10/go.mod h1:1TEt+Q3FO7Yz2zWeYO//fMxZMOiar808NqjWQQpBPtU=
3026
github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=

pkg/api/annotations.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ const (
1414
// as its tag.
1515
UseSHAAnnotationKey = "use-sha.version-checker.io"
1616

17+
//ResolveSHAToTagsKey is used to resolve image sha256 to corresponding tags
18+
ResolveSHAToTagsKey = "resolve-sha-to-tags.version-checker.io"
19+
1720
// MatchRegexAnnotationKey will enforce that tags that are looked up must
1821
// match this regex. UseMetaDataAnnotationKey is not required when this is
1922
// set. All other options are ignored when this is set.

pkg/api/options.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ type Options struct {
99

1010
// UseSHA cannot be used with any other options
1111
UseSHA bool `json:"use-sha,omitempty"`
12+
// Resolve SHA to a TAG
13+
ResolveSHAToTags bool `json:"resolve-sha-to-tags,omitempty"`
1214

1315
MatchRegex *string `json:"match-regex,omitempty"`
1416

pkg/controller/checker/checker.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,22 @@ func (c *Checker) Container(ctx context.Context, log *logrus.Entry,
4444
imageURL, currentTag, currentSHA := urlTagSHAFromImage(container.Image)
4545
usingSHA, usingTag := len(currentSHA) > 0, len(currentTag) > 0
4646

47+
if opts.ResolveSHAToTags {
48+
49+
if len(*opts.OverrideURL) > 0 {
50+
imageURL = *opts.OverrideURL
51+
}
52+
resolvedTag, err := c.search.ResolveSHAToTag(ctx, imageURL, currentSHA)
53+
54+
if len(resolvedTag) > 0 && err == nil {
55+
log.Infof("Successfully resolved tag for sha256: %s at url: %s", currentSHA, imageURL)
56+
currentTag = resolvedTag
57+
usingSHA = false
58+
usingTag = true
59+
}
60+
}
61+
62+
// If using latest or no tag, then compare on SHA
4763
if c.isLatestOrEmptyTag(currentTag) {
4864
c.handleLatestOrEmptyTag(log, currentTag, currentSHA, opts)
4965
usingTag = false

pkg/controller/internal/fake/search/search.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,18 @@ import (
1111
var _ search.Searcher = &FakeSearch{}
1212

1313
type FakeSearch struct {
14-
latestImageF func() (*api.ImageTag, error)
14+
latestImageF func() (*api.ImageTag, error)
15+
resolveSHAToTagF func() (string, error)
1516
}
1617

1718
func New() *FakeSearch {
1819
return &FakeSearch{
1920
latestImageF: func() (*api.ImageTag, error) {
2021
return nil, nil
2122
},
23+
resolveSHAToTagF: func() (string, error) {
24+
return "", nil
25+
},
2226
}
2327
}
2428

@@ -33,5 +37,8 @@ func (f *FakeSearch) LatestImage(context.Context, string, *api.Options) (*api.Im
3337
return f.latestImageF()
3438
}
3539

40+
func (f *FakeSearch) ResolveSHAToTag(ctx context.Context, imageURL string, imageSHA string) (string, error) {
41+
return f.resolveSHAToTagF()
42+
}
3643
func (f *FakeSearch) Run(time.Duration) {
3744
}

pkg/controller/options/options.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func (b *Builder) Options(name string) (*api.Options, error) {
3636
// Define the handlers
3737
handlers := []optionsHandler{
3838
b.handleSHAOption,
39+
b.handleSHAToTagOption,
3940
b.handleMetadataOption,
4041
b.handleRegexOption,
4142
b.handlePinMajorOption,
@@ -53,7 +54,9 @@ func (b *Builder) Options(name string) (*api.Options, error) {
5354

5455
// Ensure UseSHA is not used with other semver options
5556
if opts.UseSHA && setNonSha {
56-
errs = append(errs, fmt.Sprintf("cannot define %q with any semver options", b.index(name, api.UseSHAAnnotationKey)))
57+
errs = append(errs,
58+
fmt.Sprintf("cannot define %q with any semver options", b.index(name, api.UseSHAAnnotationKey)),
59+
)
5760
}
5861

5962
if len(errs) > 0 {
@@ -68,6 +71,12 @@ func (b *Builder) handleSHAOption(name string, opts *api.Options, setNonSha *boo
6871
}
6972
return nil
7073
}
74+
func (b *Builder) handleSHAToTagOption(name string, opts *api.Options, setNonSha *bool, errs *[]string) error {
75+
if ResolveSHAToTags, ok := b.ans[b.index(name, api.ResolveSHAToTagsKey)]; ok && ResolveSHAToTags == "true" {
76+
opts.ResolveSHAToTags = true
77+
}
78+
return nil
79+
}
7180

7281
func (b *Builder) handleMetadataOption(name string, opts *api.Options, setNonSha *bool, errs *[]string) error {
7382
if useMetaData, ok := b.ans[b.index(name, api.UseMetaDataAnnotationKey)]; ok && useMetaData == "true" {

pkg/controller/options/options_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,16 @@ func TestBuild(t *testing.T) {
111111
},
112112
expErr: "",
113113
},
114+
"output options for resolve sha": {
115+
containerName: "test-name",
116+
annotations: map[string]string{
117+
api.ResolveSHAToTagsKey + "/test-name": "true",
118+
},
119+
expOptions: &api.Options{
120+
ResolveSHAToTags: true,
121+
},
122+
expErr: "",
123+
},
114124
"bool options that don't have 'true' and nothing": {
115125
containerName: "test-name",
116126
annotations: map[string]string{

pkg/controller/search/search.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
// Searcher is the interface for Search to facilitate testing.
1818
type Searcher interface {
1919
LatestImage(context.Context, string, *api.Options) (*api.ImageTag, error)
20+
ResolveSHAToTag(ctx context.Context, imageURL string, imageSHA string) (string, error)
2021
}
2122

2223
// Search is the implementation for the searching and caching of image URLs.
@@ -65,6 +66,16 @@ func (s *Search) LatestImage(ctx context.Context, imageURL string, opts *api.Opt
6566
return lastestImage.(*api.ImageTag), nil
6667
}
6768

69+
func (s *Search) ResolveSHAToTag(ctx context.Context, imageURL string, imageSHA string) (string, error) {
70+
71+
tag, err := s.versionGetter.ResolveSHAToTag(ctx, imageURL, imageSHA)
72+
if err != nil {
73+
return "", fmt.Errorf("failed to resolve sha to tag: %w", err)
74+
}
75+
76+
return tag, err
77+
}
78+
6879
// calculateHashIndex returns a hash index given an imageURL and options.
6980
func calculateHashIndex(imageURL string, opts *api.Options) (string, error) {
7081
optsJSON, err := json.Marshal(opts)

pkg/version/version.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,24 @@ func (v *Version) LatestTagFromImage(ctx context.Context, imageURL string, opts
7474
return tag, err
7575
}
7676

77+
// ResolveSHAToTag Resolve a SHA to a tag if possible
78+
func (v *Version) ResolveSHAToTag(ctx context.Context, imageURL string, imageSHA string) (string, error) {
79+
80+
tagsI, err := v.imageCache.Get(ctx, imageURL, imageURL, nil)
81+
if err != nil {
82+
return "", err
83+
}
84+
tags := tagsI.([]api.ImageTag)
85+
86+
for i := range tags {
87+
if tags[i].SHA == imageSHA {
88+
return tags[i].Tag, nil
89+
}
90+
}
91+
92+
return "", nil
93+
}
94+
7795
// Fetch returns the given image tags for a given image URL.
7896
func (v *Version) Fetch(ctx context.Context, imageURL string, _ *api.Options) (interface{}, error) {
7997
// fetch tags from image URL

0 commit comments

Comments
 (0)