Skip to content

Commit

Permalink
Add e2e tests for build 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 Dec 22, 2021
1 parent 607cc94 commit 5b89963
Show file tree
Hide file tree
Showing 34 changed files with 1,164 additions and 165 deletions.
68 changes: 68 additions & 0 deletions cmd/flux/build_kustomization_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// +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
assert assertFunc
}{
{
name: "no args",
args: "build kustomization podinfo",
assert: assertError("invalid resource path \"\""),
},
{
name: "build podinfo",
args: "build kustomization podinfo --path ./testdata/build-kustomization/podinfo",
assert: assertGoldenFile("./testdata/build-kustomization/podinfo-result.yaml"),
},
{
name: "build podinfo without service",
args: "build kustomization podinfo --path ./testdata/build-kustomization/delete-service",
assert: assertGoldenFile("./testdata/build-kustomization/podinfo-without-service-result.yaml"),
},
}

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

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmd := cmdTestCase{
args: tt.args + " --namespace=" + tmpl["fluxns"],
assert: tt.assert,
}
cmd.runTestCmd(t)
})
}
}
4 changes: 3 additions & 1 deletion cmd/flux/diff_kustomization.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,13 @@ func diffKsCmdRun(cmd *cobra.Command, args []string) error {
return err
}

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

cmd.Print(output)

return nil

}
127 changes: 127 additions & 0 deletions cmd/flux/diff_kustomization_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// +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"
"github.com/fluxcd/flux2/internal/kustomization"
"github.com/fluxcd/pkg/ssa"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"os"
"strings"
"testing"
)

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(rootArgs.kubeconfig, rootArgs.kubecontext, tmpl["fluxns"], "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 + " --namespace=" + 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
20 changes: 20 additions & 0 deletions cmd/flux/testdata/build-kustomization/delete-service/hpa.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: podinfo
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: podinfo
minReplicas: 2
maxReplicas: 4
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
# scale up if usage is above
# 99% of the requested CPU (100m)
averageUtilization: 99
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ./deployment.yaml
- ./hpa.yaml
Loading

0 comments on commit 5b89963

Please sign in to comment.