Skip to content

Commit 90658e4

Browse files
fix
1 parent 44d1784 commit 90658e4

4 files changed

Lines changed: 75 additions & 15 deletions

File tree

cloudprofilesync/source.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import (
77
"context"
88
"encoding/json"
99
"errors"
10-
"fmt"
1110
"strings"
1211

1312
gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1"
13+
"github.com/go-logr/logr"
1414
"golang.org/x/sync/semaphore"
1515
"oras.land/oras-go/v2/registry/remote"
1616
"oras.land/oras-go/v2/registry/remote/auth"
@@ -77,6 +77,7 @@ type Source interface {
7777
}
7878

7979
type OCI struct {
80+
log logr.Logger
8081
repo *remote.Repository
8182
sema *semaphore.Weighted
8283
}
@@ -89,7 +90,7 @@ type OCIParams struct {
8990
Parallel int64 `json:"parallel"`
9091
}
9192

92-
func NewOCI(params OCIParams, insecure bool) (*OCI, error) {
93+
func NewOCI(params OCIParams, insecure bool, log logr.Logger) (*OCI, error) {
9394
// Create a new OCI repository
9495
repo, err := remote.NewRepository(params.Registry + "/" + params.Repository)
9596
if err != nil {
@@ -109,6 +110,7 @@ func NewOCI(params OCIParams, insecure bool) (*OCI, error) {
109110
repo.PlainHTTP = insecure
110111

111112
return &OCI{
113+
log: log,
112114
repo: repo,
113115
sema: semaphore.NewWeighted(params.Parallel),
114116
}, nil
@@ -134,7 +136,8 @@ func (o *OCI) GetVersions(ctx context.Context) ([]SourceImage, error) {
134136
defer o.sema.Release(1)
135137
_, reader, err := o.repo.FetchReference(ctx, tag)
136138
if err != nil {
137-
out <- Result[SourceImage]{err: err}
139+
o.log.V(1).Info("skipping tag: failed to fetch manifest", "tag", tag, "error", err)
140+
out <- Result[SourceImage]{}
138141
return
139142
}
140143
defer reader.Close()
@@ -143,12 +146,14 @@ func (o *OCI) GetVersions(ctx context.Context) ([]SourceImage, error) {
143146
}{}
144147
err = json.NewDecoder(reader).Decode(&manifest)
145148
if err != nil {
146-
out <- Result[SourceImage]{err: err}
149+
o.log.V(1).Info("skipping tag: failed to decode manifest", "tag", tag, "error", err)
150+
out <- Result[SourceImage]{}
147151
return
148152
}
149153
arch, ok := manifest.Annotations["architecture"]
150154
if !ok {
151-
out <- Result[SourceImage]{err: fmt.Errorf("architecture annotation not found in descriptor. tag: %s", tag)}
155+
o.log.V(1).Info("skipping tag: architecture annotation not found", "tag", tag)
156+
out <- Result[SourceImage]{}
152157
return
153158
}
154159
var capabilities gardencorev1beta1.Capabilities
@@ -184,6 +189,9 @@ func (o *OCI) GetVersions(ctx context.Context) ([]SourceImage, error) {
184189
errs = append(errs, result.err)
185190
continue
186191
}
192+
if result.value.Version == "" {
193+
continue
194+
}
187195
images = append(images, result.value)
188196
}
189197
return images, errors.Join(errs...)

cloudprofilesync/source_test.go

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strings"
1010

1111
gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1"
12+
"github.com/go-logr/logr"
1213
. "github.com/onsi/ginkgo/v2"
1314
. "github.com/onsi/gomega"
1415
"github.com/opencontainers/image-spec/specs-go"
@@ -59,7 +60,7 @@ var _ = Describe("OCISource", func() {
5960
Registry: registryAddr,
6061
Repository: "repo",
6162
Parallel: 4,
62-
}, true)
63+
}, true, logr.Discard())
6364
Expect(err).To(Succeed())
6465
versions, err := oci.GetVersions(ctx)
6566
Expect(err).To(Succeed())
@@ -103,7 +104,7 @@ var _ = Describe("OCISource", func() {
103104
Registry: registryAddr,
104105
Repository: "repo-caps",
105106
Parallel: 4,
106-
}, true)
107+
}, true, logr.Discard())
107108
Expect(err).To(Succeed())
108109
versions, err := oci.GetVersions(ctx)
109110
Expect(err).To(Succeed())
@@ -148,14 +149,64 @@ var _ = Describe("OCISource", func() {
148149
Registry: registryAddr,
149150
Repository: "repo-legacy",
150151
Parallel: 4,
151-
}, true)
152+
}, true, logr.Discard())
152153
Expect(err).To(Succeed())
153154
versions, err := oci.GetVersions(ctx)
154155
Expect(err).To(Succeed())
155156
Expect(versions).To(HaveLen(1))
156157
Expect(versions[0].Capabilities).To(BeNil())
157158
})
158159

