cluster/tools/kartongips: init
This forks bitnami/kubecfg into kartongips. The rationale is that we
want to implement hscloud-specific functionality that wouldn't really be
upstreamable into kubecfg (like secret support, mulit-cluster support).
We forked off from github.com/q3k/kubecfg at commit b6817a94492c561ed61a44eeea2d92dcf2e6b8c0.
Change-Id: If5ba513905e0a86f971576fe7061a471c1d8b398
diff --git a/cluster/tools/kartongips/utils/resolver.go b/cluster/tools/kartongips/utils/resolver.go
new file mode 100644
index 0000000..9a9cc84
--- /dev/null
+++ b/cluster/tools/kartongips/utils/resolver.go
@@ -0,0 +1,165 @@
+// Copyright 2017 The kubecfg authors
+//
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package utils
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+
+ "github.com/genuinetools/reg/registry"
+ "github.com/genuinetools/reg/repoutils"
+)
+
+const defaultRegistry = "registry-1.docker.io"
+
+// ImageName represents the parts of a docker image name
+type ImageName struct {
+ // eg: "myregistryhost:5000/fedora/httpd:version1.0"
+ Registry string // "myregistryhost:5000"
+ Repository string // "fedora"
+ Name string // "httpd"
+ Tag string // "version1.0"
+ Digest string
+}
+
+// String implements the Stringer interface
+func (n ImageName) String() string {
+ buf := bytes.Buffer{}
+ if n.Registry != "" {
+ buf.WriteString(n.Registry)
+ buf.WriteString("/")
+ }
+ if n.Repository != "" {
+ buf.WriteString(n.Repository)
+ buf.WriteString("/")
+ }
+ buf.WriteString(n.Name)
+ if n.Digest != "" {
+ buf.WriteString("@")
+ buf.WriteString(n.Digest)
+ } else {
+ buf.WriteString(":")
+ buf.WriteString(n.Tag)
+ }
+ return buf.String()
+}
+
+// RegistryRepoName returns the "repository" as used in the registry URL
+func (n ImageName) RegistryRepoName() string {
+ repo := n.Repository
+ if repo == "" {
+ repo = "library"
+ }
+ return fmt.Sprintf("%s/%s", repo, n.Name)
+}
+
+// RegistryURL returns the deduced base URL of the registry for this image
+func (n ImageName) RegistryURL() string {
+ reg := n.Registry
+ if reg == "" {
+ reg = defaultRegistry
+ }
+ return fmt.Sprintf("https://%s", reg)
+}
+
+// ParseImageName parses a docker image into an ImageName struct.
+func ParseImageName(image string) (ImageName, error) {
+ ret := ImageName{}
+
+ img, err := registry.ParseImage(image)
+ if err != nil {
+ return ret, err
+ }
+
+ ret.Registry = img.Domain
+ ret.Name = img.Path
+ ret.Digest = img.Digest.String()
+ ret.Tag = img.Tag
+
+ return ret, nil
+}
+
+// Resolver is able to resolve docker image names into more specific forms
+type Resolver interface {
+ Resolve(image *ImageName) error
+}
+
+// NewIdentityResolver returns a resolver that does only trivial
+// :latest canonicalisation
+func NewIdentityResolver() Resolver {
+ return identityResolver{}
+}
+
+type identityResolver struct{}
+
+func (r identityResolver) Resolve(image *ImageName) error {
+ return nil
+}
+
+// NewRegistryResolver returns a resolver that looks up a docker
+// registry to resolve digests
+func NewRegistryResolver(opt registry.Opt) Resolver {
+ return ®istryResolver{
+ opt: opt,
+ cache: make(map[string]string),
+ }
+}
+
+type registryResolver struct {
+ opt registry.Opt
+ cache map[string]string
+}
+
+func (r *registryResolver) Resolve(n *ImageName) error {
+ // TODO: get context from caller.
+ ctx := context.Background()
+
+ if n.Digest != "" {
+ // Already has explicit digest
+ return nil
+ }
+
+ if digest, ok := r.cache[n.String()]; ok {
+ n.Digest = digest
+ return nil
+ }
+
+ img, err := registry.ParseImage(n.String())
+ if err != nil {
+ return fmt.Errorf("unable to parse image name: %v", err)
+ }
+
+ auth, err := repoutils.GetAuthConfig("", "", img.Domain)
+ if err != nil {
+ return fmt.Errorf("unable to get auth config for registry: %v", err)
+ }
+
+ c, err := registry.New(ctx, auth, r.opt)
+ if err != nil {
+ return fmt.Errorf("unable to create registry client: %v", err)
+ }
+
+ digest, err := c.Digest(ctx, img)
+ if err != nil {
+ return fmt.Errorf("unable to get digest from the registry: %v", err)
+ }
+
+ n.Digest = digest.String()
+ r.cache[n.String()] = n.Digest
+
+ return nil
+}