Skip to content

Commit b958109

Browse files
committed
Add option to configure version check
1 parent 1806777 commit b958109

File tree

9 files changed

+203
-59
lines changed

9 files changed

+203
-59
lines changed

docs/api-reference/landscapekit-v1alpha1.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,15 @@ string
378378
</tr>
379379
</tbody>
380380
</table>
381+
<h3 id="landscape.config.gardener.cloud/v1alpha1.VersionCheckMode">VersionCheckMode
382+
(<code>string</code> alias)</p></h3>
383+
<p>
384+
(<em>Appears on:</em>
385+
<a href="#landscape.config.gardener.cloud/v1alpha1.VersionConfiguration">VersionConfiguration</a>)
386+
</p>
387+
<p>
388+
<p>VersionCheckMode controls the behavior when the tool version doesn&rsquo;t match the component version.</p>
389+
</p>
381390
<h3 id="landscape.config.gardener.cloud/v1alpha1.VersionConfiguration">VersionConfiguration
382391
</h3>
383392
<p>
@@ -410,6 +419,22 @@ DefaultVersionsUpdateStrategy
410419
Possible values are &ldquo;Disabled&rdquo; (default) and &ldquo;ReleaseBranch&rdquo;.</p>
411420
</td>
412421
</tr>
422+
<tr>
423+
<td>
424+
<code>checkMode</code></br>
425+
<em>
426+
<a href="#landscape.config.gardener.cloud/v1alpha1.VersionCheckMode">
427+
VersionCheckMode
428+
</a>
429+
</em>
430+
</td>
431+
<td>
432+
<em>(Optional)</em>
433+
<p>CheckMode determines the behavior when the tool version doesn&rsquo;t match the gardener-landscape-kit version in the component vector.
434+
Possible values are &ldquo;Strict&rdquo; (default) and &ldquo;Warning&rdquo;.
435+
In strict mode, version mismatches cause errors. In warning mode, only warnings are logged.</p>
436+
</td>
437+
</tr>
413438
</tbody>
414439
</table>
415440
<hr/>

example/20-componentconfig-glk.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ kind: LandscapeKitConfiguration
1919
# - component-name
2020
# versionConfig:
2121
# defaultVersionsUpdateStrategy: ReleaseBranch
22+
# checkMode: Strict # or Warning

pkg/apis/config/v1alpha1/types.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,31 @@ var AllowedDefaultVersionsUpdateStrategies = []string{
112112
string(DefaultVersionsUpdateStrategyDisabled),
113113
}
114114

115+
// VersionCheckMode controls the behavior when the tool version doesn't match the component version.
116+
type VersionCheckMode string
117+
118+
const (
119+
// VersionCheckModeStrict indicates that version mismatches should cause an error.
120+
VersionCheckModeStrict VersionCheckMode = "Strict"
121+
// VersionCheckModeWarning indicates that version mismatches should only log a warning.
122+
VersionCheckModeWarning VersionCheckMode = "Warning"
123+
)
124+
125+
// AllowedVersionCheckModes lists all allowed version check modes.
126+
var AllowedVersionCheckModes = []string{
127+
string(VersionCheckModeStrict),
128+
string(VersionCheckModeWarning),
129+
}
130+
115131
// VersionConfiguration contains configuration for versioning.
116132
type VersionConfiguration struct {
117133
// UpdateStrategy determines whether the versions in the default vector should be updated from the release branch on resolve.
118134
// Possible values are "Disabled" (default) and "ReleaseBranch".
119135
// +optional
120136
DefaultVersionsUpdateStrategy *DefaultVersionsUpdateStrategy `json:"defaultVersionsUpdateStrategy,omitempty"`
137+
// CheckMode determines the behavior when the tool version doesn't match the gardener-landscape-kit version in the component vector.
138+
// Possible values are "Strict" (default) and "Warning".
139+
// In strict mode, version mismatches cause errors. In warning mode, only warnings are logged.
140+
// +optional
141+
CheckMode *VersionCheckMode `json:"checkMode,omitempty"`
121142
}

pkg/apis/config/v1alpha1/validation/validation.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,5 +165,9 @@ func ValidateVersionConfig(conf *configv1alpha1.VersionConfiguration, fldPath *f
165165
allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultVersionsUpdateStrategy"), *conf.DefaultVersionsUpdateStrategy, "allowed values are: "+strings.Join(configv1alpha1.AllowedDefaultVersionsUpdateStrategies, ", ")))
166166
}
167167

