github.com/argoproj/argo-cd/v3@v3.2.1/test/e2e/project_management_test.go (about) 1 package e2e 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "strconv" 8 "strings" 9 "testing" 10 "time" 11 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 "k8s.io/apimachinery/pkg/api/errors" 15 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 16 "k8s.io/apimachinery/pkg/fields" 17 "k8s.io/utils/ptr" 18 19 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" 20 "github.com/argoproj/argo-cd/v3/test/e2e/fixture" 21 "github.com/argoproj/argo-cd/v3/util/argo" 22 ) 23 24 func assertProjHasEvent(t *testing.T, a *v1alpha1.AppProject, message string, reason string) { 25 t.Helper() 26 list, err := fixture.KubeClientset.CoreV1().Events(fixture.TestNamespace()).List(t.Context(), metav1.ListOptions{ 27 FieldSelector: fields.SelectorFromSet(map[string]string{ 28 "involvedObject.name": a.Name, 29 "involvedObject.uid": string(a.UID), 30 "involvedObject.namespace": fixture.TestNamespace(), 31 }).String(), 32 }) 33 require.NoError(t, err) 34 35 for i := range list.Items { 36 event := list.Items[i] 37 if event.Reason == reason && strings.Contains(event.Message, message) { 38 return 39 } 40 } 41 t.Errorf("Unable to find event with reason=%s; message=%s", reason, message) 42 } 43 44 func TestProjectCreation(t *testing.T) { 45 fixture.EnsureCleanState(t) 46 47 projectName := "proj-" + fixture.Name() 48 _, err := fixture.RunCli("proj", "create", projectName, 49 "--description", "Test description", 50 "-d", "https://192.168.99.100:8443,default", 51 "-d", "https://192.168.99.100:8443,service", 52 "-s", "https://github.com/argoproj/argo-cd.git", 53 "--orphaned-resources") 54 require.NoError(t, err) 55 56 proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(t.Context(), projectName, metav1.GetOptions{}) 57 require.NoError(t, err) 58 assert.Equal(t, projectName, proj.Name) 59 assert.Len(t, proj.Spec.Destinations, 2) 60 61 assert.Equal(t, "https://192.168.99.100:8443", proj.Spec.Destinations[0].Server) 62 assert.Equal(t, "default", proj.Spec.Destinations[0].Namespace) 63 64 assert.Equal(t, "https://192.168.99.100:8443", proj.Spec.Destinations[1].Server) 65 assert.Equal(t, "service", proj.Spec.Destinations[1].Namespace) 66 67 assert.Len(t, proj.Spec.SourceRepos, 1) 68 assert.Equal(t, "https://github.com/argoproj/argo-cd.git", proj.Spec.SourceRepos[0]) 69 70 assert.NotNil(t, proj.Spec.OrphanedResources) 71 assert.False(t, proj.Spec.OrphanedResources.IsWarn()) 72 73 assertProjHasEvent(t, proj, "create", argo.EventReasonResourceCreated) 74 75 // create a manifest with the same name to upsert 76 newDescription := "Upserted description" 77 proj.Spec.Description = newDescription 78 proj.ResourceVersion = "" 79 data, err := json.Marshal(proj) 80 stdinString := string(data) 81 require.NoError(t, err) 82 83 // fail without upsert flag 84 _, err = fixture.RunCliWithStdin(stdinString, false, "proj", "create", 85 "-f", "-") 86 require.Error(t, err) 87 88 // succeed with the upsert flag 89 _, err = fixture.RunCliWithStdin(stdinString, false, "proj", "create", 90 "-f", "-", "--upsert") 91 require.NoError(t, err) 92 proj, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(t.Context(), projectName, metav1.GetOptions{}) 93 require.NoError(t, err) 94 assert.Equal(t, newDescription, proj.Spec.Description) 95 } 96 97 func TestProjectDeletion(t *testing.T) { 98 fixture.EnsureCleanState(t) 99 100 projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) 101 proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create( 102 t.Context(), &v1alpha1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: projectName}}, metav1.CreateOptions{}) 103 require.NoError(t, err) 104 105 _, err = fixture.RunCli("proj", "delete", projectName) 106 require.NoError(t, err) 107 108 _, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(t.Context(), projectName, metav1.GetOptions{}) 109 assert.True(t, errors.IsNotFound(err)) 110 assertProjHasEvent(t, proj, "delete", argo.EventReasonResourceDeleted) 111 } 112 113 func TestSetProject(t *testing.T) { 114 fixture.EnsureCleanState(t) 115 116 projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) 117 _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create( 118 t.Context(), &v1alpha1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: projectName}}, metav1.CreateOptions{}) 119 require.NoError(t, err) 120 121 _, err = fixture.RunCli("proj", "set", projectName, 122 "--description", "updated description", 123 "-d", "https://192.168.99.100:8443,default", 124 "-d", "https://192.168.99.100:8443,service", 125 "--orphaned-resources-warn=false") 126 require.NoError(t, err) 127 128 proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(t.Context(), projectName, metav1.GetOptions{}) 129 require.NoError(t, err) 130 assert.Equal(t, projectName, proj.Name) 131 assert.Len(t, proj.Spec.Destinations, 2) 132 133 assert.Equal(t, "https://192.168.99.100:8443", proj.Spec.Destinations[0].Server) 134 assert.Equal(t, "default", proj.Spec.Destinations[0].Namespace) 135 136 assert.Equal(t, "https://192.168.99.100:8443", proj.Spec.Destinations[1].Server) 137 assert.Equal(t, "service", proj.Spec.Destinations[1].Namespace) 138 139 assert.NotNil(t, proj.Spec.OrphanedResources) 140 assert.False(t, proj.Spec.OrphanedResources.IsWarn()) 141 142 assertProjHasEvent(t, proj, "update", argo.EventReasonResourceUpdated) 143 } 144 145 func TestAddProjectDestination(t *testing.T) { 146 fixture.EnsureCleanState(t) 147 148 projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) 149 _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create( 150 t.Context(), &v1alpha1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: projectName}}, metav1.CreateOptions{}) 151 require.NoError(t, err, "Unable to create project") 152 153 _, err = fixture.RunCli("proj", "add-destination", projectName, 154 "https://192.168.99.100:8443", 155 "test1", 156 ) 157 require.NoError(t, err, "Unable to add project destination") 158 159 _, err = fixture.RunCli("proj", "add-destination", projectName, 160 "https://192.168.99.100:8443", 161 "test1", 162 ) 163 require.ErrorContains(t, err, "already defined") 164 165 _, err = fixture.RunCli("proj", "add-destination", projectName, 166 "!*", 167 "test1", 168 ) 169 require.ErrorContains(t, err, "server has an invalid format, '!*'") 170 171 _, err = fixture.RunCli("proj", "add-destination", projectName, 172 "https://192.168.99.100:8443", 173 "!*", 174 ) 175 require.ErrorContains(t, err, "namespace has an invalid format, '!*'") 176 177 proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(t.Context(), projectName, metav1.GetOptions{}) 178 require.NoError(t, err) 179 assert.Equal(t, projectName, proj.Name) 180 assert.Len(t, proj.Spec.Destinations, 1) 181 182 assert.Equal(t, "https://192.168.99.100:8443", proj.Spec.Destinations[0].Server) 183 assert.Equal(t, "test1", proj.Spec.Destinations[0].Namespace) 184 assertProjHasEvent(t, proj, "update", argo.EventReasonResourceUpdated) 185 } 186 187 func TestAddProjectDestinationWithName(t *testing.T) { 188 fixture.EnsureCleanState(t) 189 190 projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) 191 _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create( 192 t.Context(), &v1alpha1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: projectName}}, metav1.CreateOptions{}) 193 require.NoError(t, err, "Unable to create project") 194 195 _, err = fixture.RunCli("proj", "add-destination", projectName, 196 "in-cluster", 197 "test1", 198 "--name", 199 ) 200 require.NoError(t, err, "Unable to add project destination") 201 202 proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(t.Context(), projectName, metav1.GetOptions{}) 203 require.NoError(t, err) 204 assert.Equal(t, projectName, proj.Name) 205 assert.Len(t, proj.Spec.Destinations, 1) 206 207 assert.Empty(t, proj.Spec.Destinations[0].Server) 208 assert.Equal(t, "in-cluster", proj.Spec.Destinations[0].Name) 209 assert.Equal(t, "test1", proj.Spec.Destinations[0].Namespace) 210 assertProjHasEvent(t, proj, "update", argo.EventReasonResourceUpdated) 211 } 212 213 func TestRemoveProjectDestination(t *testing.T) { 214 fixture.EnsureCleanState(t) 215 216 projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) 217 _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create(t.Context(), &v1alpha1.AppProject{ 218 ObjectMeta: metav1.ObjectMeta{Name: projectName}, 219 Spec: v1alpha1.AppProjectSpec{ 220 Destinations: []v1alpha1.ApplicationDestination{{ 221 Server: "https://192.168.99.100:8443", 222 Namespace: "test", 223 }}, 224 }, 225 }, metav1.CreateOptions{}) 226 require.NoError(t, err, "Unable to create project") 227 228 _, err = fixture.RunCli("proj", "remove-destination", projectName, 229 "https://192.168.99.100:8443", 230 "test", 231 ) 232 require.NoError(t, err, "Unable to remove project destination") 233 234 _, err = fixture.RunCli("proj", "remove-destination", projectName, 235 "https://192.168.99.100:8443", 236 "test1", 237 ) 238 require.ErrorContains(t, err, "does not exist") 239 240 proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(t.Context(), projectName, metav1.GetOptions{}) 241 require.NoError(t, err, "Unable to get project") 242 assert.Equal(t, projectName, proj.Name) 243 assert.Empty(t, proj.Spec.Destinations) 244 assertProjHasEvent(t, proj, "update", argo.EventReasonResourceUpdated) 245 } 246 247 func TestAddProjectSource(t *testing.T) { 248 fixture.EnsureCleanState(t) 249 250 projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) 251 _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create( 252 t.Context(), &v1alpha1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: projectName}}, metav1.CreateOptions{}) 253 require.NoError(t, err, "Unable to create project") 254 255 _, err = fixture.RunCli("proj", "add-source", projectName, "https://github.com/argoproj/argo-cd.git") 256 require.NoError(t, err, "Unable to add project source") 257 258 _, err = fixture.RunCli("proj", "add-source", projectName, "https://github.com/argoproj/argo-cd.git") 259 require.NoError(t, err) 260 261 proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(t.Context(), projectName, metav1.GetOptions{}) 262 require.NoError(t, err) 263 assert.Equal(t, projectName, proj.Name) 264 assert.Len(t, proj.Spec.SourceRepos, 1) 265 266 assert.Equal(t, "https://github.com/argoproj/argo-cd.git", proj.Spec.SourceRepos[0]) 267 } 268 269 func TestRemoveProjectSource(t *testing.T) { 270 fixture.EnsureCleanState(t) 271 272 projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) 273 _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create(t.Context(), &v1alpha1.AppProject{ 274 ObjectMeta: metav1.ObjectMeta{Name: projectName}, 275 Spec: v1alpha1.AppProjectSpec{ 276 SourceRepos: []string{"https://github.com/argoproj/argo-cd.git"}, 277 }, 278 }, metav1.CreateOptions{}) 279 280 require.NoError(t, err) 281 282 _, err = fixture.RunCli("proj", "remove-source", projectName, "https://github.com/argoproj/argo-cd.git") 283 284 require.NoError(t, err) 285 286 _, err = fixture.RunCli("proj", "remove-source", projectName, "https://github.com/argoproj/argo-cd.git") 287 require.NoError(t, err) 288 289 proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(t.Context(), projectName, metav1.GetOptions{}) 290 require.NoError(t, err) 291 assert.Equal(t, projectName, proj.Name) 292 assert.Empty(t, proj.Spec.SourceRepos) 293 assertProjHasEvent(t, proj, "update", argo.EventReasonResourceUpdated) 294 } 295 296 func TestUseJWTToken(t *testing.T) { 297 fixture.EnsureCleanState(t) 298 299 projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) 300 appName := "app-" + strconv.FormatInt(time.Now().Unix(), 10) 301 roleName := "roleTest" 302 roleName2 := "roleTest2" 303 testApp := &v1alpha1.Application{ 304 ObjectMeta: metav1.ObjectMeta{ 305 Name: appName, 306 }, 307 Spec: v1alpha1.ApplicationSpec{ 308 Source: &v1alpha1.ApplicationSource{ 309 RepoURL: fixture.RepoURL(fixture.RepoURLTypeFile), 310 Path: "guestbook", 311 }, 312 Destination: v1alpha1.ApplicationDestination{ 313 Server: v1alpha1.KubernetesInternalAPIServerAddr, 314 Namespace: fixture.TestNamespace(), 315 }, 316 Project: projectName, 317 }, 318 } 319 _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create(t.Context(), &v1alpha1.AppProject{ 320 ObjectMeta: metav1.ObjectMeta{Name: projectName}, 321 Spec: v1alpha1.AppProjectSpec{ 322 Destinations: []v1alpha1.ApplicationDestination{{ 323 Server: v1alpha1.KubernetesInternalAPIServerAddr, 324 Namespace: fixture.TestNamespace(), 325 }}, 326 SourceRepos: []string{"*"}, 327 }, 328 }, metav1.CreateOptions{}) 329 require.NoError(t, err) 330 331 _, err = fixture.AppClientset.ArgoprojV1alpha1().Applications(fixture.TestNamespace()).Create(t.Context(), testApp, metav1.CreateOptions{}) 332 require.NoError(t, err) 333 334 _, err = fixture.RunCli("proj", "role", "create", projectName, roleName) 335 require.NoError(t, err) 336 337 roleGetResult, err := fixture.RunCli("proj", "role", "get", projectName, roleName) 338 require.NoError(t, err) 339 assert.True(t, strings.HasSuffix(roleGetResult, "ID ISSUED-AT EXPIRES-AT")) 340 341 _, err = fixture.RunCli("proj", "role", "create-token", projectName, roleName) 342 require.NoError(t, err) 343 344 // Create second role with kubectl, to test that it will not affect 1st role 345 _, err = fixture.Run("", "kubectl", "patch", "appproject", projectName, "--type", "merge", 346 "-n", fixture.TestNamespace(), 347 "-p", fmt.Sprintf(`{"spec":{"roles":[{"name":%q},{"name":%q}]}}`, roleName, roleName2)) 348 require.NoError(t, err) 349 350 _, err = fixture.RunCli("proj", "role", "create-token", projectName, roleName2) 351 require.NoError(t, err) 352 353 for _, action := range []string{"get", "update", "sync", "create", "override", "*"} { 354 _, err = fixture.RunCli("proj", "role", "add-policy", projectName, roleName, "-a", action, "-o", "*", "-p", "allow") 355 require.NoError(t, err) 356 } 357 358 newProj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(t.Context(), projectName, metav1.GetOptions{}) 359 require.NoError(t, err) 360 assert.Len(t, newProj.Status.JWTTokensByRole[roleName].Items, 1) 361 assert.ElementsMatch(t, newProj.Status.JWTTokensByRole[roleName].Items, newProj.Spec.Roles[0].JWTTokens) 362 363 roleGetResult, err = fixture.RunCli("proj", "role", "get", projectName, roleName) 364 require.NoError(t, err) 365 assert.Contains(t, roleGetResult, strconv.FormatInt(newProj.Status.JWTTokensByRole[roleName].Items[0].IssuedAt, 10)) 366 367 _, err = fixture.RunCli("proj", "role", "delete-token", projectName, roleName, strconv.FormatInt(newProj.Status.JWTTokensByRole[roleName].Items[0].IssuedAt, 10)) 368 require.NoError(t, err) 369 newProj, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(t.Context(), projectName, metav1.GetOptions{}) 370 require.NoError(t, err) 371 assert.Nil(t, newProj.Status.JWTTokensByRole[roleName].Items) 372 assert.Nil(t, newProj.Spec.Roles[0].JWTTokens) 373 } 374 375 func TestAddOrphanedIgnore(t *testing.T) { 376 fixture.EnsureCleanState(t) 377 378 projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) 379 _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create( 380 t.Context(), &v1alpha1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: projectName}}, metav1.CreateOptions{}) 381 require.NoError(t, err, "Unable to create project") 382 383 _, err = fixture.RunCli("proj", "add-orphaned-ignore", projectName, 384 "group", 385 "kind", 386 "--name", 387 "name", 388 ) 389 require.NoError(t, err, "Unable to add resource to orphaned ignore") 390 391 _, err = fixture.RunCli("proj", "add-orphaned-ignore", projectName, 392 "group", 393 "kind", 394 "--name", 395 "name", 396 ) 397 require.ErrorContains(t, err, "already defined") 398 399 proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(t.Context(), projectName, metav1.GetOptions{}) 400 require.NoError(t, err) 401 assert.Equal(t, projectName, proj.Name) 402 assert.Len(t, proj.Spec.OrphanedResources.Ignore, 1) 403 404 assert.Equal(t, "group", proj.Spec.OrphanedResources.Ignore[0].Group) 405 assert.Equal(t, "kind", proj.Spec.OrphanedResources.Ignore[0].Kind) 406 assert.Equal(t, "name", proj.Spec.OrphanedResources.Ignore[0].Name) 407 assertProjHasEvent(t, proj, "update", argo.EventReasonResourceUpdated) 408 } 409 410 func TestRemoveOrphanedIgnore(t *testing.T) { 411 fixture.EnsureCleanState(t) 412 413 projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) 414 _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create(t.Context(), &v1alpha1.AppProject{ 415 ObjectMeta: metav1.ObjectMeta{Name: projectName}, 416 Spec: v1alpha1.AppProjectSpec{ 417 OrphanedResources: &v1alpha1.OrphanedResourcesMonitorSettings{ 418 Warn: ptr.To(true), 419 Ignore: []v1alpha1.OrphanedResourceKey{{Group: "group", Kind: "kind", Name: "name"}}, 420 }, 421 }, 422 }, metav1.CreateOptions{}) 423 require.NoError(t, err, "Unable to create project") 424 425 _, err = fixture.RunCli("proj", "remove-orphaned-ignore", projectName, 426 "group", 427 "kind", 428 "--name", 429 "name", 430 ) 431 require.NoError(t, err, "Unable to remove resource from orphaned ignore list") 432 433 _, err = fixture.RunCli("proj", "remove-orphaned-ignore", projectName, 434 "group", 435 "kind", 436 "--name", 437 "name", 438 ) 439 require.ErrorContains(t, err, "does not exist") 440 441 proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(t.Context(), projectName, metav1.GetOptions{}) 442 require.NoError(t, err, "Unable to get project") 443 assert.Equal(t, projectName, proj.Name) 444 assert.Empty(t, proj.Spec.OrphanedResources.Ignore) 445 assertProjHasEvent(t, proj, "update", argo.EventReasonResourceUpdated) 446 } 447 448 func createAndConfigGlobalProject() error { 449 // Create global project 450 projectGlobalName := "proj-g-" + fixture.Name() 451 _, err := fixture.RunCli("proj", "create", projectGlobalName, 452 "--description", "Test description", 453 "-d", "https://192.168.99.100:8443,default", 454 "-d", "https://192.168.99.100:8443,service", 455 "-s", "https://github.com/argoproj/argo-cd.git", 456 "--orphaned-resources") 457 if err != nil { 458 return err 459 } 460 461 projGlobal, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectGlobalName, metav1.GetOptions{}) 462 if err != nil { 463 return err 464 } 465 466 projGlobal.Spec.NamespaceResourceBlacklist = []metav1.GroupKind{ 467 {Group: "", Kind: "Service"}, 468 } 469 470 projGlobal.Spec.NamespaceResourceWhitelist = []metav1.GroupKind{ 471 {Group: "", Kind: "Deployment"}, 472 } 473 474 projGlobal.Spec.ClusterResourceWhitelist = []metav1.GroupKind{ 475 {Group: "", Kind: "Job"}, 476 } 477 478 projGlobal.Spec.ClusterResourceBlacklist = []metav1.GroupKind{ 479 {Group: "", Kind: "Pod"}, 480 } 481 482 projGlobal.Spec.SyncWindows = v1alpha1.SyncWindows{} 483 win := &v1alpha1.SyncWindow{Kind: "deny", Schedule: "* * * * *", Duration: "1h", Applications: []string{"*"}} 484 projGlobal.Spec.SyncWindows = append(projGlobal.Spec.SyncWindows, win) 485 486 _, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Update(context.Background(), projGlobal, metav1.UpdateOptions{}) 487 if err != nil { 488 return err 489 } 490 491 // Configure global project settings 492 globalProjectsSettings := `data: 493 accounts.config-service: apiKey 494 globalProjects: | 495 - labelSelector: 496 matchExpressions: 497 - key: opt 498 operator: In 499 values: 500 - me 501 - you 502 projectName: %s` 503 504 _, err = fixture.Run("", "kubectl", "patch", "cm", "argocd-cm", 505 "-n", fixture.TestNamespace(), 506 "-p", fmt.Sprintf(globalProjectsSettings, projGlobal.Name)) 507 if err != nil { 508 return err 509 } 510 511 return nil 512 } 513 514 func TestGetVirtualProjectNoMatch(t *testing.T) { 515 fixture.EnsureCleanState(t) 516 err := createAndConfigGlobalProject() 517 require.NoError(t, err) 518 519 // Create project which does not match global project settings 520 projectName := "proj-" + fixture.Name() 521 _, err = fixture.RunCli("proj", "create", projectName, 522 "--description", "Test description", 523 "-d", v1alpha1.KubernetesInternalAPIServerAddr+",*", 524 "-s", "*", 525 "--orphaned-resources") 526 require.NoError(t, err) 527 528 proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(t.Context(), projectName, metav1.GetOptions{}) 529 require.NoError(t, err) 530 531 // Create an app belongs to proj project 532 _, err = fixture.RunCli("app", "create", fixture.Name(), "--repo", fixture.RepoURL(fixture.RepoURLTypeFile), 533 "--path", guestbookPath, "--project", proj.Name, "--dest-server", v1alpha1.KubernetesInternalAPIServerAddr, "--dest-namespace", fixture.DeploymentNamespace()) 534 require.NoError(t, err) 535 536 // App trying to sync a resource which is not blacked listed anywhere 537 _, err = fixture.RunCli("app", "sync", fixture.Name(), "--resource", "apps:Deployment:guestbook-ui", "--timeout", strconv.Itoa(10)) 538 require.NoError(t, err) 539 540 // app trying to sync a resource which is black listed by global project 541 _, err = fixture.RunCli("app", "sync", fixture.Name(), "--resource", ":Service:guestbook-ui", "--timeout", strconv.Itoa(10)) 542 require.NoError(t, err) 543 } 544 545 func TestGetVirtualProjectMatch(t *testing.T) { 546 fixture.EnsureCleanState(t) 547 err := createAndConfigGlobalProject() 548 require.NoError(t, err) 549 550 // Create project which matches global project settings 551 projectName := "proj-" + fixture.Name() 552 _, err = fixture.RunCli("proj", "create", projectName, 553 "--description", "Test description", 554 "-d", v1alpha1.KubernetesInternalAPIServerAddr+",*", 555 "-s", "*", 556 "--orphaned-resources") 557 require.NoError(t, err) 558 559 proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(t.Context(), projectName, metav1.GetOptions{}) 560 require.NoError(t, err) 561 562 // Add a label to this project so that this project match global project selector 563 proj.Labels = map[string]string{"opt": "me"} 564 _, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Update(t.Context(), proj, metav1.UpdateOptions{}) 565 require.NoError(t, err) 566 567 // Create an app belongs to proj project 568 _, err = fixture.RunCli("app", "create", fixture.Name(), "--repo", fixture.RepoURL(fixture.RepoURLTypeFile), 569 "--path", guestbookPath, "--project", proj.Name, "--dest-server", v1alpha1.KubernetesInternalAPIServerAddr, "--dest-namespace", fixture.DeploymentNamespace()) 570 require.NoError(t, err) 571 572 // App trying to sync a resource which is not blacked listed anywhere 573 _, err = fixture.RunCli("app", "sync", fixture.Name(), "--resource", "apps:Deployment:guestbook-ui", "--timeout", strconv.Itoa(10)) 574 require.ErrorContains(t, err, "blocked by sync window") 575 576 // app trying to sync a resource which is black listed by global project 577 _, err = fixture.RunCli("app", "sync", fixture.Name(), "--resource", ":Service:guestbook-ui", "--timeout", strconv.Itoa(10)) 578 assert.ErrorContains(t, err, "blocked by sync window") 579 } 580 581 func TestAddProjectDestinationServiceAccount(t *testing.T) { 582 fixture.EnsureCleanState(t) 583 584 projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) 585 _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create( 586 t.Context(), &v1alpha1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: projectName}}, metav1.CreateOptions{}) 587 require.NoError(t, err, "Unable to create project") 588 589 // Given, an existing project 590 // When, a default destination service account with all valid fields is added to it, 591 // Then, there is no error. 592 _, err = fixture.RunCli("proj", "add-destination-service-account", projectName, 593 "https://192.168.99.100:8443", 594 "test-ns", 595 "test-sa", 596 ) 597 require.NoError(t, err, "Unable to add project destination service account") 598 599 // Given, an existing project 600 // When, a default destination service account with empty namespace is added to it, 601 // Then, there is no error. 602 _, err = fixture.RunCli("proj", "add-destination-service-account", projectName, 603 "https://192.168.99.100:8443", 604 "", 605 "test-sa", 606 ) 607 require.NoError(t, err, "Unable to add project destination service account") 608 609 // Given, an existing project, 610 // When, a default destination service account is added with a custom service account namespace, 611 // Then, there is no error. 612 _, err = fixture.RunCli("proj", "add-destination-service-account", projectName, 613 "https://192.168.99.100:8443", 614 "test-ns1", 615 "test-sa", 616 "--service-account-namespace", 617 "default", 618 ) 619 require.NoError(t, err, "Unable to add project destination service account") 620 621 // Given, an existing project, 622 // When, a duplicate default destination service account is added, 623 // Then, there is an error with appropriate message. 624 _, err = fixture.RunCli("proj", "add-destination-service-account", projectName, 625 "https://192.168.99.100:8443", 626 "test-ns", 627 "test-sa", 628 ) 629 require.ErrorContains(t, err, "already defined") 630 631 // Given, an existing project, 632 // When, a duplicate default destination service account is added, 633 // Then, there is an error with appropriate message. 634 _, err = fixture.RunCli("proj", "add-destination-service-account", projectName, 635 "https://192.168.99.100:8443", 636 "test-ns", 637 "asdf", 638 ) 639 require.ErrorContains(t, err, "already added") 640 641 // Given, an existing project, 642 // When, a default destination service account with negation glob pattern for server is added, 643 // Then, there is an error with appropriate message. 644 _, err = fixture.RunCli("proj", "add-destination-service-account", projectName, 645 "!*", 646 "test-ns", 647 "test-sa", 648 ) 649 require.ErrorContains(t, err, "server has an invalid format, '!*'") 650 651 // Given, an existing project, 652 // When, a default destination service account with negation glob pattern for server is added, 653 // Then, there is an error with appropriate message. 654 _, err = fixture.RunCli("proj", "add-destination-service-account", projectName, 655 "!abc", 656 "test-ns", 657 "test-sa", 658 ) 659 require.ErrorContains(t, err, "server has an invalid format, '!abc'") 660 661 // Given, an existing project, 662 // When, a default destination service account with negation glob pattern for namespace is added, 663 // Then, there is an error with appropriate message. 664 _, err = fixture.RunCli("proj", "add-destination-service-account", projectName, 665 "https://192.168.99.100:8443", 666 "!*", 667 "test-sa", 668 ) 669 require.ErrorContains(t, err, "namespace has an invalid format, '!*'") 670 671 // Given, an existing project, 672 // When, a default destination service account with negation glob pattern for namespace is added, 673 // Then, there is an error with appropriate message. 674 _, err = fixture.RunCli("proj", "add-destination-service-account", projectName, 675 "https://192.168.99.100:8443", 676 "!abc", 677 "test-sa", 678 ) 679 require.ErrorContains(t, err, "namespace has an invalid format, '!abc'") 680 681 // Given, an existing project, 682 // When, a default destination service account with empty service account is added, 683 // Then, there is an error with appropriate message. 684 _, err = fixture.RunCli("proj", "add-destination-service-account", projectName, 685 "https://192.168.99.100:8443", 686 "test-ns", 687 "", 688 ) 689 require.ErrorContains(t, err, "defaultServiceAccount has an invalid format, ''") 690 691 // Given, an existing project, 692 // When, a default destination service account with service account having just white spaces is added, 693 // Then, there is an error with appropriate message. 694 _, err = fixture.RunCli("proj", "add-destination-service-account", projectName, 695 "https://192.168.99.100:8443", 696 "test-ns", 697 " ", 698 ) 699 require.ErrorContains(t, err, "defaultServiceAccount has an invalid format, ' '") 700 701 // Given, an existing project, 702 // When, a default destination service account with service account having backwards slash char is added, 703 // Then, there is an error with appropriate message. 704 _, err = fixture.RunCli("proj", "add-destination-service-account", projectName, 705 "https://192.168.99.100:8443", 706 "test-ns", 707 "test\\sa", 708 ) 709 require.ErrorContains(t, err, "defaultServiceAccount has an invalid format, 'test\\\\sa'") 710 711 // Given, an existing project, 712 // When, a default destination service account with service account having forward slash char is added, 713 // Then, there is an error with appropriate message. 714 _, err = fixture.RunCli("proj", "add-destination-service-account", projectName, 715 "https://192.168.99.100:8443", 716 "test-ns", 717 "test/sa", 718 ) 719 require.ErrorContains(t, err, "defaultServiceAccount has an invalid format, 'test/sa'") 720 721 // Given, an existing project, 722 // When, a default destination service account with service account having square braces char is added, 723 // Then, there is an error with appropriate message. 724 _, err = fixture.RunCli("proj", "add-destination-service-account", projectName, 725 "https://192.168.99.100:8443", 726 "test-ns", 727 "[test-sa]", 728 ) 729 require.ErrorContains(t, err, "defaultServiceAccount has an invalid format, '[test-sa]'") 730 731 // Given, an existing project, 732 // When, a default destination service account with service account having curly braces char is added, 733 // Then, there is an error with appropriate message. 734 _, err = fixture.RunCli("proj", "add-destination-service-account", projectName, 735 "https://192.168.99.100:8443", 736 "test-ns", 737 "{test-sa}", 738 ) 739 require.ErrorContains(t, err, "defaultServiceAccount has an invalid format, '{test-sa}'") 740 741 // Given, an existing project, 742 // When, a default destination service account with service account having curly braces char is added, 743 // Then, there is an error with appropriate message. 744 _, err = fixture.RunCli("proj", "add-destination-service-account", projectName, 745 "[[ech*", 746 "test-ns", 747 "test-sa", 748 ) 749 require.ErrorContains(t, err, "server has an invalid format, '[[ech*'") 750 751 // Given, an existing project, 752 // When, a default destination service account with service account having curly braces char is added, 753 // Then, there is an error with appropriate message. 754 _, err = fixture.RunCli("proj", "add-destination-service-account", projectName, 755 "https://192.168.99.100:8443", 756 "[[ech*", 757 "test-sa", 758 ) 759 require.ErrorContains(t, err, "namespace has an invalid format, '[[ech*'") 760 761 proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(t.Context(), projectName, metav1.GetOptions{}) 762 require.NoError(t, err) 763 assert.Equal(t, projectName, proj.Name) 764 assert.Len(t, proj.Spec.DestinationServiceAccounts, 3) 765 766 assert.Equal(t, "https://192.168.99.100:8443", proj.Spec.DestinationServiceAccounts[0].Server) 767 assert.Equal(t, "test-ns", proj.Spec.DestinationServiceAccounts[0].Namespace) 768 assert.Equal(t, "test-sa", proj.Spec.DestinationServiceAccounts[0].DefaultServiceAccount) 769 770 assert.Equal(t, "https://192.168.99.100:8443", proj.Spec.DestinationServiceAccounts[1].Server) 771 assert.Empty(t, proj.Spec.DestinationServiceAccounts[1].Namespace) 772 assert.Equal(t, "test-sa", proj.Spec.DestinationServiceAccounts[1].DefaultServiceAccount) 773 774 assert.Equal(t, "https://192.168.99.100:8443", proj.Spec.DestinationServiceAccounts[2].Server) 775 assert.Equal(t, "test-ns1", proj.Spec.DestinationServiceAccounts[2].Namespace) 776 assert.Equal(t, "default:test-sa", proj.Spec.DestinationServiceAccounts[2].DefaultServiceAccount) 777 778 assertProjHasEvent(t, proj, "update", argo.EventReasonResourceUpdated) 779 }