Update imagetxt to align with 0.3, add tests; make content image type public

This commit is contained in:
Matt Nikkel
2022-03-07 19:58:23 -05:00
parent 2165c54508
commit 5cb6a5ef60
6 changed files with 248 additions and 26 deletions
+11 -20
View File
@@ -22,8 +22,8 @@ type ImageTxt struct {
IncludeSources map[string]bool
ExcludeSources map[string]bool
getter local.Opener
lock *sync.Mutex
getter local.Opener
computed bool
contents map[name.Reference]artifact.OCI
}
@@ -34,23 +34,6 @@ type Option interface {
Apply(*ImageTxt) error
}
type withRef string
func (o withRef) Apply(it *ImageTxt) error {
ref := string(o)
if strings.HasPrefix(ref, "http") || strings.HasPrefix(ref, "https") {
it.getter = local.RemoteOpener(ref)
} else {
it.getter = local.LocalOpener(ref)
}
return nil
}
func WithRef(ref string) Option {
return withRef(ref)
}
type withIncludeSources []string
func (o withIncludeSources) Apply(it *ImageTxt) error {
@@ -83,11 +66,19 @@ func WithExcludeSources(exclude ...string) Option {
return withExcludeSources(exclude)
}
func New(opts ...Option) (*ImageTxt, error) {
func New(ref string, opts ...Option) (*ImageTxt, error) {
it := &ImageTxt{
Ref: ref,
lock: &sync.Mutex{},
}
if strings.HasPrefix(ref, "http") || strings.HasPrefix(ref, "https") {
it.getter = local.RemoteOpener(ref)
} else {
it.getter = local.LocalOpener(ref)
}
for i, o := range opts {
if err := o.Apply(it); err != nil {
return nil, fmt.Errorf("invalid option %d: %v", i, err)
@@ -139,7 +130,7 @@ func (it *ImageTxt) compute() error {
}
var pullAll bool
var targetSources map[string]bool
targetSources := make(map[string]bool)
if len(foundSources) == 0 || (len(it.IncludeSources) == 0 && len(it.ExcludeSources) == 0) {
// pull all found images
+216
View File
@@ -0,0 +1,216 @@
package imagetxt
import (
"errors"
"fmt"
"net/http"
"net/http/httptest"
"os"
"testing"
"github.com/google/go-containerregistry/pkg/name"
artifacts "github.com/rancherfederal/hauler/pkg/artifact"
"github.com/rancherfederal/hauler/pkg/content/image"
)
var (
ErrInvalidRef = errors.New("invalid reference")
ErrRefNotFound = errors.New("ref not found")
ErrRefNotImage = errors.New("ref is not image")
ErrExtraRefsFound = errors.New("extra refs found in contents")
)
var (
testServer *httptest.Server
)
func TestMain(m *testing.M) {
setup()
code := m.Run()
teardown()
os.Exit(code)
}
func setup() {
dir := http.Dir("./testdata/http/")
h := http.FileServer(dir)
testServer = httptest.NewServer(h)
}
func teardown() {
if testServer != nil {
testServer.Close()
}
}
type failKind string
const (
failKindNew = failKind("New")
failKindContents = failKind("Contents")
)
func checkError(checkedFailKind failKind) func(*testing.T, error, bool, failKind) {
return func(cet *testing.T, err error, testShouldFail bool, testFailKind failKind) {
if err != nil {
// if error should not have happened at all OR error should have happened
// at a different point, test failed
if !testShouldFail || testFailKind != checkedFailKind {
cet.Fatalf("unexpected error at %s: %v", checkedFailKind, err)
}
// test should fail at this point, test passed
return
}
// if no error occurred but error should have happened at this point, test
// failed
if testShouldFail && testFailKind == checkedFailKind {
cet.Fatalf("unexpected nil error at %s", checkedFailKind)
}
}
}
func TestImageTxtCollection(t *testing.T) {
type testEntry struct {
Name string
Ref string
IncludeSources []string
ExcludeSources []string
ExpectedImages []string
ShouldFail bool
FailKind failKind
}
tt := []testEntry{
{
Name: "http ref basic",
Ref: fmt.Sprintf("%s/images-http.txt", testServer.URL),
ExpectedImages: []string{
"busybox",
"nginx:1.19",
"rancher/hyperkube:v1.21.7-rancher1",
"docker.io/rancher/klipper-lb:v0.3.4",
"quay.io/jetstack/cert-manager-controller:v1.6.1",
},
},
{
Name: "http ref sources format pull all",
Ref: fmt.Sprintf("%s/images-src-http.txt", testServer.URL),
ExpectedImages: []string{
"busybox",
"nginx:1.19",
"rancher/hyperkube:v1.21.7-rancher1",
"docker.io/rancher/klipper-lb:v0.3.4",
"quay.io/jetstack/cert-manager-controller:v1.6.1",
},
},
{
Name: "http ref sources format include sources A",
Ref: fmt.Sprintf("%s/images-src-http.txt", testServer.URL),
IncludeSources: []string{
"core", "rke",
},
ExpectedImages: []string{
"busybox",
"nginx:1.19",
"rancher/hyperkube:v1.21.7-rancher1",
},
},
{
Name: "http ref sources format include sources B",
Ref: fmt.Sprintf("%s/images-src-http.txt", testServer.URL),
IncludeSources: []string{
"nginx", "rancher", "cert-manager",
},
ExpectedImages: []string{
"nginx:1.19",
"rancher/hyperkube:v1.21.7-rancher1",
"docker.io/rancher/klipper-lb:v0.3.4",
"quay.io/jetstack/cert-manager-controller:v1.6.1",
},
},
{
Name: "http ref sources format exclude sources A",
Ref: fmt.Sprintf("%s/images-src-http.txt", testServer.URL),
ExcludeSources: []string{
"cert-manager",
},
ExpectedImages: []string{
"busybox",
"nginx:1.19",
"rancher/hyperkube:v1.21.7-rancher1",
"docker.io/rancher/klipper-lb:v0.3.4",
},
},
{
Name: "http ref sources format exclude sources B",
Ref: fmt.Sprintf("%s/images-src-http.txt", testServer.URL),
ExcludeSources: []string{
"core",
},
ExpectedImages: []string{
"nginx:1.19",
"rancher/hyperkube:v1.21.7-rancher1",
"docker.io/rancher/klipper-lb:v0.3.4",
"quay.io/jetstack/cert-manager-controller:v1.6.1",
},
},
{
Name: "local file ref",
Ref: "./testdata/images-file.txt",
ExpectedImages: []string{
"busybox",
"nginx:1.19",
"rancher/hyperkube:v1.21.7-rancher1",
"docker.io/rancher/klipper-lb:v0.3.4",
"quay.io/jetstack/cert-manager-controller:v1.6.1",
},
},
}
checkErrorNew := checkError(failKindNew)
checkErrorContents := checkError(failKindContents)
for _, curTest := range tt {
t.Run(curTest.Name, func(innerT *testing.T) {
curImageTxt, err := New(curTest.Ref,
WithIncludeSources(curTest.IncludeSources...),
WithExcludeSources(curTest.ExcludeSources...),
)
checkErrorNew(innerT, err, curTest.ShouldFail, curTest.FailKind)
ociContents, err := curImageTxt.Contents()
checkErrorContents(innerT, err, curTest.ShouldFail, curTest.FailKind)
if err := checkImages(ociContents, curTest.ExpectedImages); err != nil {
innerT.Fatal(err)
}
})
}
}
func checkImages(content map[name.Reference]artifacts.OCI, refs []string) error {
contentCopy := make(map[name.Reference]artifacts.OCI, len(content))
for k, v := range content {
contentCopy[k] = v
}
for _, ref := range refs {
nameRef, err := name.ParseReference(ref)
if err != nil {
return fmt.Errorf("ref %s: %w", ref, ErrInvalidRef)
}
target, ok := content[nameRef]
if !ok {
return fmt.Errorf("ref %s: %w", ref, ErrRefNotFound)
}
if _, ok := target.(*image.Image); !ok {
return fmt.Errorf("got underlying type %T: %w", target, ErrRefNotImage)
}
delete(contentCopy, nameRef)
}
if len(contentCopy) != 0 {
return ErrExtraRefsFound
}
return nil
}
+5
View File
@@ -0,0 +1,5 @@
busybox
nginx:1.19
rancher/hyperkube:v1.21.7-rancher1
docker.io/rancher/klipper-lb:v0.3.4
quay.io/jetstack/cert-manager-controller:v1.6.1
@@ -0,0 +1,5 @@
busybox core
nginx:1.19 core,nginx
rancher/hyperkube:v1.21.7-rancher1 rancher,rke
docker.io/rancher/klipper-lb:v0.3.4 rancher,k3s
quay.io/jetstack/cert-manager-controller:v1.6.1 cert-manager
+5
View File
@@ -0,0 +1,5 @@
busybox
nginx:1.19
rancher/hyperkube:v1.21.7-rancher1
docker.io/rancher/klipper-lb:v0.3.4
quay.io/jetstack/cert-manager-controller:v1.6.1
+6 -6
View File
@@ -8,9 +8,9 @@ import (
"github.com/rancherfederal/hauler/pkg/artifact"
)
var _ artifact.OCI = (*image)(nil)
var _ artifact.OCI = (*Image)(nil)
func (i *image) MediaType() string {
func (i *Image) MediaType() string {
mt, err := i.Image.MediaType()
if err != nil {
return ""
@@ -18,15 +18,15 @@ func (i *image) MediaType() string {
return string(mt)
}
func (i *image) RawConfig() ([]byte, error) {
func (i *Image) RawConfig() ([]byte, error) {
return i.RawConfigFile()
}
type image struct {
type Image struct {
gv1.Image
}
func NewImage(ref string) (*image, error) {
func NewImage(ref string) (*Image, error) {
r, err := name.ParseReference(ref)
if err != nil {
return nil, err
@@ -37,7 +37,7 @@ func NewImage(ref string) (*image, error) {
return nil, err
}
return &image{
return &Image{
Image: img,
}, nil
}