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

     1  package e2e
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"sort"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/argoproj/gitops-engine/pkg/health"
    12  	. "github.com/argoproj/gitops-engine/pkg/sync/common"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  
    16  	. "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
    17  	"github.com/argoproj/argo-cd/v3/test/e2e/fixture"
    18  	. "github.com/argoproj/argo-cd/v3/test/e2e/fixture/app"
    19  	"github.com/argoproj/argo-cd/v3/util/errors"
    20  )
    21  
    22  // make sure we can echo back the Git creds
    23  func TestCustomToolWithGitCreds(t *testing.T) {
    24  	ctx := Given(t)
    25  	ctx.
    26  		And(func() {
    27  			go startCMPServer(t, "./testdata/cmp-gitcreds")
    28  			time.Sleep(100 * time.Millisecond)
    29  			t.Setenv("ARGOCD_BINARY_NAME", "argocd")
    30  		}).
    31  		CustomCACertAdded().
    32  		// add the private repo with credentials
    33  		HTTPSRepoURLAdded(true).
    34  		RepoURLType(fixture.RepoURLTypeHTTPS).
    35  		Path("cmp-gitcreds").
    36  		When().
    37  		CreateApp().
    38  		Sync().
    39  		Then().
    40  		Expect(OperationPhaseIs(OperationSucceeded)).
    41  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
    42  		Expect(HealthIs(health.HealthStatusHealthy)).
    43  		And(func(_ *Application) {
    44  			output, err := fixture.Run("", "kubectl", "-n", fixture.DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.GitAskpass}")
    45  			require.NoError(t, err)
    46  			assert.Equal(t, "argocd", output)
    47  		})
    48  }
    49  
    50  // make sure we can echo back the Git creds
    51  func TestCustomToolWithGitCredsTemplate(t *testing.T) {
    52  	ctx := Given(t)
    53  	ctx.
    54  		And(func() {
    55  			go startCMPServer(t, "./testdata/cmp-gitcredstemplate")
    56  			time.Sleep(100 * time.Millisecond)
    57  			t.Setenv("ARGOCD_BINARY_NAME", "argocd")
    58  		}).
    59  		CustomCACertAdded().
    60  		// add the git creds template
    61  		HTTPSCredentialsUserPassAdded().
    62  		// add the private repo without credentials
    63  		HTTPSRepoURLAdded(false).
    64  		RepoURLType(fixture.RepoURLTypeHTTPS).
    65  		Path("cmp-gitcredstemplate").
    66  		When().
    67  		CreateApp().
    68  		Sync().
    69  		Then().
    70  		Expect(OperationPhaseIs(OperationSucceeded)).
    71  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
    72  		Expect(HealthIs(health.HealthStatusHealthy)).
    73  		And(func(_ *Application) {
    74  			output, err := fixture.Run("", "kubectl", "-n", fixture.DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.GitAskpass}")
    75  			require.NoError(t, err)
    76  			assert.Equal(t, "argocd", output)
    77  		}).
    78  		And(func(_ *Application) {
    79  			output, err := fixture.Run("", "kubectl", "-n", fixture.DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.GitUsername}")
    80  			require.NoError(t, err)
    81  			assert.Empty(t, output)
    82  		}).
    83  		And(func(_ *Application) {
    84  			output, err := fixture.Run("", "kubectl", "-n", fixture.DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.GitPassword}")
    85  			require.NoError(t, err)
    86  			assert.Empty(t, output)
    87  		})
    88  }
    89  
    90  // make sure we can read the Git creds stored in a temporary file
    91  func TestCustomToolWithSSHGitCreds(t *testing.T) {
    92  	ctx := Given(t)
    93  	// path does not matter, we ignore it
    94  	ctx.
    95  		And(func() {
    96  			go startCMPServer(t, "./testdata/cmp-gitsshcreds")
    97  			time.Sleep(100 * time.Millisecond)
    98  			t.Setenv("ARGOCD_BINARY_NAME", "argocd")
    99  		}).
   100  		// add the private repo with ssh credentials
   101  		CustomSSHKnownHostsAdded().
   102  		SSHRepoURLAdded(true).
   103  		RepoURLType(fixture.RepoURLTypeSSH).
   104  		Path("cmp-gitsshcreds").
   105  		When().
   106  		CreateApp().
   107  		Sync().
   108  		Sync().
   109  		Then().
   110  		Expect(OperationPhaseIs(OperationSucceeded)).
   111  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   112  		Expect(HealthIs(health.HealthStatusHealthy)).
   113  		And(func(_ *Application) {
   114  			output, err := fixture.Run("", "kubectl", "-n", fixture.DeploymentNamespace(), "get", "cm", fixture.Name(), "-o", "jsonpath={.metadata.annotations.GitSSHCommand}")
   115  			require.NoError(t, err)
   116  			assert.Regexp(t, `-i [^ ]+`, output, "test plugin expects $GIT_SSH_COMMAND to contain the option '-i <path to ssh private key>'")
   117  		}).
   118  		And(func(_ *Application) {
   119  			output, err := fixture.Run("", "kubectl", "-n", fixture.DeploymentNamespace(), "get", "cm", fixture.Name(), "-o", "jsonpath={.metadata.annotations.GitSSHCredsFileSHA}")
   120  			require.NoError(t, err)
   121  			assert.Regexp(t, `\w+\s+[\/\w]+`, output, "git ssh credentials file should be able to be read, hashing the contents")
   122  		})
   123  }
   124  
   125  func TestCustomToolWithSSHGitCredsDisabled(t *testing.T) {
   126  	ctx := Given(t)
   127  	// path does not matter, we ignore it
   128  	ctx.
   129  		And(func() {
   130  			go startCMPServer(t, "./testdata/cmp-gitsshcreds-disable-provide")
   131  			time.Sleep(100 * time.Millisecond)
   132  			t.Setenv("ARGOCD_BINARY_NAME", "argocd")
   133  		}).
   134  		CustomCACertAdded().
   135  		// add the private repo with ssh credentials
   136  		CustomSSHKnownHostsAdded().
   137  		SSHRepoURLAdded(true).
   138  		RepoURLType(fixture.RepoURLTypeSSH).
   139  		Path("cmp-gitsshcreds").
   140  		When().
   141  		IgnoreErrors().
   142  		CreateApp("--validate=false").
   143  		Sync().
   144  		Then().
   145  		Expect(OperationPhaseIs(OperationError)).
   146  		Expect(SyncStatusIs(SyncStatusCodeUnknown))
   147  }
   148  
   149  // make sure we can echo back the env
   150  func TestCustomToolWithEnv(t *testing.T) {
   151  	ctx := Given(t)
   152  	ctx.
   153  		And(func() {
   154  			go startCMPServer(t, "./testdata/cmp-fileName")
   155  			time.Sleep(100 * time.Millisecond)
   156  			t.Setenv("ARGOCD_BINARY_NAME", "argocd")
   157  		}).
   158  		// does not matter what the path is
   159  		Path("cmp-fileName").
   160  		When().
   161  		CreateFromFile(func(app *Application) {
   162  			app.Spec.Source.Plugin = &ApplicationSourcePlugin{
   163  				Env: Env{{
   164  					Name:  "FOO",
   165  					Value: "bar",
   166  				}, {
   167  					Name:  "EMPTY",
   168  					Value: "",
   169  				}},
   170  			}
   171  		}).
   172  		Sync().
   173  		Then().
   174  		Expect(OperationPhaseIs(OperationSucceeded)).
   175  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   176  		Expect(HealthIs(health.HealthStatusHealthy)).
   177  		And(func(_ *Application) {
   178  			output, err := fixture.Run("", "kubectl", "-n", fixture.DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.Bar}")
   179  			require.NoError(t, err)
   180  			assert.Equal(t, "baz", output)
   181  		}).
   182  		And(func(_ *Application) {
   183  			output, err := fixture.Run("", "kubectl", "-n", fixture.DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.Foo}")
   184  			require.NoError(t, err)
   185  			assert.Equal(t, "bar", output)
   186  		}).
   187  		And(func(_ *Application) {
   188  			expectedKubeVersion := fixture.GetVersions(t).ServerVersion.Format("%s.%s")
   189  			output, err := fixture.Run("", "kubectl", "-n", fixture.DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.KubeVersion}")
   190  			require.NoError(t, err)
   191  			assert.Equal(t, expectedKubeVersion, output)
   192  		}).
   193  		And(func(_ *Application) {
   194  			expectedAPIVersion := fixture.GetApiResources(t)
   195  			expectedAPIVersionSlice := strings.Split(expectedAPIVersion, ",")
   196  			sort.Strings(expectedAPIVersionSlice)
   197  
   198  			output, err := fixture.Run("", "kubectl", "-n", fixture.DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.KubeApiVersion}")
   199  			require.NoError(t, err)
   200  			outputSlice := strings.Split(output, ",")
   201  			sort.Strings(outputSlice)
   202  
   203  			assert.Equal(t, expectedAPIVersionSlice, outputSlice)
   204  		})
   205  }
   206  
   207  // make sure we can sync and diff with --local
   208  func TestCustomToolSyncAndDiffLocal(t *testing.T) {
   209  	testdataPath, err := filepath.Abs("testdata")
   210  	require.NoError(t, err)
   211  	ctx := Given(t)
   212  	appPath := filepath.Join(testdataPath, "guestbook")
   213  	ctx.
   214  		And(func() {
   215  			go startCMPServer(t, "./testdata/cmp-kustomize")
   216  			time.Sleep(100 * time.Millisecond)
   217  			t.Setenv("ARGOCD_BINARY_NAME", "argocd")
   218  		}).
   219  		// does not matter what the path is
   220  		Path("guestbook").
   221  		When().
   222  		CreateApp("--config-management-plugin", "cmp-kustomize-v1.0").
   223  		Sync("--local", appPath, "--local-repo-root", testdataPath).
   224  		Then().
   225  		Expect(OperationPhaseIs(OperationSucceeded)).
   226  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   227  		Expect(HealthIs(health.HealthStatusHealthy)).
   228  		And(func(_ *Application) {
   229  			errors.NewHandler(t).FailOnErr(fixture.RunCli("app", "sync", ctx.AppName(), "--local", appPath, "--local-repo-root", testdataPath))
   230  		}).
   231  		And(func(_ *Application) {
   232  			errors.NewHandler(t).FailOnErr(fixture.RunCli("app", "diff", ctx.AppName(), "--local", appPath, "--local-repo-root", testdataPath))
   233  		})
   234  }
   235  
   236  func startCMPServer(t *testing.T, configFile string) {
   237  	t.Helper()
   238  	pluginSockFilePath := fixture.TmpDir + fixture.PluginSockFilePath
   239  	t.Setenv("ARGOCD_BINARY_NAME", "argocd-cmp-server")
   240  	// ARGOCD_PLUGINSOCKFILEPATH should be set as the same value as repo server env var
   241  	t.Setenv("ARGOCD_PLUGINSOCKFILEPATH", pluginSockFilePath)
   242  	if _, err := os.Stat(pluginSockFilePath); os.IsNotExist(err) {
   243  		// path/to/whatever does not exist
   244  		err := os.Mkdir(pluginSockFilePath, 0o700)
   245  		require.NoError(t, err)
   246  	}
   247  	errors.NewHandler(t).FailOnErr(fixture.RunWithStdin("", "", "../../dist/argocd", "--config-dir-path", configFile))
   248  }
   249  
   250  // Discover by fileName
   251  func TestCMPDiscoverWithFileName(t *testing.T) {
   252  	pluginName := "cmp-fileName"
   253  	Given(t).
   254  		And(func() {
   255  			go startCMPServer(t, "./testdata/cmp-fileName")
   256  			time.Sleep(100 * time.Millisecond)
   257  			t.Setenv("ARGOCD_BINARY_NAME", "argocd")
   258  		}).
   259  		Path(pluginName + "/subdir").
   260  		When().
   261  		CreateApp().
   262  		Sync().
   263  		Then().
   264  		Expect(OperationPhaseIs(OperationSucceeded)).
   265  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   266  		Expect(HealthIs(health.HealthStatusHealthy))
   267  }
   268  
   269  // Discover by Find glob
   270  func TestCMPDiscoverWithFindGlob(t *testing.T) {
   271  	Given(t).
   272  		And(func() {
   273  			go startCMPServer(t, "./testdata/cmp-find-glob")
   274  			time.Sleep(100 * time.Millisecond)
   275  			t.Setenv("ARGOCD_BINARY_NAME", "argocd")
   276  		}).
   277  		Path("guestbook").
   278  		When().
   279  		CreateApp().
   280  		Sync().
   281  		Then().
   282  		Expect(OperationPhaseIs(OperationSucceeded)).
   283  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   284  		Expect(HealthIs(health.HealthStatusHealthy))
   285  }
   286  
   287  // Discover by Plugin Name
   288  func TestCMPDiscoverWithPluginName(t *testing.T) {
   289  	Given(t).
   290  		And(func() {
   291  			go startCMPServer(t, "./testdata/cmp-find-glob")
   292  			time.Sleep(100 * time.Millisecond)
   293  			t.Setenv("ARGOCD_BINARY_NAME", "argocd")
   294  		}).
   295  		Path("guestbook").
   296  		When().
   297  		CreateFromFile(func(app *Application) {
   298  			// specifically mention the plugin to use (name is based on <plugin name>-<version>
   299  			app.Spec.Source.Plugin = &ApplicationSourcePlugin{Name: "cmp-find-glob-v1.0"}
   300  		}).
   301  		Sync().
   302  		Then().
   303  		Expect(OperationPhaseIs(OperationSucceeded)).
   304  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   305  		Expect(HealthIs(health.HealthStatusHealthy))
   306  }
   307  
   308  // Discover by Find command
   309  func TestCMPDiscoverWithFindCommandWithEnv(t *testing.T) {
   310  	pluginName := "cmp-find-command"
   311  	ctx := Given(t)
   312  	ctx.
   313  		And(func() {
   314  			go startCMPServer(t, "./testdata/cmp-find-command")
   315  			time.Sleep(100 * time.Millisecond)
   316  			t.Setenv("ARGOCD_BINARY_NAME", "argocd")
   317  		}).
   318  		Path(pluginName).
   319  		When().
   320  		CreateApp().
   321  		Sync().
   322  		Then().
   323  		Expect(OperationPhaseIs(OperationSucceeded)).
   324  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   325  		Expect(HealthIs(health.HealthStatusHealthy)).
   326  		And(func(_ *Application) {
   327  			output, err := fixture.Run("", "kubectl", "-n", fixture.DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.Bar}")
   328  			require.NoError(t, err)
   329  			assert.Equal(t, "baz", output)
   330  		}).
   331  		And(func(_ *Application) {
   332  			expectedKubeVersion := fixture.GetVersions(t).ServerVersion.Format("%s.%s")
   333  			output, err := fixture.Run("", "kubectl", "-n", fixture.DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.KubeVersion}")
   334  			require.NoError(t, err)
   335  			assert.Equal(t, expectedKubeVersion, output)
   336  		}).
   337  		And(func(_ *Application) {
   338  			expectedAPIVersion := fixture.GetApiResources(t)
   339  			expectedAPIVersionSlice := strings.Split(expectedAPIVersion, ",")
   340  			sort.Strings(expectedAPIVersionSlice)
   341  
   342  			output, err := fixture.Run("", "kubectl", "-n", fixture.DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.KubeApiVersion}")
   343  			require.NoError(t, err)
   344  			outputSlice := strings.Split(output, ",")
   345  			sort.Strings(outputSlice)
   346  
   347  			assert.Equal(t, expectedAPIVersionSlice, outputSlice)
   348  		})
   349  }
   350  
   351  func TestPruneResourceFromCMP(t *testing.T) {
   352  	Given(t).
   353  		And(func() {
   354  			go startCMPServer(t, "./testdata/cmp-find-glob")
   355  			time.Sleep(100 * time.Millisecond)
   356  			t.Setenv("ARGOCD_BINARY_NAME", "argocd")
   357  		}).
   358  		Path("guestbook").
   359  		When().
   360  		CreateApp().
   361  		Sync().
   362  		Then().
   363  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   364  		When().
   365  		Delete(true).
   366  		Then().
   367  		Expect(DoesNotExist()).
   368  		AndAction(func() {
   369  			_, err := fixture.Run("", "kubectl", "-n", fixture.DeploymentNamespace(), "get", "deployment", "guestbook-ui")
   370  			require.Error(t, err)
   371  		})
   372  }
   373  
   374  func TestPreserveFileModeForCMP(t *testing.T) {
   375  	Given(t).
   376  		And(func() {
   377  			go startCMPServer(t, "./testdata/cmp-preserve-file-mode")
   378  			time.Sleep(100 * time.Millisecond)
   379  			t.Setenv("ARGOCD_BINARY_NAME", "argocd")
   380  		}).
   381  		Path("cmp-preserve-file-mode").
   382  		When().
   383  		CreateFromFile(func(app *Application) {
   384  			app.Spec.Source.Plugin = &ApplicationSourcePlugin{Name: "cmp-preserve-file-mode-v1.0"}
   385  		}).
   386  		Refresh(RefreshTypeNormal).
   387  		Then().
   388  		And(func(app *Application) {
   389  			require.Len(t, app.Status.Resources, 1)
   390  			assert.Equal(t, "ConfigMap", app.Status.Resources[0].Kind)
   391  		})
   392  }
   393  
   394  func TestCMPWithSymlinkPartialFiles(t *testing.T) {
   395  	Given(t, fixture.WithTestData("testdata2")).
   396  		And(func() {
   397  			go startCMPServer(t, "./testdata2/cmp-symlink")
   398  			time.Sleep(100 * time.Millisecond)
   399  			t.Setenv("ARGOCD_BINARY_NAME", "argocd")
   400  		}).
   401  		Path("guestbook-partial-symlink-files").
   402  		When().
   403  		CreateApp().
   404  		Sync().
   405  		Then().
   406  		Expect(OperationPhaseIs(OperationSucceeded)).
   407  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   408  		Expect(HealthIs(health.HealthStatusHealthy))
   409  }
   410  
   411  func TestCMPWithSymlinkFiles(t *testing.T) {
   412  	Given(t, fixture.WithTestData("testdata2")).
   413  		And(func() {
   414  			go startCMPServer(t, "./testdata2/cmp-symlink")
   415  			time.Sleep(100 * time.Millisecond)
   416  			t.Setenv("ARGOCD_BINARY_NAME", "argocd")
   417  		}).
   418  		Path("guestbook-symlink-files").
   419  		When().
   420  		CreateApp().
   421  		Sync().
   422  		Then().
   423  		Expect(OperationPhaseIs(OperationSucceeded)).
   424  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   425  		Expect(HealthIs(health.HealthStatusHealthy))
   426  }
   427  
   428  func TestCMPWithSymlinkFolder(t *testing.T) {
   429  	Given(t, fixture.WithTestData("testdata2")).
   430  		And(func() {
   431  			go startCMPServer(t, "./testdata2/cmp-symlink")
   432  			time.Sleep(100 * time.Millisecond)
   433  			t.Setenv("ARGOCD_BINARY_NAME", "argocd")
   434  		}).
   435  		Path("guestbook-symlink-folder").
   436  		When().
   437  		CreateApp().
   438  		Sync().
   439  		Then().
   440  		Expect(OperationPhaseIs(OperationSucceeded)).
   441  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   442  		Expect(HealthIs(health.HealthStatusHealthy))
   443  }