mirror of
https://github.com/fluxcd/flux2.git
synced 2025-10-29 15:28:04 +08:00
Remove fakeclient and use testenv for flux cmd tests
Remove use of the fake client, and replace with a real client connected to the testEnv. This required fixes to the yaml files as the testEnv has stricter verifcation of objects. This also meant it was not possible to test a GitRepository with a missing artifact since that is not a valid state. The tests are slower than before, taking around 7-10 seconds each because the testEnv is setup and destroyed for every test. These will be sped up in a follow up PR. Signed-off-by: Allen Porter <allen@thebends.org>
This commit is contained in:
@ -25,7 +25,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
||||
)
|
||||
|
||||
@ -127,18 +126,6 @@ func NewRootFlags() rootFlags {
|
||||
return rf
|
||||
}
|
||||
|
||||
type rootContext struct {
|
||||
kubeManager utils.KubeManager
|
||||
}
|
||||
|
||||
var rootCtx = NewRootContext()
|
||||
|
||||
func NewRootContext() rootContext {
|
||||
var rc rootContext
|
||||
rc.kubeManager = utils.DefaultKubeManager()
|
||||
return rc
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
configureKubeconfig()
|
||||
|
||||
@ -15,12 +15,12 @@ func TestMain(m *testing.M) {
|
||||
// Ensure tests print consistent timestamps regardless of timezone
|
||||
os.Setenv("TZ", "UTC")
|
||||
|
||||
// Install Flux
|
||||
km, err := NewTestEnvKubeManager(ExistingClusterMode)
|
||||
// Install Flux.
|
||||
// Creating the test env manager sets rootArgs client flags
|
||||
_, err := NewTestEnvKubeManager(ExistingClusterMode)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("error creating kube manager: '%w'", err))
|
||||
}
|
||||
rootCtx.kubeManager = km
|
||||
output, err := executeCommand("install --components-extra=image-reflector-controller,image-automation-controller")
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("install falied: %s error:'%w'", output, err))
|
||||
|
||||
@ -13,23 +13,21 @@ import (
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/mattn/go-shellwords"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
)
|
||||
|
||||
func readYamlObjects(objectFile string) ([]client.Object, error) {
|
||||
func readYamlObjects(objectFile string) ([]unstructured.Unstructured, error) {
|
||||
obj, err := os.ReadFile(objectFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objects := []client.Object{}
|
||||
objects := []unstructured.Unstructured{}
|
||||
reader := k8syaml.NewYAMLReader(bufio.NewReader(bytes.NewReader(obj)))
|
||||
for {
|
||||
doc, err := reader.Read()
|
||||
@ -44,7 +42,7 @@ func readYamlObjects(objectFile string) ([]client.Object, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objects = append(objects, unstructuredObj)
|
||||
objects = append(objects, *unstructuredObj)
|
||||
}
|
||||
return objects, nil
|
||||
}
|
||||
@ -59,9 +57,18 @@ func (m *testEnvKubeManager) NewClient(kubeconfig string, kubecontext string) (c
|
||||
return m.client, nil
|
||||
}
|
||||
|
||||
func (m *testEnvKubeManager) CreateObjects(clientObjects []client.Object) error {
|
||||
func (m *testEnvKubeManager) CreateObjects(clientObjects []unstructured.Unstructured) error {
|
||||
for _, obj := range clientObjects {
|
||||
err := m.client.Create(context.Background(), obj)
|
||||
// First create the object then set its status if present in the
|
||||
// yaml file. Make a copy first since creating an object may overwrite
|
||||
// the status.
|
||||
createObj := obj.DeepCopy()
|
||||
err := m.client.Create(context.Background(), createObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
obj.SetResourceVersion(createObj.GetResourceVersion())
|
||||
err = m.client.Status().Update(context.Background(), &obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -78,15 +85,11 @@ func (m *testEnvKubeManager) Stop() error {
|
||||
|
||||
func NewTestEnvKubeManager(testClusterMode TestClusterMode) (*testEnvKubeManager, error) {
|
||||
switch testClusterMode {
|
||||
case FakeClusterMode:
|
||||
c := fakeclient.NewClientBuilder().WithScheme(utils.NewScheme()).Build()
|
||||
return &testEnvKubeManager{
|
||||
client: c,
|
||||
}, nil
|
||||
case TestEnvClusterMode:
|
||||
useExistingCluster := false
|
||||
testEnv := &envtest.Environment{
|
||||
UseExistingCluster: &useExistingCluster,
|
||||
CRDDirectoryPaths: []string{"manifests"},
|
||||
}
|
||||
cfg, err := testEnv.Start()
|
||||
if err != nil {
|
||||
@ -150,8 +153,7 @@ func NewTestEnvKubeManager(testClusterMode TestClusterMode) (*testEnvKubeManager
|
||||
type TestClusterMode int
|
||||
|
||||
const (
|
||||
FakeClusterMode = TestClusterMode(iota + 1)
|
||||
TestEnvClusterMode
|
||||
TestEnvClusterMode = TestClusterMode(iota + 1)
|
||||
ExistingClusterMode
|
||||
)
|
||||
|
||||
@ -181,7 +183,6 @@ func (cmd *cmdTestCase) runTestCmd(t *testing.T) {
|
||||
}
|
||||
|
||||
if km != nil {
|
||||
rootCtx.kubeManager = km
|
||||
defer km.Stop()
|
||||
}
|
||||
|
||||
|
||||
29
cmd/flux/testdata/trace/deployment.yaml
vendored
29
cmd/flux/testdata/trace/deployment.yaml
vendored
@ -1,8 +1,19 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: flux-system
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: podinfo
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: podinfo
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: podinfo
|
||||
helm.toolkit.fluxcd.io/name: podinfo
|
||||
@ -11,11 +22,18 @@ metadata:
|
||||
namespace: podinfo
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: podinfo
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: podinfo
|
||||
spec:
|
||||
containers:
|
||||
- name: hello
|
||||
command: [ "echo hello world" ]
|
||||
image: busybox
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
@ -33,6 +51,7 @@ spec:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
namespace: flux-system
|
||||
interval: 5m
|
||||
status:
|
||||
conditions:
|
||||
- lastTransitionTime: "2021-07-16T15:42:20Z"
|
||||
@ -56,11 +75,14 @@ spec:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
version: 6.0.0
|
||||
interval: 5m
|
||||
status:
|
||||
artifact:
|
||||
checksum: cf13ba96773d9a879cd052c86e73199b3f96c854
|
||||
lastUpdateTime: "2021-08-01T04:42:55Z"
|
||||
revision: 6.0.0
|
||||
path: "example"
|
||||
url: "example"
|
||||
conditions:
|
||||
- lastTransitionTime: "2021-07-16T15:32:09Z"
|
||||
message: 'Fetched revision: 6.0.0'
|
||||
@ -86,6 +108,8 @@ status:
|
||||
checksum: 8411f23d07d3701f0e96e7d9e503b7936d7e1d56
|
||||
lastUpdateTime: "2021-07-11T00:25:46Z"
|
||||
revision: 8411f23d07d3701f0e96e7d9e503b7936d7e1d56
|
||||
path: "example"
|
||||
url: "example"
|
||||
conditions:
|
||||
- lastTransitionTime: "2021-07-11T00:25:46Z"
|
||||
message: 'Fetched revision: 8411f23d07d3701f0e96e7d9e503b7936d7e1d56'
|
||||
@ -104,6 +128,8 @@ spec:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
validation: client
|
||||
interval: 5m
|
||||
prune: true
|
||||
status:
|
||||
conditions:
|
||||
- lastTransitionTime: "2021-08-01T04:52:56Z"
|
||||
@ -126,4 +152,5 @@ spec:
|
||||
branch: main
|
||||
secretRef:
|
||||
name: flux-system
|
||||
|
||||
interval: 5m
|
||||
url: ssh://git@github.com/example/repo
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
|
||||
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
|
||||
@ -1,73 +0,0 @@
|
||||
---
|
||||
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
|
||||
|
||||
17
cmd/flux/testdata/trace/helmrelease.yaml
vendored
17
cmd/flux/testdata/trace/helmrelease.yaml
vendored
@ -1,4 +1,14 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: flux-system
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: podinfo
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
@ -15,6 +25,7 @@ spec:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
namespace: flux-system
|
||||
interval: 5m
|
||||
status:
|
||||
conditions:
|
||||
- lastTransitionTime: "2021-07-16T15:42:20Z"
|
||||
@ -38,6 +49,8 @@ spec:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
validation: client
|
||||
interval: 5m
|
||||
prune: false
|
||||
status:
|
||||
conditions:
|
||||
- lastTransitionTime: "2021-08-01T04:52:56Z"
|
||||
@ -62,14 +75,16 @@ spec:
|
||||
secretRef:
|
||||
name: flux-system
|
||||
url: ssh://git@github.com/example/repo
|
||||
interval: 5m
|
||||
status:
|
||||
artifact:
|
||||
lastUpdateTime: "2021-08-01T04:28:42Z"
|
||||
revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||
path: "example"
|
||||
url: "example"
|
||||
conditions:
|
||||
- lastTransitionTime: "2021-07-20T00:48:16Z"
|
||||
message: 'Fetched revision: main/696f056df216eea4f9401adbee0ff744d4df390f'
|
||||
reason: GitOperationSucceed
|
||||
status: "True"
|
||||
type: Ready
|
||||
|
||||
|
||||
@ -89,7 +89,7 @@ func traceCmdRun(cmd *cobra.Command, args []string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := rootCtx.kubeManager.NewClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ import (
|
||||
func TestTraceNoArgs(t *testing.T) {
|
||||
cmd := cmdTestCase{
|
||||
args: "trace",
|
||||
testClusterMode: FakeClusterMode,
|
||||
testClusterMode: TestEnvClusterMode,
|
||||
wantError: true,
|
||||
goldenValue: "object name is required",
|
||||
}
|
||||
@ -19,7 +19,7 @@ func TestTraceNoArgs(t *testing.T) {
|
||||
func TestTraceDeployment(t *testing.T) {
|
||||
cmd := cmdTestCase{
|
||||
args: "trace podinfo -n podinfo --kind deployment --api-version=apps/v1",
|
||||
testClusterMode: FakeClusterMode,
|
||||
testClusterMode: TestEnvClusterMode,
|
||||
wantError: false,
|
||||
goldenFile: "testdata/trace/deployment.txt",
|
||||
objectFile: "testdata/trace/deployment.yaml",
|
||||
@ -30,21 +30,10 @@ func TestTraceDeployment(t *testing.T) {
|
||||
func TestTraceHelmRelease(t *testing.T) {
|
||||
cmd := cmdTestCase{
|
||||
args: "trace podinfo -n podinfo --kind HelmRelease --api-version=helm.toolkit.fluxcd.io/v2beta1",
|
||||
testClusterMode: FakeClusterMode,
|
||||
testClusterMode: TestEnvClusterMode,
|
||||
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",
|
||||
testClusterMode: FakeClusterMode,
|
||||
wantError: false,
|
||||
goldenFile: "testdata/trace/helmrelease-missing-git-ref.txt",
|
||||
objectFile: "testdata/trace/helmrelease-missing-git-ref.yaml",
|
||||
}
|
||||
cmd.runTestCmd(t)
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
func TestVersion(t *testing.T) {
|
||||
cmd := cmdTestCase{
|
||||
args: "--version",
|
||||
testClusterMode: TestEnvClusterMode,
|
||||
goldenValue: "flux version 0.0.0-dev.0\n",
|
||||
}
|
||||
cmd.runTestCmd(t)
|
||||
|
||||
@ -131,36 +131,6 @@ func KubeConfig(kubeConfigPath string, kubeContext string) (*rest.Config, error)
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// KubeManger creates a Kubernetes client.Client. This interface exists to
|
||||
// facilitate unit testing and provide a fake client.
|
||||
type KubeManager interface {
|
||||
NewClient(string, string) (client.WithWatch, error)
|
||||
}
|
||||
|
||||
type defaultKubeManager struct{}
|
||||
|
||||
func DefaultKubeManager() KubeManager {
|
||||
var manager defaultKubeManager
|
||||
return manager
|
||||
}
|
||||
|
||||
func (m defaultKubeManager) NewClient(kubeConfigPath string, kubeContext string) (client.WithWatch, error) {
|
||||
cfg, err := KubeConfig(kubeConfigPath, kubeContext)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("kubernetes client initialization failed: %w", err)
|
||||
}
|
||||
|
||||
scheme := NewScheme()
|
||||
kubeClient, err := client.NewWithWatch(cfg, client.Options{
|
||||
Scheme: scheme,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("kubernetes client initialization failed: %w", err)
|
||||
}
|
||||
|
||||
return kubeClient, nil
|
||||
}
|
||||
|
||||
// Create the Scheme, methods for serializing and deserializing API objects
|
||||
// which can be shared by tests.
|
||||
func NewScheme() *apiruntime.Scheme {
|
||||
@ -180,9 +150,20 @@ func NewScheme() *apiruntime.Scheme {
|
||||
}
|
||||
|
||||
func KubeClient(kubeConfigPath string, kubeContext string) (client.WithWatch, error) {
|
||||
m := DefaultKubeManager()
|
||||
kubeClient, err := m.NewClient(kubeConfigPath, kubeContext)
|
||||
return kubeClient, err
|
||||
cfg, err := KubeConfig(kubeConfigPath, kubeContext)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("kubernetes client initialization failed: %w", err)
|
||||
}
|
||||
|
||||
scheme := NewScheme()
|
||||
kubeClient, err := client.NewWithWatch(cfg, client.Options{
|
||||
Scheme: scheme,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("kubernetes client initialization failed: %w", err)
|
||||
}
|
||||
|
||||
return kubeClient, nil
|
||||
}
|
||||
|
||||
// SplitKubeConfigPath splits the given KUBECONFIG path based on the runtime OS
|
||||
|
||||
Reference in New Issue
Block a user