From 89e7b76d48f4bebc9a4083a6de346e6778b205e3 Mon Sep 17 00:00:00 2001 From: jregan Date: Thu, 28 Nov 2019 07:23:37 -0800 Subject: [PATCH] Start making examples more visible. --- .../baseandoverlaysmall_test.go | 71 +++----- api/krusty/doc.go | 9 +- api/krusty/kustomizer.go | 2 +- api/krusty/testingharness_test.go | 168 ++++++++++++++++++ 4 files changed, 202 insertions(+), 48 deletions(-) rename api/{internal/target => krusty}/baseandoverlaysmall_test.go (81%) create mode 100644 api/krusty/testingharness_test.go diff --git a/api/internal/target/baseandoverlaysmall_test.go b/api/krusty/baseandoverlaysmall_test.go similarity index 81% rename from api/internal/target/baseandoverlaysmall_test.go rename to api/krusty/baseandoverlaysmall_test.go index 68bd498d9..4e8c45456 100644 --- a/api/internal/target/baseandoverlaysmall_test.go +++ b/api/krusty/baseandoverlaysmall_test.go @@ -1,19 +1,18 @@ // Copyright 2019 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 -package target_test +package krusty_test import ( "strings" "testing" - "sigs.k8s.io/kustomize/api/konfig" - "sigs.k8s.io/kustomize/api/loader" - kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" + . "sigs.k8s.io/kustomize/api/krusty" + "sigs.k8s.io/kustomize/api/types" ) func TestOrderPreserved(t *testing.T) { - th := kusttest_test.NewKustTestHarness(t, "/app/prod") + th := makeTestHarness(t) th.WriteK("/app/base", ` namePrefix: b- resources: @@ -65,11 +64,7 @@ kind: Namespace metadata: name: myNs2 `) - - m, err := th.MakeKustTarget().MakeCustomizedResMap() - if err != nil { - t.Fatalf("Err: %v", err) - } + m := th.Run("/app/prod", th.MakeDefaultOptions()) th.AssertActualEqualsExpected(m, ` apiVersion: v1 kind: Namespace @@ -104,7 +99,7 @@ metadata: } func TestBaseInResourceList(t *testing.T) { - th := kusttest_test.NewKustTestHarness(t, "/app/prod") + th := makeTestHarness(t) th.WriteK("/app/prod", ` namePrefix: b- resources: @@ -124,10 +119,7 @@ spec: selector: backend: bungie `) - m, err := th.MakeKustTarget().MakeCustomizedResMap() - if err != nil { - t.Fatalf("Err: %v", err) - } + m := th.Run("/app/prod", th.MakeDefaultOptions()) th.AssertActualEqualsExpected(m, ` apiVersion: v1 kind: Service @@ -139,7 +131,7 @@ spec: `) } -func writeSmallBase(th *kusttest_test.KustTestHarness) { +func writeSmallBase(th testingHarness) { th.WriteK("/app/base", ` namePrefix: a- commonLabels: @@ -177,12 +169,9 @@ spec: } func TestSmallBase(t *testing.T) { - th := kusttest_test.NewKustTestHarness(t, "/app/base") + th := makeTestHarness(t) writeSmallBase(th) - m, err := th.MakeKustTarget().MakeCustomizedResMap() - if err != nil { - t.Fatalf("Err: %v", err) - } + m := th.Run("/app/base", th.MakeDefaultOptions()) th.AssertActualEqualsExpected(m, ` apiVersion: apps/v1 kind: Deployment @@ -220,7 +209,7 @@ spec: } func TestSmallOverlay(t *testing.T) { - th := kusttest_test.NewKustTestHarness(t, "/app/overlay") + th := makeTestHarness(t) writeSmallBase(th) th.WriteK("/app/overlay", ` namePrefix: b- @@ -251,10 +240,7 @@ metadata: spec: replicas: 1000 `) - m, err := th.MakeKustTarget().MakeCustomizedResMap() - if err != nil { - t.Fatalf("Err: %v", err) - } + m := th.Run("/app/overlay", th.MakeDefaultOptions()) th.AssertActualEqualsExpected(m, ` apiVersion: apps/v1 kind: Deployment @@ -298,9 +284,7 @@ spec: } func TestSharedPatchDisAllowed(t *testing.T) { - th := kusttest_test.NewKustTestHarnessFull( - t, "/app/overlay", - loader.RestrictionRootOnly, konfig.DisabledPluginConfig()) + th := makeTestHarness(t) writeSmallBase(th) th.WriteK("/app/overlay", ` commonLabels: @@ -318,10 +302,11 @@ metadata: spec: replicas: 1000 `) - _, err := th.MakeKustTarget().MakeCustomizedResMap() - if err == nil { - t.Fatalf("expected error") - } + err := th.RunWithErr("/app/overlay", func() Options { + o := th.MakeDefaultOptions() + o.LoadRestrictions = types.LoadRestrictionsRootOnly + return o + }()) if !strings.Contains( err.Error(), "security; file '/app/shared/deployment-patch.yaml' is not in or below '/app/overlay'") { @@ -330,9 +315,7 @@ spec: } func TestSharedPatchAllowed(t *testing.T) { - th := kusttest_test.NewKustTestHarnessFull( - t, "/app/overlay", - loader.RestrictionNone, konfig.DisabledPluginConfig()) + th := makeTestHarness(t) writeSmallBase(th) th.WriteK("/app/overlay", ` commonLabels: @@ -350,10 +333,11 @@ metadata: spec: replicas: 1000 `) - m, err := th.MakeKustTarget().MakeCustomizedResMap() - if err != nil { - t.Fatalf("Err: %v", err) - } + m := th.Run("/app/overlay", func() Options { + o := th.MakeDefaultOptions() + o.LoadRestrictions = types.LoadRestrictionsNone + return o + }()) th.AssertActualEqualsExpected(m, ` apiVersion: apps/v1 kind: Deployment @@ -397,7 +381,7 @@ spec: } func TestSmallOverlayJSONPatch(t *testing.T) { - th := kusttest_test.NewKustTestHarness(t, "/app/overlay") + th := makeTestHarness(t) writeSmallBase(th) th.WriteK("/app/overlay", ` resources: @@ -415,10 +399,7 @@ patchesJson6902: path: /spec/selector/backend value: beagle `) - m, err := th.MakeKustTarget().MakeCustomizedResMap() - if err != nil { - t.Fatalf("Err: %v", err) - } + m := th.Run("/app/overlay", th.MakeDefaultOptions()) th.AssertActualEqualsExpected(m, ` apiVersion: apps/v1 kind: Deployment diff --git a/api/krusty/doc.go b/api/krusty/doc.go index c0198674e..bf516ca94 100644 --- a/api/krusty/doc.go +++ b/api/krusty/doc.go @@ -1,6 +1,11 @@ // Copyright 2019 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 -// Package krusty holds a very high level API to kustomize. -// The functions here should be similar to the CLI api. +// Package krusty is intended as the entry point package +// for those seeking to add kustomize ability to other +// programs. +// +// To use, follow the example of the kustomize CLI's 'build' +// command. Also, see the high level tests in this package, +// which serve a dual purpose as examples. package krusty diff --git a/api/krusty/kustomizer.go b/api/krusty/kustomizer.go index 61a43f9d5..9312482e0 100644 --- a/api/krusty/kustomizer.go +++ b/api/krusty/kustomizer.go @@ -22,7 +22,7 @@ import ( // performing an exec to a kustomize CLI subprocess. // To use, load a filesystem with kustomization files (any // number of overlays and bases), then make a Kustomizer -// injected with the given fileystem, then call Build. +// injected with the given fileystem, then call Run. type Kustomizer struct { fSys filesys.FileSystem options *Options diff --git a/api/krusty/testingharness_test.go b/api/krusty/testingharness_test.go new file mode 100644 index 000000000..424005e31 --- /dev/null +++ b/api/krusty/testingharness_test.go @@ -0,0 +1,168 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "fmt" + "path/filepath" + "strings" + "testing" + + "sigs.k8s.io/kustomize/api/filesys" + "sigs.k8s.io/kustomize/api/konfig" + . "sigs.k8s.io/kustomize/api/krusty" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" +) + +type testingHarness struct { + t *testing.T + fSys filesys.FileSystem +} + +func makeTestHarness(t *testing.T) testingHarness { + return testingHarness{ + t: t, + fSys: filesys.MakeFsInMemory(), + } +} + +func (th testingHarness) WriteK(path string, content string) { + th.fSys.WriteFile( + filepath.Join( + path, + konfig.DefaultKustomizationFileName()), []byte(` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +`+content)) +} + +func (th testingHarness) WriteF(path string, content string) { + th.fSys.WriteFile(path, []byte(content)) +} + +func (th testingHarness) MakeDefaultOptions() Options { + return Options{ + LoadRestrictions: types.LoadRestrictionsRootOnly, + PluginConfig: konfig.DisabledPluginConfig(), + } +} + +func (th testingHarness) MakeEnabledPluginConfig() *types.PluginConfig { + // TODO: this doesn't work yet - need to set an env var. + // TODO: steal from kusttest_test.NewPluginTestEnv + c, err := konfig.EnabledPluginConfig() + if err != nil { + th.t.Fatal(err) + } + return c +} + +// Run, failing on error. +func (th testingHarness) Run(path string, o Options) resmap.ResMap { + m, err := MakeKustomizer(th.fSys, &o).Run(path) + if err != nil { + th.t.Fatal(err) + } + return m +} + +// Run, failing if there is no error. +func (th testingHarness) RunWithErr(path string, o Options) error { + _, err := MakeKustomizer(th.fSys, &o).Run(path) + if err == nil { + th.t.Fatalf("expected error") + } + return err +} + +func (th testingHarness) AssertActualEqualsExpected( + m resmap.ResMap, expected string) { + th.AssertActualEqualsExpectedWithTweak(m, nil, expected) +} + +func (th testingHarness) AssertActualEqualsExpectedWithTweak( + m resmap.ResMap, tweaker func([]byte) []byte, expected string) { + if m == nil { + th.t.Fatalf("Map should not be nil.") + } + // Ignore leading linefeed in expected value + // to ease readability of tests. + if len(expected) > 0 && expected[0] == 10 { + expected = expected[1:] + } + actual, err := m.AsYaml() + if err != nil { + th.t.Fatalf("Unexpected err: %v", err) + } + if tweaker != nil { + actual = tweaker(actual) + } + if string(actual) != expected { + th.reportDiffAndFail(actual, expected) + } +} + +// Pretty printing of file differences. +func (th testingHarness) reportDiffAndFail(actual []byte, expected string) { + sE, maxLen := convertToArray(expected) + sA, _ := convertToArray(string(actual)) + fmt.Println("===== ACTUAL BEGIN ========================================") + fmt.Print(string(actual)) + fmt.Println("===== ACTUAL END ==========================================") + format := fmt.Sprintf("%%s %%-%ds %%s\n", maxLen+4) + limit := 0 + if len(sE) < len(sA) { + limit = len(sE) + } else { + limit = len(sA) + } + fmt.Printf(format, " ", "EXPECTED", "ACTUAL") + fmt.Printf(format, " ", "--------", "------") + for i := 0; i < limit; i++ { + fmt.Printf(format, hint(sE[i], sA[i]), sE[i], sA[i]) + } + if len(sE) < len(sA) { + for i := len(sE); i < len(sA); i++ { + fmt.Printf(format, "X", "", sA[i]) + } + } else { + for i := len(sA); i < len(sE); i++ { + fmt.Printf(format, "X", sE[i], "") + } + } + th.t.Fatalf("Expected not equal to actual") +} + +func convertToArray(x string) ([]string, int) { + a := strings.Split(strings.TrimSuffix(x, "\n"), "\n") + maxLen := 0 + for i, v := range a { + z := tabToSpace(v) + if len(z) > maxLen { + maxLen = len(z) + } + a[i] = z + } + return a, maxLen +} + +func hint(a, b string) string { + if a == b { + return " " + } + return "X" +} + +func tabToSpace(input string) string { + var result []string + for _, i := range input { + if i == 9 { + result = append(result, " ") + } else { + result = append(result, string(i)) + } + } + return strings.Join(result, "") +}