add some tests for image discovery, flesh out boot process

This commit is contained in:
Josh Wolf
2021-06-10 14:42:13 -06:00
parent 4287dd643d
commit 541d774857
8 changed files with 167 additions and 66 deletions
+42 -28
View File
@@ -2,8 +2,6 @@ package packager
import (
"bytes"
"encoding/json"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
@@ -12,13 +10,20 @@ import (
"github.com/rancher/fleet/pkg/helmdeployer"
"github.com/rancher/fleet/pkg/manifest"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/client-go/util/jsonpath"
"strings"
)
type Imager interface {
Images() ([]string, error)
}
type discoveredImages []string
func (d discoveredImages) Images() ([]string, error) {
return d, nil
}
//ConcatImages will gather images from various Imager sources and return a single slilce
func ConcatImages(imager ...Imager) (map[name.Reference]v1.Image, error) {
m := make(map[name.Reference]v1.Image)
@@ -43,7 +48,7 @@ func ConcatImages(imager ...Imager) (map[name.Reference]v1.Image, error) {
return m, nil
}
func IdentifyImages(b *fleetapi.Bundle) (map[name.Reference]v1.Image, error) {
func IdentifyImages(b *fleetapi.Bundle) (discoveredImages, error) {
opts := fleetapi.BundleDeploymentOptions{
DefaultNamespace: "default",
}
@@ -56,13 +61,17 @@ func IdentifyImages(b *fleetapi.Bundle) (map[name.Reference]v1.Image, error) {
return nil, err
}
var di discoveredImages
for _, o := range objs {
u := o.(*unstructured.Unstructured)
_ = u
imageFromRuntimeObject(u)
imgs, err := imageFromRuntimeObject(o.(*unstructured.Unstructured))
if err != nil {
return nil, err
}
di = append(di, imgs...)
}
return nil, err
return di, err
}
//ResolveRemoteRefs will return a slice of remote images resolved from their fully qualified name
@@ -70,6 +79,9 @@ func ResolveRemoteRefs(images ...string) (map[name.Reference]v1.Image, error) {
m := make(map[name.Reference]v1.Image)
for _, i := range images {
if i == "" { continue }
//TODO: This will error out if remote is a v1 image, do better error handling for this
ref, err := name.ParseReference(i)
if err != nil {
return nil, err
@@ -87,40 +99,42 @@ func ResolveRemoteRefs(images ...string) (map[name.Reference]v1.Image, error) {
}
var knownImagePaths = []string{
"spec.template.spec.containers.#.image",
"{.spec.template.spec.containers[*].image}",
}
////imageFromRuntimeObject will return any images found in known obj specs
func imageFromRuntimeObject(obj *unstructured.Unstructured) ([]string, error) {
data, err := obj.MarshalJSON()
if err != nil {
func imageFromRuntimeObject(obj *unstructured.Unstructured) (images []string, err error) {
objData, _ := obj.MarshalJSON()
var data interface{}
if err := json.Unmarshal(objData, &data); err != nil {
return nil, err
}
var images []string
j := jsonpath.New("imagePath")
var imageData interface{}
err = json.Unmarshal(data, &imageData)
if err != nil {
return nil, err
}
j := jsonpath.New("")
j.AllowMissingKeys(true)
for _, path := range knownImagePaths {
j.Parse(path)
buf := new(bytes.Buffer)
err = j.Execute(buf, imageData)
r, err := parseJSONPath(data, j, path)
if err != nil {
return nil, err
}
images = append(images, buf.String())
images = append(images, r...)
}
return images, nil
}
func parseJSONPath(input interface{}, parser *jsonpath.JSONPath, template string) ([]string, error) {
buf := new(bytes.Buffer)
if err := parser.Parse(template); err != nil {
return nil, err
}
if err := parser.Execute(buf, input); err != nil {
return nil, err
}
r:= strings.Split(buf.String(), " ")
return r, nil
}
+83
View File
@@ -1 +1,84 @@
package packager
import (
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/client-go/util/jsonpath"
"reflect"
"testing"
)
var (
jsona = []byte(`{
"flatImage": "name/of/image:with-tag",
"deeply": {
"nested": {
"image": "another/image/name:with-a-tag",
"set": [
{ "image": "first/in/list:123" },
{ "image": "second/in:456" }
]
}
}
}`)
)
func Test_parseJSONPath(t *testing.T) {
var data interface{}
if err := json.Unmarshal(jsona, &data); err != nil {
t.Errorf("failed to unmarshal test article, %v", err)
}
j := jsonpath.New("")
type args struct {
input interface{}
name string
template string
}
tests := []struct {
name string
args args
want []string
wantErr bool
}{
{
name: "should find flat path with string result",
args: args{
input: data,
name: "wut",
template: "{.flatImage}",
},
want: []string{"name/of/image:with-tag"},
},
{
name: "should find nested path with string result",
args: args{
input: data,
name: "wut",
template: "{.deeply.nested.image}",
},
want: []string{"another/image/name:with-a-tag"},
},
{
name: "should find nested path with slice result",
args: args{
input: data,
name: "wut",
template: "{.deeply.nested.set[*].image}",
},
want: []string{"first/in/list:123", "second/in:456"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseJSONPath(tt.args.input, j, tt.args.template)
if (err != nil) != tt.wantErr {
t.Errorf("parseJSONPath() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("parseJSONPath() got = %v, want %v", got, tt.want)
}
})
}
}
+6 -2
View File
@@ -28,6 +28,8 @@ func Create(ctx context.Context, p v1alpha1.Package, fsys fs.PkgFs, a archiver.A
Compress: true,
}
var di discoveredImages
//Get and write bundles to disk
for _, path := range p.Spec.Paths {
bundleName := filepath.Base(path)
@@ -39,11 +41,13 @@ func Create(ctx context.Context, p v1alpha1.Package, fsys fs.PkgFs, a archiver.A
//TODO: Figure out why bundle.Open doesn't return with GVK
bn := fleetapi.NewBundle("fleet-local", bundleName, *fb.Definition)
_, err = IdentifyImages(bn)
imgs, err := IdentifyImages(bn)
if err != nil {
return err
}
di = append(di, imgs...)
if err := fsys.AddBundle(bn); err != nil {
return err
}
@@ -68,7 +72,7 @@ func Create(ctx context.Context, p v1alpha1.Package, fsys fs.PkgFs, a archiver.A
return err
}
imgMap, err := ConcatImages(d, p.Spec.Fleet)
imgMap, err := ConcatImages(d, p.Spec.Fleet, di)
if err != nil {
return err
}