Skip to content

Commit 9685c23

Browse files
authored
Merge pull request #933 from k8s-infra-cherrypick-robot/cherry-pick-928-to-release-0.24
[release-0.24] 🐛 Allow non canonical image paths
2 parents d21153e + bfa63ce commit 9685c23

File tree

4 files changed

+142
-4
lines changed

4 files changed

+142
-4
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.24.4
55
require (
66
github.com/MakeNowJust/heredoc v1.0.0
77
github.com/Masterminds/goutils v1.1.1
8+
github.com/distribution/reference v0.6.0
89
github.com/evanphx/json-patch/v5 v5.9.11
910
github.com/go-errors/errors v1.5.1
1011
github.com/go-logr/logr v1.4.3
@@ -40,7 +41,6 @@ require (
4041
github.com/cespare/xxhash/v2 v2.3.0 // indirect
4142
github.com/cloudflare/circl v1.6.1 // indirect
4243
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
43-
github.com/distribution/reference v0.6.0 // indirect
4444
github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46 // indirect
4545
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
4646
github.com/felixge/httpsnoop v1.0.4 // indirect

internal/controller/image_overrides.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ limitations under the License.
1717
package controller
1818

1919
import (
20+
"errors"
2021
"fmt"
2122

23+
"github.com/distribution/reference"
2224
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2325
"k8s.io/client-go/kubernetes/scheme"
2426

@@ -39,13 +41,34 @@ func imageOverrides(component string, overrides configclient.Client) func(objs [
3941
}
4042

4143
return fixImages(objs, func(image string) (string, error) {
42-
return overrides.ImageMeta().AlterImage(component, image)
44+
return alterImage(component, image, overrides.ImageMeta())
4345
})
4446
}
4547

4648
return imageOverridesWrapper
4749
}
4850

51+
// alterImage accepts images as is, including non canonical formats.
52+
// If image overrides fail due to non canonical format, the original image is returned unchanged.
53+
// Allowing non canonical formats is designed for advanced users who may want to use such formats intentionally.
54+
func alterImage(component, imageString string, imageMeta configclient.ImageMetaClient) (string, error) {
55+
result, err := imageMeta.AlterImage(component, imageString)
56+
if err != nil {
57+
if isCanonicalError(err) {
58+
return imageString, nil
59+
}
60+
61+
return "", err
62+
}
63+
64+
return result, nil
65+
}
66+
67+
// isCanonicalError checks if error is about non canonical image format.
68+
func isCanonicalError(err error) bool {
69+
return errors.Is(err, reference.ErrNameNotCanonical)
70+
}
71+
4972
// fixImages alters images using the give alter func
5073
// NB. The implemented approach is specific for the provider components YAML & for the cert-manager manifest; it is not
5174
// intended to cover all the possible objects used to deploy containers existing in Kubernetes.

internal/controller/image_overrides_test.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"fmt"
2121
"testing"
2222

23+
"github.com/distribution/reference"
2324
. "github.com/onsi/gomega"
2425
appsv1 "k8s.io/api/apps/v1"
2526
corev1 "k8s.io/api/core/v1"
@@ -169,3 +170,117 @@ func TestFixImages(t *testing.T) {
169170
})
170171
}
171172
}
173+
174+
// mockImageMetaClient is a test double for configclient.ImageMetaClient.
175+
type mockImageMetaClient struct {
176+
alterFunc func(component, image string) (string, error)
177+
}
178+
179+
func (m *mockImageMetaClient) AlterImage(component, image string) (string, error) {
180+
return m.alterFunc(component, image)
181+
}
182+
183+
func TestAlterImage(t *testing.T) {
184+
tests := []struct {
185+
name string
186+
component string
187+
image string
188+
mockFunc func(component, image string) (string, error)
189+
want string
190+
wantErr bool
191+
}{
192+
{
193+
name: "canonical image with override applies override",
194+
component: "cluster-api",
195+
image: "example.com/controller:v1.0.0",
196+
mockFunc: func(component, image string) (string, error) {
197+
return "example.com/custom:v2.0.0", nil
198+
},
199+
want: "example.com/custom:v2.0.0",
200+
wantErr: false,
201+
},
202+
{
203+
name: "non-canonical image returns original on canonical error",
204+
component: "cluster-api",
205+
image: "example.com/controller:v1.0.0",
206+
mockFunc: func(component, image string) (string, error) {
207+
return "", reference.ErrNameNotCanonical
208+
},
209+
want: "example.com/controller:v1.0.0",
210+
wantErr: false,
211+
},
212+
{
213+
name: "other errors are propagated",
214+
component: "cluster-api",
215+
image: "example.com/controller:v1.0.0",
216+
mockFunc: func(component, image string) (string, error) {
217+
return "", fmt.Errorf("test")
218+
},
219+
want: "",
220+
wantErr: true,
221+
},
222+
}
223+
224+
for _, tt := range tests {
225+
t.Run(tt.name, func(t *testing.T) {
226+
g := NewWithT(t)
227+
228+
mock := &mockImageMetaClient{alterFunc: tt.mockFunc}
229+
result, err := alterImage(tt.component, tt.image, mock)
230+
231+
if tt.wantErr {
232+
g.Expect(err).To(HaveOccurred())
233+
return
234+
}
235+
236+
g.Expect(err).ToNot(HaveOccurred())
237+
g.Expect(result).To(Equal(tt.want))
238+
})
239+
}
240+
}
241+
242+
func TestIsCanonicalError(t *testing.T) {
243+
tests := []struct {
244+
name string
245+
err error
246+
want bool
247+
}{
248+
{
249+
name: "nil error returns false",
250+
err: nil,
251+
want: false,
252+
},
253+
{
254+
name: "ErrNameNotCanonical returns true",
255+
err: reference.ErrNameNotCanonical,
256+
want: true,
257+
},
258+
{
259+
name: "wrapped ErrNameNotCanonical returns true",
260+
err: fmt.Errorf("parse error: %w", reference.ErrNameNotCanonical),
261+
want: true,
262+
},
263+
{
264+
name: "other error returns false",
265+
err: fmt.Errorf("test"),
266+
want: false,
267+
},
268+
{
269+
name: "couldn't parse image name error returns false",
270+
err: fmt.Errorf("couldn't parse image name: invalid format"),
271+
want: false,
272+
},
273+
{
274+
name: "empty error message returns false",
275+
err: fmt.Errorf(""),
276+
want: false,
277+
},
278+
}
279+
280+
for _, tt := range tests {
281+
t.Run(tt.name, func(t *testing.T) {
282+
g := NewWithT(t)
283+
g.Expect(isCanonicalError(tt.err)).To(Equal(tt.want))
284+
})
285+
}
286+
}

scripts/ci-install-mdbook.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,5 @@ curl https://sh.rustup.rs -sSf | sh -s -- -y
3030

3131
# Install mdbook and dependencies
3232
cargo install mdbook --version "$VERSION" --root "$OUTPUT_PATH"
33-
cargo install mdbook-fs-summary --root "$OUTPUT_PATH"
34-
cargo install mdbook-toc --root "$OUTPUT_PATH"
33+
cargo install mdbook-fs-summary --version "=0.2.0" --root "$OUTPUT_PATH"
34+
cargo install mdbook-toc --version "=0.14.2" --root "$OUTPUT_PATH"

0 commit comments

Comments
 (0)