160+
It("skips tags without architecture annotation and returns remaining images", func(ctx SpecContext) {
161+
repo, err := remote.NewRepository(registryAddr + "/repo-missing-arch")
162+
Expect(err).To(Succeed())
163+
repo.PlainHTTP = true
164+
165+
withArch := ocispec.Index{
166+
Versioned: specs.Versioned{SchemaVersion: 2},
167+
Manifests: []ocispec.Descriptor{
168+
{MediaType: ocispec.MediaTypeImageManifest, Size: 0, Digest: ocispec.DescriptorEmptyJSON.Digest},
169+
},
170+
Annotations: map[string]string{
171+
"architecture": "amd64",
172+
},
173+
}
174+
withArchBlob, err := json.Marshal(withArch)
175+
Expect(err).To(Succeed())
176+
withArchDesc := content.NewDescriptorFromBytes(ocispec.MediaTypeImageIndex, withArchBlob)
177+
178+
noArch := ocispec.Index{
179+
Versioned: specs.Versioned{SchemaVersion: 2},
180+
Manifests: []ocispec.Descriptor{
181+
{MediaType: ocispec.MediaTypeImageManifest, Size: 0, Digest: ocispec.DescriptorEmptyJSON.Digest},
182+
},
183+
}
184+
noArchBlob, err := json.Marshal(noArch)
185+
Expect(err).To(Succeed())
186+
noArchDesc := content.NewDescriptorFromBytes(ocispec.MediaTypeImageIndex, noArchBlob)
187+
188+
err = repo.Push(ctx, ocispec.DescriptorEmptyJSON, strings.NewReader("{}"))
189+
Expect(err).To(Succeed())
190+
err = repo.PushReference(ctx, withArchDesc, bytes.NewReader(withArchBlob), "1.0.0")
191+
Expect(err).To(Succeed())
192+
err = repo.Push(ctx, ocispec.DescriptorEmptyJSON, strings.NewReader("{}"))
193+
Expect(err).To(Succeed())
194+
err = repo.PushReference(ctx, noArchDesc, bytes.NewReader(noArchBlob), "1.0.1")
195+
Expect(err).To(Succeed())
196+
197+
oci, err := cloudprofilesync.NewOCI(cloudprofilesync.OCIParams{
198+
Registry: registryAddr,
199+
Repository: "repo-missing-arch",
200+
Parallel: 4,
201+
}, true, logr.Discard())
202+
Expect(err).To(Succeed())
203+
versions, err := oci.GetVersions(ctx)
204+
Expect(err).To(Succeed())
205+
Expect(versions).To(HaveLen(1))
206+
Expect(versions[0].Version).To(Equal("1.0.0"))
207+
})
208+
209+
159210
It("leaves Capabilities nil when feature_set contains no valid values", func(ctx SpecContext) {
160211
repo, err := remote.NewRepository(registryAddr + "/repo-no-valid-features")
161212
Expect(err).To(Succeed())
@@ -185,7 +236,7 @@ var _ = Describe("OCISource", func() {
185236
Registry: registryAddr,
186237
Repository: "repo-no-valid-features",
187238
Parallel: 4,
188-
}, true)
239+
}, true, logr.Discard())
189240
Expect(err).To(Succeed())
190241
versions, err := oci.GetVersions(ctx)
191242
Expect(err).To(Succeed())

controllers/managedcloudprofile_controller.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const (
3636

3737
// OCISourceFactory defines an interface for creating OCI sources.
3838
type OCISourceFactory interface {
39-
Create(params cloudprofilesync.OCIParams, insecure bool) (cloudprofilesync.Source, error)
39+
Create(params cloudprofilesync.OCIParams, insecure bool, log logr.Logger) (cloudprofilesync.Source, error)
4040
}
4141

4242
type RegistryClient interface {
@@ -52,8 +52,8 @@ func (k *KeppelClient) GetTags(ctx context.Context, registry, repository string)
5252
// DefaultOCISourceFactory is the default implementation of OCISourceFactory.
5353
type DefaultOCISourceFactory struct{}
5454

55-
func (f *DefaultOCISourceFactory) Create(params cloudprofilesync.OCIParams, insecure bool) (cloudprofilesync.Source, error) {
56-
return cloudprofilesync.NewOCI(params, insecure)
55+
func (f *DefaultOCISourceFactory) Create(params cloudprofilesync.OCIParams, insecure bool, log logr.Logger) (cloudprofilesync.Source, error) {
56+
return cloudprofilesync.NewOCI(params, insecure, log)
5757
}
5858

5959
type Reconciler struct {
@@ -288,7 +288,7 @@ func (r *Reconciler) updateMachineImages(ctx context.Context, log logr.Logger, u
288288
Username: update.Source.OCI.Username,
289289
Password: string(password),
290290
Parallel: 1,
291-
}, update.Source.OCI.Insecure)
291+
}, update.Source.OCI.Insecure, log)
292292
if err != nil {
293293
return fmt.Errorf("failed to initialize OCI source: %w", err)
294294
}

controllers/managedcloudprofile_controller_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"sigs.k8s.io/controller-runtime/pkg/client"
1818

1919
gardenerv1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1"
20+
"github.com/go-logr/logr"
2021
providercfg "github.com/ironcore-dev/gardener-extension-provider-ironcore-metal/pkg/apis/metal/v1alpha1"
2122
. "github.com/onsi/ginkgo/v2"
2223
. "github.com/onsi/gomega"
@@ -49,11 +50,11 @@ func (f *fakeOCISource) GetVersions(ctx context.Context) ([]cloudprofilesync.Sou
4950

5051
type fakeFactory struct{}
5152

52-
func (f *fakeFactory) Create(params cloudprofilesync.OCIParams, insecure bool) (cloudprofilesync.Source, error) {
53+
func (f *fakeFactory) Create(params cloudprofilesync.OCIParams, insecure bool, _ logr.Logger) (cloudprofilesync.Source, error) {
5354
return &fakeOCISource{}, nil
5455
}
5556

56-
func (m *mockOCIFactory) Create(params cloudprofilesync.OCIParams, insecure bool) (cloudprofilesync.Source, error) {
57+
func (m *mockOCIFactory) Create(params cloudprofilesync.OCIParams, insecure bool, _ logr.Logger) (cloudprofilesync.Source, error) {
5758
return m.createFunc(params, insecure)
5859
}
5960

0 commit comments

Comments
 (0)