Skip to content

Commit aafeaff

Browse files
authored
support GEP-31 in place update (#38)
* support GEP-31 in place update * fix: linter * fix: golangci-lint gomod version * constants for capabilities name * fix: golangci-lint gomod version * fix: comments * fix: typos
1 parent d69a5c1 commit aafeaff

7 files changed

Lines changed: 102 additions & 20 deletions

File tree

.golangci.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ linters:
7474
settings:
7575
dupword:
7676
# Do not choke on SQL statements like `INSERT INTO things (foo, bar, baz) VALUES (TRUE, TRUE, TRUE)`.
77-
ignore: [ "TRUE", "FALSE", "NULL" ]
77+
ignore: ["TRUE", "FALSE", "NULL"]
7878
errcheck:
7979
check-type-assertions: false
8080
# Report about assignment of errors to blank identifier.
@@ -103,7 +103,7 @@ linters:
103103
min-occurrences: 5
104104
ignore-tests: true
105105
ignore-string-values:
106-
- '^[a-zA-Z_-]{1,16}$' # ignore short identifiers like "account" or "project_id"
106+
- "^[a-zA-Z_-]{1,16}$" # ignore short identifiers like "account" or "project_id"
107107
gocritic:
108108
enabled-checks:
109109
- boolExprSimplify
@@ -136,7 +136,7 @@ linters:
136136
# for github.com/sapcc/vpa_butler
137137
- k8s.io/client-go
138138
toolchain-forbidden: true
139-
go-version-pattern: 1\.\d+(\.0)?$
139+
go-version-pattern: 1\.\d+(\.\d+)?$
140140
gosec:
141141
excludes:
142142
# gosec wants us to set a short ReadHeaderTimeout to avoid Slowloris attacks, but doing so would expose us to Keep-Alive race conditions (see https://iximiuz.com/en/posts/reverse-proxy-http-keep-alive-and-502s/

.typos.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# SPDX-License-Identifier: Apache-2.0
44

55
[default.extend-words]
6+
ded = "ded"
67

78
[files]
89
extend-exclude = [

Makefile.maker.yaml

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ metadata:
44
url: https://github.com/cobaltcore-dev/cloud-profile-sync
55

66
binaries:
7-
- name: cloud-profile-sync
7+
- name: cloud-profile-sync
88
fromPackage: .
9-
installTo: bin/
9+
installTo: bin/
1010

1111
golang:
1212
setGoModVersion: true
@@ -35,12 +35,16 @@ renovate:
3535

3636
reuse:
3737
annotations:
38-
- paths:
39-
- '**/zz_generated.deepcopy.go'
40-
- 'crd/*'
41-
SPDX-FileCopyrightText: 2020 The Kubernetes Authors # this is the copyright line for kubebuilder which generates those files
42-
SPDX-License-Identifier: Apache-2.0
38+
- paths:
39+
- "**/zz_generated.deepcopy.go"
40+
- "crd/*"
41+
SPDX-FileCopyrightText: 2020 The Kubernetes Authors # this is the copyright line for kubebuilder which generates those files
42+
SPDX-License-Identifier: Apache-2.0
4343

4444
verbatim: |
4545
gardener-crds: install-controller-gen
4646
@controller-gen crd paths="github.com/gardener/gardener/pkg/apis/core/v1beta1" output:crd:artifacts:config=crd
47+
48+
typos:
49+
extendWords:
50+
ded: ded

cloudprofilesync/imageupdater.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ func (iu *ImageUpdater) Update(ctx context.Context, cpSpec *gardenerv1beta1.Clou
8686
}
8787

8888
for _, sourceImage := range sourceImages {
89+
supportInPlaceUpdate := slices.Contains(sourceImage.Capabilities[FeatureCapability], USIFeature)
8990
// Always write the full tag version (legacy path, safe for running Shoots).
9091
if idx, exists := existingVersions[sourceImage.Version]; exists {
9192
image.Versions[idx].Architectures = sourceImage.Architectures
@@ -103,6 +104,11 @@ func (iu *ImageUpdater) Update(ctx context.Context, cpSpec *gardenerv1beta1.Clou
103104
},
104105
Architectures: sourceImage.Architectures,
105106
})
107+
if supportInPlaceUpdate {
108+
image.Versions[len(image.Versions)-1].InPlaceUpdates = &gardenerv1beta1.InPlaceUpdates{
109+
Supported: supportInPlaceUpdate,
110+
}
111+
}
106112
existingVersions[sourceImage.Version] = len(image.Versions) - 1
107113
}
108114
}
@@ -116,13 +122,23 @@ func (iu *ImageUpdater) Update(ctx context.Context, cpSpec *gardenerv1beta1.Clou
116122
existing.Architectures = append(existing.Architectures, arch)
117123
}
118124
}
125+
if supportInPlaceUpdate {
126+
existing.InPlaceUpdates = &gardenerv1beta1.InPlaceUpdates{
127+
Supported: supportInPlaceUpdate,
128+
}
129+
}
119130
} else {
120131
image.Versions = append(image.Versions, gardenerv1beta1.MachineImageVersion{
121132
ExpirableVersion: gardenerv1beta1.ExpirableVersion{
122133
Version: sourceImage.CleanVersion,
123134
},
124135
Architectures: slices.Clone(sourceImage.Architectures),
125136
})
137+
if supportInPlaceUpdate {
138+
image.Versions[len(image.Versions)-1].InPlaceUpdates = &gardenerv1beta1.InPlaceUpdates{
139+
Supported: supportInPlaceUpdate,
140+
}
141+
}
126142
existingVersions[sourceImage.CleanVersion] = len(image.Versions) - 1
127143
}
128144
}

