github.com/argoproj/argo-cd/v3@v3.2.1/test/e2e/fixture/app/actions.go (about)

     1  package app
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"os"
     7  	"slices"
     8  	"strconv"
     9  
    10  	rbacv1 "k8s.io/api/rbac/v1"
    11  
    12  	log "github.com/sirupsen/logrus"
    13  	"github.com/stretchr/testify/require"
    14  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    15  
    16  	client "github.com/argoproj/argo-cd/v3/pkg/apiclient/application"
    17  	"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
    18  	"github.com/argoproj/argo-cd/v3/test/e2e/fixture"
    19  	"github.com/argoproj/argo-cd/v3/util/grpc"
    20  )
    21  
    22  // this implements the "when" part of given/when/then
    23  //
    24  // none of the func implement error checks, and that is complete intended, you should check for errors
    25  // using the Then()
    26  type Actions struct {
    27  	context      *Context
    28  	lastOutput   string
    29  	lastError    error
    30  	ignoreErrors bool
    31  }
    32  
    33  func (a *Actions) IgnoreErrors() *Actions {
    34  	a.ignoreErrors = true
    35  	return a
    36  }
    37  
    38  func (a *Actions) DoNotIgnoreErrors() *Actions {
    39  	a.ignoreErrors = false
    40  	return a
    41  }
    42  
    43  func (a *Actions) PatchFile(file string, jsonPatch string) *Actions {
    44  	a.context.t.Helper()
    45  	fixture.Patch(a.context.t, a.context.path+"/"+file, jsonPatch)
    46  	return a
    47  }
    48  
    49  func (a *Actions) DeleteFile(file string) *Actions {
    50  	a.context.t.Helper()
    51  	fixture.Delete(a.context.t, a.context.path+"/"+file)
    52  	return a
    53  }
    54  
    55  func (a *Actions) WriteFile(fileName, fileContents string) *Actions {
    56  	a.context.t.Helper()
    57  	fixture.WriteFile(a.context.t, a.context.path+"/"+fileName, fileContents)
    58  	return a
    59  }
    60  
    61  func (a *Actions) AddFile(fileName, fileContents string) *Actions {
    62  	a.context.t.Helper()
    63  	fixture.AddFile(a.context.t, a.context.path+"/"+fileName, fileContents)
    64  	return a
    65  }
    66  
    67  func (a *Actions) AddSignedFile(fileName, fileContents string) *Actions {
    68  	a.context.t.Helper()
    69  	fixture.AddSignedFile(a.context.t, a.context.path+"/"+fileName, fileContents)
    70  	return a
    71  }
    72  
    73  func (a *Actions) AddSignedTag(name string) *Actions {
    74  	a.context.t.Helper()
    75  	fixture.AddSignedTag(a.context.t, name)
    76  	return a
    77  }
    78  
    79  func (a *Actions) AddTag(name string) *Actions {
    80  	a.context.t.Helper()
    81  	fixture.AddTag(a.context.t, name)
    82  	return a
    83  }
    84  
    85  func (a *Actions) AddAnnotatedTag(name string, message string) *Actions {
    86  	a.context.t.Helper()
    87  	fixture.AddAnnotatedTag(a.context.t, name, message)
    88  	return a
    89  }
    90  
    91  func (a *Actions) AddTagWithForce(name string) *Actions {
    92  	a.context.t.Helper()
    93  	fixture.AddTagWithForce(a.context.t, name)
    94  	return a
    95  }
    96  
    97  func (a *Actions) RemoveSubmodule() *Actions {
    98  	a.context.t.Helper()
    99  	fixture.RemoveSubmodule(a.context.t)
   100  	return a
   101  }
   102  
   103  func (a *Actions) CreateFromPartialFile(data string, flags ...string) *Actions {
   104  	a.context.t.Helper()
   105  	tmpFile, err := os.CreateTemp("", "")
   106  	require.NoError(a.context.t, err)
   107  	_, err = tmpFile.WriteString(data)
   108  	require.NoError(a.context.t, err)
   109  
   110  	args := append([]string{
   111  		"app", "create",
   112  		"-f", tmpFile.Name(),
   113  		"--name", a.context.AppName(),
   114  		"--repo", fixture.RepoURL(a.context.repoURLType),
   115  		"--dest-server", a.context.destServer,
   116  		"--dest-namespace", fixture.DeploymentNamespace(),
   117  	}, flags...)
   118  	if a.context.appNamespace != "" {
   119  		args = append(args, "--app-namespace", a.context.appNamespace)
   120  	}
   121  	defer tmpFile.Close()
   122  	a.runCli(args...)
   123  	return a
   124  }
   125  
   126  func (a *Actions) CreateFromFile(handler func(app *v1alpha1.Application), flags ...string) *Actions {
   127  	a.context.t.Helper()
   128  	app := &v1alpha1.Application{
   129  		ObjectMeta: metav1.ObjectMeta{
   130  			Name:      a.context.AppName(),
   131  			Namespace: a.context.AppNamespace(),
   132  		},
   133  		Spec: v1alpha1.ApplicationSpec{
   134  			Project: a.context.project,
   135  			Source: &v1alpha1.ApplicationSource{
   136  				RepoURL: fixture.RepoURL(a.context.repoURLType),
   137  				Path:    a.context.path,
   138  			},
   139  			Destination: v1alpha1.ApplicationDestination{
   140  				Server:    a.context.destServer,
   141  				Namespace: fixture.DeploymentNamespace(),
   142  			},
   143  		},
   144  	}
   145  	source := app.Spec.GetSource()
   146  	if a.context.namePrefix != "" || a.context.nameSuffix != "" {
   147  		source.Kustomize = &v1alpha1.ApplicationSourceKustomize{
   148  			NamePrefix: a.context.namePrefix,
   149  			NameSuffix: a.context.nameSuffix,
   150  		}
   151  	}
   152  	if a.context.configManagementPlugin != "" {
   153  		source.Plugin = &v1alpha1.ApplicationSourcePlugin{
   154  			Name: a.context.configManagementPlugin,
   155  		}
   156  	}
   157  
   158  	if len(a.context.parameters) > 0 {
   159  		log.Fatal("v1alpha1.Application parameters or json tlas are not supported")
   160  	}
   161  
   162  	if a.context.directoryRecurse {
   163  		source.Directory = &v1alpha1.ApplicationSourceDirectory{Recurse: true}
   164  	}
   165  	app.Spec.Source = &source
   166  
   167  	handler(app)
   168  	data := grpc.MustMarshal(app)
   169  	tmpFile, err := os.CreateTemp("", "")
   170  	require.NoError(a.context.t, err)
   171  	_, err = tmpFile.Write(data)
   172  	require.NoError(a.context.t, err)
   173  
   174  	args := append([]string{
   175  		"app", "create",
   176  		"-f", tmpFile.Name(),
   177  	}, flags...)
   178  	defer tmpFile.Close()
   179  	a.runCli(args...)
   180  	return a
   181  }
   182  
   183  func (a *Actions) CreateMultiSourceAppFromFile(flags ...string) *Actions {
   184  	a.context.t.Helper()
   185  	app := &v1alpha1.Application{
   186  		ObjectMeta: metav1.ObjectMeta{
   187  			Name:      a.context.AppName(),
   188  			Namespace: a.context.AppNamespace(),
   189  		},
   190  		Spec: v1alpha1.ApplicationSpec{
   191  			Project: a.context.project,
   192  			Sources: a.context.sources,
   193  			Destination: v1alpha1.ApplicationDestination{
   194  				Server:    a.context.destServer,
   195  				Namespace: fixture.DeploymentNamespace(),
   196  			},
   197  			SyncPolicy: &v1alpha1.SyncPolicy{
   198  				Automated: &v1alpha1.SyncPolicyAutomated{
   199  					SelfHeal: true,
   200  				},
   201  			},
   202  		},
   203  	}
   204  
   205  	data := grpc.MustMarshal(app)
   206  	tmpFile, err := os.CreateTemp("", "")
   207  	require.NoError(a.context.t, err)
   208  	_, err = tmpFile.Write(data)
   209  	require.NoError(a.context.t, err)
   210  
   211  	args := append([]string{
   212  		"app", "create",
   213  		"-f", tmpFile.Name(),
   214  	}, flags...)
   215  	defer tmpFile.Close()
   216  	a.runCli(args...)
   217  	return a
   218  }
   219  
   220  func (a *Actions) CreateWithNoNameSpace(args ...string) *Actions {
   221  	args = a.prepareCreateAppArgs(args)
   222  	//  are you adding new context values? if you only use them for this func, then use args instead
   223  	a.runCli(args...)
   224  	return a
   225  }
   226  
   227  func (a *Actions) CreateApp(args ...string) *Actions {
   228  	args = a.prepareCreateAppArgs(args)
   229  	args = append(args, "--dest-namespace", fixture.DeploymentNamespace())
   230  
   231  	//  are you adding new context values? if you only use them for this func, then use args instead
   232  	a.runCli(args...)
   233  
   234  	return a
   235  }
   236  
   237  func (a *Actions) prepareCreateAppArgs(args []string) []string {
   238  	a.context.t.Helper()
   239  	args = append([]string{
   240  		"app", "create", a.context.AppQualifiedName(),
   241  	}, args...)
   242  
   243  	if a.context.drySourceRevision != "" || a.context.drySourcePath != "" || a.context.syncSourcePath != "" || a.context.syncSourceBranch != "" || a.context.hydrateToBranch != "" {
   244  		args = append(args, "--dry-source-repo", fixture.RepoURL(a.context.repoURLType))
   245  	} else {
   246  		var repo string
   247  		if a.context.ociRegistryPath != "" && a.context.repoURLType == fixture.RepoURLTypeOCI {
   248  			repo = fmt.Sprintf("%s/%s", a.context.ociRegistry, a.context.ociRegistryPath)
   249  		} else {
   250  			repo = fixture.RepoURL(a.context.repoURLType)
   251  		}
   252  		args = append(args, "--repo", repo)
   253  	}
   254  
   255  	if a.context.destName != "" && a.context.isDestServerInferred && !slices.Contains(args, "--dest-server") {
   256  		args = append(args, "--dest-name", a.context.destName)
   257  	} else {
   258  		args = append(args, "--dest-server", a.context.destServer)
   259  	}
   260  	if a.context.path != "" {
   261  		args = append(args, "--path", a.context.path)
   262  	}
   263  
   264  	if a.context.drySourceRevision != "" {
   265  		args = append(args, "--dry-source-revision", a.context.drySourceRevision)
   266  	}
   267  
   268  	if a.context.drySourcePath != "" {
   269  		args = append(args, "--dry-source-path", a.context.drySourcePath)
   270  	}
   271  
   272  	if a.context.syncSourceBranch != "" {
   273  		args = append(args, "--sync-source-branch", a.context.syncSourceBranch)
   274  	}
   275  
   276  	if a.context.syncSourcePath != "" {
   277  		args = append(args, "--sync-source-path", a.context.syncSourcePath)
   278  	}
   279  
   280  	if a.context.hydrateToBranch != "" {
   281  		args = append(args, "--hydrate-to-branch", a.context.hydrateToBranch)
   282  	}
   283  
   284  	if a.context.chart != "" {
   285  		args = append(args, "--helm-chart", a.context.chart)
   286  	}
   287  
   288  	if a.context.env != "" {
   289  		args = append(args, "--env", a.context.env)
   290  	}
   291  
   292  	for _, parameter := range a.context.parameters {
   293  		args = append(args, "--parameter", parameter)
   294  	}
   295  
   296  	args = append(args, "--project", a.context.project)
   297  
   298  	if a.context.namePrefix != "" {
   299  		args = append(args, "--nameprefix", a.context.namePrefix)
   300  	}
   301  
   302  	if a.context.nameSuffix != "" {
   303  		args = append(args, "--namesuffix", a.context.nameSuffix)
   304  	}
   305  
   306  	if a.context.configManagementPlugin != "" {
   307  		args = append(args, "--config-management-plugin", a.context.configManagementPlugin)
   308  	}
   309  
   310  	if a.context.revision != "" {
   311  		args = append(args, "--revision", a.context.revision)
   312  	}
   313  	if a.context.helmPassCredentials {
   314  		args = append(args, "--helm-pass-credentials")
   315  	}
   316  	if a.context.helmSkipCrds {
   317  		args = append(args, "--helm-skip-crds")
   318  	}
   319  	if a.context.helmSkipSchemaValidation {
   320  		args = append(args, "--helm-skip-schema-validation")
   321  	}
   322  	if a.context.helmSkipTests {
   323  		args = append(args, "--helm-skip-tests")
   324  	}
   325  	return args
   326  }
   327  
   328  func (a *Actions) Declarative(filename string) *Actions {
   329  	a.context.t.Helper()
   330  	return a.DeclarativeWithCustomRepo(filename, fixture.RepoURL(a.context.repoURLType))
   331  }
   332  
   333  func (a *Actions) DeclarativeWithCustomRepo(filename string, repoURL string) *Actions {
   334  	a.context.t.Helper()
   335  	values := map[string]any{
   336  		"ArgoCDNamespace":     fixture.TestNamespace(),
   337  		"DeploymentNamespace": fixture.DeploymentNamespace(),
   338  		"Name":                a.context.AppName(),
   339  		"Path":                a.context.path,
   340  		"Project":             a.context.project,
   341  		"RepoURL":             repoURL,
   342  	}
   343  	a.lastOutput, a.lastError = fixture.Declarative(a.context.t, filename, values)
   344  	a.verifyAction()
   345  	return a
   346  }
   347  
   348  func (a *Actions) PatchApp(patch string) *Actions {
   349  	a.context.t.Helper()
   350  	a.runCli("app", "patch", a.context.AppQualifiedName(), "--patch", patch)
   351  	return a
   352  }
   353  
   354  func (a *Actions) PatchAppHttp(patch string) *Actions { //nolint:revive //FIXME(var-naming)
   355  	a.context.t.Helper()
   356  	var application v1alpha1.Application
   357  	patchType := "merge"
   358  	appName := a.context.AppQualifiedName()
   359  	appNamespace := a.context.AppNamespace()
   360  	patchRequest := &client.ApplicationPatchRequest{
   361  		Name:         &appName,
   362  		PatchType:    &patchType,
   363  		Patch:        &patch,
   364  		AppNamespace: &appNamespace,
   365  	}
   366  	jsonBytes, err := json.MarshalIndent(patchRequest, "", "  ")
   367  	require.NoError(a.context.t, err)
   368  	err = fixture.DoHttpJsonRequest("PATCH",
   369  		fmt.Sprintf("/api/v1/applications/%v", appName),
   370  		&application,
   371  		jsonBytes...)
   372  	require.NoError(a.context.t, err)
   373  	return a
   374  }
   375  
   376  func (a *Actions) AppSet(flags ...string) *Actions {
   377  	a.context.t.Helper()
   378  	args := []string{"app", "set", a.context.AppQualifiedName()}
   379  	args = append(args, flags...)
   380  	a.runCli(args...)
   381  	return a
   382  }
   383  
   384  func (a *Actions) AppUnSet(flags ...string) *Actions {
   385  	a.context.t.Helper()
   386  	args := []string{"app", "unset", a.context.AppQualifiedName()}
   387  	args = append(args, flags...)
   388  	a.runCli(args...)
   389  	return a
   390  }
   391  
   392  func (a *Actions) Sync(args ...string) *Actions {
   393  	a.context.t.Helper()
   394  	args = append([]string{"app", "sync"}, args...)
   395  	if a.context.name != "" {
   396  		args = append(args, a.context.AppQualifiedName())
   397  	}
   398  	args = append(args, "--timeout", strconv.Itoa(a.context.timeout))
   399  
   400  	if a.context.async {
   401  		args = append(args, "--async")
   402  	}
   403  
   404  	if a.context.prune {
   405  		args = append(args, "--prune")
   406  	}
   407  
   408  	if a.context.resource != "" {
   409  		// Waiting for the app to be successfully created.
   410  		// Else the sync would fail to retrieve the app resources.
   411  		a.context.Sleep(5)
   412  		args = append(args, "--resource", a.context.resource)
   413  	}
   414  
   415  	if a.context.localPath != "" {
   416  		args = append(args, "--local", a.context.localPath)
   417  	}
   418  
   419  	if a.context.force {
   420  		args = append(args, "--force")
   421  	}
   422  
   423  	if a.context.applyOutOfSyncOnly {
   424  		args = append(args, "--apply-out-of-sync-only")
   425  	}
   426  
   427  	if a.context.replace {
   428  		args = append(args, "--replace")
   429  	}
   430  
   431  	//  are you adding new context values? if you only use them for this func, then use args instead
   432  
   433  	a.runCli(args...)
   434  
   435  	return a
   436  }
   437  
   438  func (a *Actions) ConfirmDeletion() *Actions {
   439  	a.context.t.Helper()
   440  
   441  	a.runCli("app", "confirm-deletion", a.context.AppQualifiedName())
   442  
   443  	return a
   444  }
   445  
   446  func (a *Actions) TerminateOp() *Actions {
   447  	a.context.t.Helper()
   448  	a.runCli("app", "terminate-op", a.context.AppQualifiedName())
   449  	return a
   450  }
   451  
   452  func (a *Actions) Refresh(refreshType v1alpha1.RefreshType) *Actions {
   453  	a.context.t.Helper()
   454  	flag := map[v1alpha1.RefreshType]string{
   455  		v1alpha1.RefreshTypeNormal: "--refresh",
   456  		v1alpha1.RefreshTypeHard:   "--hard-refresh",
   457  	}[refreshType]
   458  
   459  	a.runCli("app", "get", a.context.AppQualifiedName(), flag)
   460  
   461  	return a
   462  }
   463  
   464  func (a *Actions) Get() *Actions {
   465  	a.context.t.Helper()
   466  	a.runCli("app", "get", a.context.AppQualifiedName())
   467  	return a
   468  }
   469  
   470  func (a *Actions) Delete(cascade bool) *Actions {
   471  	a.context.t.Helper()
   472  	a.runCli("app", "delete", a.context.AppQualifiedName(), fmt.Sprintf("--cascade=%v", cascade), "--yes")
   473  	return a
   474  }
   475  
   476  func (a *Actions) DeleteBySelector(selector string) *Actions {
   477  	a.context.t.Helper()
   478  	a.runCli("app", "delete", "--selector="+selector, "--yes")
   479  	return a
   480  }
   481  
   482  func (a *Actions) DeleteBySelectorWithWait(selector string) *Actions {
   483  	a.context.t.Helper()
   484  	a.runCli("app", "delete", "--selector="+selector, "--yes", "--wait")
   485  	return a
   486  }
   487  
   488  func (a *Actions) Wait(args ...string) *Actions {
   489  	a.context.t.Helper()
   490  	args = append([]string{"app", "wait"}, args...)
   491  	if a.context.name != "" {
   492  		args = append(args, a.context.AppQualifiedName())
   493  	}
   494  	args = append(args, "--timeout", strconv.Itoa(a.context.timeout))
   495  	a.runCli(args...)
   496  	return a
   497  }
   498  
   499  func (a *Actions) SetParamInSettingConfigMap(key, value string) *Actions {
   500  	a.context.t.Helper()
   501  	require.NoError(a.context.t, fixture.SetParamInSettingConfigMap(key, value))
   502  	return a
   503  }
   504  
   505  func (a *Actions) And(block func()) *Actions {
   506  	a.context.t.Helper()
   507  	block()
   508  	return a
   509  }
   510  
   511  func (a *Actions) Then() *Consequences {
   512  	a.context.t.Helper()
   513  	return &Consequences{a.context, a, 15}
   514  }
   515  
   516  func (a *Actions) runCli(args ...string) {
   517  	a.context.t.Helper()
   518  	a.lastOutput, a.lastError = fixture.RunCli(args...)
   519  	a.verifyAction()
   520  }
   521  
   522  func (a *Actions) verifyAction() {
   523  	a.context.t.Helper()
   524  	if !a.ignoreErrors {
   525  		a.Then().Expect(Success(""))
   526  	}
   527  }
   528  
   529  func (a *Actions) SetTrackingMethod(trackingMethod string) *Actions {
   530  	a.context.t.Helper()
   531  	require.NoError(a.context.t, fixture.SetTrackingMethod(trackingMethod))
   532  	return a
   533  }
   534  
   535  func (a *Actions) SetInstallationID(installationID string) *Actions {
   536  	a.context.t.Helper()
   537  	require.NoError(a.context.t, fixture.SetInstallationID(installationID))
   538  	return a
   539  }
   540  
   541  func (a *Actions) SetTrackingLabel(trackingLabel string) *Actions {
   542  	a.context.t.Helper()
   543  	require.NoError(a.context.t, fixture.SetTrackingLabel(trackingLabel))
   544  	return a
   545  }
   546  
   547  func (a *Actions) WithImpersonationEnabled(serviceAccountName string, policyRules []rbacv1.PolicyRule) *Actions {
   548  	a.context.t.Helper()
   549  	require.NoError(a.context.t, fixture.SetImpersonationEnabled("true"))
   550  	if serviceAccountName == "" || policyRules == nil {
   551  		return a
   552  	}
   553  	require.NoError(a.context.t, fixture.CreateRBACResourcesForImpersonation(serviceAccountName, policyRules))
   554  	return a
   555  }
   556  
   557  func (a *Actions) WithImpersonationDisabled() *Actions {
   558  	a.context.t.Helper()
   559  	require.NoError(a.context.t, fixture.SetImpersonationEnabled("false"))
   560  	return a
   561  }