From 11dfdf32e87cc707370b937dfa06d555cc0b908e Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Sun, 12 Dec 2021 17:45:19 -0800 Subject: [PATCH] feature (validate): stricter stemcell version validations --- pkg/cargo/validate.go | 39 +++++++++++ pkg/cargo/validate_test.go | 131 +++++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+) diff --git a/pkg/cargo/validate.go b/pkg/cargo/validate.go index 481673ff6..9c08ee882 100644 --- a/pkg/cargo/validate.go +++ b/pkg/cargo/validate.go @@ -74,3 +74,42 @@ func checkComponentVersionsAndConstraint(spec ComponentSpec, lock ComponentLock, return nil } + +func checkStemcell(spec Kilnfile, lock KilnfileLock) []error { + v, err := semver.NewVersion(lock.Stemcell.Version) + if err != nil { + return []error{fmt.Errorf("invalid lock version %q in Kilnfile.lock: %w", + lock.Stemcell.Version, err)} + } + + if spec.Stemcell.Version != "" { + c, err := semver.NewConstraint(spec.Stemcell.Version) + if err != nil { + return []error{fmt.Errorf("invalid version constraint %q in Kilnfile: %w", + lock.Stemcell.Version, err)} + } + + matches, errs := c.Validate(v) + if !matches { + return []error{fmt.Errorf("stemcell version %s in Kilnfile.lock does not match constraint %q: %v", + lock.Stemcell.Version, spec.Stemcell.Version, errs)} + } + } + + var result []error + for index, componentLock := range lock.Releases { + if componentLock.StemcellOS == "" { + continue + } + if componentLock.StemcellOS != lock.Stemcell.OS { + result = append(result, fmt.Errorf("spec %s (index %d in Kilnfile) has stemcell os that does not match the stemcell lock os", + componentLock.Name, index)) + } + if componentLock.StemcellVersion != lock.Stemcell.Version { + result = append(result, fmt.Errorf("spec %s (index %d in Kilnfile) has stemcell version that does not match the stemcell lock (expected %s but got %s)", + componentLock.Name, index, lock.Stemcell.Version, componentLock.StemcellVersion)) + } + } + + return result +} diff --git a/pkg/cargo/validate_test.go b/pkg/cargo/validate_test.go index 9cf3bb444..64be3c057 100644 --- a/pkg/cargo/validate_test.go +++ b/pkg/cargo/validate_test.go @@ -247,3 +247,134 @@ func TestValidate_checkComponentVersionsAndConstraint(t *testing.T) { )) }) } + +func Test_checkStemcell_valid(t *testing.T) { + t.Parallel() + please := NewWithT(t) + results := checkStemcell(Kilnfile{ + Releases: []ComponentSpec{ + {Name: "banana", Version: "1.2.3"}, + {Name: "lemon", Version: "2.2.2"}, + }, + Stemcell: Stemcell{ + OS: "fruit", + Version: "500.*", + }, + }, KilnfileLock{ + Releases: []ComponentLock{ + {Name: "banana", Version: "1.2.3", StemcellOS: "fruit", StemcellVersion: "500.4"}, + {Name: "lemon", Version: "2.2.2"}, + }, + Stemcell: Stemcell{ + OS: "fruit", + Version: "500.4", + }, + }) + please.Expect(results).To(HaveLen(0)) +} + +func Test_checkStemcell_wrong_version(t *testing.T) { + t.Parallel() + please := NewWithT(t) + results := checkStemcell(Kilnfile{ + Releases: []ComponentSpec{ + {Name: "banana", Version: "1.2.3"}, + {Name: "lemon", Version: "2.2.2"}, + }, + Stemcell: Stemcell{ + OS: "fruit", + Version: "500.*", + }, + }, KilnfileLock{ + Releases: []ComponentLock{ + {Name: "banana", Version: "1.2.3", StemcellOS: "fruit", StemcellVersion: "400"}, + {Name: "lemon", Version: "2.2.2"}, + }, + Stemcell: Stemcell{ + OS: "fruit", + Version: "500.4", + }, + }) + please.Expect(results).To(HaveLen(1)) + please.Expect(results[0]).To(MatchError(ContainSubstring("has stemcell version that does not match the stemcell lock"))) +} + +func Test_checkStemcell_wrong_os_name(t *testing.T) { + t.Parallel() + please := NewWithT(t) + results := checkStemcell(Kilnfile{ + Releases: []ComponentSpec{ + {Name: "banana", Version: "1.2.3"}, + {Name: "lemon", Version: "2.2.2"}, + }, + Stemcell: Stemcell{ + OS: "fruit", + Version: "500.*", + }, + }, KilnfileLock{ + Releases: []ComponentLock{ + {Name: "banana", Version: "1.2.3", StemcellOS: "soap", StemcellVersion: "500.4"}, + {Name: "lemon", Version: "2.2.2"}, + }, + Stemcell: Stemcell{ + OS: "fruit", + Version: "500.4", + }, + }) + please.Expect(results).To(HaveLen(1)) + please.Expect(results[0]).To(MatchError(ContainSubstring("stemcell os that does not match the stemcell lock os"))) +} + +func Test_checkStemcell_invalid_version_lock(t *testing.T) { + t.Parallel() + please := NewWithT(t) + results := checkStemcell(Kilnfile{ + Stemcell: Stemcell{ + OS: "fruit", + Version: "500.0", + }, + }, KilnfileLock{ + Stemcell: Stemcell{ + OS: "fruit", + Version: "FAIL", + }, + }) + please.Expect(results).To(HaveLen(1)) + please.Expect(results[0]).To(MatchError(ContainSubstring("invalid lock version"))) +} + +func Test_checkStemcell_invalid_version_constraint(t *testing.T) { + t.Parallel() + please := NewWithT(t) + results := checkStemcell(Kilnfile{ + Stemcell: Stemcell{ + OS: "fruit", + Version: "FAIL", + }, + }, KilnfileLock{ + Stemcell: Stemcell{ + OS: "fruit", + Version: "2.0.0", + }, + }) + please.Expect(results).To(HaveLen(1)) + please.Expect(results[0]).To(MatchError(ContainSubstring("invalid version constraint"))) +} + +func Test_checkStemcell_lock_version_does_not_match_constraint(t *testing.T) { + t.Parallel() + please := NewWithT(t) + results := checkStemcell(Kilnfile{ + Stemcell: Stemcell{ + OS: "fruit", + Version: "400.*", + }, + }, KilnfileLock{ + Stemcell: Stemcell{ + OS: "fruit", + Version: "111.222", + }, + }) + please.Expect(results).To(HaveLen(1)) + please.Expect(results[0]).To(MatchError(ContainSubstring("does not match constraint"))) +}