github.com/kyma-project/kyma-environment-broker@v0.0.1/cmd/broker/suite_test.go (about) 1 package main 2 3 import ( 4 "context" 5 "fmt" 6 "sort" 7 "strings" 8 "testing" 9 "time" 10 11 reconcilerApi "github.com/kyma-incubator/reconciler/pkg/keb" 12 kebConfig "github.com/kyma-project/kyma-environment-broker/internal/config" 13 14 "github.com/kyma-project/kyma-environment-broker/internal/reconciler" 15 16 "github.com/kyma-project/kyma-environment-broker/internal/fixture" 17 18 "github.com/google/uuid" 19 "github.com/kyma-project/control-plane/components/provisioner/pkg/gqlschema" 20 "github.com/kyma-project/kyma-environment-broker/common/director" 21 "github.com/kyma-project/kyma-environment-broker/common/gardener" 22 "github.com/kyma-project/kyma-environment-broker/common/hyperscaler" 23 hyperscalerautomock "github.com/kyma-project/kyma-environment-broker/common/hyperscaler/automock" 24 "github.com/kyma-project/kyma-environment-broker/common/orchestration" 25 "github.com/kyma-project/kyma-environment-broker/internal" 26 "github.com/kyma-project/kyma-environment-broker/internal/avs" 27 "github.com/kyma-project/kyma-environment-broker/internal/broker" 28 "github.com/kyma-project/kyma-environment-broker/internal/edp" 29 "github.com/kyma-project/kyma-environment-broker/internal/event" 30 "github.com/kyma-project/kyma-environment-broker/internal/ias" 31 "github.com/kyma-project/kyma-environment-broker/internal/notification" 32 kebOrchestration "github.com/kyma-project/kyma-environment-broker/internal/orchestration" 33 "github.com/kyma-project/kyma-environment-broker/internal/process" 34 "github.com/kyma-project/kyma-environment-broker/internal/process/input" 35 "github.com/kyma-project/kyma-environment-broker/internal/process/input/automock" 36 "github.com/kyma-project/kyma-environment-broker/internal/process/provisioning" 37 "github.com/kyma-project/kyma-environment-broker/internal/process/upgrade_cluster" 38 "github.com/kyma-project/kyma-environment-broker/internal/process/upgrade_kyma" 39 "github.com/kyma-project/kyma-environment-broker/internal/provisioner" 40 kebRuntime "github.com/kyma-project/kyma-environment-broker/internal/runtime" 41 "github.com/kyma-project/kyma-environment-broker/internal/runtimeoverrides" 42 "github.com/kyma-project/kyma-environment-broker/internal/runtimeversion" 43 "github.com/kyma-project/kyma-environment-broker/internal/storage" 44 "github.com/pivotal-cf/brokerapi/v8/domain" 45 "github.com/sirupsen/logrus" 46 "github.com/stretchr/testify/assert" 47 "github.com/stretchr/testify/mock" 48 "github.com/stretchr/testify/require" 49 coreV1 "k8s.io/api/core/v1" 50 metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 51 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 52 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 53 "k8s.io/apimachinery/pkg/runtime" 54 "k8s.io/apimachinery/pkg/runtime/schema" 55 "k8s.io/apimachinery/pkg/util/wait" 56 "k8s.io/client-go/dynamic" 57 "sigs.k8s.io/controller-runtime/pkg/client/fake" 58 ) 59 60 const ( 61 globalAccountLabel = "account" 62 subAccountLabel = "subaccount" 63 runtimeIDAnnotation = "kcp.provisioner.kyma-project.io/runtime-id" 64 defaultNamespace = "kcp-system" 65 defaultKymaVer = "2.4.0" 66 kymaVersionsConfigName = "kyma-versions" 67 defaultRegion = "cf-eu10" 68 globalAccountID = "dummy-ga-id" 69 dashboardURL = "http://console.garden-dummy.kyma.io" 70 operationID = "provisioning-op-id" 71 deprovisioningOpID = "deprovisioning-op-id" 72 reDeprovisioningOpID = "re-deprovisioning-op-id" 73 instanceID = "instance-id" 74 dbSecretKey = "1234567890123456" 75 76 pollingInterval = 3 * time.Millisecond 77 ) 78 79 var ( 80 shootGVK = schema.GroupVersionKind{Group: "core.gardener.cloud", Version: "v1beta1", Kind: "Shoot"} 81 ) 82 83 type OrchestrationSuite struct { 84 gardenerNamespace string 85 provisionerClient *provisioner.FakeClient 86 kymaQueue *process.Queue 87 clusterQueue *process.Queue 88 storage storage.BrokerStorage 89 gardenerClient dynamic.Interface 90 reconcilerClient *reconciler.FakeClient 91 92 t *testing.T 93 } 94 95 func NewOrchestrationSuite(t *testing.T, additionalKymaVersions []string) *OrchestrationSuite { 96 logs := logrus.New() 97 logs.Formatter.(*logrus.TextFormatter).TimestampFormat = "15:04:05.000" 98 99 var cfg Config 100 cfg.OrchestrationConfig = kebOrchestration.Config{ 101 KymaVersion: defaultKymaVer, 102 KubernetesVersion: "", 103 } 104 cfg.Reconciler = reconciler.Config{ 105 ProvisioningTimeout: time.Second, 106 } 107 cfg.Notification = notification.Config{ 108 Url: "", 109 } 110 111 optionalComponentsDisablers := kebRuntime.ComponentsDisablers{} 112 optComponentsSvc := kebRuntime.NewOptionalComponentsService(optionalComponentsDisablers) 113 114 disabledComponentsProvider := kebRuntime.NewDisabledComponentsProvider() 115 116 componentListProvider := &automock.ComponentListProvider{} 117 componentListProvider.On("AllComponents", mock.AnythingOfType("internal.RuntimeVersionData"), mock.AnythingOfType("*internal.ConfigForPlan")).Return([]internal. 118 KymaComponent{}, nil) 119 120 oidcDefaults := fixture.FixOIDCConfigDTO() 121 122 ctx, _ := context.WithTimeout(context.Background(), 20*time.Minute) 123 db := storage.NewMemoryStorage() 124 sch := runtime.NewScheme() 125 require.NoError(t, coreV1.AddToScheme(sch)) 126 127 kymaVer := "2.4.0" 128 cli := fake.NewClientBuilder().WithScheme(sch).WithRuntimeObjects(fixK8sResources(kymaVer, additionalKymaVersions)...).Build() 129 configProvider := kebConfig.NewConfigProvider( 130 kebConfig.NewConfigMapReader(ctx, cli, logrus.New(), defaultKymaVer), 131 kebConfig.NewConfigMapKeysValidator(), 132 kebConfig.NewConfigMapConverter()) 133 inputFactory, err := input.NewInputBuilderFactory(optComponentsSvc, disabledComponentsProvider, componentListProvider, 134 configProvider, input.Config{ 135 MachineImageVersion: "coreos", 136 KubernetesVersion: "1.18", 137 MachineImage: "253", 138 ProvisioningTimeout: time.Minute, 139 URL: "http://localhost", 140 DefaultGardenerShootPurpose: "testing", 141 }, kymaVer, map[string]string{"cf-eu10": "europe"}, cfg.FreemiumProviders, oidcDefaults) 142 require.NoError(t, err) 143 144 reconcilerClient := reconciler.NewFakeClient() 145 gardenerClient := gardener.NewDynamicFakeClient() 146 provisionerClient := provisioner.NewFakeClient() 147 const gardenerProject = "testing" 148 gardenerNamespace := fmt.Sprintf("garden-%s", gardenerProject) 149 150 eventBroker := event.NewPubSub(logs) 151 152 runtimeOverrides := runtimeoverrides.NewRuntimeOverrides(ctx, cli) 153 154 runtimeVerConfigurator := runtimeversion.NewRuntimeVersionConfigurator(kymaVer, runtimeversion.NewAccountVersionMapping(ctx, cli, defaultNamespace, kymaVersionsConfigName, logs), nil) 155 156 avsClient, _ := avs.NewClient(ctx, avs.Config{}, logs) 157 avsDel := avs.NewDelegator(avsClient, avs.Config{}, db.Operations()) 158 upgradeEvaluationManager := avs.NewEvaluationManager(avsDel, avs.Config{}) 159 runtimeLister := kebOrchestration.NewRuntimeLister(db.Instances(), db.Operations(), kebRuntime.NewConverter(defaultRegion), logs) 160 runtimeResolver := orchestration.NewGardenerRuntimeResolver(gardenerClient, gardenerNamespace, runtimeLister, logs) 161 162 notificationFakeClient := notification.NewFakeClient() 163 notificationBundleBuilder := notification.NewBundleBuilder(notificationFakeClient, cfg.Notification) 164 165 kymaQueue := NewKymaOrchestrationProcessingQueue(ctx, db, runtimeOverrides, provisionerClient, eventBroker, inputFactory, &upgrade_kyma.TimeSchedule{ 166 Retry: 2 * time.Millisecond, 167 StatusCheck: 20 * time.Millisecond, 168 UpgradeKymaTimeout: 4 * time.Second, 169 }, 250*time.Millisecond, runtimeVerConfigurator, runtimeResolver, upgradeEvaluationManager, &cfg, avs.NewInternalEvalAssistant(cfg.Avs), reconcilerClient, notificationBundleBuilder, logs, cli, 1000) 170 171 clusterQueue := NewClusterOrchestrationProcessingQueue(ctx, db, provisionerClient, eventBroker, inputFactory, &upgrade_cluster.TimeSchedule{ 172 Retry: 2 * time.Millisecond, 173 StatusCheck: 20 * time.Millisecond, 174 UpgradeClusterTimeout: 4 * time.Second, 175 }, 250*time.Millisecond, runtimeResolver, upgradeEvaluationManager, notificationBundleBuilder, logs, cli, cfg, 1000) 176 177 kymaQueue.SpeedUp(1000) 178 clusterQueue.SpeedUp(1000) 179 180 return &OrchestrationSuite{ 181 gardenerNamespace: gardenerNamespace, 182 provisionerClient: provisionerClient, 183 kymaQueue: kymaQueue, 184 clusterQueue: clusterQueue, 185 storage: db, 186 gardenerClient: gardenerClient, 187 reconcilerClient: reconcilerClient, 188 189 t: t, 190 } 191 } 192 193 type RuntimeOptions struct { 194 GlobalAccountID string 195 SubAccountID string 196 PlatformProvider internal.CloudProvider 197 PlatformRegion string 198 Region string 199 PlanID string 200 Provider internal.CloudProvider 201 KymaVersion string 202 OverridesVersion string 203 OIDC *internal.OIDCConfigDTO 204 UserID string 205 RuntimeAdmins []string 206 } 207 208 func (o *RuntimeOptions) ProvideGlobalAccountID() string { 209 if o.GlobalAccountID != "" { 210 return o.GlobalAccountID 211 } else { 212 return uuid.New().String() 213 } 214 } 215 216 func (o *RuntimeOptions) ProvideSubAccountID() string { 217 if o.SubAccountID != "" { 218 return o.SubAccountID 219 } else { 220 return uuid.New().String() 221 } 222 } 223 224 func (o *RuntimeOptions) ProvidePlatformRegion() string { 225 if o.PlatformProvider != "" { 226 return o.PlatformRegion 227 } else { 228 return "cf-eu10" 229 } 230 } 231 232 func (o *RuntimeOptions) ProvideRegion() *string { 233 if o.Region != "" { 234 return &o.Region 235 } else { 236 r := "westeurope" 237 return &r 238 } 239 } 240 241 func (o *RuntimeOptions) ProvidePlanID() string { 242 if o.PlanID == "" { 243 return broker.AzurePlanID 244 } else { 245 return o.PlanID 246 } 247 } 248 249 func (o *RuntimeOptions) ProvideOIDC() *internal.OIDCConfigDTO { 250 if o.OIDC != nil { 251 return o.OIDC 252 } else { 253 return nil 254 } 255 } 256 257 func (o *RuntimeOptions) ProvideUserID() string { 258 return o.UserID 259 } 260 261 func (o *RuntimeOptions) ProvideRuntimeAdmins() []string { 262 if o.RuntimeAdmins != nil { 263 return o.RuntimeAdmins 264 } else { 265 return nil 266 } 267 } 268 269 func (s *OrchestrationSuite) CreateProvisionedRuntime(options RuntimeOptions) string { 270 runtimeID := uuid.New().String() 271 shootName := fmt.Sprintf("shoot%s", runtimeID) 272 planID := options.ProvidePlanID() 273 planName := broker.AzurePlanName 274 globalAccountID := options.ProvideGlobalAccountID() 275 subAccountID := options.ProvideSubAccountID() 276 instanceID := uuid.New().String() 277 runtimeStateID := uuid.New().String() 278 oidcConfig := fixture.FixOIDCConfigDTO() 279 provisioningParameters := internal.ProvisioningParameters{ 280 PlanID: planID, 281 ErsContext: internal.ERSContext{ 282 SubAccountID: subAccountID, 283 GlobalAccountID: globalAccountID, 284 }, 285 PlatformRegion: options.ProvidePlatformRegion(), 286 Parameters: internal.ProvisioningParametersDTO{ 287 Region: options.ProvideRegion(), 288 OIDC: &oidcConfig, 289 }, 290 } 291 292 instance := internal.Instance{ 293 RuntimeID: runtimeID, 294 ServicePlanID: planID, 295 ServicePlanName: planName, 296 InstanceID: instanceID, 297 GlobalAccountID: globalAccountID, 298 SubAccountID: subAccountID, 299 Parameters: provisioningParameters, 300 ProviderRegion: options.ProvidePlatformRegion(), 301 InstanceDetails: internal.InstanceDetails{ 302 RuntimeID: runtimeID, 303 ShootName: shootName, 304 ShootDomain: "fake.domain", 305 }, 306 } 307 308 provisioningOperation := internal.ProvisioningOperation{ 309 Operation: internal.Operation{ 310 State: domain.Succeeded, 311 ID: uuid.New().String(), 312 InstanceID: instanceID, 313 ProvisioningParameters: provisioningParameters, 314 InstanceDetails: internal.InstanceDetails{ 315 RuntimeID: instance.RuntimeID, 316 ShootName: shootName, 317 ShootDomain: "fake.domain", 318 }, 319 Type: internal.OperationTypeProvision, 320 }, 321 } 322 runtimeState := fixture.FixRuntimeState(runtimeStateID, runtimeID, provisioningOperation.ID) 323 runtimeState.ClusterConfig.OidcConfig = &gqlschema.OIDCConfigInput{ 324 ClientID: oidcConfig.ClientID, 325 GroupsClaim: oidcConfig.GroupsClaim, 326 IssuerURL: oidcConfig.IssuerURL, 327 SigningAlgs: oidcConfig.SigningAlgs, 328 UsernameClaim: oidcConfig.UsernameClaim, 329 UsernamePrefix: oidcConfig.UsernamePrefix, 330 } 331 shoot := &unstructured.Unstructured{Object: map[string]interface{}{ 332 "metadata": map[string]interface{}{ 333 "name": shootName, 334 "namespace": s.gardenerNamespace, 335 "labels": map[string]interface{}{ 336 globalAccountLabel: globalAccountID, 337 subAccountLabel: subAccountID, 338 }, 339 "annotations": map[string]interface{}{ 340 runtimeIDAnnotation: runtimeID, 341 }, 342 }, 343 "spec": map[string]interface{}{ 344 "region": options.ProvidePlatformRegion(), 345 "maintenance": map[string]interface{}{ 346 "timeWindow": map[string]interface{}{ 347 "begin": "030000+0000", 348 "end": "040000+0000", 349 }, 350 }, 351 }, 352 }} 353 shoot.SetGroupVersionKind(shootGVK) 354 355 require.NoError(s.t, s.storage.Instances().Insert(instance)) 356 require.NoError(s.t, s.storage.Operations().InsertOperation(provisioningOperation.Operation)) 357 require.NoError(s.t, s.storage.RuntimeStates().Insert(runtimeState)) 358 _, err := s.gardenerClient.Resource(gardener.ShootResource).Namespace(s.gardenerNamespace).Create(context.Background(), shoot, v1.CreateOptions{}) 359 require.NoError(s.t, err) 360 361 provisioningOperation.InputCreator = fixture.FixInputCreator(internal.Azure) 362 s.provisionerClient.Provision(provisioningOperation) 363 364 return runtimeID 365 } 366 367 func (s *OrchestrationSuite) createOrchestration(oType orchestration.Type, queue *process.Queue, params orchestration.Parameters) string { 368 now := time.Now() 369 o := internal.Orchestration{ 370 OrchestrationID: uuid.New().String(), 371 Type: oType, 372 State: orchestration.Pending, 373 Description: "started processing of Kyma upgrade", 374 Parameters: params, 375 CreatedAt: now, 376 UpdatedAt: now, 377 } 378 require.NoError(s.t, s.storage.Orchestrations().Insert(o)) 379 380 queue.Add(o.OrchestrationID) 381 return o.OrchestrationID 382 } 383 384 func (s *OrchestrationSuite) CreateUpgradeKymaOrchestration(params orchestration.Parameters) string { 385 return s.createOrchestration(orchestration.UpgradeKymaOrchestration, s.kymaQueue, params) 386 } 387 388 func (s *OrchestrationSuite) CreateUpgradeClusterOrchestration(params orchestration.Parameters) string { 389 return s.createOrchestration(orchestration.UpgradeClusterOrchestration, s.clusterQueue, params) 390 } 391 392 func (s *OrchestrationSuite) finishOperationByProvisioner(operationType gqlschema.OperationType, runtimeID string) { 393 err := wait.Poll(time.Millisecond*100, 2*time.Second, func() (bool, error) { 394 status := s.provisionerClient.FindOperationByRuntimeIDAndType(runtimeID, operationType) 395 if status.ID != nil { 396 s.provisionerClient.FinishProvisionerOperation(*status.ID, gqlschema.OperationStateSucceeded) 397 return true, nil 398 } 399 return false, nil 400 }) 401 assert.NoError(s.t, err, "timeout waiting for provisioner operation to exist") 402 } 403 404 func (s *OrchestrationSuite) FinishUpgradeOperationByReconciler(runtimeID string) { 405 err := wait.Poll(time.Millisecond*20, 2*time.Second, func() (bool, error) { 406 c, err := s.reconcilerClient.GetLatestCluster(runtimeID) 407 if err != nil { 408 return false, nil 409 } 410 if c.ConfigurationVersion == 0 { 411 return false, nil 412 } 413 s.reconcilerClient.ChangeClusterState(runtimeID, c.ConfigurationVersion, reconcilerApi.StatusReady) 414 return true, nil 415 }) 416 417 assert.NoError(s.t, err, "timeout waiting for reconciler cluster to exist") 418 } 419 420 func (s *OrchestrationSuite) FinishUpgradeShootOperationByProvisioner(runtimeID string) { 421 s.finishOperationByProvisioner(gqlschema.OperationTypeUpgradeShoot, runtimeID) 422 } 423 424 func (s *OrchestrationSuite) WaitForOrchestrationState(orchestrationID string, state string) { 425 var orchestration *internal.Orchestration 426 err := wait.PollImmediate(100*time.Millisecond, 2*time.Second, func() (done bool, err error) { 427 orchestration, _ = s.storage.Orchestrations().GetByID(orchestrationID) 428 return orchestration.State == state, nil 429 }) 430 assert.NoError(s.t, err, "timeout waiting for the orchestration expected state %s. The existing orchestration %+v", state, orchestration) 431 } 432 433 func (s *OrchestrationSuite) AssertRuntimeUpgraded(runtimeID string, version string) { 434 c, _ := s.reconcilerClient.LastClusterConfig(runtimeID) 435 assert.Equal(s.t, version, c.KymaConfig.Version, "The runtime %s expected to be upgraded", runtimeID) 436 } 437 438 func (s *OrchestrationSuite) AssertRuntimeNotUpgraded(runtimeID string) { 439 _, err := s.reconcilerClient.LastClusterConfig(runtimeID) 440 // error means not found 441 assert.Error(s.t, err) 442 } 443 444 func (s *OrchestrationSuite) AssertShootUpgraded(runtimeID string) { 445 assert.True(s.t, s.provisionerClient.IsShootUpgraded(runtimeID), "The shoot %s expected to be upgraded", runtimeID) 446 } 447 448 func (s *OrchestrationSuite) AssertShootNotUpgraded(runtimeID string) { 449 assert.False(s.t, s.provisionerClient.IsShootUpgraded(runtimeID), "The shoot %s expected to be not upgraded", runtimeID) 450 } 451 452 func fixK8sResources(defaultKymaVersion string, additionalKymaVersions []string) []runtime.Object { 453 var resources []runtime.Object 454 override := &coreV1.ConfigMap{ 455 ObjectMeta: metaV1.ObjectMeta{ 456 Name: "overrides", 457 Namespace: "kcp-system", 458 Labels: map[string]string{ 459 fmt.Sprintf("overrides-version-%s", defaultKymaVersion): "true", 460 "overrides-plan-azure": "true", 461 "overrides-plan-trial": "true", 462 "overrides-plan-aws": "true", 463 "overrides-plan-free": "true", 464 "overrides-plan-gcp": "true", 465 "overrides-plan-own_cluster": "true", 466 "overrides-version-2.0.0-rc4": "true", 467 "overrides-version-2.0.0": "true", 468 }, 469 }, 470 Data: map[string]string{ 471 "foo": "bar", 472 "global.booleanOverride.enabled": "false", 473 }, 474 } 475 scOverride := &coreV1.ConfigMap{ 476 ObjectMeta: metaV1.ObjectMeta{ 477 Name: "service-catalog2-overrides", 478 Namespace: "kcp-system", 479 Labels: map[string]string{ 480 fmt.Sprintf("overrides-version-%s", defaultKymaVersion): "true", 481 "overrides-plan-azure": "true", 482 "overrides-plan-trial": "true", 483 "overrides-plan-aws": "true", 484 "overrides-plan-free": "true", 485 "overrides-plan-gcp": "true", 486 "overrides-version-2.0.0-rc4": "true", 487 "overrides-version-2.0.0": "true", 488 "component": "service-catalog2", 489 }, 490 }, 491 Data: map[string]string{ 492 "setting-one": "1234", 493 }, 494 } 495 496 for _, version := range additionalKymaVersions { 497 override.ObjectMeta.Labels[fmt.Sprintf("overrides-version-%s", version)] = "true" 498 scOverride.ObjectMeta.Labels[fmt.Sprintf("overrides-version-%s", version)] = "true" 499 } 500 501 orchestrationConfig := &coreV1.ConfigMap{ 502 ObjectMeta: metaV1.ObjectMeta{ 503 Name: "orchestration-config", 504 Namespace: "kcp-system", 505 Labels: map[string]string{}, 506 }, 507 Data: map[string]string{ 508 "maintenancePolicy": `{ 509 "rules": [ 510 511 ], 512 "default": { 513 "days": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], 514 "timeBegin": "010000+0000", 515 "timeEnd": "010000+0000" 516 } 517 }`, 518 }, 519 } 520 521 kebCfg := &coreV1.ConfigMap{ 522 ObjectMeta: metaV1.ObjectMeta{ 523 Name: "keb-config", 524 Namespace: "kcp-system", 525 Labels: map[string]string{ 526 "keb-config": "true", 527 fmt.Sprintf("runtime-version-%s", defaultKymaVersion): "true", 528 }, 529 }, 530 Data: map[string]string{ 531 "default": ` 532 kyma-template: |- 533 apiVersion: operator.kyma-project.io/v1beta2 534 kind: Kyma 535 metadata: 536 name: my-kyma 537 namespace: kyma-system 538 spec: 539 sync: 540 strategy: secret 541 channel: stable 542 modules: 543 - name: btp-operator 544 customResourcePolicy: CreateAndDelete 545 - name: keda 546 channel: fast 547 548 additional-components: 549 - name: "btp-operator" 550 namespace: "kyma-system"`, 551 }, 552 } 553 554 for _, version := range additionalKymaVersions { 555 kebCfg.ObjectMeta.Labels[fmt.Sprintf("runtime-version-%s", version)] = "true" 556 } 557 558 resources = append(resources, override, scOverride, orchestrationConfig, kebCfg) 559 560 return resources 561 } 562 563 type ProvisioningSuite struct { 564 provisionerClient *provisioner.FakeClient 565 provisioningManager *process.StagedManager 566 provisioningQueue *process.Queue 567 storage storage.BrokerStorage 568 directorClient *director.FakeClient 569 570 t *testing.T 571 avsServer *avs.MockAvsServer 572 reconcilerClient *reconciler.FakeClient 573 } 574 575 func NewProvisioningSuite(t *testing.T, multiZoneCluster bool, controlPlaneFailureTolerance string) *ProvisioningSuite { 576 ctx, _ := context.WithTimeout(context.Background(), 20*time.Minute) 577 logs := logrus.New() 578 db := storage.NewMemoryStorage() 579 580 cfg := fixConfig() 581 582 provisionerClient := provisioner.NewFakeClient() 583 584 optionalComponentsDisablers := kebRuntime.ComponentsDisablers{} 585 optComponentsSvc := kebRuntime.NewOptionalComponentsService(optionalComponentsDisablers) 586 587 disabledComponentsProvider := kebRuntime.NewDisabledComponentsProvider() 588 589 componentListProvider := &automock.ComponentListProvider{} 590 componentListProvider.On("AllComponents", mock.AnythingOfType("internal.RuntimeVersionData"), mock.AnythingOfType("*internal.ConfigForPlan")).Return([]internal.KymaComponent{}, nil) 591 592 oidcDefaults := fixture.FixOIDCConfigDTO() 593 594 sch := runtime.NewScheme() 595 require.NoError(t, coreV1.AddToScheme(sch)) 596 additionalKymaVersions := []string{"1.19", "1.20", "main"} 597 cli := fake.NewFakeClientWithScheme(sch, fixK8sResources(defaultKymaVer, additionalKymaVersions)...) 598 configProvider := kebConfig.NewConfigProvider( 599 kebConfig.NewConfigMapReader(ctx, cli, logrus.New(), defaultKymaVer), 600 kebConfig.NewConfigMapKeysValidator(), 601 kebConfig.NewConfigMapConverter()) 602 inputFactory, err := input.NewInputBuilderFactory(optComponentsSvc, disabledComponentsProvider, componentListProvider, 603 configProvider, input.Config{ 604 MachineImageVersion: "coreos", 605 KubernetesVersion: "1.18", 606 MachineImage: "253", 607 ProvisioningTimeout: time.Minute, 608 URL: "http://localhost", 609 DefaultGardenerShootPurpose: "testing", 610 MultiZoneCluster: multiZoneCluster, 611 ControlPlaneFailureTolerance: controlPlaneFailureTolerance, 612 }, defaultKymaVer, map[string]string{"cf-eu10": "europe"}, cfg.FreemiumProviders, oidcDefaults) 613 require.NoError(t, err) 614 615 server := avs.NewMockAvsServer(t) 616 mockServer := avs.FixMockAvsServer(server) 617 avsConfig := avs.Config{ 618 OauthTokenEndpoint: fmt.Sprintf("%s/oauth/token", mockServer.URL), 619 ApiEndpoint: fmt.Sprintf("%s/api/v2/evaluationmetadata", mockServer.URL), 620 } 621 622 client, err := avs.NewClient(context.TODO(), avsConfig, logrus.New()) 623 assert.NoError(t, err) 624 avsDel := avs.NewDelegator(client, avsConfig, db.Operations()) 625 externalEvalAssistant := avs.NewExternalEvalAssistant(cfg.Avs) 626 internalEvalAssistant := avs.NewInternalEvalAssistant(cfg.Avs) 627 externalEvalCreator := provisioning.NewExternalEvalCreator(avsDel, cfg.Avs.Disabled, externalEvalAssistant) 628 629 runtimeOverrides := runtimeoverrides.NewRuntimeOverrides(ctx, cli) 630 accountVersionMapping := runtimeversion.NewAccountVersionMapping(ctx, cli, cfg.VersionConfig.Namespace, cfg.VersionConfig.Name, logs) 631 runtimeVerConfigurator := runtimeversion.NewRuntimeVersionConfigurator(cfg.KymaVersion, accountVersionMapping, nil) 632 633 edpClient := edp.NewFakeClient() 634 635 accountProvider := fixAccountProvider() 636 637 directorClient := director.NewFakeClient() 638 639 reconcilerClient := reconciler.NewFakeClient() 640 641 eventBroker := event.NewPubSub(logs) 642 643 provisionManager := process.NewStagedManager(db.Operations(), eventBroker, cfg.OperationTimeout, cfg.Provisioning, logs.WithField("provisioning", "manager")) 644 provisioningQueue := NewProvisioningProcessingQueue(ctx, provisionManager, workersAmount, cfg, db, provisionerClient, inputFactory, avsDel, 645 internalEvalAssistant, externalEvalCreator, runtimeVerConfigurator, runtimeOverrides, edpClient, accountProvider, 646 reconcilerClient, fakeK8sClientProvider(cli), cli, logs) 647 648 provisioningQueue.SpeedUp(10000) 649 provisionManager.SpeedUp(10000) 650 651 return &ProvisioningSuite{ 652 provisionerClient: provisionerClient, 653 provisioningManager: provisionManager, 654 provisioningQueue: provisioningQueue, 655 storage: db, 656 directorClient: directorClient, 657 avsServer: server, 658 reconcilerClient: reconcilerClient, 659 660 t: t, 661 } 662 } 663 664 func (s *ProvisioningSuite) CreateProvisioning(options RuntimeOptions) string { 665 provisioningParameters := internal.ProvisioningParameters{ 666 PlanID: options.ProvidePlanID(), 667 ErsContext: internal.ERSContext{ 668 GlobalAccountID: globalAccountID, 669 SubAccountID: options.ProvideSubAccountID(), 670 UserID: options.ProvideUserID(), 671 }, 672 PlatformProvider: options.PlatformProvider, 673 Parameters: internal.ProvisioningParametersDTO{ 674 Region: options.ProvideRegion(), 675 KymaVersion: options.KymaVersion, 676 OverridesVersion: options.OverridesVersion, 677 OIDC: options.ProvideOIDC(), 678 RuntimeAdministrators: options.ProvideRuntimeAdmins(), 679 }, 680 } 681 682 shootName := gardener.CreateShootName() 683 684 operation, err := internal.NewProvisioningOperationWithID(operationID, instanceID, provisioningParameters) 685 require.NoError(s.t, err) 686 operation.ShootName = shootName 687 operation.ShootDomain = fmt.Sprintf("%s.%s.%s", shootName, "garden-dummy", strings.Trim("kyma.io", ".")) 688 operation.ShootDNSProviders = gardener.DNSProvidersData{} 689 operation.DashboardURL = dashboardURL 690 operation.State = orchestration.Pending 691 692 err = s.storage.Operations().InsertOperation(operation.Operation) 693 require.NoError(s.t, err) 694 695 err = s.storage.Instances().Insert(internal.Instance{ 696 InstanceID: instanceID, 697 GlobalAccountID: globalAccountID, 698 SubAccountID: "dummy-sa", 699 ServiceID: provisioningParameters.ServiceID, 700 ServiceName: broker.KymaServiceName, 701 ServicePlanID: provisioningParameters.PlanID, 702 ServicePlanName: broker.AzurePlanName, 703 DashboardURL: dashboardURL, 704 Parameters: operation.ProvisioningParameters, 705 }) 706 707 s.provisioningQueue.Add(operation.ID) 708 return operation.ID 709 } 710 711 func (s *ProvisioningSuite) CreateUnsuspension(options RuntimeOptions) string { 712 provisioningParameters := internal.ProvisioningParameters{ 713 PlanID: options.ProvidePlanID(), 714 ErsContext: internal.ERSContext{ 715 GlobalAccountID: globalAccountID, 716 SubAccountID: options.ProvideSubAccountID(), 717 }, 718 PlatformRegion: options.ProvidePlatformRegion(), 719 Parameters: internal.ProvisioningParametersDTO{ 720 Region: options.ProvideRegion(), 721 }, 722 } 723 724 operation, err := internal.NewProvisioningOperationWithID(operationID, instanceID, provisioningParameters) 725 operation.State = orchestration.Pending 726 // in the real processing the URL is set in the handler 727 operation.DashboardURL = dashboardURL 728 require.NoError(s.t, err) 729 730 err = s.storage.Operations().InsertOperation(operation.Operation) 731 require.NoError(s.t, err) 732 733 instance := &internal.Instance{ 734 InstanceID: instanceID, 735 GlobalAccountID: globalAccountID, 736 SubAccountID: "dummy-sa", 737 ServiceID: provisioningParameters.ServiceID, 738 ServiceName: broker.KymaServiceName, 739 ServicePlanID: provisioningParameters.PlanID, 740 ServicePlanName: broker.AzurePlanName, 741 DashboardURL: dashboardURL, 742 Parameters: operation.ProvisioningParameters, 743 } 744 err = s.storage.Instances().Insert(*instance) 745 746 suspensionOp := internal.NewSuspensionOperationWithID("susp-id", instance) 747 suspensionOp.CreatedAt = time.Now().AddDate(0, 0, -10) 748 suspensionOp.State = domain.Succeeded 749 s.storage.Operations().InsertDeprovisioningOperation(suspensionOp) 750 751 s.provisioningQueue.Add(operation.ID) 752 return operation.ID 753 } 754 755 func (s *ProvisioningSuite) WaitForProvisioningState(operationID string, state domain.LastOperationState) { 756 var op *internal.Operation 757 err := wait.PollImmediate(pollingInterval, 2*time.Second, func() (done bool, err error) { 758 op, _ = s.storage.Operations().GetOperationByID(operationID) 759 return op.State == state, nil 760 }) 761 assert.NoError(s.t, err, "timeout waiting for the operation expected state %s. The existing operation %+v", state, op) 762 } 763 764 func (s *ProvisioningSuite) FinishProvisioningOperationByProvisionerAndReconciler(operationID string) { 765 var op *internal.Operation 766 err := wait.PollImmediate(pollingInterval, 2*time.Second, func() (done bool, err error) { 767 op, _ = s.storage.Operations().GetOperationByID(operationID) 768 if op.RuntimeID != "" { 769 return true, nil 770 } 771 return false, nil 772 }) 773 assert.NoError(s.t, err, "timeout waiting for the operation with runtimeID. The existing operation %+v", op) 774 775 s.finishOperationByProvisioner(gqlschema.OperationTypeProvision, op.RuntimeID) 776 777 err = wait.PollImmediate(pollingInterval, 2*time.Second, func() (done bool, err error) { 778 op, _ = s.storage.Operations().GetOperationByID(operationID) 779 if op.ClusterConfigurationVersion != 0 { 780 return true, nil 781 } 782 return false, nil 783 }) 784 assert.NoError(s.t, err, "timeout waiting for the operation with Cluster Configuration Version. The existing operation %+v", op) 785 786 s.finishOperationByReconciler(op) 787 } 788 789 func (s *ProvisioningSuite) AssertProvisionerStartedProvisioning(operationID string) { 790 // wait until ProvisioningOperation reaches CreateRuntime step 791 var provisioningOp *internal.Operation 792 err := wait.Poll(pollingInterval, 2*time.Second, func() (bool, error) { 793 op, err := s.storage.Operations().GetOperationByID(operationID) 794 assert.NoError(s.t, err) 795 if op.ProvisionerOperationID != "" { 796 provisioningOp = op 797 return true, nil 798 } 799 return false, nil 800 }) 801 assert.NoError(s.t, err) 802 803 var status gqlschema.OperationStatus 804 err = wait.Poll(pollingInterval, 2*time.Second, func() (bool, error) { 805 status = s.provisionerClient.FindOperationByRuntimeIDAndType(provisioningOp.RuntimeID, gqlschema.OperationTypeProvision) 806 if status.ID != nil { 807 return true, nil 808 } 809 return false, nil 810 }) 811 assert.NoError(s.t, err) 812 assert.Equal(s.t, gqlschema.OperationStateInProgress, status.State) 813 } 814 815 func (s *ProvisioningSuite) AssertAllStagesFinished(operationID string) { 816 operation, _ := s.storage.Operations().GetProvisioningOperationByID(operationID) 817 steps := s.provisioningManager.GetAllStages() 818 for _, stage := range steps { 819 assert.True(s.t, operation.IsStageFinished(stage)) 820 } 821 } 822 823 func (s *ProvisioningSuite) finishOperationByReconciler(op *internal.Operation) { 824 time.Sleep(50 * time.Millisecond) 825 err := wait.Poll(pollingInterval, 10*time.Second, func() (bool, error) { 826 state, err := s.reconcilerClient.GetCluster(op.RuntimeID, op.ClusterConfigurationVersion) 827 if err != nil { 828 return false, err 829 } 830 if state.Cluster != "" { 831 s.reconcilerClient.ChangeClusterState(op.RuntimeID, op.ClusterConfigurationVersion, reconcilerApi.StatusReady) 832 return true, nil 833 } 834 return false, nil 835 }) 836 assert.NoError(s.t, err) 837 } 838 839 func (s *ProvisioningSuite) finishOperationByProvisioner(operationType gqlschema.OperationType, runtimeID string) { 840 err := wait.Poll(pollingInterval, 2*time.Second, func() (bool, error) { 841 status := s.provisionerClient.FindOperationByRuntimeIDAndType(runtimeID, operationType) 842 if status.ID != nil { 843 s.provisionerClient.FinishProvisionerOperation(*status.ID, gqlschema.OperationStateSucceeded) 844 return true, nil 845 } 846 return false, nil 847 }) 848 assert.NoError(s.t, err, "timeout waiting for provisioner operation to exist") 849 } 850 851 func (s *ProvisioningSuite) AssertProvisioningRequest() { 852 input := s.fetchProvisionInput() 853 854 labels := input.RuntimeInput.Labels 855 assert.Equal(s.t, instanceID, labels["broker_instance_id"]) 856 assert.Contains(s.t, labels, "global_subaccount_id") 857 assert.NotEmpty(s.t, input.ClusterConfig.GardenerConfig.Name) 858 } 859 860 func (s *ProvisioningSuite) AssertKymaProfile(opID string, expectedProfile gqlschema.KymaProfile) { 861 operation, _ := s.storage.Operations().GetProvisioningOperationByID(opID) 862 s.fetchProvisionInput() 863 c, _ := s.reconcilerClient.LastClusterConfig(operation.RuntimeID) 864 865 assert.Equal(s.t, string(expectedProfile), c.KymaConfig.Profile) 866 } 867 868 func (s *ProvisioningSuite) AssertProvider(provider string) { 869 input := s.fetchProvisionInput() 870 871 assert.Equal(s.t, provider, input.ClusterConfig.GardenerConfig.Provider) 872 } 873 874 func (s *ProvisioningSuite) fetchProvisionInput() gqlschema.ProvisionRuntimeInput { 875 input := s.provisionerClient.GetLatestProvisionRuntimeInput() 876 return input 877 } 878 879 func (s *ProvisioningSuite) AssertMinimalNumberOfNodes(nodes int) { 880 input := s.fetchProvisionInput() 881 882 assert.Equal(s.t, nodes, input.ClusterConfig.GardenerConfig.AutoScalerMin) 883 } 884 885 func (s *ProvisioningSuite) AssertMaximumNumberOfNodes(nodes int) { 886 input := s.fetchProvisionInput() 887 888 assert.Equal(s.t, nodes, input.ClusterConfig.GardenerConfig.AutoScalerMax) 889 } 890 891 func (s *ProvisioningSuite) AssertMachineType(machineType string) { 892 input := s.fetchProvisionInput() 893 894 assert.Equal(s.t, machineType, input.ClusterConfig.GardenerConfig.MachineType) 895 } 896 897 func (s *ProvisioningSuite) AssertOverrides(opID string, overrides []*gqlschema.ConfigEntryInput) { 898 input := s.fetchProvisionInput() 899 900 // values in arrays need to be sorted, because globalOverrides are coming from a map and map's elements' order is not deterministic 901 sort.Slice(overrides, func(i, j int) bool { 902 return overrides[i].Key < overrides[j].Key 903 }) 904 sort.Slice(input.KymaConfig.Configuration, func(i, j int) bool { 905 return input.KymaConfig.Configuration[i].Key < input.KymaConfig.Configuration[j].Key 906 }) 907 908 assert.Equal(s.t, overrides, input.KymaConfig.Configuration) 909 } 910 911 func (s *ProvisioningSuite) AssertZonesCount(zonesCount *int, planID string) { 912 provisionInput := s.fetchProvisionInput() 913 914 switch planID { 915 case broker.AzurePlanID: 916 if zonesCount != nil { 917 assert.Equal(s.t, *zonesCount, len(provisionInput.ClusterConfig.GardenerConfig.ProviderSpecificConfig.AzureConfig.AzureZones)) 918 break 919 } 920 assert.Equal(s.t, 1, len(provisionInput.ClusterConfig.GardenerConfig.ProviderSpecificConfig.AzureConfig.AzureZones)) 921 case broker.AWSPlanID: 922 if zonesCount != nil { 923 assert.Equal(s.t, *zonesCount, len(provisionInput.ClusterConfig.GardenerConfig.ProviderSpecificConfig.AwsConfig.AwsZones)) 924 break 925 } 926 assert.Equal(s.t, 1, len(provisionInput.ClusterConfig.GardenerConfig.ProviderSpecificConfig.AwsConfig.AwsZones)) 927 case broker.GCPPlanID: 928 if zonesCount != nil { 929 assert.Equal(s.t, *zonesCount, len(provisionInput.ClusterConfig.GardenerConfig.ProviderSpecificConfig.GcpConfig.Zones)) 930 break 931 } 932 assert.Equal(s.t, 1, len(provisionInput.ClusterConfig.GardenerConfig.ProviderSpecificConfig.GcpConfig.Zones)) 933 default: 934 } 935 } 936 937 func (s *ProvisioningSuite) AssertSubscription(shared bool, ht hyperscaler.Type) { 938 input := s.fetchProvisionInput() 939 secretName := input.ClusterConfig.GardenerConfig.TargetSecret 940 if shared { 941 assert.Equal(s.t, sharedSubscription(ht), secretName) 942 } else { 943 assert.Equal(s.t, regularSubscription(ht), secretName) 944 } 945 } 946 947 func (s *ProvisioningSuite) AssertOIDC(oidcConfig gqlschema.OIDCConfigInput) { 948 input := s.fetchProvisionInput() 949 950 assert.Equal(s.t, &oidcConfig, input.ClusterConfig.GardenerConfig.OidcConfig) 951 } 952 953 func (s *ProvisioningSuite) AssertRuntimeAdmins(admins []string) { 954 input := s.fetchProvisionInput() 955 currentAdmins := input.ClusterConfig.Administrators 956 957 assert.ElementsMatch(s.t, currentAdmins, admins) 958 } 959 960 func (s *ProvisioningSuite) AssertControlPlaneFailureTolerance(level string) { 961 input := s.fetchProvisionInput() 962 if level == "" { 963 assert.Empty(s.t, input.ClusterConfig.GardenerConfig.ControlPlaneFailureTolerance) 964 } else { 965 require.NotNil(s.t, input.ClusterConfig.GardenerConfig.ControlPlaneFailureTolerance) 966 assert.Equal(s.t, level, *input.ClusterConfig.GardenerConfig.ControlPlaneFailureTolerance) 967 } 968 } 969 970 func regularSubscription(ht hyperscaler.Type) string { 971 return fmt.Sprintf("regular-%s", ht) 972 } 973 974 func sharedSubscription(ht hyperscaler.Type) string { 975 return fmt.Sprintf("shared-%s", ht) 976 } 977 978 func fixConfig() *Config { 979 return &Config{ 980 DbInMemory: true, 981 DisableProcessOperationsInProgress: false, 982 DevelopmentMode: true, 983 DumpProvisionerRequests: true, 984 OperationTimeout: 2 * time.Minute, 985 Provisioner: input.Config{ 986 ProvisioningTimeout: 2 * time.Minute, 987 DeprovisioningTimeout: 2 * time.Minute, 988 }, 989 Reconciler: reconciler.Config{ 990 ProvisioningTimeout: 5 * time.Second, 991 }, 992 Director: director.Config{}, 993 Database: storage.Config{ 994 SecretKey: dbSecretKey, 995 }, 996 Gardener: gardener.Config{ 997 Project: "kyma", 998 ShootDomain: "kyma.sap.com", 999 }, 1000 KymaVersion: defaultKymaVer, 1001 EnableOnDemandVersion: true, 1002 UpdateProcessingEnabled: true, 1003 Broker: broker.Config{ 1004 EnablePlans: []string{"azure", "trial", "aws", "own_cluster", "preview"}, 1005 AllowNetworkingParameters: true, 1006 RegionParameterIsRequired: true, 1007 ExposeSchemaWithRegionRequired: true, 1008 AllowModulesParameters: true, 1009 }, 1010 Avs: avs.Config{}, 1011 IAS: ias.Config{ 1012 IdentityProvider: ias.FakeIdentityProviderName, 1013 }, 1014 Notification: notification.Config{ 1015 Url: "http://host:8080/", 1016 }, 1017 OrchestrationConfig: kebOrchestration.Config{ 1018 KymaVersion: defaultKymaVer, 1019 Namespace: "kcp-system", 1020 Name: "orchestration-config", 1021 }, 1022 MaxPaginationPage: 100, 1023 FreemiumProviders: []string{"aws", "azure"}, 1024 EuAccessWhitelistedGlobalAccountsFilePath: "testdata/eu_access_whitelist.yaml", 1025 EuAccessRejectionMessage: "EU Access Rejection Message - see: http://google.pl", 1026 1027 Provisioning: process.StagedManagerConfiguration{MaxStepProcessingTime: time.Minute}, 1028 Deprovisioning: process.StagedManagerConfiguration{MaxStepProcessingTime: time.Minute}, 1029 Update: process.StagedManagerConfiguration{MaxStepProcessingTime: time.Minute}, 1030 } 1031 } 1032 1033 func fixAccountProvider() *hyperscalerautomock.AccountProvider { 1034 accountProvider := hyperscalerautomock.AccountProvider{} 1035 1036 accountProvider.On("GardenerSecretName", mock.Anything, mock.Anything, mock.Anything).Return( 1037 func(ht hyperscaler.Type, tn string, euaccess bool) string { return regularSubscription(ht) }, nil) 1038 1039 accountProvider.On("GardenerSharedSecretName", hyperscaler.Azure, mock.Anything).Return( 1040 func(ht hyperscaler.Type, euaccess bool) string { return sharedSubscription(ht) }, nil) 1041 1042 accountProvider.On("GardenerSharedSecretName", hyperscaler.AWS, mock.Anything).Return( 1043 func(ht hyperscaler.Type, euaccess bool) string { return sharedSubscription(ht) }, nil) 1044 1045 accountProvider.On("MarkUnusedGardenerSecretBindingAsDirty", hyperscaler.Azure, mock.Anything, mock.Anything).Return(nil) 1046 accountProvider.On("MarkUnusedGardenerSecretBindingAsDirty", hyperscaler.AWS, mock.Anything, mock.Anything).Return(nil) 1047 return &accountProvider 1048 }