diff --git a/cmd/build.go b/cmd/build.go index a14e89d..58ded99 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -19,7 +19,6 @@ import ( "net/url" "os" "path/filepath" - "sort" "strings" ) @@ -115,14 +114,13 @@ func build(cmd *cobra.Command, _ []string) error { return err } - // sort the keys so that we can - // install packages in the same - // order every time - pkgKeys := make([]string, 0) - for k := range lockFile.Packages { - pkgKeys = append(pkgKeys, k) + // validate that the configuration file lines up + // with what we expect from the lockfile + if err := lockFile.Validate(cfg.Spec); err != nil { + return err } - sort.Strings(pkgKeys) + + pkgKeys := lockFile.SortedKeys() // install packages for _, name := range pkgKeys { diff --git a/pkg/lockfile/lockfile.go b/pkg/lockfile/lockfile.go new file mode 100644 index 0000000..717549a --- /dev/null +++ b/pkg/lockfile/lockfile.go @@ -0,0 +1,73 @@ +package lockfile + +import ( + "fmt" + v1 "github.com/djcass44/all-your-base/pkg/api/v1" + "sort" +) + +// Validate checks that the configuration file lines up +// with what we expect from the lockfile and vice versa +func (l *Lock) Validate(cfg v1.BuildSpec) error { + // check that the krm packages are all in the lockfile + for _, p := range cfg.Packages { + for _, n := range p.Names { + _, ok := l.Packages[n] + if !ok { + return fmt.Errorf("package not found in lock: %s", n) + } + } + } + // check that the krm files are all in the lockfile + for _, f := range cfg.Files { + _, ok := l.Packages[f.URI] + if !ok { + return fmt.Errorf("file not found in lock: %s", f.URI) + } + } + + // now we do the reverse + + for k, v := range l.Packages { + if k == "" { + continue + } + var found bool + // check that the lock file are all present in the manifest + if v.Type == v1.PackageFile { + for _, f := range cfg.Files { + if f.URI == k { + found = true + } + } + if !found { + return fmt.Errorf("file found in lock, but not manifest: %s", k) + } + continue + } + // check that the lock packages are all in the manifest + for _, p := range cfg.Packages { + for _, n := range p.Names { + if n == k { + found = true + } + } + } + if !found { + return fmt.Errorf("package found in lock, but not manifest: %s", k) + } + } + + return nil +} + +// SortedKeys returns package names +// sorted alphabetically. +func (l *Lock) SortedKeys() []string { + pkgKeys := make([]string, 0) + for k := range l.Packages { + pkgKeys = append(pkgKeys, k) + } + sort.Strings(pkgKeys) + return pkgKeys +} diff --git a/pkg/lockfile/lockfile_test.go b/pkg/lockfile/lockfile_test.go new file mode 100644 index 0000000..4e060f3 --- /dev/null +++ b/pkg/lockfile/lockfile_test.go @@ -0,0 +1,126 @@ +package lockfile + +import ( + v1 "github.com/djcass44/all-your-base/pkg/api/v1" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestLock_Validate(t *testing.T) { + var cases = []struct { + name string + cfg v1.BuildSpec + ok bool + }{ + { + name: "matching spec", + cfg: v1.BuildSpec{ + Packages: []v1.Package{ + { + Names: []string{"test-package"}, + }, + }, + Files: []v1.File{ + { + URI: "test-file", + }, + }, + }, + ok: true, + }, + { + name: "extra package", + cfg: v1.BuildSpec{ + Packages: []v1.Package{ + { + Names: []string{"test-package", "fake-package"}, + }, + }, + Files: []v1.File{ + { + URI: "test-file", + }, + }, + }, + ok: false, + }, + { + name: "extra file", + cfg: v1.BuildSpec{ + Packages: []v1.Package{ + { + Names: []string{"test-package"}, + }, + }, + Files: []v1.File{ + { + URI: "test-file", + }, + { + URI: "https://example.com/file.tgz", + }, + }, + }, + ok: false, + }, + { + name: "extra file in lock", + cfg: v1.BuildSpec{ + Packages: []v1.Package{ + { + Names: []string{"test-package"}, + }, + }, + Files: []v1.File{}, + }, + ok: false, + }, + { + name: "extra package in lock", + cfg: v1.BuildSpec{ + Packages: []v1.Package{}, + Files: []v1.File{ + { + URI: "test-file", + }, + }, + }, + ok: false, + }, + } + + lock := &Lock{ + Packages: map[string]Package{ + "test-package": {}, + "test-file": { + Type: v1.PackageFile, + }, + }, + } + + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + err := lock.Validate(tt.cfg) + if !tt.ok { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } + +} + +func TestLock_SortedKeys(t *testing.T) { + l := &Lock{ + Packages: map[string]Package{ + "packageA": {}, + "packageC": {}, + "packageB": {}, + }, + } + + outOne := l.SortedKeys() + outTwo := l.SortedKeys() + assert.ElementsMatch(t, outOne, outTwo) +}