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/pkg/kubecfg/diff_test.go b/cluster/tools/kartongips/pkg/kubecfg/diff_test.go
new file mode 100644
index 0000000..cb95123
--- /dev/null
+++ b/cluster/tools/kartongips/pkg/kubecfg/diff_test.go
@@ -0,0 +1,198 @@
+// 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 kubecfg
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestRemoveListFields(t *testing.T) {
+ for _, tc := range []struct {
+ config, live, expected []interface{}
+ }{
+ {
+ config: []interface{}{"a"},
+ live: []interface{}{"a"},
+ expected: []interface{}{"a"},
+ },
+
+ // Check that extra fields in config are not propagated.
+ {
+ config: []interface{}{"a", "b"},
+ live: []interface{}{"a"},
+ expected: []interface{}{"a"},
+ },
+
+ // Check that extra entries in live are propagated.
+ {
+ config: []interface{}{"a"},
+ live: []interface{}{"a", "b"},
+ expected: []interface{}{"a", "b"},
+ },
+ } {
+ require.EqualValues(t, tc.expected, removeListFields(tc.config, tc.live))
+ }
+}
+
+func TestRemoveMapFields(t *testing.T) {
+ for _, tc := range []struct {
+ config, live, expected map[string]interface{}
+ }{
+ {
+ config: map[string]interface{}{"foo": "bar"},
+ live: map[string]interface{}{"foo": "bar"},
+ expected: map[string]interface{}{"foo": "bar"},
+ },
+
+ {
+ config: map[string]interface{}{"foo": "bar", "bar": "baz"},
+ live: map[string]interface{}{"foo": "bar"},
+ expected: map[string]interface{}{"foo": "bar"},
+ },
+
+ {
+ config: map[string]interface{}{"foo": "bar"},
+ live: map[string]interface{}{"foo": "bar", "bar": "baz"},
+ expected: map[string]interface{}{"foo": "bar"},
+ },
+ } {
+ require.Equal(t, tc.expected, removeMapFields(tc.config, tc.live))
+ }
+}
+
+func TestRemoveFields(t *testing.T) {
+ emptyVal := map[string]interface{}{
+ "args": map[string]interface{}{},
+ "volumes": []string{},
+ "stdin": false,
+ }
+ for _, tc := range []struct {
+ config, live, expected interface{}
+ }{
+ // Check we can handle embedded structs.
+ {
+ config: map[string]interface{}{"foo": "bar", "bar": "baz"},
+ live: map[string]interface{}{"foo": "bar"},
+ expected: map[string]interface{}{"foo": "bar"},
+ },
+ // JSON unmarshalling can return int64 for numbers
+ // https://golang.org/pkg/encoding/json/#Number
+ {
+ config: map[string]interface{}{"foo": (int64)(10)},
+ live: map[string]interface{}{},
+ expected: map[string]interface{}{},
+ },
+
+ // Check we can handle embedded lists.
+ {
+ config: []interface{}{"a", "b"},
+ live: []interface{}{"a"},
+ expected: []interface{}{"a"},
+ },
+
+ // Check we can handle arbitrary types.
+ {
+ config: "a",
+ live: "b",
+ expected: "b",
+ },
+ // Check we can handle mismatched types.
+ {
+ config: map[string]interface{}{"foo": "bar"},
+ live: []interface{}{"foo", "bar"},
+ expected: []interface{}{"foo", "bar"},
+ },
+ {
+ config: []interface{}{"foo", "bar"},
+ live: map[string]interface{}{"foo": "bar"},
+ expected: map[string]interface{}{"foo": "bar"},
+ },
+ // Check we handle empty configs by copying them as if were live
+ // (API won't return them)
+ {
+ config: emptyVal,
+ live: map[string]interface{}{},
+ expected: emptyVal,
+ },
+
+ // Check we can handle combinations.
+ {
+ config: map[string]interface{}{
+ "apiVersion": "v1",
+ "kind": "Service",
+ "metadata": map[string]interface{}{
+ "name": "foo",
+ "namespace": "default",
+ },
+ "spec": map[string]interface{}{
+ "selector": map[string]interface{}{
+ "name": "foo",
+ },
+ "ports": []interface{}{
+ map[string]interface{}{
+ "name": "http",
+ "port": 80,
+ },
+ map[string]interface{}{
+ "name": "https",
+ "port": 443,
+ },
+ },
+ },
+ },
+ live: map[string]interface{}{
+ "apiVersion": "v1",
+ "kind": "Service",
+ "metadata": map[string]interface{}{
+ "name": "foo",
+ // NB Namespace missing.
+ },
+ "spec": map[string]interface{}{
+ "selector": map[string]interface{}{
+ "bar": "foo",
+ },
+ "ports": []interface{}{
+ // NB HTTP port missing.
+ map[string]interface{}{
+ "name": "https",
+ "port": 443,
+ },
+ },
+ },
+ },
+ expected: map[string]interface{}{
+ "apiVersion": "v1",
+ "kind": "Service",
+ "metadata": map[string]interface{}{
+ "name": "foo",
+ },
+ "spec": map[string]interface{}{
+ "selector": map[string]interface{}{},
+ "ports": []interface{}{
+ map[string]interface{}{
+ "name": "https",
+ "port": 443,
+ },
+ },
+ },
+ },
+ },
+ } {
+ require.Equal(t, tc.expected, removeFields(tc.config, tc.live))
+ }
+}