168+
if conf.CheckMode != nil && !slices.Contains(configv1alpha1.AllowedVersionCheckModes, string(*conf.CheckMode)) {
169+
allErrs = append(allErrs, field.Invalid(fldPath.Child("checkMode"), *conf.CheckMode, "allowed values are: "+strings.Join(configv1alpha1.AllowedVersionCheckModes, ", ")))
170+
}
171+
168172
return allErrs
169173
}

pkg/apis/config/v1alpha1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/cmd/generate/base/base.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func run(_ context.Context, opts *options.Options) error {
5656
return fmt.Errorf("failed to register components: %w", err)
5757
}
5858

59-
if err := version.CheckGLKComponentVersion(componentOpts.GetComponentVector()); err != nil {
59+
if err := version.CheckGLKComponentVersion(componentOpts.GetComponentVector(), opts.Config, opts.Log); err != nil {
6060
return fmt.Errorf("version check failed: %w", err)
6161
}
6262

pkg/cmd/generate/landscape/landscape.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func run(_ context.Context, opts *options.Options) error {
7777
return fmt.Errorf("failed to create component options: %w", err)
7878
}
7979

80-
if err := version.CheckGLKComponentVersion(componentOpts.GetComponentVector()); err != nil {
80+
if err := version.CheckGLKComponentVersion(componentOpts.GetComponentVector(), opts.Config, opts.Log); err != nil {
8181
return fmt.Errorf("version validation failed: %w", err)
8282
}
8383

pkg/utils/version/metadata.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ import (
1212
"strings"
1313

1414
"github.com/Masterminds/semver/v3"
15+
"github.com/go-logr/logr"
1516
"github.com/spf13/afero"
1617
apimachineryversion "k8s.io/apimachinery/pkg/version"
1718
componentbaseversion "k8s.io/component-base/version"
1819

1920
"github.com/gardener/gardener-landscape-kit/componentvector"
21+
configv1alpha1 "github.com/gardener/gardener-landscape-kit/pkg/apis/config/v1alpha1"
2022
utilscomponentvector "github.com/gardener/gardener-landscape-kit/pkg/utils/componentvector"
2123
"github.com/gardener/gardener-landscape-kit/pkg/utils/files"
2224
)
@@ -142,8 +144,11 @@ func ValidateLandscapeVersionCompatibility(targetPath string, fs afero.Afero) er
142144
}
143145

144146
// CheckGLKComponentVersion validates that the tool version matches the gardener-landscape-kit
145-
// component version in the component vector. Returns an error if they don't match exactly.
146-
func CheckGLKComponentVersion(cv utilscomponentvector.Interface) error {
147+
// component version in the component vector.
148+
// The behavior depends on the checkMode in the configuration:
149+
// - If checkMode is "Strict" (or nil/default), returns an error on mismatch.
150+
// - If checkMode is "Warning", logs a warning on mismatch and returns nil.
151+
func CheckGLKComponentVersion(cv utilscomponentvector.Interface, config *configv1alpha1.LandscapeKitConfiguration, log logr.Logger) error {
147152
toolVersion := version.GitVersion
148153
componentVersion, found := cv.FindComponentVersion(componentvector.NameGardenerGardenerLandscapeKit)
149154

@@ -152,7 +157,18 @@ func CheckGLKComponentVersion(cv utilscomponentvector.Interface) error {
152157
}
153158

154159
if toolVersion != componentVersion {
155-
return fmt.Errorf("version mismatch: tool version (%s) does not match gardener-landscape-kit component version (%s) in component vector - obtain the matching gardener-landscap-kit version or adjust the component vector", toolVersion, componentVersion)
160+
// Determine the check mode (default to Strict)
161+
checkMode := configv1alpha1.VersionCheckModeStrict
162+
if config != nil && config.VersionConfig != nil && config.VersionConfig.CheckMode != nil {
163+
checkMode = *config.VersionConfig.CheckMode
164+
}
165+
166+
if checkMode == configv1alpha1.VersionCheckModeWarning {
167+
log.Info("WARNING: version mismatch - tool version does not match gardener-landscape-kit component version in component vector - obtain the matching gardener-landscape-kit version or adjust the component vector", "toolVersion", toolVersion, "componentVersion", componentVersion)
168+
return nil
169+
}
170+
171+
return fmt.Errorf("version mismatch: tool version (%s) does not match gardener-landscape-kit component version (%s) in component vector - obtain the matching gardener-landscape-kit version or adjust the component vector", toolVersion, componentVersion)
156172
}
157173

158174
return nil

pkg/utils/version/metadata_test.go

Lines changed: 126 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@ import (
88
"encoding/json"
99
"path/filepath"
1010

11+
"github.com/go-logr/logr"
1112
. "github.com/onsi/ginkgo/v2"
1213
. "github.com/onsi/gomega"
1314
"github.com/spf13/afero"
15+
"sigs.k8s.io/controller-runtime/pkg/log/zap"
1416

17+
configv1alpha1 "github.com/gardener/gardener-landscape-kit/pkg/apis/config/v1alpha1"
1518
"github.com/gardener/gardener-landscape-kit/pkg/utils/componentvector"
1619
"github.com/gardener/gardener-landscape-kit/pkg/utils/version"
1720
)
@@ -234,45 +237,115 @@ var _ = Describe("Version Metadata", func() {
234237
})
235238

236239
Describe("#CheckGLKComponentVersion", func() {
237-
It("should pass when versions match exactly", func() {
238-
// Get the current tool version
239-
currentVersion := version.Get().GitVersion
240+
var log logr.Logger
240241

241-
baseYAML := []byte(`
242-
components:
243-
- name: github.com/gardener/gardener-landscape-kit
244-
sourceRepository: https://github.com/gardener/gardener-landscape-kit
245-
version: ` + currentVersion + `
246-
`)
247-
cv, err := componentvector.NewWithOverride(baseYAML)
248-
Expect(err).NotTo(HaveOccurred())
249-
250-
err = version.CheckGLKComponentVersion(cv)
251-
Expect(err).NotTo(HaveOccurred())
242+
BeforeEach(func() {
243+
log = zap.New(zap.WriteTo(GinkgoWriter))
252244
})
253245

254-
It("should fail when tool version is different from component version", func() {
255-
currentVersion := version.Get().GitVersion
256-
differentVersion := "v0.99.99-test"
246+
type testCase struct {
247+
componentVersion string
248+
checkMode *configv1alpha1.VersionCheckMode
249+
expectError bool
250+
errorContains []string
251+
}
257252

258-
baseYAML := []byte(`
253+
DescribeTable("version checking behavior",
254+
func(tc testCase) {
255+
baseYAML := []byte(`
259256
components:
260257
- name: github.com/gardener/gardener-landscape-kit
261258
sourceRepository: https://github.com/gardener/gardener-landscape-kit
262-
version: ` + differentVersion + `
259+
version: ` + tc.componentVersion + `
263260
`)
264-
cv, err := componentvector.NewWithOverride(baseYAML)
265-
Expect(err).NotTo(HaveOccurred())
266-
267-
err = version.CheckGLKComponentVersion(cv)
268-
Expect(err).To(MatchError(And(
269-
ContainSubstring("version mismatch"),
270-
ContainSubstring(currentVersion),
271-
ContainSubstring(differentVersion),
272-
)))
273-
})
274-
275-
It("should fail when GLK component is not found", func() {
261+
cv, err := componentvector.NewWithOverride(baseYAML)
262+
Expect(err).NotTo(HaveOccurred())
263+
264+
var config *configv1alpha1.LandscapeKitConfiguration
265+
if tc.checkMode != nil {
266+
config = &configv1alpha1.LandscapeKitConfiguration{
267+
VersionConfig: &configv1alpha1.VersionConfiguration{
268+
CheckMode: tc.checkMode,
269+
},
270+
}
271+
}
272+
273+
err = version.CheckGLKComponentVersion(cv, config, log)
274+
275+
if tc.expectError {
276+
Expect(err).To(HaveOccurred())
277+
for _, substr := range tc.errorContains {
278+
Expect(err.Error()).To(ContainSubstring(substr))
279+
}
280+
} else {
281+
Expect(err).NotTo(HaveOccurred())
282+
}
283+
},
284+
Entry("should pass when versions match with nil config (default strict)",
285+
testCase{
286+
componentVersion: version.Get().GitVersion,
287+
checkMode: nil,
288+
expectError: false,
289+
}),
290+
Entry("should pass when versions match in strict mode",
291+
testCase{
292+
componentVersion: version.Get().GitVersion,
293+
checkMode: ptr(configv1alpha1.VersionCheckModeStrict),
294+
expectError: false,
295+
}),
296+
Entry("should pass when versions match in warning mode",
297+
testCase{
298+
componentVersion: version.Get().GitVersion,
299+
checkMode: ptr(configv1alpha1.VersionCheckModeWarning),
300+
expectError: false,
301+
}),
302+
Entry("should fail when versions differ in strict mode",
303+
testCase{
304+
componentVersion: "v0.99.99-test",
305+
checkMode: ptr(configv1alpha1.VersionCheckModeStrict),
306+
expectError: true,
307+
errorContains: []string{"version mismatch", version.Get().GitVersion, "v0.99.99-test"},
308+
}),
309+
Entry("should not fail when versions differ in warning mode",
310+
testCase{
311+
componentVersion: "v0.99.99-test",
312+
checkMode: ptr(configv1alpha1.VersionCheckModeWarning),
313+
expectError: false,
314+
}),
315+
Entry("should use exact string matching - v0.2.0-dev vs v0.2.0 in strict mode",
316+
func() testCase {
317+
currentVersion := version.Get().GitVersion
318+
var differentButRelated string
319+
if currentVersion == "v0.2.0-dev" {
320+
differentButRelated = "v0.2.0"
321+
} else {
322+
differentButRelated = currentVersion + "-modified"
323+
}
324+
return testCase{
325+
componentVersion: differentButRelated,
326+
checkMode: ptr(configv1alpha1.VersionCheckModeStrict),
327+
expectError: true,
328+
errorContains: []string{"version mismatch"},
329+
}
330+
}()),
331+
Entry("should use exact string matching - v0.2.0-dev vs v0.2.0 in warning mode",
332+
func() testCase {
333+
currentVersion := version.Get().GitVersion
334+
var differentButRelated string
335+
if currentVersion == "v0.2.0-dev" {
336+
differentButRelated = "v0.2.0"
337+
} else {
338+
differentButRelated = currentVersion + "-modified"
339+
}
340+
return testCase{
341+
componentVersion: differentButRelated,
342+
checkMode: ptr(configv1alpha1.VersionCheckModeWarning),
343+
expectError: false,
344+
}
345+
}()),
346+
)
347+
348+
It("should fail when GLK component is not found in both modes", func() {
276349
baseYAML := []byte(`
277350
components:
278351
- name: github.com/gardener/other-component
@@ -282,33 +355,32 @@ components:
282355
cv, err := componentvector.NewWithOverride(baseYAML)
283356
Expect(err).NotTo(HaveOccurred())
284357

285-
err = version.CheckGLKComponentVersion(cv)
286-
Expect(err).To(MatchError(ContainSubstring("gardener-landscape-kit component not found")))
287-
})
358+
// Test strict mode
359+
strictMode := configv1alpha1.VersionCheckModeStrict
360+
strictConfig := &configv1alpha1.LandscapeKitConfiguration{
361+
VersionConfig: &configv1alpha1.VersionConfiguration{
362+
CheckMode: &strictMode,
363+
},
364+
}
288365

289-
It("should use exact string matching (not semantic versioning)", func() {
290-
currentVersion := version.Get().GitVersion
366+
err = version.CheckGLKComponentVersion(cv, strictConfig, log)
367+
Expect(err).To(MatchError(ContainSubstring("gardener-landscape-kit component not found")))
291368

292-
// If current is v0.2.0-dev, test with v0.2.0 (different string, semantically related)
293-
var differentButRelated string
294-
if currentVersion == "v0.2.0-dev" {
295-
differentButRelated = "v0.2.0"
296-
} else {
297-
differentButRelated = currentVersion + "-modified"
369+
// Test warning mode
370+
warningMode := configv1alpha1.VersionCheckModeWarning
371+
warningConfig := &configv1alpha1.LandscapeKitConfiguration{
372+
VersionConfig: &configv1alpha1.VersionConfiguration{
373+
CheckMode: &warningMode,
374+
},
298375
}
299376

300-
baseYAML := []byte(`
301-
components:
302-
- name: github.com/gardener/gardener-landscape-kit
303-
sourceRepository: https://github.com/gardener/gardener-landscape-kit
304-
version: ` + differentButRelated + `
305-
`)
306-
cv, err := componentvector.NewWithOverride(baseYAML)
307-
Expect(err).NotTo(HaveOccurred())
308-
309-
err = version.CheckGLKComponentVersion(cv)
310-
Expect(err).To(HaveOccurred())
311-
Expect(err.Error()).To(ContainSubstring("version mismatch"))
377+
err = version.CheckGLKComponentVersion(cv, warningConfig, log)
378+
Expect(err).To(MatchError(ContainSubstring("gardener-landscape-kit component not found")))
312379
})
313380
})
314381
})
382+
383+
// ptr is a helper function to create a pointer to a value
384+
func ptr[T any](v T) *T {
385+
return &v
386+
}

0 commit comments

Comments
 (0)