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 }