mirror of
https://github.com/hauler-dev/hauler.git
synced 2026-08-19 04:16:27 +00:00
add some tests for image discovery, flesh out boot process
This commit is contained in:
@@ -71,6 +71,8 @@ func (o *deployOpts) Run(packagePath string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
d := v1alpha1.NewDriver(p.Spec.Driver.Kind)
|
||||
|
||||
b, err := bootstrap.NewBooter(tmpdir)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -82,14 +84,21 @@ func (o *deployOpts) Run(packagePath string) error {
|
||||
}
|
||||
|
||||
o.logger.Infof("Performing pre %s boot steps", p.Spec.Driver.Kind)
|
||||
if err := b.PreBoot(ctx, d); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.logger.Infof("Booting %s", p.Spec.Driver.Kind)
|
||||
if err := b.Boot(ctx, d); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.logger.Infof("Performing post %s boot steps", p.Spec.Driver.Kind)
|
||||
if err := b.PostBoot(ctx, d); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.logger.Infof("Success!")
|
||||
|
||||
_ = ctx
|
||||
o.logger.Infof("Success! You can access the cluster with '/opt/hauler/bin/kubectl'")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
apiVersion: hauler.cattle.io/v1alpha1
|
||||
kind: Package
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: test
|
||||
spec:
|
||||
driver:
|
||||
kind: k3s
|
||||
version: v1.21.1+k3s1
|
||||
fleet:
|
||||
version: 0.3.5
|
||||
paths:
|
||||
- ../../../testdata/rawmanifests
|
||||
@@ -11,7 +11,7 @@ type Drive interface {
|
||||
|
||||
LibPath() string
|
||||
EtcPath() string
|
||||
Config() (map[string]interface{}, error)
|
||||
Config() (*map[string]interface{}, error)
|
||||
SystemObjects() (objs []object.ObjMetadata)
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func (k k3s) Images() ([]string, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (k k3s) Config() (map[string]interface{}, error) {
|
||||
func (k k3s) Config() (*map[string]interface{}, error) {
|
||||
// TODO: This should be typed
|
||||
c := make(map[string]interface{})
|
||||
c["write-kubeconfig-mode"] = "0644"
|
||||
@@ -52,7 +52,7 @@ func (k k3s) Config() (map[string]interface{}, error) {
|
||||
//TODO: Add uid or something to ensure this works for multi-node setups
|
||||
c["node-name"] = "hauler"
|
||||
|
||||
return c, nil
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func (k k3s) SystemObjects() (objs []object.ObjMetadata) {
|
||||
@@ -74,7 +74,7 @@ func (r rke2) Images() ([]string, error) { return []string{}, n
|
||||
func (r rke2) BinURL() string { return "" }
|
||||
func (r rke2) LibPath() string { return "" }
|
||||
func (r rke2) EtcPath() string { return "" }
|
||||
func (r rke2) Config() (map[string]interface{}, error) { return nil, nil }
|
||||
func (r rke2) Config() (*map[string]interface{}, error) { return nil, nil }
|
||||
func (r rke2) SystemObjects() (objs []object.ObjMetadata) { return objs }
|
||||
|
||||
//NewDriver will return the appropriate driver given a kind, defaults to k3s
|
||||
|
||||
+14
-10
@@ -18,9 +18,9 @@ import (
|
||||
|
||||
type Booter interface {
|
||||
Init() error
|
||||
PreBoot() error
|
||||
Boot() error
|
||||
PostBoot() error
|
||||
PreBoot(context.Context) error
|
||||
Boot(context.Context, v1alpha1.Drive) error
|
||||
PostBoot(context.Context, v1alpha1.Drive) error
|
||||
}
|
||||
|
||||
type booter struct {
|
||||
@@ -55,7 +55,11 @@ func (b booter) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b booter) PreBoot() error {
|
||||
func (b booter) PreBoot(ctx context.Context, d v1alpha1.Drive) error {
|
||||
if err := b.writeConfig(d); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -68,15 +72,15 @@ func (b booter) Boot(ctx context.Context, d v1alpha1.Drive) error {
|
||||
"INSTALL_K3S_SELINUX_WARN=true",
|
||||
"INSTALL_K3S_SKIP_SELINUX_RPM=true",
|
||||
"INSTALL_K3S_BIN_DIR=/opt/hauler/bin",
|
||||
//"INSTALL_K3S_SKIP_START=true",
|
||||
}...)
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("%s\n%v", out, err)
|
||||
}
|
||||
|
||||
//TODO: Figure out what to do with output
|
||||
_ = out
|
||||
|
||||
return waitForDriver(ctx, d)
|
||||
}
|
||||
@@ -121,7 +125,7 @@ func (b booter) moveBin() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return copy.Copy(b.fs.Path(), path)
|
||||
return copy.Copy(b.fs.Bin().Path(), path)
|
||||
}
|
||||
|
||||
func (b booter) moveImages(d v1alpha1.Drive) error {
|
||||
@@ -145,7 +149,7 @@ func (b booter) moveBundles(d v1alpha1.Drive) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return copy.Copy(b.fs.Path(), path)
|
||||
return copy.Copy(b.fs.Bundle().Path(), path)
|
||||
}
|
||||
|
||||
func (b booter) moveCharts(d v1alpha1.Drive) error {
|
||||
@@ -154,7 +158,7 @@ func (b booter) moveCharts(d v1alpha1.Drive) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return copy.Copy(b.fs.Path(), path)
|
||||
return copy.Copy(b.fs.Chart().Path(), path)
|
||||
}
|
||||
|
||||
func (b booter) writeConfig(d v1alpha1.Drive) error {
|
||||
@@ -183,5 +187,5 @@ func (b booter) writeConfig(d v1alpha1.Drive) error {
|
||||
}
|
||||
|
||||
data, err := yaml.Marshal(c)
|
||||
return os.WriteFile(path, data, 0600)
|
||||
return os.WriteFile(path, data, 0644)
|
||||
}
|
||||
|
||||
+42
-28
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+6
-6
@@ -1,22 +1,22 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: frontend
|
||||
name: podinfo
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: guestbook
|
||||
app: podinfo
|
||||
tier: frontend
|
||||
replicas: 3
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: guestbook
|
||||
app: podinfo
|
||||
tier: frontend
|
||||
spec:
|
||||
containers:
|
||||
- name: php-redis
|
||||
image: gcr.io/google-samples/gb-frontend:v4
|
||||
- name: podinfo
|
||||
image: stefanprodan/podinfo:5.2.1
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
|
||||
Reference in New Issue
Block a user