cloudprofilesync/imageupdater_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,25 @@ var _ = Describe("ImageUpdater", func() {
224224
Expect(json.Unmarshal(cpSpec.ProviderConfig.Raw, &fromProvider)).To(Succeed())
225225
Expect(fromProvider).To(Equal(mockSource.images))
226226
})
227+
228+
It("in-place update support", func(ctx SpecContext) {
229+
mockSource.images = []cloudprofilesync.SourceImage{{
230+
Version: "1.0.0",
231+
Architectures: []string{"amd64"},
232+
Capabilities: map[string]gardencorev1beta1.CapabilityValues{"feature": {cloudprofilesync.USIFeature}}},
233+
}
234+
updater := cloudprofilesync.ImageUpdater{
235+
Log: logr.Discard(),
236+
Source: &mockSource,
237+
ImageName: "test",
238+
}
239+
var cpSpec gardencorev1beta1.CloudProfileSpec
240+
Expect(updater.Update(ctx, &cpSpec)).To(Succeed())
241+
Expect(cpSpec.MachineImages[0].Versions).To(HaveLen(1))
242+
Expect(cpSpec.MachineImages[0].Versions[0].Version).To(Equal("1.0.0"))
243+
Expect(cpSpec.MachineImages[0].Versions[0].InPlaceUpdates.Supported).To(BeTrue())
244+
})
245+
227246
})
228247

