Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions pkg/release/images/fake/tar.go
Original file line number Diff line number Diff line change
@@ -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
}
43 changes: 39 additions & 4 deletions pkg/release/images/tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,19 @@ type Tar struct {
// imageName is the name of the image stored in the tar file, extracted by
// reading the manifest.json file in the archive.
imageName string

// imageArchitecture is the name of the architecture stored in the tar file, extracted by
// reading the manifest and config file in the archive.
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) {
Expand All @@ -57,6 +70,7 @@ func NewTar(path, osStr, arch string) (*Tar, error) {
// metadata specification.
var metas = []struct {
RepoTags []string `json:"RepoTags"`
Config string `json:"Config"`
}{}
if err := json.Unmarshal(metaBytes, &metas); err != nil {
return nil, err
Expand All @@ -75,11 +89,28 @@ func NewTar(path, osStr, arch string) (*Tar, error) {
return nil, fmt.Errorf("found multiple image tag entries in image tar metadata.json file")
}
imageName := meta.RepoTags[0]

var config = struct {
Architecture string `json:"architecture"`
}{}
if meta.Config == "" {
return nil, fmt.Errorf("could not find any config entries in image tar metadata.json file")
}

configBytes, err := tar.ReadSingleFile(meta.Config, f)
if err != nil {
return nil, err
}
if err := json.Unmarshal(configBytes, &config); err != nil {
return nil, err
}

return &Tar{
path: path,
os: osStr,
arch: arch,
imageName: imageName,
path: path,
os: osStr,
arch: arch,
imageName: imageName,
imageArchitecture: config.Architecture,
}, nil
}

Expand All @@ -99,6 +130,10 @@ func (i *Tar) ImageName() string {
return i.imageName
}

func (i *Tar) ImageArchitecture() string {
return i.imageArchitecture
}

func (i *Tar) ImageTag() string {
s := strings.Split(i.imageName, ":")
if len(s) < 2 {
Expand Down
2 changes: 1 addition & 1 deletion pkg/release/publish/registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
10 changes: 5 additions & 5 deletions pkg/release/unpacker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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, "")
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand Down
18 changes: 16 additions & 2 deletions pkg/release/validation/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 {
Expand All @@ -79,6 +87,12 @@ func validateImageBundles(bundles map[string][]images.Tar, opts Options) []strin
if tar.ImageTag() != opts.ReleaseVersion {
violations = append(violations, fmt.Sprintf("Image %q does not have expected tag %q", tar.ImageName(), opts.ReleaseVersion))
}

expectedArch := tar.Architecture()
actualArch := tar.ImageArchitecture()
if expectedArch != actualArch {
violations = append(violations, fmt.Sprintf("Image architecture %q does not match expected architecture %q", actualArch, expectedArch))
}
}
}
return violations
Expand Down
66 changes: 66 additions & 0 deletions pkg/release/validation/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"testing"

"github.com/cert-manager/release/pkg/release"
"github.com/cert-manager/release/pkg/release/images"
"github.com/cert-manager/release/pkg/release/images/fake"
)

func TestValidate_Semver(t *testing.T) {
Expand Down Expand Up @@ -51,3 +53,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)
}
})
}
}