1
0
mirror of synced 2026-02-13 13:06:56 +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

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",