diff --git a/pkg/release/images/fake/tar.go b/pkg/release/images/fake/tar.go new file mode 100644 index 0000000..4c3b30a --- /dev/null +++ b/pkg/release/images/fake/tar.go @@ -0,0 +1,51 @@ +package fake + +import ( + "github.com/cert-manager/release/pkg/release/images" +) + +// FakeImageTar is a fake of the image.Tar struct +type FakeImageTar struct { + architecture string + imageArchitecture string + imageName string + imageTag string + filePath string + os string +} + +// New gives a new FakeImageTar +func New(imageName, imageTag, filePath, os, architecture, imageArchitecture string) images.TarInterface { + return &FakeImageTar{ + architecture: architecture, + imageArchitecture: imageArchitecture, + imageName: imageName, + imageTag: imageTag, + filePath: filePath, + os: os, + } +} + +func (f *FakeImageTar) Architecture() string { + return f.architecture +} + +func (f *FakeImageTar) ImageArchitecture() string { + return f.imageArchitecture +} + +func (f *FakeImageTar) ImageName() string { + return f.imageName +} + +func (f *FakeImageTar) ImageTag() string { + return f.imageTag +} + +func (f *FakeImageTar) OS() string { + return f.os +} + +func (f *FakeImageTar) Filepath() string { + return f.filePath +} diff --git a/pkg/release/images/tar.go b/pkg/release/images/tar.go index d372722..a30671f 100644 --- a/pkg/release/images/tar.go +++ b/pkg/release/images/tar.go @@ -44,6 +44,15 @@ type Tar struct { imageArchitecture string } +type TarInterface interface { + Architecture() string + ImageArchitecture() string + ImageName() string + ImageTag() string + Filepath() string + OS() string +} + func NewTar(path, osStr, arch string) (*Tar, error) { f, err := os.Open(path) if err != nil { diff --git a/pkg/release/publish/registry/registry.go b/pkg/release/publish/registry/registry.go index ca0a8db..434510e 100644 --- a/pkg/release/publish/registry/registry.go +++ b/pkg/release/publish/registry/registry.go @@ -27,7 +27,7 @@ func Push(image images.Tar) error { return docker.Push(image.ImageName()) } -func CreateManifestList(name string, tars []images.Tar) error { +func CreateManifestList(name string, tars []images.TarInterface) error { imageNames := make([]string, len(tars)) for i, t := range tars { imageNames[i] = t.ImageName() diff --git a/pkg/release/unpacker.go b/pkg/release/unpacker.go index 05b8e70..e5e4645 100644 --- a/pkg/release/unpacker.go +++ b/pkg/release/unpacker.go @@ -44,7 +44,7 @@ type Unpacked struct { Charts []manifests.Chart YAMLs []manifests.YAML CtlBinaryBundles []binaries.Tar - ComponentImageBundles map[string][]images.Tar + ComponentImageBundles map[string][]images.TarInterface } // Unpack takes a staged release, inspects its metadata, fetches referenced @@ -115,7 +115,7 @@ func Unpack(ctx context.Context, s *Staged) (*Unpacked, error) { // unpackServerImagesFromRelease will extract all 'image-like' tar archives // from the various 'server' .tar.gz files and return a map of component name // to a slice of images.Tar for each image in the bundle. -func unpackServerImagesFromRelease(ctx context.Context, s *Staged) (map[string][]images.Tar, error) { +func unpackServerImagesFromRelease(ctx context.Context, s *Staged) (map[string][]images.TarInterface, error) { log.Printf("Unpacking 'server' type artifacts") serverA := s.ArtifactsOfKind("server") return unpackImages(ctx, serverA, "") @@ -151,9 +151,9 @@ func unpackCtlFromRelease(ctx context.Context, s *Staged) ([]binaries.Tar, error return binaryTarBundles, nil } -func unpackImages(ctx context.Context, artifacts []StagedArtifact, trimSuffix string) (map[string][]images.Tar, error) { +func unpackImages(ctx context.Context, artifacts []StagedArtifact, trimSuffix string) (map[string][]images.TarInterface, error) { // tarBundles is a map from component name to slices of images.Tar - tarBundles := make(map[string][]images.Tar) + tarBundles := make(map[string][]images.TarInterface) for _, a := range artifacts { dir, err := extractStagedArtifactToTempDir(ctx, &a) if err != nil { @@ -172,7 +172,7 @@ func unpackImages(ctx context.Context, artifacts []StagedArtifact, trimSuffix st baseName := filepath.Base(archive) componentName := strings.TrimSuffix(baseName[:len(baseName)-len(filepath.Ext(baseName))], trimSuffix) log.Printf("Found image for component %q with name %q", componentName, imageTar.ImageName()) - tarBundles[componentName] = append(tarBundles[componentName], *imageTar) + tarBundles[componentName] = append(tarBundles[componentName], imageTar) } } return tarBundles, nil diff --git a/pkg/release/validation/validate.go b/pkg/release/validation/validate.go index 237ec08..fd44eb9 100644 --- a/pkg/release/validation/validate.go +++ b/pkg/release/validation/validate.go @@ -20,12 +20,20 @@ import ( "fmt" "strings" + "github.com/cert-manager/release/pkg/release/images" + "github.com/blang/semver" "github.com/cert-manager/release/pkg/release" - "github.com/cert-manager/release/pkg/release/images" ) +type tarImage interface { + Architecture() string + ImageArchitecture() string + ImageName() string + ImageTag() string +} + type Options struct { // ReleaseVersion is used to ensure that the artifacts in a staged release // all specify the same image tag and define a consistent version. @@ -67,7 +75,7 @@ func validateSemver(v string) error { return err } -func validateImageBundles(bundles map[string][]images.Tar, opts Options) []string { +func validateImageBundles(bundles map[string][]images.TarInterface, opts Options) []string { var violations []string for componentName, tars := range bundles { for _, tar := range tars { diff --git a/pkg/release/validation/validate_test.go b/pkg/release/validation/validate_test.go index c70e414..7fa6f6b 100644 --- a/pkg/release/validation/validate_test.go +++ b/pkg/release/validation/validate_test.go @@ -4,6 +4,10 @@ import ( "reflect" "testing" + "github.com/cert-manager/release/pkg/release/images/fake" + + "github.com/cert-manager/release/pkg/release/images" + "github.com/cert-manager/release/pkg/release" ) @@ -51,3 +55,67 @@ func TestValidate_Semver(t *testing.T) { }) } } + +func Test_validateImageBundles(t *testing.T) { + type args struct { + bundles map[string][]images.TarInterface + opts Options + } + tests := []struct { + name string + args args + want []string + }{ + { + name: "no errors on a correct image name", + args: args{ + bundles: map[string][]images.TarInterface{"controller": []images.TarInterface{fake.New("quay.io/jetstack/cert-manager-controller-amd64", "v0.15.0", "dummy", "linux", "amd64", "amd64")}}, + opts: Options{ + ReleaseVersion: "v0.15.0", + ImageRepository: "quay.io/jetstack", + }, + }, + want: nil, + }, + { + name: "error on incorrect image name", + args: args{ + bundles: map[string][]images.TarInterface{"controller": []images.TarInterface{fake.New("nginx", "v0.15.0", "dummy", "linux", "amd64", "amd64")}}, + opts: Options{ + ReleaseVersion: "v0.15.0", + ImageRepository: "quay.io/jetstack", + }, + }, + want: []string{`Image "nginx" does not match expected name "quay.io/jetstack/cert-manager-controller-amd64"`}, + }, + { + name: "error on incorrect image tag", + args: args{ + bundles: map[string][]images.TarInterface{"controller": []images.TarInterface{fake.New("quay.io/jetstack/cert-manager-controller-amd64", "v0.8.0", "dummy", "linux", "amd64", "amd64")}}, + opts: Options{ + ReleaseVersion: "v0.15.0", + ImageRepository: "quay.io/jetstack", + }, + }, + want: []string{`Image "quay.io/jetstack/cert-manager-controller-amd64" does not have expected tag "v0.15.0"`}, + }, + { + name: "error on incorrect image architecture", + args: args{ + bundles: map[string][]images.TarInterface{"controller": []images.TarInterface{fake.New("quay.io/jetstack/cert-manager-controller-arm", "v0.15.0", "dummy", "linux", "arm", "amd64")}}, + opts: Options{ + ReleaseVersion: "v0.15.0", + ImageRepository: "quay.io/jetstack", + }, + }, + want: []string{`Image architecture "amd64" does not match expected architecture "arm"`}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := validateImageBundles(tt.args.bundles, tt.args.opts); !reflect.DeepEqual(got, tt.want) { + t.Errorf("validateImageBundles() = %v, want %v", got, tt.want) + } + }) + } +}