Skip to content

Commit

Permalink
support ImageKeys config
Browse files Browse the repository at this point in the history
  • Loading branch information
cppforlife committed Jan 16, 2020
1 parent 9af53c9 commit 5ce665a
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 55 deletions.
12 changes: 12 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,15 @@ overrides:
```

For preresolved images, kbld will not connect to registry to obtain any metadata.

### ImageKeys

ImageKeys resource configures kbld to look for additional keys that reference images (in addition to using default `image` key).

```yaml
---
apiVersion: kbld.k14s.io/v1alpha1
kind: ImageKeys
keys:
- sidecarImage
```
43 changes: 43 additions & 0 deletions pkg/kbld/cmd/image_kvs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package cmd

type ImageKVs struct {
Resource interface{}
Keys []string
}

func (kvs ImageKVs) Visit(visitorFunc func(interface{}) (interface{}, bool)) {
for _, key := range kvs.Keys {
kvs.visitValues(kvs.Resource, key, visitorFunc)
}
}

func (kvs ImageKVs) visitValues(obj interface{}, key string, visitorFunc func(interface{}) (interface{}, bool)) {
switch typedObj := obj.(type) {
case map[string]interface{}:
for k, v := range typedObj {
if k == key {
if newVal, update := visitorFunc(v); update {
typedObj[k] = newVal
}
} else {
kvs.visitValues(typedObj[k], key, visitorFunc)
}
}

case map[string]string:
for k, v := range typedObj {
if k == key {
if newVal, update := visitorFunc(v); update {
typedObj[k] = newVal.(string)
}
} else {
kvs.visitValues(typedObj[k], key, visitorFunc)
}
}

case []interface{}:
for _, o := range typedObj {
kvs.visitValues(o, key, visitorFunc)
}
}
}
13 changes: 9 additions & 4 deletions pkg/kbld/cmd/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/cppforlife/go-cli-ui/ui"
uitable "github.com/cppforlife/go-cli-ui/ui/table"
cmdcore "github.com/k14s/kbld/pkg/kbld/cmd/core"
ctlconf "github.com/k14s/kbld/pkg/kbld/config"
ctlres "github.com/k14s/kbld/pkg/kbld/resources"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -33,12 +34,12 @@ func NewInspectCmd(o *InspectOptions, flagsFactory cmdcore.FlagsFactory) *cobra.
}

func (o *InspectOptions) Run() error {
rs, _, err := o.FileFlags.ResourcesAndConfig()
rs, conf, err := o.FileFlags.ResourcesAndConfig()
if err != nil {
return err
}

foundImages, err := o.findImages(rs)
foundImages, err := o.findImages(rs, conf)
if err != nil {
return err
}
Expand Down Expand Up @@ -81,11 +82,15 @@ func (o *InspectOptions) Run() error {
return nil
}

func (o *InspectOptions) findImages(rs []ctlres.Resource) ([]foundResourceWithImage, error) {
func (o *InspectOptions) findImages(rs []ctlres.Resource,
conf ctlconf.Conf) ([]foundResourceWithImage, error) {

foundImages := []foundResourceWithImage{}

for _, res := range rs {
visitValues(res.DeepCopyRaw(), imageKey, func(val interface{}) (interface{}, bool) {
imageKVs := ImageKVs{res.DeepCopyRaw(), conf.ImageKeys()}

imageKVs.Visit(func(val interface{}) (interface{}, bool) {
if imgURL, ok := val.(string); ok {
foundImages = append(foundImages, foundResourceWithImage{URL: imgURL, Resource: res})
}
Expand Down
13 changes: 9 additions & 4 deletions pkg/kbld/cmd/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/cppforlife/go-cli-ui/ui"
regname "github.com/google/go-containerregistry/pkg/name"
cmdcore "github.com/k14s/kbld/pkg/kbld/cmd/core"
ctlconf "github.com/k14s/kbld/pkg/kbld/config"
ctlimg "github.com/k14s/kbld/pkg/kbld/image"
regtarball "github.com/k14s/kbld/pkg/kbld/imagetarball"
ctlres "github.com/k14s/kbld/pkg/kbld/resources"
Expand Down Expand Up @@ -49,24 +50,28 @@ func (o *PackageOptions) Run() error {
logger := ctlimg.NewLogger(os.Stderr)
prefixedLogger := logger.NewPrefixedWriter("package | ")

allRs, err := o.FileFlags.AllResources()
rs, conf, err := o.FileFlags.ResourcesAndConfig()
if err != nil {
return err
}

foundImages, err := o.findImages(allRs, logger)
foundImages, err := o.findImages(rs, conf, logger)
if err != nil {
return err
}

return o.exportImages(foundImages, prefixedLogger)
}

func (o *PackageOptions) findImages(allRs []ctlres.Resource, logger ctlimg.Logger) (map[string]struct{}, error) {
func (o *PackageOptions) findImages(allRs []ctlres.Resource,
conf ctlconf.Conf, logger ctlimg.Logger) (map[string]struct{}, error) {

foundImages := map[string]struct{}{}

for _, res := range allRs {
visitValues(res.DeepCopyRaw(), imageKey, func(val interface{}) (interface{}, bool) {
imageKVs := ImageKVs{res.DeepCopyRaw(), conf.ImageKeys()}

imageKVs.Visit(func(val interface{}) (interface{}, bool) {
if img, ok := val.(string); ok {
foundImages[img] = struct{}{}
}
Expand Down
53 changes: 11 additions & 42 deletions pkg/kbld/cmd/resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ import (
"github.com/spf13/cobra"
)

const (
imageKey = "image"
)

type ResolveOptions struct {
ui ui.UI
depsFactory cmdcore.DepsFactory
Expand Down Expand Up @@ -65,7 +61,7 @@ func (o *ResolveOptions) Run() error {
registry := ctlimg.NewRegistry(o.RegistryFlags.AsRegistryOpts())
imgFactory := ctlimg.NewFactory(conf, registry, logger)

resolvedImages, err := o.resolveImages(nonConfigRs, imgFactory)
resolvedImages, err := o.resolveImages(nonConfigRs, conf, imgFactory)
if err != nil {
return err
}
Expand All @@ -75,7 +71,7 @@ func (o *ResolveOptions) Run() error {
prefixedLogger.WriteStr("final: %s -> %s\n", imgURL, img.URL)
}

resBss, err := o.updateRefsInResources(nonConfigRs, resolvedImages, imgFactory)
resBss, err := o.updateRefsInResources(nonConfigRs, conf, resolvedImages, imgFactory)
if err != nil {
return err
}
Expand All @@ -89,13 +85,15 @@ func (o *ResolveOptions) Run() error {
return nil
}

func (o *ResolveOptions) resolveImages(
nonConfigRs []ctlres.Resource, imgFactory ctlimg.Factory) (map[string]Image, error) {
func (o *ResolveOptions) resolveImages(nonConfigRs []ctlres.Resource,
conf ctlconf.Conf, imgFactory ctlimg.Factory) (map[string]Image, error) {

foundImages := map[string]struct{}{}

for _, res := range nonConfigRs {
visitValues(res.DeepCopyRaw(), imageKey, func(val interface{}) (interface{}, bool) {
imageKVs := ImageKVs{res.DeepCopyRaw(), conf.ImageKeys()}

imageKVs.Visit(func(val interface{}) (interface{}, bool) {
if imgURL, ok := val.(string); ok {
foundImages[imgURL] = struct{}{}
}
Expand All @@ -114,16 +112,18 @@ func (o *ResolveOptions) resolveImages(
}

func (o *ResolveOptions) updateRefsInResources(nonConfigRs []ctlres.Resource,
resolvedImages map[string]Image, imgFactory ctlimg.Factory) ([][]byte, error) {
conf ctlconf.Conf, resolvedImages map[string]Image,
imgFactory ctlimg.Factory) ([][]byte, error) {

var errs []error
var resBss [][]byte

for _, res := range nonConfigRs {
resContents := res.DeepCopyRaw()
images := []Image{}
imageKVs := ImageKVs{resContents, conf.ImageKeys()}

visitValues(resContents, imageKey, func(val interface{}) (interface{}, bool) {
imageKVs.Visit(func(val interface{}) (interface{}, bool) {
imgURL, ok := val.(string)
if !ok {
return nil, false
Expand Down Expand Up @@ -170,37 +170,6 @@ func errFromErrs(errs []error) error {
return fmt.Errorf("\n- %s", strings.Join(errStrs, "\n- "))
}

func visitValues(obj interface{}, key string, visitorFunc func(interface{}) (interface{}, bool)) {
switch typedObj := obj.(type) {
case map[string]interface{}:
for k, v := range typedObj {
if k == key {
if newVal, update := visitorFunc(v); update {
typedObj[k] = newVal
}
} else {
visitValues(typedObj[k], key, visitorFunc)
}
}

case map[string]string:
for k, v := range typedObj {
if k == key {
if newVal, update := visitorFunc(v); update {
typedObj[k] = newVal.(string)
}
} else {
visitValues(typedObj[k], key, visitorFunc)
}
}

case []interface{}:
for _, o := range typedObj {
visitValues(o, key, visitorFunc)
}
}
}

func (o *ResolveOptions) withImageMapConf(conf ctlconf.Conf) (ctlconf.Conf, error) {
if len(o.ImageMapFile) == 0 {
return conf, nil
Expand Down
11 changes: 7 additions & 4 deletions pkg/kbld/cmd/unpackage.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/ghodss/yaml"
regname "github.com/google/go-containerregistry/pkg/name"
cmdcore "github.com/k14s/kbld/pkg/kbld/cmd/core"
ctlconf "github.com/k14s/kbld/pkg/kbld/config"
ctlimg "github.com/k14s/kbld/pkg/kbld/image"
regtarball "github.com/k14s/kbld/pkg/kbld/imagetarball"
ctlres "github.com/k14s/kbld/pkg/kbld/resources"
Expand Down Expand Up @@ -53,7 +54,7 @@ func (o *UnpackageOptions) Run() error {
logger := ctlimg.NewLogger(os.Stderr)
prefixedLogger := logger.NewPrefixedWriter("unpackage | ")

nonConfigRs, _, err := o.FileFlags.ResourcesAndConfig()
nonConfigRs, conf, err := o.FileFlags.ResourcesAndConfig()
if err != nil {
return err
}
Expand All @@ -65,7 +66,7 @@ func (o *UnpackageOptions) Run() error {
}

// Update previous image references with new references
resBss, err := o.updateRefsInResources(nonConfigRs, importedImages)
resBss, err := o.updateRefsInResources(nonConfigRs, conf, importedImages)
if err != nil {
return err
}
Expand All @@ -80,15 +81,17 @@ func (o *UnpackageOptions) Run() error {
}

func (o *UnpackageOptions) updateRefsInResources(
nonConfigRs []ctlres.Resource, resolvedImages map[string]string) ([][]byte, error) {
nonConfigRs []ctlres.Resource, conf ctlconf.Conf,
resolvedImages map[string]string) ([][]byte, error) {

var missingImageErrs []error
var resBss [][]byte

for _, res := range nonConfigRs {
resContents := res.DeepCopyRaw()
imageKVs := ImageKVs{resContents, conf.ImageKeys()}

visitValues(resContents, imageKey, func(val interface{}) (interface{}, bool) {
imageKVs.Visit(func(val interface{}) (interface{}, bool) {
if img, ok := val.(string); ok {
if outputImg, found := resolvedImages[img]; found {
return outputImg, true
Expand Down
8 changes: 8 additions & 0 deletions pkg/kbld/config/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,11 @@ func (c Conf) ImageDestinations() []ImageDestination {
}
return result
}

func (c Conf) ImageKeys() []string {
result := []string{"image"} // default is just "image"
for _, config := range c.configs {
result = append(result, config.Keys...)
}
return result
}
10 changes: 9 additions & 1 deletion pkg/kbld/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ const (
sourcesKind = "Sources" // specify list of sources for building images
imageOverridesKind = "ImageOverrides" // specify alternative image urls
imageDestinationsKind = "ImageDestinations" // specify image push destinations
imageKeysKind = "ImageKeys"
)

var (
configKinds = []string{sourcesKind, imageOverridesKind, imageDestinationsKind}
configKinds = []string{sourcesKind, imageOverridesKind, imageDestinationsKind, imageKeysKind}
)

type Config struct {
Expand All @@ -25,6 +26,7 @@ type Config struct {
Sources []Source
Overrides []ImageOverride
Destinations []ImageDestination
Keys []string
}

type Source struct {
Expand Down Expand Up @@ -96,6 +98,12 @@ func (d Config) Validate() error {
}
}

for i, key := range d.Keys {
if len(key) == 0 {
return fmt.Errorf("Validating Destinations[%d]: Expected to be non-empty", i)
}
}

return nil
}

Expand Down
37 changes: 37 additions & 0 deletions test/e2e/resolve_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,40 @@ spec:
t.Fatalf("Expected >>>%s<<< to match >>>%s<<<", out, expectedOut)
}
}

func TestResolveWithImageKeys(t *testing.T) {
env := BuildEnv(t)
kbld := Kbld{t, env.Namespace, Logger{}}

input := `
kind: Object
spec:
- image: nginx:1.14.2
- customImage: nginx:1.14.2
- subPath:
anotherCustomImage: nginx:1.14.2
---
apiVersion: kbld.k14s.io/v1alpha1
kind: ImageKeys
keys:
- customImage
- anotherCustomImage
`

out, _ := kbld.RunWithOpts([]string{"-f", "-", "--images-annotation=false"}, RunOpts{
StdinReader: strings.NewReader(input),
})

expectedOut := `---
kind: Object
spec:
- image: index.docker.io/library/nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d
- customImage: index.docker.io/library/nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d
- subPath:
anotherCustomImage: index.docker.io/library/nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d
`

if out != expectedOut {
t.Fatalf("Expected >>>%s<<< to match >>>%s<<<", out, expectedOut)
}
}

0 comments on commit 5ce665a

Please sign in to comment.