1
0
mirror of synced 2026-02-06 19:05:55 +00:00

implement testEnv for e2e tests

Signed-off-by: Chanwit Kaewkasi <chanwit@gmail.com>
This commit is contained in:
Chanwit Kaewkasi
2021-08-08 00:54:42 +07:00
parent cb96bca6aa
commit 8f78263455
26 changed files with 376 additions and 44 deletions

54
cmd/flux/install_test.go Normal file
View File

@@ -0,0 +1,54 @@
// +build e2e
package main
import (
"testing"
"time"
)
func TestInstallNoArgs(t *testing.T) {
cmd := cmdTestCase{
args: "install",
wantError: false,
testClusterMode: ExistingClusterMode,
goldenFile: "testdata/install/install_no_args.golden",
}
cmd.runTestCmd(t)
testUninstallSilent(t)
time.Sleep(30 * time.Second)
}
func TestInstallExtraComponents(t *testing.T) {
cmd := cmdTestCase{
args: "install --components-extra=image-reflector-controller,image-automation-controller",
wantError: false,
testClusterMode: ExistingClusterMode,
goldenFile: "testdata/install/install_extra_components.golden",
}
cmd.runTestCmd(t)
testUninstallSilentForExtraComponents(t)
time.Sleep(30 * time.Second)
}
func testUninstallSilent(t *testing.T) {
cmd := cmdTestCase{
args: "uninstall -s",
wantError: false,
testClusterMode: ExistingClusterMode,
goldenFile: "testdata/uninstall/uninstall.golden",
}
cmd.runTestCmd(t)
}
func testUninstallSilentForExtraComponents(t *testing.T) {
cmd := cmdTestCase{
args: "uninstall -s",
wantError: false,
testClusterMode: ExistingClusterMode,
goldenFile: "testdata/uninstall/uninstall_extra_components.golden",
}
cmd.runTestCmd(t)
}

View File

