func interpretMappedContainerImage(values map[string]interface{}, cim ContainerImageMap) (image.Ref, ImageSetter, bool) {
	v, err := gabs.Consume(values)
	if err != nil {
		return image.Ref{}, nil, false
	}
	imageValue := v.Path(cim.Repository).Data()
	if img, ok := imageValue.(string); ok {
		switch {
		case cim.RepositoryOnly():
			if imgRef, err := image.ParseRef(img); err == nil {
				return imgRef, func(ref image.Ref) {
					v.SetP(ref.String(), cim.Repository)
				}, true
			}
		case cim.AllDefined():
			registryValue := v.Path(cim.Registry).Data()
			if reg, ok := registryValue.(string); ok {
				tagValue := v.Path(cim.Tag).Data()
				if tag, ok := tagValue.(string); ok {
					if imgRef, err := image.ParseRef(reg + "/" + img + ":" + tag); err == nil {
						return imgRef, func(ref image.Ref) {
							v.SetP(ref.Domain, cim.Registry)
							v.SetP(ref.Image, cim.Repository)
							v.SetP(ref.Tag, cim.Tag)
						}, true
					}
				}
			}
		case cim.RegistryRepository():
			registryValue := v.Path(cim.Registry).Data()
			if reg, ok := registryValue.(string); ok {
				if imgRef, err := image.ParseRef(reg + "/" + img); err == nil {
					return imgRef, func(ref image.Ref) {
						v.SetP(ref.Domain, cim.Registry)
						v.SetP(ref.Name.Image+":"+ref.Tag, cim.Repository)
					}, true
				}
			}
		case cim.RepositoryTag():
			tagValue := v.Path(cim.Tag).Data()
			if tag, ok := tagValue.(string); ok {
				if imgRef, err := image.ParseRef(img + ":" + tag); err == nil {
					return imgRef, func(ref image.Ref) {
						v.SetP(ref.Name.String(), cim.Repository)
						v.SetP(ref.Tag, cim.Tag)
					}, true
				}
			}
		}
	}
	return image.Ref{}, nil, false
}
interpretMappedContainerImage attempt to resolve the paths in the
`ContainerImageMap` from the given values and tries to parse the
resolved values into a valid `image.Ref`. It returns the
`image.Ref`, an `ImageSetter` that is able to modify the image in
the supplied values map, and a boolean that reflects if the
interpretation was successful.
package resource
import (
	"fmt"
	"github.com/Jeffail/gabs"
	"sort"
	"strings"
	"github.com/fluxcd/flux/pkg/image"
	"github.com/fluxcd/flux/pkg/resource"
)
const (
	ReleaseContainerName = "chart-image"
	ImageBasePath = "spec.values."
	ImageRegistryPrefix = "registry.fluxcd.io/"
	ImageRepositoryPrefix = "repository.fluxcd.io/"
	ImageTagPrefix = "tag.fluxcd.io/"
)
type ContainerImageMap struct {
	BasePath   string
	Registry   string
	Repository string
	Tag        string
}
func (c ContainerImageMap) RepositoryOnly() bool {
	return c.Repository != "" && c.Registry == "" && c.Tag == ""
}
func (c ContainerImageMap) RepositoryTag() bool {
	return c.Repository != "" && c.Tag != "" && c.Registry == ""
}
func (c ContainerImageMap) RegistryRepository() bool {
	return c.Registry != "" && c.Repository != "" && c.Tag == ""
}
func (c ContainerImageMap) AllDefined() bool {
	return c.Registry != "" && c.Repository != "" && c.Tag != ""
}
func (c ContainerImageMap) GetRegistry() string {
	if c.Registry == "" {
		return c.Registry
	}
	return fmt.Sprintf("%s%s", c.BasePath, c.Registry)
}
func (c ContainerImageMap) GetRepository() string {
	if c.Repository == "" {
		return c.Repository
	}
	return fmt.Sprintf("%s%s", c.BasePath, c.Repository)
}
func (c ContainerImageMap) GetTag() string {
	if c.Tag == "" {
		return c.Tag
	}
	return fmt.Sprintf("%s%s", c.BasePath, c.Tag)
}
func (c ContainerImageMap) MapImageRef(image image.Ref) (map[string]string, bool) {
	m := make(map[string]string)
	switch {
	case c.AllDefined():
		m[c.GetRegistry()] = image.Domain
		m[c.GetRepository()] = image.Image
		m[c.GetTag()] = image.Tag
	case c.RegistryRepository():
		m[c.GetRegistry()] = image.Domain
		m[c.GetRepository()] = image.Image + ":" + image.Tag
	case c.RepositoryTag():
		m[c.GetRepository()] = image.Name.String()
		m[c.GetTag()] = image.Tag
	case c.RepositoryOnly():
		m[c.GetRepository()] = image.String()
	default:
		return m, false
	}
	return m, true
}
type HelmRelease struct {
	baseObject
	Spec struct {
		Values map[string]interface{}
	}
}
type ImageSetter func(image.Ref)
type imageAndSetter struct {
	image  image.Ref
	setter ImageSetter
}
func sorted_containers(containers map[string]imageAndSetter) []string {
	var keys []string
	for k := range containers {
		keys = append(keys, k)
	}
	sort.Slice(keys, func(i, j int) bool {
		if keys[i] == ReleaseContainerName {
			return true
		}
		if keys[j] == ReleaseContainerName {
			return false
		}
		return keys[i] < keys[j]
	})
	return keys
}
func FindHelmReleaseContainers(annotations map[string]string, values map[string]interface{},
	visit func(string, image.Ref, ImageSetter) error) {
	containers := make(map[string]imageAndSetter)
	if image, setter, ok := interpretAsContainer(stringMap(values)); ok {
		containers[ReleaseContainerName] = imageAndSetter{image, setter}
	}
	for k, v := range values {
		if image, setter, ok := interpret(v); ok {
			containers[k] = imageAndSetter{image, setter}
		}
	}
	for k, v := range containerImageMappingsFromAnnotations(annotations) {
		if image, setter, ok := interpretMappedContainerImage(values, v); ok {
			containers[k] = imageAndSetter{image, setter}
		}
	}
	for _, k := range sorted_containers(containers) {
		visit(k, containers[k].image, containers[k].setter)
	}
}
type mapper interface {
	get(string) (interface{}, bool)
	set(string, interface{})
}
type stringMap map[string]interface{}
type anyMap map[interface{}]interface{}
func (m stringMap) get(k string) (interface{}, bool) { v, ok := m[k]; return v, ok }
func (m stringMap) set(k string, v interface{})      { m[k] = v }
func (m anyMap) get(k string) (interface{}, bool) { v, ok := m[k]; return v, ok }
func (m anyMap) set(k string, v interface{})      { m[k] = v }
func interpret(values interface{}) (image.Ref, ImageSetter, bool) {
	switch m := values.(type) {
	case map[string]interface{}:
		return interpretAsContainer(stringMap(m))
	case map[interface{}]interface{}:
		return interpretAsContainer(anyMap(m))
	}
	return image.Ref{}, nil, false
}
func interpretAsContainer(m mapper) (image.Ref, ImageSetter, bool) {
	imageValue, ok := m.get("image")
	if !ok {
		return image.Ref{}, nil, false
	}
	switch img := imageValue.(type) {
	case string:
		fullImgStr := img
		var reggy bool
		if registry, ok := m.get("registry"); ok {
			if registryStr, ok := registry.(string); ok {
				reggy = true
				fullImgStr = registryStr + "/" + fullImgStr
			}
		}
		var taggy bool
		if tag, ok := m.get("tag"); ok {
			if tagStr, ok := tag.(string); ok {
				taggy = true
				fullImgStr = fullImgStr + ":" + tagStr
			}
		}
		imageRef, err := image.ParseRef(fullImgStr)
		if err != nil {
			return image.Ref{}, nil, false
		}
		return imageRef, func(ref image.Ref) {
			switch {
			case reggy && taggy:
				m.set("registry", ref.Domain)
				m.set("image", ref.Image)
				m.set("tag", ref.Tag)
				return
			case reggy:
				m.set("registry", ref.Domain)
				m.set("image", ref.Name.Image+":"+ref.Tag)
			case taggy:
				m.set("image", ref.Name.String())
				m.set("tag", ref.Tag)
			default:
				m.set("image", ref.String())
			}
		}, true
	case map[string]interface{}:
		return interpretAsImage(stringMap(img))
	case map[interface{}]interface{}:
		return interpretAsImage(anyMap(img))
	}
	return image.Ref{}, nil, false
}
func interpretAsImage(m mapper) (image.Ref, ImageSetter, bool) {
	var imgRepo interface{}
	var ok bool
	if imgRepo, ok = m.get("repository"); !ok {
		return image.Ref{}, nil, false
	}
	if imgStr, ok := imgRepo.(string); ok {
		fullImgStr := imgStr
		var reggy bool
		if registry, ok := m.get("registry"); ok {
			if registryStr, ok := registry.(string); ok {
				reggy = ok
				fullImgStr = registryStr + "/" + fullImgStr
			}
		}
		var taggy bool
		if tag, ok := m.get("tag"); ok {
			if tagStr, ok := tag.(string); ok {
				taggy = ok
				fullImgStr = fullImgStr + ":" + tagStr
			}
		}
		imageRef, err := image.ParseRef(fullImgStr)
		if err != nil {
			return image.Ref{}, nil, false
		}
		return imageRef, func(ref image.Ref) {
			switch {
			case reggy && taggy:
				m.set("registry", ref.Domain)
				m.set("repository", ref.Image)
				m.set("tag", ref.Tag)
				return
			case reggy:
				m.set("registry", ref.Domain)
				m.set("repository", ref.Name.Image+":"+ref.Tag)
			case taggy:
				m.set("repository", ref.Name.String())
				m.set("tag", ref.Tag)
			default:
				m.set("repository", ref.String())
			}
		}, true
	}
	return image.Ref{}, nil, false
}
func containerImageMappingsFromAnnotations(annotations map[string]string) map[string]ContainerImageMap {
	cim := make(map[string]ContainerImageMap)
	for k, v := range annotations {
		switch {
		case strings.HasPrefix(k, ImageRegistryPrefix):
			container := strings.TrimPrefix(k, ImageRegistryPrefix)
			i, _ := cim[container]
			i.Registry = v
			cim[container] = i
		case strings.HasPrefix(k, ImageRepositoryPrefix):
			container := strings.TrimPrefix(k, ImageRepositoryPrefix)
			i, _ := cim[container]
			i.Repository = v
			cim[container] = i
		case strings.HasPrefix(k, ImageTagPrefix):
			container := strings.TrimPrefix(k, ImageTagPrefix)
			i, _ := cim[container]
			i.Tag = v
			cim[container] = i
		}
	}
	for k, _ := range cim {
		i, _ := cim[k]
		i.BasePath = ImageBasePath
		cim[k] = i
	}
	return cim
}