Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: add test for helm template rendering (#1127)
* test: add test for helm template rendering * add test for tolerations, affinity and nodeselector * unittest tag * remove comments * fix comments
- Loading branch information
1 parent
16937d9
commit f2d1ef7
Showing
10 changed files
with
462 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// ------------------------------------------------------------------------------------------- | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. See License.txt in the project root for license information. | ||
// -------------------------------------------------------------------------------------------- | ||
|
||
// +build unittest | ||
|
||
package tests | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
"testing" | ||
) | ||
|
||
const valuesDir = "fixtures" | ||
|
||
// TestChart is a simple snapshot-style regression test. | ||
// | ||
// This approach proves that the chart can be rendered successfully given various inputs, and that | ||
// the resulting manifests haven't changed unexpectedly since a known good state. | ||
// | ||
// When making a change to the chart, the test snapshots can be generated by running this test case | ||
// with RENDER_SNAPSHOTS=true. Then, `git diff` the new snapshots to see if the changes are expected. | ||
func TestChart(t *testing.T) { | ||
snapshots := []string{} | ||
err := filepath.Walk(valuesDir, func(path string, f os.FileInfo, err error) error { | ||
if f != nil && !f.IsDir() { | ||
snapshots = append(snapshots, path) | ||
} | ||
return nil | ||
}) | ||
|
||
if err != nil { | ||
t.Fatalf("unable to list snapshots: %v", err) | ||
} | ||
|
||
for _, snapshot := range snapshots { | ||
snapshotName, _ := filepath.Rel(valuesDir, snapshot) | ||
name := strings.TrimRight(snapshotName, ".json") | ||
|
||
t.Run(name, func(t *testing.T) { | ||
snapshotDir := fmt.Sprintf("snapshots/%s", name) | ||
|
||
if os.Getenv("RENDER_SNAPSHOTS") != "" { | ||
err := RenderChart("..", snapshot, snapshotDir) | ||
if err != nil { | ||
t.Fatalf("unable to render chart: %v", err) | ||
} | ||
|
||
return | ||
} | ||
|
||
actual, err := CaptureSnapshot("..", snapshot) | ||
if err != nil { | ||
t.Fatalf("unable to capture snapshot: %v", err) | ||
} | ||
|
||
expected, err := LoadSnapshot(snapshotDir) | ||
if err != nil { | ||
t.Fatalf("unable to load snapshot: %v", err) | ||
} | ||
|
||
actual.Diff(t, expected) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
{ | ||
"verbosityLevel": 3, | ||
"appgw": { | ||
"subscriptionId": "0000-0000-0000-0000-00000000", | ||
"resourceGroup": "resgp", | ||
"name": "gateway", | ||
"usePrivateIP": false, | ||
"shared": false | ||
}, | ||
"armAuth": { | ||
"type": "aadPodIdentity", | ||
"identityResourceID": "/a/b/c", | ||
"identityClientID": "0000-0000-0000-0000-00000000" | ||
}, | ||
"rbac": { | ||
"enabled": false | ||
}, | ||
"kubernetes": { | ||
"watchNamespace": "a,b,c", | ||
"nodeSelector": { | ||
"beta.kubernetes.io/os": "linux" | ||
}, | ||
"podAnnotations": { | ||
"custom-annotation": "custom-value" | ||
}, | ||
"tolerations": [ | ||
{ | ||
"key": "CriticalAppsOnly", | ||
"operator": "Exists" | ||
} | ||
], | ||
"affinity": { | ||
"nodeAffinity": { | ||
"preferredDuringSchedulingIgnoredDuringExecution": [ | ||
{ | ||
"weight": 100, | ||
"preference": { | ||
"matchExpressions": [ | ||
{ | ||
"key": "kubernetes.cloud.com/mode", | ||
"operator": "In", | ||
"values": [ | ||
"system" | ||
] | ||
} | ||
] | ||
} | ||
} | ||
], | ||
"requiredDuringSchedulingIgnoredDuringExecution": { | ||
"nodeSelectorTerms": [ | ||
{ | ||
"labelSelector": null, | ||
"matchExpressions": [ | ||
{ | ||
"key": "kubernetes.cloud.com/cluster", | ||
"operator": "Exists" | ||
} | ||
] | ||
} | ||
] | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
// ------------------------------------------------------------------------------------------- | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. See License.txt in the project root for license information. | ||
// -------------------------------------------------------------------------------------------- | ||
|
||
package tests | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/kylelemons/godebug/pretty" | ||
"gopkg.in/yaml.v2" | ||
) | ||
|
||
// Snapshot represents a rendered Helm chart. | ||
type Snapshot struct { | ||
files map[string][]map[interface{}]interface{} | ||
} | ||
|
||
// Diff takes an expected snapshot, and compares it to itself. | ||
func (s *Snapshot) Diff(t *testing.T, expected *Snapshot) { | ||
for file, parsedFile := range s.files { | ||
parsedExpFile, ok := expected.files[file] | ||
if !ok { | ||
t.Errorf("missing manifest: %s", file) | ||
continue | ||
} | ||
|
||
diff := pretty.Compare(parsedFile, parsedExpFile) | ||
if len(diff) == 0 { | ||
continue | ||
} | ||
|
||
t.Errorf("file %q doesn't match expectation\n%s", file, diff) | ||
} | ||
|
||
for expFile := range expected.files { | ||
if _, ok := s.files[expFile]; !ok { | ||
t.Errorf("expected snapshot has file missing in actual: %s", expFile) | ||
} | ||
} | ||
} | ||
|
||
// RenderChart renders a Helm chart to a given directory. | ||
func RenderChart(chart, values, dir string) error { | ||
err := os.MkdirAll(dir, 0755) | ||
if err != nil { | ||
return fmt.Errorf("unable to create output dir %q: %v", dir, err) | ||
} | ||
|
||
helmCmdFile := os.Getenv("HELM_CMD") | ||
if helmCmdFile == "" { | ||
helmCmdFile = "helm" | ||
} | ||
out, err := exec.Command(helmCmdFile, "template", "..", "--output-dir", dir, "--values", values).CombinedOutput() | ||
if err != nil { | ||
return &HelmError{ | ||
RawOutput: out, | ||
CommandError: err, | ||
} | ||
} | ||
|
||
return StripNonDeterministic(dir) | ||
} | ||
|
||
// CaptureSnapshot renders a new snapshot from a given Helm chart and values file. | ||
func CaptureSnapshot(chart, values string) (*Snapshot, error) { | ||
dir, err := ioutil.TempDir("", "") | ||
if err != nil { | ||
return nil, fmt.Errorf("creating tempdir: %v", err) | ||
} | ||
|
||
defer func() { | ||
err := os.RemoveAll(dir) | ||
if err != nil { | ||
panic(fmt.Errorf("unable to clean up tempdir for chart %q with values %q: %v", chart, values, err)) | ||
} | ||
}() | ||
|
||
if err := RenderChart(chart, values, dir); err != nil { | ||
return nil, err | ||
} | ||
|
||
return LoadSnapshot(dir) | ||
} | ||
|
||
// LoadSnapshot loads a snapshot from disk. Expects the directory to be a chart rendered with | ||
// `helm template --output-dir`. | ||
func LoadSnapshot(dir string) (*Snapshot, error) { | ||
s := &Snapshot{ | ||
files: map[string][]map[interface{}]interface{}{}, | ||
} | ||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { | ||
if err != nil { | ||
return err | ||
} | ||
if info.IsDir() { | ||
return nil | ||
} | ||
|
||
file, err := os.Open(path) | ||
if err != nil { | ||
return fmt.Errorf("opening file %q: %v", path, err) | ||
} | ||
defer file.Close() | ||
shortPath := strings.TrimPrefix(path, dir+"/") | ||
|
||
dec := yaml.NewDecoder(file) | ||
for { | ||
obj := map[interface{}]interface{}{} | ||
err = dec.Decode(&obj) | ||
if err == io.EOF { | ||
break | ||
} | ||
if err != nil { | ||
return fmt.Errorf("parsing file %q: %v", shortPath, err) | ||
} | ||
s.files[shortPath] = append(s.files[shortPath], obj) | ||
} | ||
|
||
return nil | ||
}) | ||
if err != nil { | ||
return nil, fmt.Errorf("walking rendered chart: %v", err) | ||
} | ||
|
||
return s, nil | ||
} | ||
|
||
// StripNonDeterministic removes properties from all of the manifests in a given directory. | ||
func StripNonDeterministic(path string) error { | ||
return filepath.Walk(path, | ||
func(path string, info os.FileInfo, err error) error { | ||
if err != nil { | ||
return err | ||
} | ||
if info.IsDir() || !strings.HasSuffix(info.Name(), ".yaml") { | ||
return nil | ||
} | ||
|
||
file, err := os.Open(path) | ||
if err != nil { | ||
return err | ||
} | ||
defer file.Close() | ||
|
||
lines := []string{} | ||
scanner := bufio.NewScanner(file) | ||
for scanner.Scan() { | ||
text := scanner.Text() | ||
if strings.Contains(text, "checksum/") || | ||
strings.Contains(text, "aks.microsoft.com/release-time") { | ||
continue | ||
} | ||
lines = append(lines, text) | ||
} | ||
|
||
return ioutil.WriteFile(path, []byte(strings.Join(lines, "\n")), 0644) | ||
}) | ||
} | ||
|
||
// HelmError represents an error returned by the Helm CLI. | ||
type HelmError struct { | ||
RawOutput []byte | ||
CommandError error | ||
} | ||
|
||
// Error satisfies the error interface. | ||
func (h *HelmError) Error() string { | ||
return fmt.Sprintf("Helm CLI error: %q - raw output:\n%s", h.CommandError.Error(), h.RawOutput) | ||
} |
10 changes: 10 additions & 0 deletions
10
.../ingress-azure/tests/snapshots/sample-config/ingress-azure/templates/aadpodidbinding.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
--- | ||
# Source: ingress-azure/templates/aadpodidbinding.yaml | ||
# Please see https://github.com/Azure/aad-pod-identity for more inromation | ||
apiVersion: "aadpodidentity.k8s.io/v1" | ||
kind: AzureIdentityBinding | ||
metadata: | ||
name: RELEASE-NAME-azidbinding-ingress-azure | ||
spec: | ||
azureIdentity: RELEASE-NAME-azid-ingress-azure | ||
selector: RELEASE-NAME-ingress-azure |
11 changes: 11 additions & 0 deletions
11
helm/ingress-azure/tests/snapshots/sample-config/ingress-azure/templates/aadpodidentity.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
--- | ||
# Source: ingress-azure/templates/aadpodidentity.yaml | ||
# Please see https://github.com/Azure/aad-pod-identity for more information | ||
apiVersion: "aadpodidentity.k8s.io/v1" | ||
kind: AzureIdentity | ||
metadata: | ||
name: RELEASE-NAME-azid-ingress-azure | ||
spec: | ||
type: 0 | ||
resourceID: /a/b/c | ||
clientID: 0000-0000-0000-0000-00000000 |
21 changes: 21 additions & 0 deletions
21
helm/ingress-azure/tests/snapshots/sample-config/ingress-azure/templates/configmap.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
--- | ||
# Source: ingress-azure/templates/configmap.yaml | ||
apiVersion: v1 | ||
kind: ConfigMap | ||
metadata: | ||
name: RELEASE-NAME-cm-ingress-azure | ||
labels: | ||
app: ingress-azure | ||
chart: ingress-azure-1.2.1 | ||
heritage: Helm | ||
release: RELEASE-NAME | ||
data: | ||
APPGW_VERBOSITY_LEVEL: "3" | ||
HTTP_SERVICE_PORT: "8123" | ||
APPGW_SUBSCRIPTION_ID: "0000-0000-0000-0000-00000000" | ||
APPGW_RESOURCE_GROUP: "resgp" | ||
APPGW_NAME: "gateway" | ||
APPGW_SUBNET_NAME: "gateway-subnet" | ||
KUBERNETES_WATCHNAMESPACE: "a,b,c" | ||
AZURE_CLIENT_ID: "0000-0000-0000-0000-00000000" | ||
USE_MANAGED_IDENTITY_FOR_POD: "true" |
Oops, something went wrong.