@@ -4,17 +4,23 @@ import (
"bufio"
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"testing"
"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 TestMain(m *testing.M) {
@@ -49,17 +55,18 @@ func readYamlObjects(objectFile string) ([]client.Object, error) {
}
// A KubeManager that can create objects that are subject to a test.
type fakeKubeManager struct {
fakeClient client.WithWatch
type testEnvKubeManager struct {
client client.WithWatch
testEnv *envtest.Environment
}
func (m *fakeKubeManager) NewClient(kubeconfig string, kubecontext string) (client.WithWatch, error) {
return m.fakeClient, nil
func (m *testEnvKubeManager) NewClient(kubeconfig string, kubecontext string) (client.WithWatch, error) {
return m.client, nil
}
func (m *fakeKubeManager) CreateObjects(clientObjects []client.Object) error {
func (m *testEnvKubeManager) CreateObjects(clientObjects []client.Object) error {
for _, obj := range clientObjects {
err := m.fakeClient.Create(context.Background(), obj)
err := m.client.Create(context.Background(), obj)
if err != nil {
return err
}
@@ -67,32 +74,92 @@ func (m *fakeKubeManager) CreateObjects(clientObjects []client.Object) error {
return nil
}
func NewFakeKubeManager() *fakeKubeManager {
c := fakeclient.NewClientBuilder().WithScheme(utils.NewScheme()).Build()
return &fakeKubeManager{
fakeClient: c,
func (m *testEnvKubeManager) Stop() error {
if m.testEnv == nil {
return fmt.Errorf("do nothing because testEnv is nil")
}
return m.testEnv.Stop()
}
// Run the command and return the captured output.
func executeCommand(cmd string) (string, error) {
args, err := shellwords.Parse(cmd)
if err != nil {
return "", err
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,
}
cfg, err := testEnv.Start()
if err != nil {
return nil, err
}
user, err := testEnv.ControlPlane.AddUser(envtest.User{
Name: "envtest-admin",
Groups: []string{"system:masters"},
}, nil)
if err != nil {
return nil, err
}
kubeConfig, err := user.KubeConfig()
if err != nil {
return nil, err
}
tmpFilename := filepath.Join("/tmp", "kubeconfig-"+time.Nanosecond.String())
ioutil.WriteFile(tmpFilename, kubeConfig, 0644)
rootArgs.kubeconfig = tmpFilename
k8sClient, err := client.NewWithWatch(cfg, client.Options{})
if err != nil {
return nil, err
}
return &testEnvKubeManager{
testEnv: testEnv,
client: k8sClient,
}, nil
case ExistingClusterMode:
// TEST_KUBECONFIG is mandatory to prevent destroying a current cluster accidentally.
testKubeConfig := os.Getenv("TEST_KUBECONFIG")
if testKubeConfig == "" {
return nil, fmt.Errorf("environment variable TEST_KUBECONFIG is required to run tests against an existing cluster")
}
rootArgs.kubeconfig = testKubeConfig
useExistingCluster := true
config, err := clientcmd.BuildConfigFromFlags("", testKubeConfig)
testEnv := &envtest.Environment{
UseExistingCluster: &useExistingCluster,
Config: config,
}
cfg, err := testEnv.Start()
if err != nil {
return nil, err
}
k8sClient, err := client.NewWithWatch(cfg, client.Options{})
if err != nil {
return nil, err
}
return &testEnvKubeManager{
testEnv: testEnv,
client: k8sClient,
}, nil
}
buf := new(bytes.Buffer)
rootCmd.SetOut(buf)
rootCmd.SetErr(buf)
rootCmd.SetArgs(args)
_, err = rootCmd.ExecuteC()
result := buf.String()
return result, err
return nil, nil
}
type TestClusterMode int
const (
FakeClusterMode = TestClusterMode(iota + 1)
TestEnvClusterMode
ExistingClusterMode
)
// Structure used for each test to load objects into kubernetes, run
// commands and assert on the expected output.
type cmdTestCase struct {
@@ -106,11 +173,20 @@ type cmdTestCase struct {
goldenFile string
// Filename that contains yaml objects to load into Kubernetes
objectFile string
// TestClusterMode to bootstrap and testing, default to Fake
testClusterMode TestClusterMode
}
func (cmd *cmdTestCase) runTestCmd(t *testing.T) {
km := NewFakeKubeManager()
rootCtx.kubeManager = km
km, err := NewTestEnvKubeManager(cmd.testClusterMode)
if err != nil {
t.Fatalf("Error creating kube manager: '%v'", err)
}
if km != nil {
rootCtx.kubeManager = km
defer km.Stop()
}
if cmd.objectFile != "" {
clientObjects, err := readYamlObjects(cmd.objectFile)
@@ -149,6 +225,27 @@ func (cmd *cmdTestCase) runTestCmd(t *testing.T) {
}
}
// 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)
logger.stderr = rootCmd.ErrOrStderr()
_, err = rootCmd.ExecuteC()
result := buf.String()
return result, err
}
func TestVersion(t *testing.T) {
cmd := cmdTestCase{
args: "--version",

View File

@@ -0,0 +1,11 @@
✚ generating manifests
✔ manifests build completed
► installing components in flux-system namespace
◎ verifying installation
✔ helm-controller: deployment ready
✔ image-automation-controller: deployment ready
✔ image-reflector-controller: deployment ready
✔ kustomize-controller: deployment ready
✔ notification-controller: deployment ready
✔ source-controller: deployment ready
✔ install finished

View File

@@ -0,0 +1,9 @@
✚ generating manifests
✔ manifests build completed
► installing components in flux-system namespace
◎ verifying installation
✔ helm-controller: deployment ready
✔ kustomize-controller: deployment ready
✔ notification-controller: deployment ready
✔ source-controller: deployment ready
✔ install finished

View File

@@ -0,0 +1,31 @@
► deleting components in flux-system namespace
✔ Deployment/flux-system/helm-controller deleted
✔ Deployment/flux-system/kustomize-controller deleted
✔ Deployment/flux-system/notification-controller deleted
✔ Deployment/flux-system/source-controller deleted
✔ Service/flux-system/notification-controller deleted
✔ Service/flux-system/source-controller deleted
✔ Service/flux-system/webhook-receiver deleted
✔ NetworkPolicy/flux-system/allow-egress deleted
✔ NetworkPolicy/flux-system/allow-scraping deleted
✔ NetworkPolicy/flux-system/allow-webhooks deleted
✔ ServiceAccount/flux-system/helm-controller deleted
✔ ServiceAccount/flux-system/kustomize-controller deleted
✔ ServiceAccount/flux-system/notification-controller deleted
✔ ServiceAccount/flux-system/source-controller deleted
✔ ClusterRole/crd-controller-flux-system deleted
✔ ClusterRoleBinding/cluster-reconciler-flux-system deleted
✔ ClusterRoleBinding/crd-controller-flux-system deleted
► deleting toolkit.fluxcd.io finalizers in all namespaces
► deleting toolkit.fluxcd.io custom resource definitions
✔ CustomResourceDefinition/alerts.notification.toolkit.fluxcd.io deleted
✔ CustomResourceDefinition/buckets.source.toolkit.fluxcd.io deleted
✔ CustomResourceDefinition/gitrepositories.source.toolkit.fluxcd.io deleted
✔ CustomResourceDefinition/helmcharts.source.toolkit.fluxcd.io deleted
✔ CustomResourceDefinition/helmreleases.helm.toolkit.fluxcd.io deleted
✔ CustomResourceDefinition/helmrepositories.source.toolkit.fluxcd.io deleted
✔ CustomResourceDefinition/kustomizations.kustomize.toolkit.fluxcd.io deleted
✔ CustomResourceDefinition/providers.notification.toolkit.fluxcd.io deleted
✔ CustomResourceDefinition/receivers.notification.toolkit.fluxcd.io deleted
✔ Namespace/flux-system deleted
✔ uninstall finished

View File

@@ -0,0 +1,38 @@
► deleting components in flux-system namespace
✔ Deployment/flux-system/helm-controller deleted
✔ Deployment/flux-system/image-automation-controller deleted
✔ Deployment/flux-system/image-reflector-controller deleted
✔ Deployment/flux-system/kustomize-controller deleted
✔ Deployment/flux-system/notification-controller deleted
✔ Deployment/flux-system/source-controller deleted
✔ Service/flux-system/notification-controller deleted
✔ Service/flux-system/source-controller deleted
✔ Service/flux-system/webhook-receiver deleted
✔ NetworkPolicy/flux-system/allow-egress deleted
✔ NetworkPolicy/flux-system/allow-scraping deleted
✔ NetworkPolicy/flux-system/allow-webhooks deleted
✔ ServiceAccount/flux-system/helm-controller deleted
✔ ServiceAccount/flux-system/image-automation-controller deleted
✔ ServiceAccount/flux-system/image-reflector-controller deleted
✔ ServiceAccount/flux-system/kustomize-controller deleted
✔ ServiceAccount/flux-system/notification-controller deleted
✔ ServiceAccount/flux-system/source-controller deleted
✔ ClusterRole/crd-controller-flux-system deleted
✔ ClusterRoleBinding/cluster-reconciler-flux-system deleted
✔ ClusterRoleBinding/crd-controller-flux-system deleted
► deleting toolkit.fluxcd.io finalizers in all namespaces
► deleting toolkit.fluxcd.io custom resource definitions
✔ CustomResourceDefinition/alerts.notification.toolkit.fluxcd.io deleted
✔ CustomResourceDefinition/buckets.source.toolkit.fluxcd.io deleted
✔ CustomResourceDefinition/gitrepositories.source.toolkit.fluxcd.io deleted
✔ CustomResourceDefinition/helmcharts.source.toolkit.fluxcd.io deleted
✔ CustomResourceDefinition/helmreleases.helm.toolkit.fluxcd.io deleted
✔ CustomResourceDefinition/helmrepositories.source.toolkit.fluxcd.io deleted
✔ CustomResourceDefinition/imagepolicies.image.toolkit.fluxcd.io deleted
✔ CustomResourceDefinition/imagerepositories.image.toolkit.fluxcd.io deleted
✔ CustomResourceDefinition/imageupdateautomations.image.toolkit.fluxcd.io deleted
✔ CustomResourceDefinition/kustomizations.kustomize.toolkit.fluxcd.io deleted
✔ CustomResourceDefinition/providers.notification.toolkit.fluxcd.io deleted
✔ CustomResourceDefinition/receivers.notification.toolkit.fluxcd.io deleted
✔ Namespace/flux-system deleted
✔ uninstall finished

View File

@@ -1,3 +1,5 @@
// +build !e2e
package main
import (
@@ -6,39 +8,43 @@ import (
func TestTraceNoArgs(t *testing.T) {
cmd := cmdTestCase{
args: "trace",
wantError: true,
goldenValue: "object name is required",
args: "trace",
testClusterMode: FakeClusterMode,
wantError: true,
goldenValue: "object name is required",
}
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",
args: "trace podinfo -n podinfo --kind deployment --api-version=apps/v1",
testClusterMode: FakeClusterMode,
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",
args: "trace podinfo -n podinfo --kind HelmRelease --api-version=helm.toolkit.fluxcd.io/v2beta1",
testClusterMode: FakeClusterMode,
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",
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)
}