229248
Describe("flag ON (dual-write clean version)", func() {
@@ -249,6 +268,7 @@ var _ = Describe("ImageUpdater", func() {
249268
versions := cpSpec.MachineImages[0].Versions
250269
versionStrings := []string{versions[0].Version, versions[1].Version}
251270
Expect(versionStrings).To(ContainElements("2254.0.0-baremetal-sci-usi-amd64", "2254.0.0"))
271+
Expect(versions[0].InPlaceUpdates.Supported).To(BeTrue())
252272
})
253273

254274
It("does not add a duplicate clean version entry on re-reconcile", func(ctx SpecContext) {
@@ -318,5 +338,29 @@ var _ = Describe("ImageUpdater", func() {
318338
Expect(cpSpec.MachineImages[0].Versions).To(HaveLen(1))
319339
Expect(cpSpec.MachineImages[0].Versions[0].Version).To(Equal("1877.0.0"))
320340
})
341+
342+
It("in-place update support", func(ctx SpecContext) {
343+
mockSource.images = []cloudprofilesync.SourceImage{{
344+
Version: "1.0.0",
345+
CleanVersion: "1.1",
346+
Architectures: []string{"amd64"},
347+
Capabilities: map[string]gardencorev1beta1.CapabilityValues{"feature": {cloudprofilesync.USIFeature}}},
348+
}
349+
updater := cloudprofilesync.ImageUpdater{
350+
Log: logr.Discard(),
351+
Source: &mockSource,
352+
ImageName: "test",
353+
EnableCapabilities: true,
354+
}
355+
var cpSpec gardencorev1beta1.CloudProfileSpec
356+
Expect(updater.Update(ctx, &cpSpec)).To(Succeed())
357+
Expect(cpSpec.MachineImages[0].Versions).To(HaveLen(2))
358+
Expect(cpSpec.MachineImages[0].Versions[0].Version).To(Equal("1.0.0"))
359+
Expect(cpSpec.MachineImages[0].Versions[0].InPlaceUpdates).NotTo(BeNil())
360+
Expect(cpSpec.MachineImages[0].Versions[0].InPlaceUpdates.Supported).To(BeTrue())
361+
Expect(cpSpec.MachineImages[0].Versions[1].Version).To(Equal("1.1.0"))
362+
Expect(cpSpec.MachineImages[0].Versions[1].InPlaceUpdates).NotTo(BeNil())
363+
Expect(cpSpec.MachineImages[0].Versions[1].InPlaceUpdates.Supported).To(BeTrue())
364+
})
321365
})
322366
})

cloudprofilesync/source.go

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,32 @@ import (
1818
"oras.land/oras-go/v2/registry/remote/retry"
1919
)
2020

21+
const (
22+
// ChostFeature represent having containerd
23+
ChostFeature = "chost"
24+
// PXEFeature represent pxe boot build
25+
PXEFeature = "_pxe"
26+
SCIFeature = "sci"
27+
SCIBaseFeature = "scibase"
28+
// CAPIFeature includes server, khost, and PXE; excludes SELinux and firewall
29+
CAPIFeature = "capi"
30+
// USIFeature shows UEFI build
31+
USIFeature = "_usi"
32+
USIDevFeature = "_usidev"
33+
34+
ArchitectureCapability = "architecture"
35+
FeatureCapability = "feature"
36+
)
37+
2138
// validFeatureValues is the allowlist of feature values extracted from the feature_set annotation.
2239
var validFeatureValues = map[string]struct{}{
23-
"chost": {},
24-
"_pxe": {},
25-
"sci": {},
26-
"capi": {},
27-
"scibase": {},
28-
"_usi": {},
29-
"_usidev": {},
40+
ChostFeature: {},
41+
PXEFeature: {},
42+
SCIFeature: {},
43+
SCIBaseFeature: {},
44+
CAPIFeature: {},
45+
USIFeature: {},
46+
USIDevFeature: {},
3047
}
3148

3249
func filterFeatureSet(featureSet string) []string {
@@ -161,8 +178,8 @@ func (o *OCI) GetVersions(ctx context.Context) ([]SourceImage, error) {
161178
features := filterFeatureSet(featureSet)
162179
if len(features) > 0 {
163180
capabilities = gardencorev1beta1.Capabilities{
164-
"architecture": {arch},
165-
"feature": features,
181+
ArchitectureCapability: {arch},
182+
FeatureCapability: features,
166183
}
167184
cleanVersion = version
168185
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/cobaltcore-dev/cloud-profile-sync
22

3-
go 1.26
3+
go 1.26.2
44

55
require (
66
github.com/blang/semver/v4 v4.0.0

0 commit comments

Comments
 (0)