Skip to content

Commit

Permalink
chore: add e2e test for admin import & export (#19564)
Browse files Browse the repository at this point in the history
  • Loading branch information
ilia-medvedev-codefresh authored Aug 16, 2024
1 parent ab1d5b6 commit 47bcb09
Show file tree
Hide file tree
Showing 8 changed files with 305 additions and 5 deletions.
79 changes: 79 additions & 0 deletions test/e2e/admin_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package e2e

import (
"context"
"testing"

"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"

. "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/test/e2e/fixture"
. "github.com/argoproj/argo-cd/v2/test/e2e/fixture/admin"
. "github.com/argoproj/argo-cd/v2/test/e2e/fixture/admin/utils"
appfixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/app"
)

func TestBackupExportImport(t *testing.T) {
var exportRawOutput string
ctx := Given(t)
// Create application in argocd namespace
appctx := appfixture.GivenWithSameState(t)

// Create application in test namespace
appctx.
Path(guestbookPath).
Name("exported-app1").
When().
CreateApp().
Then().
And(func(app *Application) {
assert.Equal(t, "exported-app1", app.Name)
assert.Equal(t, fixture.TestNamespace(), app.Namespace)
})

// Create app in other namespace
appctx.
Path(guestbookPath).
Name("exported-app-other-namespace").
SetAppNamespace(fixture.AppNamespace()).
When().
CreateApp().
Then().
And(func(app *Application) {
assert.Equal(t, "exported-app-other-namespace", app.Name)
assert.Equal(t, fixture.AppNamespace(), app.Namespace)
})

ctx.
When().
RunExport().
Then().
AndCLIOutput(func(output string, err error) {
require.NoError(t, err, "export finished with error")
exportRawOutput = output
}).
AndExportedResources(func(exportResources *ExportedResources, err error) {
require.NoError(t, err, "export format not valid")
assert.True(t, exportResources.HasResource(kube.NewResourceKey("", "ConfigMap", "", "argocd-cm")), "argocd-cm not found in export")
assert.True(t, exportResources.HasResource(kube.NewResourceKey(ApplicationSchemaGroupVersionKind.Group, ApplicationSchemaGroupVersionKind.Kind, "", "exported-app1")), "test namespace application not in export")
assert.True(t, exportResources.HasResource(kube.NewResourceKey(ApplicationSchemaGroupVersionKind.Group, ApplicationSchemaGroupVersionKind.Kind, fixture.AppNamespace(), "exported-app-other-namespace")), "app namespace application not in export")
})

// Test import - clean state
ctx = Given(t)

ctx.
When().
RunImport(exportRawOutput).
Then().
AndCLIOutput(func(output string, err error) {
require.NoError(t, err, "import finished with error")
_, err = fixture.AppClientset.ArgoprojV1alpha1().Applications(fixture.TestNamespace()).Get(context.Background(), "exported-app1", v1.GetOptions{})
require.NoError(t, err, "failed getting test namespace application after import")
_, err = fixture.AppClientset.ArgoprojV1alpha1().Applications(fixture.AppNamespace()).Get(context.Background(), "exported-app-other-namespace", v1.GetOptions{})
require.NoError(t, err, "failed getting app namespace application after import")
})
}
67 changes: 67 additions & 0 deletions test/e2e/fixture/admin/actions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package admin

import (
"github.com/argoproj/argo-cd/v2/test/e2e/fixture"
)

// this implements the "when" part of given/when/then
//
// none of the func implement error checks, and that is complete intended, you should check for errors
// using the Then()
type Actions struct {
context *Context
ignoreErrors bool
lastOutput string
lastError error
}

func (a *Actions) prepareExportCommand() []string {
a.context.t.Helper()
args := []string{"export", "--application-namespaces", fixture.AppNamespace()}

return args
}

func (a *Actions) prepareImportCommand() []string {
a.context.t.Helper()
args := []string{"import", "--application-namespaces", fixture.AppNamespace(), "-"}

return args
}

func (a *Actions) RunExport() *Actions {
a.context.t.Helper()
a.runCli(a.prepareExportCommand()...)
return a
}

func (a *Actions) RunImport(stdin string) *Actions {
a.context.t.Helper()
a.runCliWithStdin(stdin, a.prepareImportCommand()...)
return a
}

func (a *Actions) IgnoreErrors() *Actions {
a.ignoreErrors = true
return a
}

func (a *Actions) DoNotIgnoreErrors() *Actions {
a.ignoreErrors = false
return a
}

func (a *Actions) runCli(args ...string) {
a.context.t.Helper()
a.lastOutput, a.lastError = RunCli(args...)
}

func (a *Actions) runCliWithStdin(stdin string, args ...string) {
a.context.t.Helper()
a.lastOutput, a.lastError = RunCliWithStdin(stdin, args...)
}

func (a *Actions) Then() *Consequences {
a.context.t.Helper()
return &Consequences{a.context, a}
}
37 changes: 37 additions & 0 deletions test/e2e/fixture/admin/consequences.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package admin

import (
. "github.com/argoproj/argo-cd/v2/test/e2e/fixture/admin/utils"
)

// this implements the "then" part of given/when/then
type Consequences struct {
context *Context
actions *Actions
}

func (c *Consequences) And(block func()) *Consequences {
c.context.t.Helper()
block()
return c
}

func (c *Consequences) AndCLIOutput(block func(output string, err error)) *Consequences {
c.context.t.Helper()
block(c.actions.lastOutput, c.actions.lastError)
return c
}

// For use after running export with the exported resources desirialized
func (c *Consequences) AndExportedResources(block func(resources *ExportedResources, err error)) {
result, err := GetExportedResourcesFromOutput(c.actions.lastOutput)
block(&result, err)
}

func (c *Consequences) Given() *Context {
return c.context
}

func (c *Consequences) When() *Actions {
return c.actions
}
41 changes: 41 additions & 0 deletions test/e2e/fixture/admin/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package admin

import (
"testing"

"github.com/argoproj/argo-cd/v2/test/e2e/fixture"
"github.com/argoproj/argo-cd/v2/util/env"
)

// this implements the "given" part of given/when/then
type Context struct {
t *testing.T
// seconds
timeout int
name string
}

func Given(t *testing.T) *Context {
fixture.EnsureCleanState(t)
return GivenWithSameState(t)
}

func GivenWithSameState(t *testing.T) *Context {
// ARGOCE_E2E_DEFAULT_TIMEOUT can be used to override the default timeout
// for any context.
timeout := env.ParseNumFromEnv("ARGOCD_E2E_DEFAULT_TIMEOUT", 20, 0, 180)
return &Context{
t: t,
name: fixture.Name(),
timeout: timeout,
}
}

func (c *Context) And(block func()) *Context {
block()
return c
}

func (c *Context) When() *Actions {
return &Actions{context: c}
}
15 changes: 15 additions & 0 deletions test/e2e/fixture/admin/fixture.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package admin

import (
"github.com/argoproj/argo-cd/v2/test/e2e/fixture"
)

// For admin CLI with kubernetes context
func RunCli(args ...string) (string, error) {
return RunCliWithStdin("", args...)
}

func RunCliWithStdin(stdin string, args ...string) (string, error) {
args = append([]string{"admin", "--namespace", fixture.TestNamespace()}, args...)
return fixture.RunCliWithStdin(stdin, true, args...)
}
48 changes: 48 additions & 0 deletions test/e2e/fixture/admin/utils/backup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package utils

import (
"fmt"
"strings"

kube "github.com/argoproj/gitops-engine/pkg/utils/kube"
yaml "gopkg.in/yaml.v3"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

type ExportedResources []unstructured.Unstructured

func GetExportedResourcesFromOutput(output string) (ExportedResources, error) {
var resources []unstructured.Unstructured
docs := strings.Split(output, "---")

for _, doc := range docs {
doc = strings.TrimSpace(doc)
if len(doc) == 0 {
continue
}

var resourceData map[string]interface{}

if err := yaml.Unmarshal([]byte(doc), &resourceData); err != nil {
return nil, fmt.Errorf("error unmarshaling YAML: %w", err)
}

resource := unstructured.Unstructured{Object: resourceData}
resources = append(resources, resource)
}

return resources, nil
}

func (e ExportedResources) HasResource(resource kube.ResourceKey) bool {
for _, res := range e {
if res.GetObjectKind().GroupVersionKind().Group == resource.Group &&
res.GetKind() == resource.Kind &&
res.GetName() == resource.Name &&
res.GetNamespace() == resource.Namespace {
return true
}
}

return false
}
19 changes: 16 additions & 3 deletions test/e2e/fixture/fixture.go
Original file line number Diff line number Diff line change
Expand Up @@ -741,15 +741,20 @@ func RunCliWithRetry(maxRetries int, args ...string) (string, error) {
}

func RunCli(args ...string) (string, error) {
return RunCliWithStdin("", args...)
return RunCliWithStdin("", false, args...)
}

func RunCliWithStdin(stdin string, args ...string) (string, error) {
func RunCliWithStdin(stdin string, isKubeConextOnlyCli bool, args ...string) (string, error) {
if plainText {
args = append(args, "--plaintext")
}

args = append(args, "--server", apiServerAddress, "--auth-token", token, "--insecure")
// For commands executed with Kubernetes context server argument causes a conflict (for those commands server argument is for KubeAPI server), also authentication is not required
if !isKubeConextOnlyCli {
args = append(args, "--server", apiServerAddress, "--auth-token", token)
}

args = append(args, "--insecure")

return RunWithStdin(stdin, "", "../../dist/argocd", args...)
}
Expand Down Expand Up @@ -1010,3 +1015,11 @@ func RecordTestRun(t *testing.T) {
t.Fatalf("could not write to %s: %v", rf, err)
}
}

func GetApiServerAddress() string {
return apiServerAddress
}

func GetToken() string {
return token
}
4 changes: 2 additions & 2 deletions test/e2e/project_management_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,12 @@ func TestProjectCreation(t *testing.T) {
require.NoError(t, err)

// fail without upsert flag
_, err = fixture.RunCliWithStdin(stdinString, "proj", "create",
_, err = fixture.RunCliWithStdin(stdinString, false, "proj", "create",
"-f", "-")
require.Error(t, err)

// succeed with the upsert flag
_, err = fixture.RunCliWithStdin(stdinString, "proj", "create",
_, err = fixture.RunCliWithStdin(stdinString, false, "proj", "create",
"-f", "-", "--upsert")
require.NoError(t, err)
proj, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{})
Expand Down

0 comments on commit 47bcb09

Please sign in to comment.