Skip to content

Commit

Permalink
Add e2e tests for build/diff kustomization
Browse files Browse the repository at this point in the history
Signed-off-by: Soule BA <soule@weave.works>
  • Loading branch information
souleb committed Jan 18, 2022
1 parent 2a93631 commit 1fdfeee
Show file tree
Hide file tree
Showing 35 changed files with 1,467 additions and 206 deletions.
2 changes: 1 addition & 1 deletion cmd/flux/build_kustomization.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func buildKsCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("invalid resource path %q", buildKsArgs.path)
}

builder, err := kustomization.NewBuilder(rootArgs.kubeconfig, rootArgs.kubecontext, rootArgs.namespace, name, buildKsArgs.path, kustomization.WithTimeout(rootArgs.timeout))
builder, err := kustomization.NewBuilder(kubeconfigArgs, name, buildKsArgs.path, kustomization.WithTimeout(rootArgs.timeout))
if err != nil {
return err
}
Expand Down
83 changes: 83 additions & 0 deletions cmd/flux/build_kustomization_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//go:build unit
// +build unit

/*
Copyright 2021 The Flux 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 main

import (
"testing"
)

func setup(t *testing.T, tmpl map[string]string) {
t.Helper()
testEnv.CreateObjectFile("./testdata/build-kustomization/podinfo-source.yaml", tmpl, t)
testEnv.CreateObjectFile("./testdata/build-kustomization/podinfo-kustomization.yaml", tmpl, t)
}

func TestBuildKustomization(t *testing.T) {
tests := []struct {
name string
args string
resultFile string
assertFunc string
}{
{
name: "no args",
args: "build kustomization podinfo",
resultFile: "invalid resource path \"\"",
assertFunc: "assertError",
},
{
name: "build podinfo",
args: "build kustomization podinfo --path ./testdata/build-kustomization/podinfo",
resultFile: "./testdata/build-kustomization/podinfo-result.yaml",
assertFunc: "assertGoldenTemplateFile",
},
{
name: "build podinfo without service",
args: "build kustomization podinfo --path ./testdata/build-kustomization/delete-service",
resultFile: "./testdata/build-kustomization/podinfo-without-service-result.yaml",
assertFunc: "assertGoldenTemplateFile",
},
}

tmpl := map[string]string{
"fluxns": allocateNamespace("flux-system"),
}
setup(t, tmpl)

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var assert assertFunc

switch tt.assertFunc {
case "assertGoldenTemplateFile":
assert = assertGoldenTemplateFile(tt.resultFile, tmpl)
case "assertError":
assert = assertError(tt.resultFile)
}

cmd := cmdTestCase{
args: tt.args + " -n " + tmpl["fluxns"],
assert: assert,
}

cmd.runTestCmd(t)
})
}
}
6 changes: 4 additions & 2 deletions cmd/flux/diff_kustomization.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,18 @@ func diffKsCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("invalid resource path %q", diffKsArgs.path)
}

builder, err := kustomization.NewBuilder(rootArgs.kubeconfig, rootArgs.kubecontext, rootArgs.namespace, name, diffKsArgs.path, kustomization.WithTimeout(rootArgs.timeout))
builder, err := kustomization.NewBuilder(kubeconfigArgs, name, diffKsArgs.path, kustomization.WithTimeout(rootArgs.timeout))
if err != nil {
return err
}

err = builder.Diff()
output, err := builder.Diff()
if err != nil {
return err
}

cmd.Print(output)

return nil

}
129 changes: 129 additions & 0 deletions cmd/flux/diff_kustomization_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
//go:build unit
// +build unit

/*
Copyright 2021 The Flux 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 main

import (
"context"
"os"
"strings"
"testing"

"github.com/fluxcd/flux2/internal/kustomization"
"github.com/fluxcd/pkg/ssa"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

func TestDiffKustomization(t *testing.T) {
tests := []struct {
name string
args string
objectFile string
assert assertFunc
}{
{
name: "no args",
args: "diff kustomization podinfo",
objectFile: "",
assert: assertError("invalid resource path \"\""),
},
{
name: "diff nothing deployed",
args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo",
objectFile: "",
assert: assertGoldenFile("./testdata/diff-kustomization/nothing-is-deployed.golden"),
},
{
name: "diff with a deployment object",
args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo",
objectFile: "./testdata/diff-kustomization/deployment.yaml",
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-deployment.golden"),
},
{
name: "diff with a drifted service object",
args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo",
objectFile: "./testdata/diff-kustomization/service.yaml",
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-drifted-service.golden"),
},
{
name: "diff with a drifted secret object",
args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo",
objectFile: "./testdata/diff-kustomization/secret.yaml",
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-drifted-secret.golden"),
},
{
name: "diff with a drifted key in sops secret object",
args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo",
objectFile: "./testdata/diff-kustomization/key-sops-secret.yaml",
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-drifted-key-sops-secret.golden"),
},
{
name: "diff with a drifted value in sops secret object",
args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo",
objectFile: "./testdata/diff-kustomization/value-sops-secret.yaml",
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-drifted-value-sops-secret.golden"),
},
}

tmpl := map[string]string{
"fluxns": allocateNamespace("flux-system"),
}

b, _ := kustomization.NewBuilder(kubeconfigArgs, "podinfo", "")

resourceManager, err := b.Manager()
if err != nil {
t.Fatal(err)
}

setup(t, tmpl)

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.objectFile != "" {
resourceManager.ApplyAll(context.Background(), createObjectFromFile(tt.objectFile, tmpl, t), ssa.DefaultApplyOptions())
}
cmd := cmdTestCase{
args: tt.args + " -n " + tmpl["fluxns"],
assert: tt.assert,
}
cmd.runTestCmd(t)
if tt.objectFile != "" {
testEnv.DeleteObjectFile(tt.objectFile, tmpl, t)
}
})
}
}

func createObjectFromFile(objectFile string, templateValues map[string]string, t *testing.T) []*unstructured.Unstructured {
buf, err := os.ReadFile(objectFile)
if err != nil {
t.Fatalf("Error reading file '%s': %v", objectFile, err)
}
content, err := executeTemplate(string(buf), templateValues)
if err != nil {
t.Fatalf("Error evaluating template file '%s': '%v'", objectFile, err)
}
clientObjects, err := readYamlObjects(strings.NewReader(content))
if err != nil {
t.Fatalf("Error decoding yaml file '%s': %v", objectFile, err)
}

return clientObjects
}
40 changes: 35 additions & 5 deletions cmd/flux/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ func allocateNamespace(prefix string) string {
return fmt.Sprintf("%s-%d", prefix, id)
}

func readYamlObjects(rdr io.Reader) ([]unstructured.Unstructured, error) {
objects := []unstructured.Unstructured{}
func readYamlObjects(rdr io.Reader) ([]*unstructured.Unstructured, error) {
objects := []*unstructured.Unstructured{}
reader := k8syaml.NewYAMLReader(bufio.NewReader(rdr))
for {
doc, err := reader.Read()
Expand All @@ -65,7 +65,7 @@ func readYamlObjects(rdr io.Reader) ([]unstructured.Unstructured, error) {
if err != nil {
return nil, err
}
objects = append(objects, *unstructuredObj)
objects = append(objects, unstructuredObj)
}
return objects, nil
}
Expand Down Expand Up @@ -96,7 +96,7 @@ func (m *testEnvKubeManager) CreateObjectFile(objectFile string, templateValues
}
}

func (m *testEnvKubeManager) CreateObjects(clientObjects []unstructured.Unstructured, t *testing.T) error {
func (m *testEnvKubeManager) CreateObjects(clientObjects []*unstructured.Unstructured, t *testing.T) error {
for _, obj := range clientObjects {
// First create the object then set its status if present in the
// yaml file. Make a copy first since creating an object may overwrite
Expand All @@ -107,14 +107,44 @@ func (m *testEnvKubeManager) CreateObjects(clientObjects []unstructured.Unstruct
return err
}
obj.SetResourceVersion(createObj.GetResourceVersion())
err = m.client.Status().Update(context.Background(), &obj)
err = m.client.Status().Update(context.Background(), obj)
if err != nil {
return err
}
}
return nil
}

func (m *testEnvKubeManager) DeleteObjectFile(objectFile string, templateValues map[string]string, t *testing.T) {
buf, err := os.ReadFile(objectFile)
if err != nil {
t.Fatalf("Error reading file '%s': %v", objectFile, err)
}
content, err := executeTemplate(string(buf), templateValues)
if err != nil {
t.Fatalf("Error evaluating template file '%s': '%v'", objectFile, err)
}
clientObjects, err := readYamlObjects(strings.NewReader(content))
if err != nil {
t.Fatalf("Error decoding yaml file '%s': %v", objectFile, err)
}
err = m.DeleteObjects(clientObjects, t)
if err != nil {
t.Logf("Error deleting test objects: '%v'", err)
}
}

func (m *testEnvKubeManager) DeleteObjects(clientObjects []*unstructured.Unstructured, t *testing.T) error {
for _, obj := range clientObjects {
err := m.client.Delete(context.Background(), obj)
if err != nil {
return err
}
}

return nil
}

func (m *testEnvKubeManager) Stop() error {
if m.testEnv == nil {
return fmt.Errorf("do nothing because testEnv is nil")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: podinfo
spec:
minReadySeconds: 3
revisionHistoryLimit: 5
progressDeadlineSeconds: 60
strategy:
rollingUpdate:
maxUnavailable: 0
type: RollingUpdate
selector:
matchLabels:
app: podinfo
template:
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9797"
labels:
app: podinfo
spec:
containers:
- name: podinfod
image: ghcr.io/stefanprodan/podinfo:6.0.3
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 9898
protocol: TCP
- name: http-metrics
containerPort: 9797
protocol: TCP
- name: grpc
containerPort: 9999
protocol: TCP
command:
- ./podinfo
- --port=9898
- --port-metrics=9797
- --grpc-port=9999
- --grpc-service-name=podinfo
- --level=info
- --random-delay=false
- --random-error=false
env:
- name: PODINFO_UI_COLOR
value: "#34577c"
livenessProbe:
exec:
command:
- podcli
- check
- http
- localhost:9898/healthz
initialDelaySeconds: 5
timeoutSeconds: 5
readinessProbe:
exec:
command:
- podcli
- check
- http
- localhost:9898/readyz
initialDelaySeconds: 5
timeoutSeconds: 5
resources:
limits:
cpu: 2000m
memory: 512Mi
requests:
cpu: 100m
memory: 64Mi
Loading

0 comments on commit 1fdfeee

Please sign in to comment.