github.com/kyma-project/kyma-environment-broker@v0.0.1/common/orchestration/resolver_test.go (about) 1 package orchestration 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "testing" 7 "time" 8 9 "github.com/sirupsen/logrus" 10 "github.com/stretchr/testify/require" 11 12 "github.com/stretchr/testify/assert" 13 14 brokerapi "github.com/pivotal-cf/brokerapi/v8/domain" 15 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 16 k8s "k8s.io/apimachinery/pkg/runtime" 17 dynamicfake "k8s.io/client-go/dynamic/fake" 18 k8stesting "k8s.io/client-go/testing" 19 20 "github.com/kyma-project/kyma-environment-broker/common/gardener" 21 "github.com/kyma-project/kyma-environment-broker/common/runtime" 22 ) 23 24 const ( 25 shootNamespace = "garden-kyma" 26 27 globalAccountID1 = "f8576376-603b-40a8-9225-0edc65052463" 28 globalAccountID2 = "cb4d9447-8a6c-47d4-a2cd-48fa8121a91e" 29 globalAccountID3 = "cb4d9447-8a6c-47d4-a2cd-48fa8121a91e" 30 31 region1 = "westeurope" 32 region2 = "centralus" 33 region3 = "uksouth" 34 35 plan1 = "azure" 36 plan2 = "gcp" 37 ) 38 39 func TestResolver_Resolve(t *testing.T) { 40 client := newFakeGardenerClient() 41 lister := newRuntimeListerMock() 42 defer lister.AssertExpectations(t) 43 logger := newLogDummy() 44 resolver := NewGardenerRuntimeResolver(client, shootNamespace, lister, logger) 45 46 expectedRuntime1 := expectedRuntime{ 47 shoot: &shoot1, 48 runtime: &runtime1, 49 } 50 expectedRuntime2 := expectedRuntime{ 51 shoot: &shoot2, 52 runtime: &runtime2, 53 } 54 expectedRuntime3 := expectedRuntime{ 55 shoot: &shoot3, 56 runtime: &runtime3, 57 } 58 expectedRuntime10 := expectedRuntime{ 59 shoot: &shoot10, 60 runtime: &runtime10, 61 } 62 63 for tn, tc := range map[string]struct { 64 Target TargetSpec 65 ExpectedRuntimes []expectedRuntime 66 }{ 67 "IncludeAll": { 68 Target: TargetSpec{ 69 Include: []RuntimeTarget{ 70 { 71 Target: TargetAll, 72 }, 73 }, 74 Exclude: nil, 75 }, 76 ExpectedRuntimes: []expectedRuntime{expectedRuntime1, expectedRuntime2, expectedRuntime3, expectedRuntime10}, 77 }, 78 "IncludeAllExcludeOne": { 79 Target: TargetSpec{ 80 Include: []RuntimeTarget{ 81 { 82 Target: TargetAll, 83 }, 84 }, 85 Exclude: []RuntimeTarget{ 86 { 87 GlobalAccount: expectedRuntime2.runtime.GlobalAccountID, 88 SubAccount: expectedRuntime2.runtime.SubAccountID, 89 }, 90 }, 91 }, 92 ExpectedRuntimes: []expectedRuntime{expectedRuntime1, expectedRuntime3, expectedRuntime10}, 93 }, 94 "ExcludeAll": { 95 Target: TargetSpec{ 96 Include: []RuntimeTarget{ 97 { 98 Target: TargetAll, 99 }, 100 }, 101 Exclude: []RuntimeTarget{ 102 { 103 Target: TargetAll, 104 }, 105 }, 106 }, 107 ExpectedRuntimes: []expectedRuntime{}, 108 }, 109 "IncludeOne": { 110 Target: TargetSpec{ 111 Include: []RuntimeTarget{ 112 { 113 GlobalAccount: expectedRuntime2.runtime.GlobalAccountID, 114 SubAccount: expectedRuntime2.runtime.SubAccountID, 115 }, 116 }, 117 Exclude: nil, 118 }, 119 ExpectedRuntimes: []expectedRuntime{expectedRuntime2}, 120 }, 121 "IncludeRuntime": { 122 Target: TargetSpec{ 123 Include: []RuntimeTarget{ 124 { 125 RuntimeID: "runtime-id-1", 126 }, 127 }, 128 Exclude: nil, 129 }, 130 ExpectedRuntimes: []expectedRuntime{expectedRuntime1}, 131 }, 132 "IncludeInstance": { 133 Target: TargetSpec{ 134 Include: []RuntimeTarget{ 135 { 136 InstanceID: "instance-id-1", 137 }, 138 }, 139 Exclude: nil, 140 }, 141 ExpectedRuntimes: []expectedRuntime{expectedRuntime1}, 142 }, 143 "IncludeTenant": { 144 Target: TargetSpec{ 145 Include: []RuntimeTarget{ 146 { 147 GlobalAccount: globalAccountID1, 148 }, 149 }, 150 Exclude: nil, 151 }, 152 ExpectedRuntimes: []expectedRuntime{expectedRuntime1, expectedRuntime2, expectedRuntime10}, 153 }, 154 "IncludeRegion": { 155 Target: TargetSpec{ 156 Include: []RuntimeTarget{ 157 { 158 Region: "europe|eu|uk", 159 }, 160 }, 161 Exclude: nil, 162 }, 163 ExpectedRuntimes: []expectedRuntime{expectedRuntime1, expectedRuntime3, expectedRuntime10}, 164 }, 165 "IncludePlanName": { 166 Target: TargetSpec{ 167 Include: []RuntimeTarget{ 168 { 169 PlanName: plan1, 170 }, 171 }, 172 Exclude: nil, 173 }, 174 ExpectedRuntimes: []expectedRuntime{expectedRuntime2, expectedRuntime3, expectedRuntime10}, 175 }, 176 "IncludeShoot": { 177 Target: TargetSpec{ 178 Include: []RuntimeTarget{ 179 { 180 Shoot: expectedRuntime1.shoot.GetName(), 181 }, 182 }, 183 Exclude: nil, 184 }, 185 ExpectedRuntimes: []expectedRuntime{expectedRuntime1}, 186 }, 187 } { 188 t.Run(tn, func(t *testing.T) { 189 // when 190 runtimes, err := resolver.Resolve(tc.Target) 191 192 // then 193 assert.Nil(t, err) 194 195 if len(tc.ExpectedRuntimes) != 0 { 196 assertRuntimeTargets(t, tc.ExpectedRuntimes, runtimes) 197 } else { 198 assert.Empty(t, runtimes) 199 } 200 }) 201 } 202 } 203 204 func TestResolver_Resolve_GardenerFailure(t *testing.T) { 205 // given 206 fake := k8stesting.Fake{} 207 client := gardener.NewDynamicFakeClient() 208 client.Fake = fake 209 fake.AddReactor("list", "shoots", func(action k8stesting.Action) (bool, k8s.Object, error) { 210 return true, nil, fmt.Errorf("fake gardener client failure") 211 }) 212 lister := newRuntimeListerMock() 213 defer lister.AssertExpectations(t) 214 logger := newLogDummy() 215 resolver := NewGardenerRuntimeResolver(client, shootNamespace, lister, logger) 216 217 // when 218 runtimes, err := resolver.Resolve(TargetSpec{ 219 Include: []RuntimeTarget{ 220 { 221 Target: TargetAll, 222 }, 223 }, 224 Exclude: nil, 225 }) 226 227 // then 228 assert.NotNil(t, err) 229 assert.Len(t, runtimes, 0) 230 } 231 232 func TestResolver_Resolve_StorageFailure(t *testing.T) { 233 // given 234 client := newFakeGardenerClient() 235 lister := &RuntimeListerMock{} 236 lister.On("ListAllRuntimes").Return( 237 nil, 238 fmt.Errorf("mock storage failure"), 239 ) 240 defer lister.AssertExpectations(t) 241 logger := newLogDummy() 242 resolver := NewGardenerRuntimeResolver(client, shootNamespace, lister, logger) 243 244 // when 245 runtimes, err := resolver.Resolve(TargetSpec{ 246 Include: []RuntimeTarget{ 247 { 248 Target: TargetAll, 249 }, 250 }, 251 Exclude: nil, 252 }) 253 254 // then 255 assert.NotNil(t, err) 256 assert.Len(t, runtimes, 0) 257 } 258 259 var ( 260 shoot1 = fixShoot(1, globalAccountID1, region1) 261 shoot2 = fixShoot(2, globalAccountID1, region2) 262 shoot3 = fixShoot(3, globalAccountID2, region3) 263 shoot4 = fixShoot(4, globalAccountID3, region1) 264 shoot5 = fixShoot(5, globalAccountID1, region1) 265 shoot6 = fixShoot(6, globalAccountID1, region1) 266 // shoot7 is purposefully missing to test missing cluster scenario 267 shoot8 = fixShoot(8, globalAccountID1, region1) 268 shoot9 = fixShoot(9, globalAccountID1, region1) 269 shoot10 = fixShoot(10, globalAccountID1, region1) 270 shoot11 = fixShoot(11, globalAccountID1, region1) 271 272 runtime1 = fixRuntimeDTO(1, globalAccountID1, plan2, runtimeOpState{provision: string(brokerapi.Succeeded)}) 273 runtime2 = fixRuntimeDTO(2, globalAccountID1, plan1, runtimeOpState{provision: string(brokerapi.Succeeded)}) 274 runtime3 = fixRuntimeDTO(3, globalAccountID2, plan1, runtimeOpState{provision: string(brokerapi.Succeeded)}) 275 runtime4 = fixRuntimeDTO(4, globalAccountID3, plan1, runtimeOpState{provision: string(brokerapi.Succeeded), deprovision: string(brokerapi.InProgress)}) 276 runtime5 = fixRuntimeDTO(5, globalAccountID3, plan1, runtimeOpState{provision: string(brokerapi.Failed)}) 277 runtime6 = fixRuntimeDTO(6, globalAccountID3, plan2, runtimeOpState{provision: string(brokerapi.InProgress)}) 278 runtime7 = fixRuntimeDTO(7, globalAccountID1, plan1, runtimeOpState{provision: string(brokerapi.Succeeded)}) 279 runtime8 = fixRuntimeDTO(8, globalAccountID1, plan1, runtimeOpState{provision: string(brokerapi.Succeeded), suspension: string(brokerapi.Succeeded)}) 280 runtime9 = fixRuntimeDTO(9, globalAccountID1, plan1, runtimeOpState{provision: string(brokerapi.Succeeded), suspension: string(brokerapi.InProgress)}) 281 runtime10 = fixRuntimeDTO(10, globalAccountID1, plan1, runtimeOpState{provision: string(brokerapi.Succeeded), suspension: string(brokerapi.Succeeded), unsuspension: string(brokerapi.Succeeded)}) 282 runtime11 = fixRuntimeDTO(11, globalAccountID1, plan1, runtimeOpState{provision: string(brokerapi.Succeeded), suspension: string(brokerapi.Succeeded), unsuspension: string(brokerapi.Failed)}) 283 ) 284 285 func fixShoot(id int, globalAccountID, region string) unstructured.Unstructured { 286 return unstructured.Unstructured{ 287 Object: map[string]interface{}{ 288 "apiVersion": "core.gardener.cloud/v1beta1", 289 "kind": "Shoot", 290 "metadata": map[string]interface{}{ 291 "name": fmt.Sprintf("shoot%d", id), 292 "namespace": shootNamespace, 293 "labels": map[string]interface{}{ 294 globalAccountLabel: globalAccountID, 295 subAccountLabel: fmt.Sprintf("subaccount-id-%d", id), 296 }, 297 "annotations": map[string]interface{}{ 298 runtimeIDAnnotation: fmt.Sprintf("runtime-id-%d", id), 299 }, 300 }, 301 "spec": map[string]interface{}{ 302 "region": region, 303 "maintenance": map[string]interface{}{ 304 "timeWindow": map[string]interface{}{ 305 "begin": "030000+0000", 306 "end": "040000+0000", 307 }, 308 }, 309 }, 310 }, 311 } 312 } 313 314 type runtimeOpState struct { 315 provision string 316 deprovision string 317 suspension string 318 unsuspension string 319 } 320 321 func fixRuntimeDTO(id int, globalAccountID, planName string, state runtimeOpState) runtime.RuntimeDTO { 322 rt := runtime.RuntimeDTO{ 323 InstanceID: fmt.Sprintf("instance-id-%d", id), 324 RuntimeID: fmt.Sprintf("runtime-id-%d", id), 325 GlobalAccountID: globalAccountID, 326 SubAccountID: fmt.Sprintf("subaccount-id-%d", id), 327 ServicePlanName: planName, 328 Status: runtime.RuntimeStatus{ 329 Provisioning: &runtime.Operation{ 330 State: state.provision, 331 CreatedAt: time.Now(), 332 }, 333 }, 334 } 335 336 deprovTime := time.Now().Add(time.Minute) 337 if state.suspension != "" { 338 rt.Status.Suspension = &runtime.OperationsData{} 339 rt.Status.Suspension.Count = 1 340 rt.Status.Suspension.TotalCount = 1 341 rt.Status.Suspension.Data = []runtime.Operation{ 342 { 343 State: state.suspension, 344 CreatedAt: deprovTime, 345 }, 346 } 347 state.deprovision = state.suspension 348 } 349 350 if state.deprovision != "" { 351 rt.Status.Deprovisioning = &runtime.Operation{ 352 State: state.deprovision, 353 CreatedAt: deprovTime, 354 } 355 } 356 357 if state.unsuspension != "" { 358 rt.Status.Unsuspension = &runtime.OperationsData{} 359 rt.Status.Unsuspension.Count = 1 360 rt.Status.Unsuspension.TotalCount = 1 361 rt.Status.Unsuspension.Data = []runtime.Operation{ 362 { 363 State: state.unsuspension, 364 CreatedAt: deprovTime.Add(time.Minute), 365 }, 366 } 367 } 368 369 return rt 370 } 371 372 type expectedRuntime struct { 373 shoot *unstructured.Unstructured 374 runtime *runtime.RuntimeDTO 375 } 376 377 func newFakeGardenerClient() *dynamicfake.FakeDynamicClient { 378 client := gardener.NewDynamicFakeClient( 379 &shoot1, 380 &shoot2, 381 &shoot3, 382 &shoot4, 383 &shoot5, 384 &shoot6, 385 &shoot8, 386 &shoot9, 387 &shoot10, 388 ) 389 390 return client 391 } 392 393 func newRuntimeListerMock() *RuntimeListerMock { 394 lister := &RuntimeListerMock{} 395 lister.On("ListAllRuntimes").Maybe().Return( 396 []runtime.RuntimeDTO{ 397 runtime1, 398 runtime2, 399 runtime3, 400 runtime4, 401 runtime5, 402 runtime6, 403 runtime7, 404 runtime8, 405 runtime9, 406 runtime10, 407 }, 408 nil, 409 ) 410 return lister 411 } 412 413 func newLogDummy() *logrus.Entry { 414 rawLgr := logrus.New() 415 rawLgr.Out = ioutil.Discard 416 lgr := rawLgr.WithField("testing", true) 417 418 return lgr 419 } 420 421 func lookupRuntime(runtimeID string, runtimes []Runtime) *Runtime { 422 for _, r := range runtimes { 423 if r.RuntimeID == runtimeID { 424 return &r 425 } 426 } 427 428 return nil 429 } 430 431 func assertRuntimeTargets(t *testing.T, expectedRuntimes []expectedRuntime, runtimes []Runtime) { 432 require.Equal(t, len(expectedRuntimes), len(runtimes)) 433 434 for _, e := range expectedRuntimes { 435 r := lookupRuntime(e.runtime.RuntimeID, runtimes) 436 s := gardener.Shoot{*e.shoot} 437 require.NotNil(t, r) 438 assert.Equal(t, e.runtime.InstanceID, r.InstanceID) 439 assert.Equal(t, e.runtime.GlobalAccountID, r.GlobalAccountID) 440 assert.Equal(t, e.runtime.SubAccountID, r.SubAccountID) 441 assert.Equal(t, s.GetName(), r.ShootName) 442 assert.Equal(t, s.GetSpecMaintenanceTimeWindowBegin(), r.MaintenanceWindowBegin.Format(maintenanceWindowFormat)) 443 assert.Equal(t, s.GetSpecMaintenanceTimeWindowEnd(), r.MaintenanceWindowEnd.Format(maintenanceWindowFormat)) 444 } 445 }