mirror of
https://github.com/hauler-dev/hauler.git
synced 2026-02-14 18:09:51 +00:00
Merge pull request #124 from nikkelma/0.2-image-txts
[0.2] Add ImageTxts collection
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/rancherfederal/hauler/pkg/apis/hauler.cattle.io/v1alpha1"
|
||||
"github.com/rancherfederal/hauler/pkg/apis/hauler.cattle.io/v1alpha2"
|
||||
tchart "github.com/rancherfederal/hauler/pkg/collection/chart"
|
||||
"github.com/rancherfederal/hauler/pkg/collection/imagetxt"
|
||||
"github.com/rancherfederal/hauler/pkg/collection/k3s"
|
||||
"github.com/rancherfederal/hauler/pkg/content"
|
||||
"github.com/rancherfederal/hauler/pkg/log"
|
||||
@@ -158,6 +159,26 @@ func SyncCmd(ctx context.Context, o *SyncOpts, s *store.Store) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// collection.hauler.cattle.io/v1alpha1 ImageTxts
|
||||
case v1alpha1.ImageTxtsContentKind:
|
||||
var cfg v1alpha1.ImageTxts
|
||||
if err := yaml.Unmarshal(doc, &cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, cfgIt := range cfg.Spec.ImageTxts {
|
||||
it, err := imagetxt.New(cfgIt.Ref,
|
||||
imagetxt.WithIncludeSources(cfgIt.Sources.Include...),
|
||||
imagetxt.WithExcludeSources(cfgIt.Sources.Exclude...),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("convert ImageTxt %s: %v", cfg.Name, err)
|
||||
}
|
||||
|
||||
if _, err := s.AddCollection(ctx, it); err != nil {
|
||||
return fmt.Errorf("add ImageTxt %s to store: %v", cfg.Name, err)
|
||||
}
|
||||
}
|
||||
// collection.hauler.cattle.io/v1alpha1 unknown
|
||||
default:
|
||||
return fmt.Errorf("unsupported Kind %s for %s", gvk.Kind, gvk.GroupVersion().String())
|
||||
|
||||
30
pkg/apis/hauler.cattle.io/v1alpha1/imagetxt.go
Normal file
30
pkg/apis/hauler.cattle.io/v1alpha1/imagetxt.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
ImageTxtsContentKind = "ImageTxts"
|
||||
)
|
||||
|
||||
type ImageTxts struct {
|
||||
*metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec ImageTxtsSpec `json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
type ImageTxtsSpec struct {
|
||||
ImageTxts []ImageTxt `json:"imageTxts,omitempty"`
|
||||
}
|
||||
|
||||
type ImageTxt struct {
|
||||
Ref string `json:"ref,omitempty"`
|
||||
Sources ImageTxtSources `json:"sources,omitempty"`
|
||||
}
|
||||
|
||||
type ImageTxtSources struct {
|
||||
Include []string `json:"include,omitempty"`
|
||||
Exclude []string `json:"exclude,omitempty"`
|
||||
}
|
||||
30
pkg/apis/hauler.cattle.io/v1alpha2/imagetxt.go
Normal file
30
pkg/apis/hauler.cattle.io/v1alpha2/imagetxt.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package v1alpha2
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
ImageTxtsContentKind = "ImageTxts"
|
||||
)
|
||||
|
||||
type ImageTxts struct {
|
||||
*metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec ImageTxtsSpec `json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
type ImageTxtsSpec struct {
|
||||
ImageTxts []ImageTxt `json:"imageTxts,omitempty"`
|
||||
}
|
||||
|
||||
type ImageTxt struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Sources ImageTxtSources `json:"sources,omitempty"`
|
||||
}
|
||||
|
||||
type ImageTxtSources struct {
|
||||
Include []string `json:"include,omitempty"`
|
||||
Exclude []string `json:"exclude,omitempty"`
|
||||
}
|
||||
@@ -9,8 +9,6 @@ import (
|
||||
"github.com/rancherfederal/hauler/pkg/artifact/types"
|
||||
)
|
||||
|
||||
type Opener func() (io.ReadCloser, error)
|
||||
|
||||
func LayerFromOpener(opener Opener, opts ...LayerOption) (v1.Layer, error) {
|
||||
var err error
|
||||
|
||||
|
||||
25
pkg/artifact/local/opener.go
Normal file
25
pkg/artifact/local/opener.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package local
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Opener func() (io.ReadCloser, error)
|
||||
|
||||
func LocalOpener(path string) Opener {
|
||||
return func() (io.ReadCloser, error) {
|
||||
return os.Open(path)
|
||||
}
|
||||
}
|
||||
|
||||
func RemoteOpener(url string) Opener {
|
||||
return func() (io.ReadCloser, error) {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Body, nil
|
||||
}
|
||||
}
|
||||
240
pkg/collection/imagetxt/imagetxt.go
Normal file
240
pkg/collection/imagetxt/imagetxt.go
Normal file
@@ -0,0 +1,240 @@
|
||||
package imagetxt
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/rancherfederal/hauler/pkg/artifact"
|
||||
"github.com/rancherfederal/hauler/pkg/artifact/local"
|
||||
"github.com/rancherfederal/hauler/pkg/content/image"
|
||||
"github.com/rancherfederal/hauler/pkg/log"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
)
|
||||
|
||||
type ImageTxt struct {
|
||||
Ref string
|
||||
IncludeSources map[string]bool
|
||||
ExcludeSources map[string]bool
|
||||
|
||||
lock *sync.Mutex
|
||||
getter local.Opener
|
||||
computed bool
|
||||
contents map[name.Reference]artifact.OCI
|
||||
}
|
||||
|
||||
var _ artifact.Collection = (*ImageTxt)(nil)
|
||||
|
||||
type Option interface {
|
||||
Apply(*ImageTxt) error
|
||||
}
|
||||
|
||||
type withIncludeSources []string
|
||||
|
||||
func (o withIncludeSources) Apply(it *ImageTxt) error {
|
||||
if it.IncludeSources == nil {
|
||||
it.IncludeSources = make(map[string]bool)
|
||||
}
|
||||
for _, s := range o {
|
||||
it.IncludeSources[s] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func WithIncludeSources(include ...string) Option {
|
||||
return withIncludeSources(include)
|
||||
}
|
||||
|
||||
type withExcludeSources []string
|
||||
|
||||
func (o withExcludeSources) Apply(it *ImageTxt) error {
|
||||
if it.ExcludeSources == nil {
|
||||
it.ExcludeSources = make(map[string]bool)
|
||||
}
|
||||
for _, s := range o {
|
||||
it.ExcludeSources[s] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func WithExcludeSources(exclude ...string) Option {
|
||||
return withExcludeSources(exclude)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
return it, nil
|
||||
}
|
||||
|
||||
func (it *ImageTxt) Contents() (map[name.Reference]artifact.OCI, error) {
|
||||
it.lock.Lock()
|
||||
defer it.lock.Unlock()
|
||||
if !it.computed {
|
||||
if err := it.compute(); err != nil {
|
||||
return nil, fmt.Errorf("compute OCI layout: %v", err)
|
||||
}
|
||||
it.computed = true
|
||||
}
|
||||
return it.contents, nil
|
||||
}
|
||||
|
||||
func (it *ImageTxt) compute() error {
|
||||
// TODO - pass in logger from context
|
||||
l := log.NewLogger(os.Stdout)
|
||||
|
||||
it.contents = make(map[name.Reference]artifact.OCI)
|
||||
|
||||
r, err := it.getter()
|
||||
if err != nil {
|
||||
return fmt.Errorf("fetch image.txt ref %s: %v", it.Ref, err)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
if _, err := io.Copy(buf, r); err != nil {
|
||||
return fmt.Errorf("read image.txt ref %s: %v", it.Ref, err)
|
||||
}
|
||||
|
||||
entries, err := splitImagesTxt(buf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parse image.txt ref %s: %v", it.Ref, err)
|
||||
}
|
||||
|
||||
foundSources := make(map[string]bool)
|
||||
for _, e := range entries {
|
||||
for s := range e.Sources {
|
||||
foundSources[s] = true
|
||||
}
|
||||
}
|
||||
|
||||
var pullAll bool
|
||||
targetSources := make(map[string]bool)
|
||||
|
||||
if len(foundSources) == 0 || (len(it.IncludeSources) == 0 && len(it.ExcludeSources) == 0) {
|
||||
// pull all found images
|
||||
pullAll = true
|
||||
|
||||
if len(foundSources) == 0 {
|
||||
l.Infof("image txt file appears to have no sources; pulling all found images")
|
||||
if len(it.IncludeSources) != 0 || len(it.ExcludeSources) != 0 {
|
||||
l.Warnf("ImageTxt provided include or exclude sources; ignoring")
|
||||
}
|
||||
} else if len(it.IncludeSources) == 0 && len(it.ExcludeSources) == 0 {
|
||||
l.Infof("image-sources txt file not filtered; pulling all found images")
|
||||
}
|
||||
} else {
|
||||
// determine sources to pull
|
||||
if len(it.IncludeSources) != 0 && len(it.ExcludeSources) != 0 {
|
||||
l.Warnf("ImageTxt provided include and exclude sources; using only include sources")
|
||||
}
|
||||
|
||||
if len(it.IncludeSources) != 0 {
|
||||
targetSources = it.IncludeSources
|
||||
} else {
|
||||
for s := range foundSources {
|
||||
targetSources[s] = true
|
||||
}
|
||||
for s := range it.ExcludeSources {
|
||||
delete(targetSources, s)
|
||||
}
|
||||
}
|
||||
var targetSourcesArr []string
|
||||
for s := range targetSources {
|
||||
targetSourcesArr = append(targetSourcesArr, s)
|
||||
}
|
||||
l.Infof("pulling images covering sources %s", strings.Join(targetSourcesArr, ", "))
|
||||
}
|
||||
|
||||
for _, e := range entries {
|
||||
var matchesSourceFilter bool
|
||||
if pullAll {
|
||||
l.Infof("pulling image %s", e.Reference)
|
||||
} else {
|
||||
for s := range e.Sources {
|
||||
if targetSources[s] {
|
||||
matchesSourceFilter = true
|
||||
l.Infof("pulling image %s (matched source %s)", e.Reference, s)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if pullAll || matchesSourceFilter {
|
||||
curImage, err := image.NewImage(e.Reference.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("pull image %s: %v", e.Reference, err)
|
||||
}
|
||||
it.contents[e.Reference] = curImage
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type imageTxtEntry struct {
|
||||
Reference name.Reference
|
||||
Sources map[string]bool
|
||||
}
|
||||
|
||||
func splitImagesTxt(r io.Reader) ([]imageTxtEntry, error) {
|
||||
var entries []imageTxtEntry
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
curEntry := imageTxtEntry{
|
||||
Sources: make(map[string]bool),
|
||||
}
|
||||
|
||||
lineContent := scanner.Text()
|
||||
if lineContent == "" || strings.HasPrefix(lineContent, "#") {
|
||||
// skip past empty and commented lines
|
||||
continue
|
||||
}
|
||||
splitContent := strings.Split(lineContent, " ")
|
||||
if len(splitContent) > 2 {
|
||||
return nil, fmt.Errorf(
|
||||
"invalid image.txt format: must contain only an image reference and sources separated by space; invalid line: %q",
|
||||
lineContent)
|
||||
}
|
||||
|
||||
curRef, err := name.ParseReference(splitContent[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid reference %s: %v", splitContent[0], err)
|
||||
}
|
||||
curEntry.Reference = curRef
|
||||
|
||||
if len(splitContent) == 2 {
|
||||
for _, source := range strings.Split(splitContent[1], ",") {
|
||||
curEntry.Sources[source] = true
|
||||
}
|
||||
}
|
||||
|
||||
entries = append(entries, curEntry)
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("scan contents: %v", err)
|
||||
}
|
||||
|
||||
return entries, nil
|
||||
}
|
||||
216
pkg/collection/imagetxt/imagetxt_test.go
Normal file
216
pkg/collection/imagetxt/imagetxt_test.go
Normal 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
pkg/collection/imagetxt/testdata/http/images-http.txt
vendored
Normal file
5
pkg/collection/imagetxt/testdata/http/images-http.txt
vendored
Normal 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
|
||||
5
pkg/collection/imagetxt/testdata/http/images-src-http.txt
vendored
Normal file
5
pkg/collection/imagetxt/testdata/http/images-src-http.txt
vendored
Normal file
@@ -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
pkg/collection/imagetxt/testdata/images-file.txt
vendored
Normal file
5
pkg/collection/imagetxt/testdata/images-file.txt
vendored
Normal 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
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user