diff --git a/apptests/environment/environment.go b/apptests/environment/environment.go index d5eb55ef3..8949633ed 100644 --- a/apptests/environment/environment.go +++ b/apptests/environment/environment.go @@ -6,6 +6,7 @@ import ( "context" "fmt" "io" + "os" "path/filepath" "time" @@ -23,7 +24,9 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/klog/v2" genericCLient "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" ) const ( @@ -49,7 +52,7 @@ type Env struct { func (e *Env) Provision(ctx context.Context) error { var err error - kustomizePath, err := AbsolutePathToBase() + kustomizePath, err := absolutePathToBase() if err != nil { return err } @@ -155,11 +158,15 @@ func waitForFluxDeploymentsReady(ctx context.Context, typedClient *typedclient.C isDeploymentReady := func(ctx context.Context, deployment appsv1.Deployment) wait.ConditionWithContextFunc { return func(ctx context.Context) (done bool, err error) { deploymentObj, err := typedClient.Clientset().AppsV1(). - Deployments(kommanderFluxNamespace). + Deployments(deployment.Namespace). Get(ctx, deployment.Name, metav1.GetOptions{}) if err != nil { return false, err } + if deploymentObj.Generation > deploymentObj.Status.ObservedGeneration { + return false, nil + } + return deploymentObj.Status.ReadyReplicas == deploymentObj.Status.Replicas, nil } } @@ -184,6 +191,8 @@ func (e *Env) SetK8sClient(k8sClient *typedclient.Client) { // ApplyKustomizations applies the kustomizations located in the given path. func (e *Env) ApplyKustomizations(ctx context.Context, path string, substitutions map[string]string) error { + log.SetLogger(klog.NewKlogr()) + if path == "" { return fmt.Errorf("requirement argument: path is not specified") } @@ -199,25 +208,47 @@ func (e *Env) ApplyKustomizations(ctx context.Context, path string, substitution buf := bytes.NewBuffer(out) dec := yaml.NewYAMLOrJSONDecoder(buf, 1<<20) // default buffer size is 1MB - obj := unstructured.Unstructured{} - if err = dec.Decode(&obj); err != nil && err != io.EOF { - return fmt.Errorf("could not decode kustomization for path: %s :%w", path, err) - } - - genericClient, err := genericCLient.New(e.K8sClient.Config(), genericCLient.Options{}) + genericClient, err := genericCLient.New(e.K8sClient.Config(), genericCLient.Options{ + Scheme: flux.NewScheme(), + }) if err != nil { return fmt.Errorf("could not create the generic client for path: %s :%w", path, err) } - err = genericClient.Patch(ctx, &obj, genericCLient.Apply, genericCLient.ForceOwnership, genericCLient.FieldOwner("k-cli")) - if err != nil { - return fmt.Errorf("could not patch the kustomization resources for path: %s :%w", path, err) + for { + var obj unstructured.Unstructured + err = dec.Decode(&obj) + if err == io.EOF { + break + } + if err != nil { + return fmt.Errorf("could not decode kustomization for path: %s :%w", path, err) + } + + err = genericClient.Patch(ctx, &obj, genericCLient.Apply, genericCLient.ForceOwnership, genericCLient.FieldOwner("k-cli")) + if err != nil { + return fmt.Errorf("could not patch the kustomization resources for path: %s :%w", path, err) + } } return nil } -// AbsolutePathToBase returns the absolute path to common/base directory. -func AbsolutePathToBase() (string, error) { - return filepath.Abs("../../common/base") +// absolutePathToBase returns the absolute path to common/base directory from the given working directory. +func absolutePathToBase() (string, error) { + wd, err := os.Getwd() + if err != nil { + return "", err + } + + // determining the execution path. + var base string + _, err = os.Stat(filepath.Join(wd, "common", "base")) + if os.IsNotExist(err) { + base = "../.." + } else { + base = "" + } + + return filepath.Join(wd, base, "common", "base"), nil } diff --git a/apptests/environment/environment_test.go b/apptests/environment/environment_test.go index 9c09347fa..ba2512f8b 100644 --- a/apptests/environment/environment_test.go +++ b/apptests/environment/environment_test.go @@ -2,12 +2,18 @@ package environment import ( "context" + "fmt" "testing" + "time" "github.com/fluxcd/flux2/v2/pkg/manifestgen" + sourcev1beta2 "github.com/fluxcd/source-controller/api/v1beta2" + "github.com/mesosphere/kommander-applications/apptests/flux" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/wait" + genericCLient "sigs.k8s.io/controller-runtime/pkg/client" ) func TestProvision(t *testing.T) { @@ -41,5 +47,67 @@ func TestProvision(t *testing.T) { assert.NoError(t, err) assert.Equal(t, deploymentObj.Status.Replicas, deploymentObj.Status.ReadyReplicas) } +} + +func TestApplyKustomizations(t *testing.T) { + ctx := context.Background() + env := &Env{} + + // set the kustomizePath to common/base directory + kustomizePath, err := absolutePathToBase() + assert.NoError(t, err) + fmt.Println(kustomizePath) + + // create a kind cluster and install fluxcd on it + cluster, k8sClient, err := provisionEnv(ctx) + assert.NoError(t, err) + defer env.Destroy(ctx) + + env.SetK8sClient(k8sClient) + env.SetCluster(cluster) + + // apply common/base kustomizations + err = env.ApplyKustomizations(ctx, kustomizePath, nil) + assert.NoError(t, err) + // assert that following HelmRepository (as an example) is created + hr := &sourcev1beta2.HelmRepository{ + TypeMeta: metav1.TypeMeta{ + Kind: "source.toolkit.fluxcd.io", + APIVersion: "v1beta2", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "vmware-tanzu.github.io", + Namespace: kommanderFluxNamespace, + }, + } + + client, err := genericCLient.New(env.K8sClient.Config(), genericCLient.Options{Scheme: flux.NewScheme()}) + assert.NoError(t, err) + + // set timeout on the context + ctx, cancel := context.WithTimeout(ctx, 5*time.Minute) + defer cancel() + + // assert that eventually helmRelease object is reconciled + err = wait.PollUntilContextCancel(ctx, pollInterval, true, func(ctx context.Context) (done bool, err error) { + err = client.Get(ctx, genericCLient.ObjectKeyFromObject(hr), hr) + if err != nil { + return false, err + } + for _, cond := range hr.Status.Conditions { + if cond.Status == metav1.ConditionTrue { + return true, nil + } + } + return false, nil + }) + assert.NoError(t, err) + assert.NotNil(t, hr) +} + +func TestAbsolutePathToBase(t *testing.T) { + pathToBase, err := absolutePathToBase() + assert.NoError(t, err) + assert.Contains(t, pathToBase, "kommander-applications/common/base") } diff --git a/apptests/go.mod b/apptests/go.mod index bee58778d..d7a11c760 100644 --- a/apptests/go.mod +++ b/apptests/go.mod @@ -19,6 +19,7 @@ require ( k8s.io/apimachinery v0.28.2 k8s.io/cli-runtime v0.28.2 k8s.io/client-go v0.28.2 + k8s.io/klog/v2 v2.100.1 sigs.k8s.io/cli-utils v0.35.0 sigs.k8s.io/controller-runtime v0.16.2 sigs.k8s.io/kind v0.20.0 @@ -107,7 +108,6 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/component-base v0.28.2 // indirect - k8s.io/klog/v2 v2.100.1 // indirect k8s.io/kube-openapi v0.0.0-20231009201959-f62364c3c354 // indirect k8s.io/kubectl v0.28.2 // indirect k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect