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/acquire.go b/cluster/tools/kartongips/utils/acquire.go
new file mode 100644
index 0000000..c979316
--- /dev/null
+++ b/cluster/tools/kartongips/utils/acquire.go
@@ -0,0 +1,226 @@
+// 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 (
+ "bufio"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/url"
+ "os"
+ "path/filepath"
+
+ jsonnet "github.com/google/go-jsonnet"
+ log "github.com/sirupsen/logrus"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/util/yaml"
+)
+
+// Read fetches and decodes K8s objects by path.
+// TODO: Replace this with something supporting more sophisticated
+// content negotiation.
+func Read(vm *jsonnet.VM, path string) ([]runtime.Object, error) {
+ ext := filepath.Ext(path)
+ if ext == ".json" {
+ f, err := os.Open(path)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ return jsonReader(f)
+ } else if ext == ".yaml" {
+ f, err := os.Open(path)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ return yamlReader(f)
+ } else if ext == ".jsonnet" {
+ return jsonnetReader(vm, path)
+ }
+
+ return nil, fmt.Errorf("Unknown file extension: %s", path)
+}
+
+func jsonReader(r io.Reader) ([]runtime.Object, error) {
+ data, err := ioutil.ReadAll(r)
+ if err != nil {
+ return nil, err
+ }
+ obj, _, err := unstructured.UnstructuredJSONScheme.Decode(data, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+ return []runtime.Object{obj}, nil
+}
+
+func yamlReader(r io.ReadCloser) ([]runtime.Object, error) {
+ decoder := yaml.NewYAMLReader(bufio.NewReader(r))
+ ret := []runtime.Object{}
+ for {
+ bytes, err := decoder.Read()
+ if err == io.EOF {
+ break
+ } else if err != nil {
+ return nil, err
+ }
+ if len(bytes) == 0 {
+ continue
+ }
+ jsondata, err := yaml.ToJSON(bytes)
+ if err != nil {
+ return nil, err
+ }
+ obj, _, err := unstructured.UnstructuredJSONScheme.Decode(jsondata, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+ ret = append(ret, obj)
+ }
+ return ret, nil
+}
+
+type walkContext struct {
+ parent *walkContext
+ label string
+}
+
+func (c *walkContext) String() string {
+ parent := ""
+ if c.parent != nil {
+ parent = c.parent.String()
+ }
+ return parent + c.label
+}
+
+func jsonWalk(parentCtx *walkContext, obj interface{}) ([]interface{}, error) {
+ switch o := obj.(type) {
+ case nil:
+ return []interface{}{}, nil
+ case map[string]interface{}:
+ if o["kind"] != nil && o["apiVersion"] != nil {
+ return []interface{}{o}, nil
+ }
+ ret := []interface{}{}
+ for k, v := range o {
+ ctx := walkContext{
+ parent: parentCtx,
+ label: "." + k,
+ }
+ children, err := jsonWalk(&ctx, v)
+ if err != nil {
+ return nil, err
+ }
+ ret = append(ret, children...)
+ }
+ return ret, nil
+ case []interface{}:
+ ret := make([]interface{}, 0, len(o))
+ for i, v := range o {
+ ctx := walkContext{
+ parent: parentCtx,
+ label: fmt.Sprintf("[%d]", i),
+ }
+ children, err := jsonWalk(&ctx, v)
+ if err != nil {
+ return nil, err
+ }
+ ret = append(ret, children...)
+ }
+ return ret, nil
+ default:
+ return nil, fmt.Errorf("Looking for kubernetes object at %s, but instead found %T", parentCtx, o)
+ }
+}
+
+func jsonnetReader(vm *jsonnet.VM, path string) ([]runtime.Object, error) {
+ // TODO: Read via Importer, so we support HTTP, etc for first
+ // file too.
+ abs, err := filepath.Abs(path)
+ if err != nil {
+ return nil, err
+ }
+ pathUrl := &url.URL{Scheme: "file", Path: filepath.ToSlash(abs)}
+
+ bytes, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, err
+ }
+
+ jsonstr, err := vm.EvaluateSnippet(pathUrl.String(), string(bytes))
+ if err != nil {
+ return nil, err
+ }
+
+ log.Debugf("jsonnet result is: %s", jsonstr)
+
+ var top interface{}
+ if err = json.Unmarshal([]byte(jsonstr), &top); err != nil {
+ return nil, err
+ }
+
+ objs, err := jsonWalk(&walkContext{label: "<top>"}, top)
+ if err != nil {
+ return nil, err
+ }
+
+ ret := make([]runtime.Object, 0, len(objs))
+ for _, v := range objs {
+ obj := &unstructured.Unstructured{Object: v.(map[string]interface{})}
+ if obj.IsList() {
+ // TODO: Use obj.ToList with newer apimachinery
+ list := &unstructured.UnstructuredList{
+ Object: obj.Object,
+ }
+ err := obj.EachListItem(func(item runtime.Object) error {
+ castItem := item.(*unstructured.Unstructured)
+ list.Items = append(list.Items, *castItem)
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
+ ret = append(ret, list)
+ } else {
+ ret = append(ret, obj)
+ }
+ }
+
+ return ret, nil
+}
+
+// FlattenToV1 expands any List-type objects into their members, and
+// cooerces everything to v1.Unstructured. Panics if coercion
+// encounters an unexpected object type.
+func FlattenToV1(objs []runtime.Object) []*unstructured.Unstructured {
+ ret := make([]*unstructured.Unstructured, 0, len(objs))
+ for _, obj := range objs {
+ switch o := obj.(type) {
+ case *unstructured.UnstructuredList:
+ for i := range o.Items {
+ ret = append(ret, &o.Items[i])
+ }
+ case *unstructured.Unstructured:
+ ret = append(ret, o)
+ default:
+ panic("Unexpected unstructured object type")
+ }
+ }
+ return ret
+}