mirror of https://github.com/fluxcd/flux2.git
Add tests for flux trace command
Add tests for flux trace command that fake out the kubernetes client, load objects from a yaml file and create them in the client, and assert on the output of the trace command to an expected golden file. This is a follow up from the suggestions in PR https://github.com/fluxcd/flux2/pull/1626 which suggested that additional testing would be helpful. This test approach is modeled after the helm command tests. This required some changes to the kubernetes client setup to make it possible to use a fake. If we agree this pattern makes sense, it can be applied to other commands. Signed-off-by: Allen Porter <allen@thebends.org>pull/1671/head
parent
eba6706f15
commit
351d287d88
@ -0,0 +1,26 @@
|
||||
|
||||
Object: deployment/podinfo
|
||||
Namespace: podinfo
|
||||
Status: Managed by Flux
|
||||
---
|
||||
HelmRelease: podinfo
|
||||
Namespace: podinfo
|
||||
Revision: 6.0.0
|
||||
Status: Last reconciled at 2021-07-16 15:42:20 +0000 UTC
|
||||
Message: Release reconciliation succeeded
|
||||
---
|
||||
HelmChart: podinfo-podinfo
|
||||
Namespace: flux-system
|
||||
Chart: podinfo
|
||||
Version: 6.0.0
|
||||
Revision: 6.0.0
|
||||
Status: Last reconciled at 2021-07-16 15:32:09 +0000 UTC
|
||||
Message: Fetched revision: 6.0.0
|
||||
---
|
||||
HelmRepository: podinfo
|
||||
Namespace: flux-system
|
||||
URL: https://stefanprodan.github.io/podinfo
|
||||
Revision: 8411f23d07d3701f0e96e7d9e503b7936d7e1d56
|
||||
Status: Last reconciled at 2021-07-11 00:25:46 +0000 UTC
|
||||
Message: Fetched revision: 8411f23d07d3701f0e96e7d9e503b7936d7e1d56
|
||||
|
@ -0,0 +1,129 @@
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: podinfo
|
||||
helm.toolkit.fluxcd.io/name: podinfo
|
||||
helm.toolkit.fluxcd.io/namespace: podinfo
|
||||
name: podinfo
|
||||
namespace: podinfo
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: podinfo
|
||||
spec:
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
labels:
|
||||
kustomize.toolkit.fluxcd.io/name: infrastructure
|
||||
kustomize.toolkit.fluxcd.io/namespace: flux-system
|
||||
name: podinfo
|
||||
namespace: podinfo
|
||||
spec:
|
||||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
namespace: flux-system
|
||||
status:
|
||||
conditions:
|
||||
- lastTransitionTime: "2021-07-16T15:42:20Z"
|
||||
message: Release reconciliation succeeded
|
||||
reason: ReconciliationSucceeded
|
||||
status: "True"
|
||||
type: Ready
|
||||
helmChart: flux-system/podinfo-podinfo
|
||||
lastAppliedRevision: 6.0.0
|
||||
lastAttemptedRevision: 6.0.0
|
||||
lastAttemptedValuesChecksum: c31db75d05b7515eba2eef47bd71038c74b2e531
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta1
|
||||
kind: HelmChart
|
||||
metadata:
|
||||
name: podinfo-podinfo
|
||||
namespace: flux-system
|
||||
spec:
|
||||
chart: podinfo
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
version: 6.0.0
|
||||
status:
|
||||
artifact:
|
||||
checksum: cf13ba96773d9a879cd052c86e73199b3f96c854
|
||||
lastUpdateTime: "2021-08-01T04:42:55Z"
|
||||
revision: 6.0.0
|
||||
conditions:
|
||||
- lastTransitionTime: "2021-07-16T15:32:09Z"
|
||||
message: 'Fetched revision: 6.0.0'
|
||||
reason: ChartPullSucceeded
|
||||
status: "True"
|
||||
type: Ready
|
||||
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
labels:
|
||||
kustomize.toolkit.fluxcd.io/name: infrastructure
|
||||
kustomize.toolkit.fluxcd.io/namespace: flux-system
|
||||
name: podinfo
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 5m
|
||||
timeout: 1m0s
|
||||
url: https://stefanprodan.github.io/podinfo
|
||||
status:
|
||||
artifact:
|
||||
checksum: 8411f23d07d3701f0e96e7d9e503b7936d7e1d56
|
||||
lastUpdateTime: "2021-07-11T00:25:46Z"
|
||||
revision: 8411f23d07d3701f0e96e7d9e503b7936d7e1d56
|
||||
conditions:
|
||||
- lastTransitionTime: "2021-07-11T00:25:46Z"
|
||||
message: 'Fetched revision: 8411f23d07d3701f0e96e7d9e503b7936d7e1d56'
|
||||
reason: IndexationSucceed
|
||||
status: "True"
|
||||
type: Ready
|
||||
---
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1beta1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: infrastructure
|
||||
namespace: flux-system
|
||||
spec:
|
||||
path: ./infrastructure/
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
validation: client
|
||||
status:
|
||||
conditions:
|
||||
- lastTransitionTime: "2021-08-01T04:52:56Z"
|
||||
message: 'Applied revision: main/696f056df216eea4f9401adbee0ff744d4df390f'
|
||||
reason: ReconciliationSucceeded
|
||||
status: "True"
|
||||
type: Ready
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta1
|
||||
kind: GitRepository
|
||||
metadata:
|
||||
labels:
|
||||
kustomize.toolkit.fluxcd.io/name: flux-system
|
||||
kustomize.toolkit.fluxcd.io/namespace: flux-system
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
spec:
|
||||
gitImplementation: go-git
|
||||
ref:
|
||||
branch: main
|
||||
secretRef:
|
||||
name: flux-system
|
||||
|
@ -0,0 +1,19 @@
|
||||
|
||||
Object: HelmRelease/podinfo
|
||||
Namespace: podinfo
|
||||
Status: Managed by Flux
|
||||
---
|
||||
Kustomization: infrastructure
|
||||
Namespace: flux-system
|
||||
Path: ./infrastructure
|
||||
Revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||
Status: Last reconciled at 2021-08-01 04:52:56 +0000 UTC
|
||||
Message: Applied revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||
---
|
||||
GitRepository: flux-system
|
||||
Namespace: flux-system
|
||||
URL: ssh://git@github.com/example/repo
|
||||
Revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||
Status: Last reconciled at 2021-07-20 00:48:16 +0000 UTC
|
||||
Message: Fetched revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||
|
@ -0,0 +1,73 @@
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
labels:
|
||||
kustomize.toolkit.fluxcd.io/name: infrastructure
|
||||
kustomize.toolkit.fluxcd.io/namespace: flux-system
|
||||
name: podinfo
|
||||
namespace: podinfo
|
||||
spec:
|
||||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
namespace: flux-system
|
||||
status:
|
||||
conditions:
|
||||
- lastTransitionTime: "2021-07-16T15:42:20Z"
|
||||
message: Release reconciliation succeeded
|
||||
reason: ReconciliationSucceeded
|
||||
status: "True"
|
||||
type: Ready
|
||||
helmChart: flux-system/podinfo-podinfo
|
||||
lastAppliedRevision: 6.0.0
|
||||
lastAttemptedRevision: 6.0.0
|
||||
lastAttemptedValuesChecksum: c31db75d05b7515eba2eef47bd71038c74b2e531
|
||||
---
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1beta1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: infrastructure
|
||||
namespace: flux-system
|
||||
spec:
|
||||
path: ./infrastructure
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
validation: client
|
||||
status:
|
||||
conditions:
|
||||
- lastTransitionTime: "2021-08-01T04:52:56Z"
|
||||
message: 'Applied revision: main/696f056df216eea4f9401adbee0ff744d4df390f'
|
||||
reason: ReconciliationSucceeded
|
||||
status: "True"
|
||||
type: Ready
|
||||
lastAppliedRevision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta1
|
||||
kind: GitRepository
|
||||
metadata:
|
||||
labels:
|
||||
kustomize.toolkit.fluxcd.io/name: flux-system
|
||||
kustomize.toolkit.fluxcd.io/namespace: flux-system
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
spec:
|
||||
gitImplementation: go-git
|
||||
secretRef:
|
||||
name: flux-system
|
||||
url: ssh://git@github.com/example/repo
|
||||
status:
|
||||
artifact:
|
||||
lastUpdateTime: "2021-08-01T04:28:42Z"
|
||||
revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||
conditions:
|
||||
- lastTransitionTime: "2021-07-20T00:48:16Z"
|
||||
message: 'Fetched revision: main/696f056df216eea4f9401adbee0ff744d4df390f'
|
||||
reason: GitOperationSucceed
|
||||
status: "True"
|
||||
type: Ready
|
||||
|
@ -0,0 +1,20 @@
|
||||
|
||||
Object: HelmRelease/podinfo
|
||||
Namespace: podinfo
|
||||
Status: Managed by Flux
|
||||
---
|
||||
Kustomization: infrastructure
|
||||
Namespace: flux-system
|
||||
Path: ./infrastructure
|
||||
Revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||
Status: Last reconciled at 2021-08-01 04:52:56 +0000 UTC
|
||||
Message: Applied revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||
---
|
||||
GitRepository: flux-system
|
||||
Namespace: flux-system
|
||||
URL: ssh://git@github.com/example/repo
|
||||
Branch: main
|
||||
Revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||
Status: Last reconciled at 2021-07-20 00:48:16 +0000 UTC
|
||||
Message: Fetched revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||
|
@ -0,0 +1,75 @@
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
labels:
|
||||
kustomize.toolkit.fluxcd.io/name: infrastructure
|
||||
kustomize.toolkit.fluxcd.io/namespace: flux-system
|
||||
name: podinfo
|
||||
namespace: podinfo
|
||||
spec:
|
||||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
namespace: flux-system
|
||||
status:
|
||||
conditions:
|
||||
- lastTransitionTime: "2021-07-16T15:42:20Z"
|
||||
message: Release reconciliation succeeded
|
||||
reason: ReconciliationSucceeded
|
||||
status: "True"
|
||||
type: Ready
|
||||
helmChart: flux-system/podinfo-podinfo
|
||||
lastAppliedRevision: 6.0.0
|
||||
lastAttemptedRevision: 6.0.0
|
||||
lastAttemptedValuesChecksum: c31db75d05b7515eba2eef47bd71038c74b2e531
|
||||
---
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1beta1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: infrastructure
|
||||
namespace: flux-system
|
||||
spec:
|
||||
path: ./infrastructure
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
validation: client
|
||||
status:
|
||||
conditions:
|
||||
- lastTransitionTime: "2021-08-01T04:52:56Z"
|
||||
message: 'Applied revision: main/696f056df216eea4f9401adbee0ff744d4df390f'
|
||||
reason: ReconciliationSucceeded
|
||||
status: "True"
|
||||
type: Ready
|
||||
lastAppliedRevision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta1
|
||||
kind: GitRepository
|
||||
metadata:
|
||||
labels:
|
||||
kustomize.toolkit.fluxcd.io/name: flux-system
|
||||
kustomize.toolkit.fluxcd.io/namespace: flux-system
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
spec:
|
||||
gitImplementation: go-git
|
||||
ref:
|
||||
branch: main
|
||||
secretRef:
|
||||
name: flux-system
|
||||
url: ssh://git@github.com/example/repo
|
||||
status:
|
||||
artifact:
|
||||
lastUpdateTime: "2021-08-01T04:28:42Z"
|
||||
revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||
conditions:
|
||||
- lastTransitionTime: "2021-07-20T00:48:16Z"
|
||||
message: 'Fetched revision: main/696f056df216eea4f9401adbee0ff744d4df390f'
|
||||
reason: GitOperationSucceed
|
||||
status: "True"
|
||||
type: Ready
|
||||
|
@ -0,0 +1 @@
|
||||
object name is required
|
@ -0,0 +1,180 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
shellwords "github.com/mattn/go-shellwords"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Ensure tests print consistent timestamps regardless of timezone
|
||||
os.Setenv("TZ", "UTC")
|
||||
}
|
||||
|
||||
func readYamlObjects(objectFile string) ([]client.Object, error) {
|
||||
obj, err := ioutil.ReadFile(objectFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objects := []client.Object{}
|
||||
reader := k8syaml.NewYAMLReader(bufio.NewReader(bytes.NewReader(obj)))
|
||||
for {
|
||||
doc, err := reader.Read()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
unstructuredObj := &unstructured.Unstructured{}
|
||||
decoder := k8syaml.NewYAMLOrJSONDecoder(bytes.NewBuffer(doc), len(doc))
|
||||
err = decoder.Decode(unstructuredObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objects = append(objects, unstructuredObj)
|
||||
}
|
||||
return objects, nil
|
||||
}
|
||||
|
||||
// A KubeManager that can create objects that are subject to a test.
|
||||
type fakeKubeManager struct {
|
||||
fakeClient client.Client
|
||||
}
|
||||
|
||||
func (m *fakeKubeManager) NewClient(kubeconfig string, kubecontext string) (client.Client, error) {
|
||||
return m.fakeClient, nil
|
||||
}
|
||||
|
||||
func (m *fakeKubeManager) CreateObjects(clientObjects []client.Object) error {
|
||||
for _, obj := range clientObjects {
|
||||
err := m.fakeClient.Create(context.Background(), obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewFakeKubeManager() *fakeKubeManager {
|
||||
c := fakeclient.NewClientBuilder().WithScheme(utils.NewScheme()).Build()
|
||||
return &fakeKubeManager{
|
||||
fakeClient: c,
|
||||
}
|
||||
}
|
||||
|
||||
// Run the command and return the captured output.
|
||||
func executeCommand(cmd string) (string, error) {
|
||||
args, err := shellwords.Parse(cmd)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
rootCmd.SetOut(buf)
|
||||
rootCmd.SetErr(buf)
|
||||
rootCmd.SetArgs(args)
|
||||
|
||||
_, err = rootCmd.ExecuteC()
|
||||
result := buf.String()
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Structure used for each test to load objects into kubernetes, run
|
||||
// commands and assert on the expected output.
|
||||
type cmdTestCase struct {
|
||||
// The command line arguments to test.
|
||||
args string
|
||||
// When true, the test expects the command to fail.
|
||||
wantError bool
|
||||
// Filename that contains the expected test output.
|
||||
goldenFile string
|
||||
// Filename that contains yaml objects to load into Kubernetes
|
||||
objectFile string
|
||||
}
|
||||
|
||||
func (cmd *cmdTestCase) runTestCmd(t *testing.T) {
|
||||
km := NewFakeKubeManager()
|
||||
rootCtx.kubeManager = km
|
||||
|
||||
if cmd.objectFile != "" {
|
||||
clientObjects, err := readYamlObjects(cmd.objectFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Error loading yaml: '%v'", err)
|
||||
}
|
||||
err = km.CreateObjects(clientObjects)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating test objects: '%v'", err)
|
||||
}
|
||||
}
|
||||
|
||||
actual, err := executeCommand(cmd.args)
|
||||
if (err != nil) != cmd.wantError {
|
||||
t.Fatalf("Expected error='%v', Got: %v", cmd.wantError, err)
|
||||
}
|
||||
if err != nil {
|
||||
actual = err.Error()
|
||||
}
|
||||
contents, err := ioutil.ReadFile(cmd.goldenFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading golden file: '%s'", err)
|
||||
}
|
||||
expected := strings.TrimSuffix(string(contents), "\n")
|
||||
diff := cmp.Diff(expected, actual)
|
||||
if diff != "" {
|
||||
t.Errorf("Mismatch from '%s' (-want +got):\n%s", cmd.goldenFile, diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTraceNoArgs(t *testing.T) {
|
||||
cmd := cmdTestCase{
|
||||
args: "trace",
|
||||
wantError: true,
|
||||
goldenFile: "testdata/trace/no-args.txt",
|
||||
}
|
||||
cmd.runTestCmd(t)
|
||||
}
|
||||
|
||||
func TestTraceDeployment(t *testing.T) {
|
||||
cmd := cmdTestCase{
|
||||
args: "trace podinfo -n podinfo --kind deployment --api-version=apps/v1",
|
||||
wantError: false,
|
||||
goldenFile: "testdata/trace/deployment.txt",
|
||||
objectFile: "testdata/trace/deployment.yaml",
|
||||
}
|
||||
cmd.runTestCmd(t)
|
||||
}
|
||||
|
||||
func TestTraceHelmRelease(t *testing.T) {
|
||||
cmd := cmdTestCase{
|
||||
args: "trace podinfo -n podinfo --kind HelmRelease --api-version=helm.toolkit.fluxcd.io/v2beta1",
|
||||
wantError: false,
|
||||
goldenFile: "testdata/trace/helmrelease.txt",
|
||||
objectFile: "testdata/trace/helmrelease.yaml",
|
||||
}
|
||||
cmd.runTestCmd(t)
|
||||
}
|
||||
|
||||
func TestTraceHelmReleaseMissingGitRef(t *testing.T) {
|
||||
cmd := cmdTestCase{
|
||||
args: "trace podinfo -n podinfo --kind HelmRelease --api-version=helm.toolkit.fluxcd.io/v2beta1",
|
||||
wantError: false,
|
||||
goldenFile: "testdata/trace/helmrelease-missing-git-ref.txt",
|
||||
objectFile: "testdata/trace/helmrelease-missing-git-ref.yaml",
|
||||
}
|
||||
cmd.runTestCmd(t)
|
||||
}
|
Loading…
Reference in New Issue