github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/broker/plans.go (about) 1 package broker 2 3 import ( 4 "strings" 5 6 "github.com/kyma-incubator/compass/components/director/pkg/jsonschema" 7 8 "github.com/pivotal-cf/brokerapi/v8/domain" 9 10 "github.com/kyma-project/kyma-environment-broker/internal" 11 ) 12 13 const ( 14 AllPlansSelector = "all_plans" 15 16 GCPPlanID = "ca6e5357-707f-4565-bbbd-b3ab732597c6" 17 GCPPlanName = "gcp" 18 AWSPlanID = "361c511f-f939-4621-b228-d0fb79a1fe15" 19 AWSPlanName = "aws" 20 AzurePlanID = "4deee563-e5ec-4731-b9b1-53b42d855f0c" 21 AzurePlanName = "azure" 22 AzureLitePlanID = "8cb22518-aa26-44c5-91a0-e669ec9bf443" 23 AzureLitePlanName = "azure_lite" 24 TrialPlanID = "7d55d31d-35ae-4438-bf13-6ffdfa107d9f" 25 TrialPlanName = "trial" 26 OpenStackPlanID = "03b812ac-c991-4528-b5bd-08b303523a63" 27 OpenStackPlanName = "openstack" 28 FreemiumPlanID = "b1a5764e-2ea1-4f95-94c0-2b4538b37b55" 29 FreemiumPlanName = "free" 30 OwnClusterPlanID = "03e3cb66-a4c6-4c6a-b4b0-5d42224debea" 31 OwnClusterPlanName = "own_cluster" 32 PreviewPlanID = "5cb3d976-b85c-42ea-a636-79cadda109a9" 33 PreviewPlanName = "preview" 34 ) 35 36 var PlanNamesMapping = map[string]string{ 37 GCPPlanID: GCPPlanName, 38 AWSPlanID: AWSPlanName, 39 AzurePlanID: AzurePlanName, 40 AzureLitePlanID: AzureLitePlanName, 41 TrialPlanID: TrialPlanName, 42 OpenStackPlanID: OpenStackPlanName, 43 FreemiumPlanID: FreemiumPlanName, 44 OwnClusterPlanID: OwnClusterPlanName, 45 PreviewPlanID: PreviewPlanName, 46 } 47 48 var PlanIDsMapping = map[string]string{ 49 AzurePlanName: AzurePlanID, 50 AWSPlanName: AWSPlanID, 51 AzureLitePlanName: AzureLitePlanID, 52 GCPPlanName: GCPPlanID, 53 TrialPlanName: TrialPlanID, 54 OpenStackPlanName: OpenStackPlanID, 55 FreemiumPlanName: FreemiumPlanID, 56 OwnClusterPlanName: OwnClusterPlanID, 57 PreviewPlanName: PreviewPlanID, 58 } 59 60 type TrialCloudRegion string 61 62 const ( 63 Europe TrialCloudRegion = "europe" 64 Us TrialCloudRegion = "us" 65 Asia TrialCloudRegion = "asia" 66 ) 67 68 var validRegionsForTrial = map[TrialCloudRegion]struct{}{ 69 Europe: {}, 70 Us: {}, 71 Asia: {}, 72 } 73 74 type JSONSchemaValidator interface { 75 ValidateString(json string) (jsonschema.ValidationResult, error) 76 } 77 78 func AzureRegions(euRestrictedAccess bool) []string { 79 if euRestrictedAccess { 80 return []string{ 81 "switzerlandnorth", 82 } 83 } 84 return []string{ 85 "eastus", 86 "centralus", 87 "westus2", 88 "uksouth", 89 "northeurope", 90 "westeurope", 91 "japaneast", 92 "southeastasia", 93 } 94 } 95 96 func GCPRegions() []string { 97 return []string{ 98 "europe-west3", 99 "asia-south1", 100 "us-central1"} 101 } 102 103 func AWSRegions(euRestrictedAccess bool) []string { 104 // be aware of zones defined in internal/provider/aws_provider.go 105 if euRestrictedAccess { 106 return []string{"eu-central-1"} 107 } 108 return []string{"eu-central-1", "eu-west-2", "ca-central-1", "sa-east-1", "us-east-1", "us-west-1", 109 "ap-northeast-1", "ap-northeast-2", "ap-south-1", "ap-southeast-1", "ap-southeast-2"} 110 } 111 112 func OpenStackRegions() []string { 113 return []string{"eu-de-1", "ap-sa-1"} 114 } 115 116 func requiredSchemaProperties(regionParameterIsRequired bool) []string { 117 if regionParameterIsRequired { 118 return []string{"name", "region"} 119 } 120 return []string{"name"} 121 } 122 123 func requiredTrialSchemaProperties() []string { 124 return []string{"name"} 125 } 126 127 func requiredOwnClusterSchemaProperties() []string { 128 return []string{"name", "kubeconfig", "shootName", "shootDomain"} 129 } 130 131 func OpenStackSchema(machineTypesDisplay map[string]string, machineTypes []string, additionalParams, update bool, regionParameterIsRequired, modulesEnabled bool) *map[string]interface{} { 132 properties := NewProvisioningProperties(machineTypesDisplay, machineTypes, OpenStackRegions(), update, modulesEnabled) 133 properties.AutoScalerMax.Maximum = 40 134 if !update { 135 properties.AutoScalerMax.Default = 8 136 } 137 if regionParameterIsRequired { 138 properties.Region.MinLength = 1 139 } 140 141 return createSchemaWithProperties(properties, additionalParams, update, requiredSchemaProperties(regionParameterIsRequired)) 142 } 143 144 func PreviewSchema(machineTypesDisplay map[string]string, machineTypes []string, additionalParams, update bool, euAccessRestricted bool, regionParameterIsRequired, modulesEnabled bool) *map[string]interface{} { 145 properties := NewProvisioningProperties(machineTypesDisplay, machineTypes, AWSRegions(euAccessRestricted), update, modulesEnabled) 146 properties.AutoScalerMax.Minimum = 3 147 properties.AutoScalerMin.Minimum = 3 148 properties.Networking = NewNetworkingSchema() 149 if regionParameterIsRequired { 150 properties.Region.MinLength = 1 151 } 152 return createSchemaWithProperties(properties, additionalParams, update, requiredSchemaProperties(regionParameterIsRequired)) 153 } 154 155 func GCPSchema(machineTypesDisplay map[string]string, machineTypes []string, additionalParams, update bool, regionParameterIsRequired, modulesEnabled bool) *map[string]interface{} { 156 properties := NewProvisioningProperties(machineTypesDisplay, machineTypes, GCPRegions(), update, modulesEnabled) 157 properties.AutoScalerMax.Minimum = 3 158 properties.AutoScalerMin.Minimum = 3 159 if regionParameterIsRequired { 160 properties.Region.MinLength = 1 161 } 162 return createSchemaWithProperties(properties, additionalParams, update, requiredSchemaProperties(regionParameterIsRequired)) 163 } 164 165 func AWSSchema(machineTypesDisplay map[string]string, machineTypes []string, additionalParams, update bool, euAccessRestricted bool, regionParameterIsRequired, modulesEnabled bool) *map[string]interface{} { 166 properties := NewProvisioningProperties(machineTypesDisplay, machineTypes, AWSRegions(euAccessRestricted), update, modulesEnabled) 167 properties.AutoScalerMax.Minimum = 3 168 properties.AutoScalerMin.Minimum = 3 169 if regionParameterIsRequired { 170 properties.Region.MinLength = 1 171 } 172 return createSchemaWithProperties(properties, additionalParams, update, requiredSchemaProperties(regionParameterIsRequired)) 173 } 174 175 func AzureSchema(machineTypesDisplay map[string]string, machineTypes []string, additionalParams, update bool, euAccessRestricted bool, regionParameterIsRequired, modulesEnabled bool) *map[string]interface{} { 176 properties := NewProvisioningProperties(machineTypesDisplay, machineTypes, AzureRegions(euAccessRestricted), update, modulesEnabled) 177 properties.AutoScalerMax.Minimum = 3 178 properties.AutoScalerMin.Minimum = 3 179 if regionParameterIsRequired { 180 properties.Region.MinLength = 1 181 } 182 return createSchemaWithProperties(properties, additionalParams, update, requiredSchemaProperties(regionParameterIsRequired)) 183 } 184 185 func AzureLiteSchema(machineTypesDisplay map[string]string, machineTypes []string, additionalParams, update bool, euAccessRestricted bool, regionParameterIsRequired, modulesEnabled bool) *map[string]interface{} { 186 properties := NewProvisioningProperties(machineTypesDisplay, machineTypes, AzureRegions(euAccessRestricted), update, modulesEnabled) 187 properties.AutoScalerMax.Maximum = 40 188 189 if !update { 190 properties.AutoScalerMax.Default = 10 191 properties.AutoScalerMin.Default = 2 192 } 193 if regionParameterIsRequired { 194 properties.Region.MinLength = 1 195 } 196 197 return createSchemaWithProperties(properties, additionalParams, update, requiredSchemaProperties(regionParameterIsRequired)) 198 } 199 200 func FreemiumSchema(provider internal.CloudProvider, additionalParams, update bool, euAccessRestricted bool, regionParameterIsRequired, modulesEnabled bool) *map[string]interface{} { 201 if update && !additionalParams { 202 return empty() 203 } 204 205 var regions []string 206 switch provider { 207 case internal.AWS: 208 regions = AWSRegions(euAccessRestricted) 209 case internal.Azure: 210 regions = AzureRegions(euAccessRestricted) 211 default: 212 regions = AWSRegions(euAccessRestricted) 213 } 214 properties := ProvisioningProperties{ 215 Name: NameProperty(), 216 Region: &Type{ 217 Type: "string", 218 Enum: ToInterfaceSlice(regions), 219 }, 220 } 221 if !update { 222 properties.Networking = NewNetworkingSchema() 223 properties.Modules = NewModulesSchema(modulesEnabled) 224 } 225 if regionParameterIsRequired { 226 properties.Region.MinLength = 1 227 } 228 229 return createSchemaWithProperties(properties, additionalParams, update, requiredSchemaProperties(regionParameterIsRequired)) 230 } 231 232 func TrialSchema(additionalParams, update, modulesEnabled bool) *map[string]interface{} { 233 properties := ProvisioningProperties{ 234 Name: NameProperty(), 235 } 236 237 if !update { 238 properties.Modules = NewModulesSchema(modulesEnabled) 239 } 240 241 if update && !additionalParams { 242 return empty() 243 } 244 245 return createSchemaWithProperties(properties, additionalParams, update, requiredTrialSchemaProperties()) 246 } 247 248 func OwnClusterSchema(update, modulesEnabled bool) *map[string]interface{} { 249 properties := ProvisioningProperties{ 250 Name: NameProperty(), 251 ShootName: ShootNameProperty(), 252 ShootDomain: ShootDomainProperty(), 253 UpdateProperties: UpdateProperties{ 254 Kubeconfig: KubeconfigProperty(), 255 }, 256 } 257 258 if update { 259 return createSchemaWith(properties.UpdateProperties, update, requiredOwnClusterSchemaProperties()) 260 } else { 261 properties.Modules = NewModulesSchema(modulesEnabled) 262 return createSchemaWith(properties, update, requiredOwnClusterSchemaProperties()) 263 } 264 } 265 266 func empty() *map[string]interface{} { 267 empty := make(map[string]interface{}, 0) 268 return &empty 269 } 270 271 func createSchemaWithProperties(properties ProvisioningProperties, additionalParams, update bool, requiered []string) *map[string]interface{} { 272 if additionalParams { 273 properties.IncludeAdditional() 274 } 275 276 if update { 277 return createSchemaWith(properties.UpdateProperties, update, requiered) 278 } else { 279 return createSchemaWith(properties, update, requiered) 280 } 281 } 282 283 func createSchemaWith(properties interface{}, update bool, requiered []string) *map[string]interface{} { 284 schema := NewSchema(properties, update, requiered) 285 286 return unmarshalSchema(schema) 287 } 288 289 func unmarshalSchema(schema *RootSchema) *map[string]interface{} { 290 target := make(map[string]interface{}) 291 schema.ControlsOrder = DefaultControlsOrder() 292 293 unmarshaled := unmarshalOrPanic(schema, &target).(*map[string]interface{}) 294 295 // update controls order 296 props := (*unmarshaled)[PropertiesKey].(map[string]interface{}) 297 controlsOrder := (*unmarshaled)[ControlsOrderKey].([]interface{}) 298 (*unmarshaled)[ControlsOrderKey] = filter(&controlsOrder, props) 299 300 return unmarshaled 301 } 302 303 // Plans is designed to hold plan defaulting logic 304 // keep internal/hyperscaler/azure/config.go in sync with any changes to available zones 305 func Plans(plans PlansConfig, provider internal.CloudProvider, includeAdditionalParamsInSchema bool, euAccessRestricted bool, regionParameterIsRequired, modulesEnabled bool) map[string]domain.ServicePlan { 306 awsMachines := []string{"m5.xlarge", "m5.2xlarge", "m5.4xlarge", "m5.8xlarge", "m5.12xlarge", "m6i.xlarge", "m6i.2xlarge", "m6i.4xlarge", "m6i.8xlarge", "m6i.12xlarge"} 307 awsMachinesDisplay := map[string]string{ 308 // source: https://aws.amazon.com/ec2/instance-types/m5/ 309 "m5.xlarge": "m5.xlarge (4vCPU, 16GB RAM)", 310 "m5.2xlarge": "m5.2xlarge (8vCPU, 32GB RAM)", 311 "m5.4xlarge": "m5.4xlarge (16vCPU, 64GB RAM)", 312 "m5.8xlarge": "m5.8xlarge (32vCPU, 128GB RAM)", 313 "m5.12xlarge": "m5.12xlarge (48vCPU, 192GB RAM)", 314 // source: https://aws.amazon.com/ec2/instance-types/m6i/ 315 "m6i.xlarge": "m6i.xlarge (4vCPU, 16GB RAM)", 316 "m6i.2xlarge": "m6i.2xlarge (8vCPU, 32GB RAM)", 317 "m6i.4xlarge": "m6i.4xlarge (16vCPU, 64GB RAM)", 318 "m6i.8xlarge": "m6i.8xlarge (32vCPU, 128GB RAM)", 319 "m6i.12xlarge": "m6i.12xlarge (48vCPU, 192GB RAM)", 320 } 321 322 // awsHASchema := AWSHASchema(awsMachinesDisplay, awsMachines, includeAdditionalParamsInSchema, false) 323 324 // source: https://cloud.google.com/compute/docs/general-purpose-machines#e2_limitations 325 gcpMachines := []string{"n2-standard-4", "n2-standard-8", "n2-standard-16", "n2-standard-32", "n2-standard-48"} 326 gcpMachinesDisplay := map[string]string{ 327 "n2-standard-4": "n2-standard-4 (4vCPU, 16GB RAM)", 328 "n2-standard-8": "n2-standard-8 (8vCPU, 32GB RAM)", 329 "n2-standard-16": "n2-standard-16 (16vCPU, 64GB RAM)", 330 "n2-standard-32": "n2-standard-32 (32vCPU, 128GB RAM)", 331 "n2-standard-48": "n2-standard-48 (48vCPU, 192B RAM)", 332 } 333 gcpSchema := GCPSchema(gcpMachinesDisplay, gcpMachines, includeAdditionalParamsInSchema, false, regionParameterIsRequired, modulesEnabled) 334 335 openStackMachines := []string{"g_c4_m16", "g_c8_m32"} 336 openStackMachinesDisplay := map[string]string{ 337 "g_c4_m16": "g_c4_m16 (4vCPU, 16GB RAM)", 338 "g_c8_m32": "g_c8_m32 (8vCPU, 32GB RAM)", 339 } 340 openstackSchema := OpenStackSchema(openStackMachinesDisplay, openStackMachines, includeAdditionalParamsInSchema, false, regionParameterIsRequired, modulesEnabled) 341 342 // source: https://docs.microsoft.com/en-us/azure/cloud-services/cloud-services-sizes-specs#dv3-series 343 azureMachines := []string{"Standard_D4_v3", "Standard_D8_v3", "Standard_D16_v3", "Standard_D32_v3", "Standard_D48_v3", "Standard_D64_v3"} 344 azureMachinesDisplay := map[string]string{ 345 "Standard_D4_v3": "Standard_D4_v3 (4vCPU, 16GB RAM)", 346 "Standard_D8_v3": "Standard_D8_v3 (8vCPU, 32GB RAM)", 347 "Standard_D16_v3": "Standard_D16_v3 (16vCPU, 64GB RAM)", 348 "Standard_D32_v3": "Standard_D32_v3 (32vCPU, 128GB RAM)", 349 "Standard_D48_v3": "Standard_D48_v3 (48vCPU, 192GB RAM)", 350 "Standard_D64_v3": "Standard_D64_v3 (64vCPU, 256GB RAM)", 351 } 352 azureSchema := AzureSchema(azureMachinesDisplay, azureMachines, includeAdditionalParamsInSchema, false, euAccessRestricted, regionParameterIsRequired, modulesEnabled) 353 354 azureLiteMachines := []string{"Standard_D4_v3"} 355 azureLiteMachinesDisplay := map[string]string{ 356 "Standard_D4_v3": azureMachinesDisplay["Standard_D4_v3"], 357 } 358 azureLiteSchema := AzureLiteSchema(azureLiteMachinesDisplay, azureLiteMachines, includeAdditionalParamsInSchema, false, euAccessRestricted, regionParameterIsRequired, modulesEnabled) 359 freemiumSchema := FreemiumSchema(provider, includeAdditionalParamsInSchema, false, euAccessRestricted, regionParameterIsRequired, modulesEnabled) 360 trialSchema := TrialSchema(includeAdditionalParamsInSchema, false, modulesEnabled) 361 ownClusterSchema := OwnClusterSchema(false, modulesEnabled) 362 363 // Schemas exposed on v2/catalog endpoint - different than provisioningRawSchema to allow backwards compatibility 364 // when a machine type switch is introduced 365 // switch to m6 if m6 is available in all regions 366 awsCatalogMachines := []string{"m5.xlarge", "m5.2xlarge", "m5.4xlarge", "m5.8xlarge", "m5.12xlarge"} 367 awsCatalogMachinesDisplay := map[string]string{ 368 "m5.xlarge": awsMachinesDisplay["m5.xlarge"], 369 "m5.2xlarge": awsMachinesDisplay["m5.2xlarge"], 370 "m5.4xlarge": awsMachinesDisplay["m5.4xlarge"], 371 "m5.8xlarge": awsMachinesDisplay["m5.8xlarge"], 372 "m5.12xlarge": awsMachinesDisplay["m5.12xlarge"], 373 } 374 awsCatalogSchema := AWSSchema(awsCatalogMachinesDisplay, awsCatalogMachines, includeAdditionalParamsInSchema, false, euAccessRestricted, regionParameterIsRequired, modulesEnabled) 375 376 previewCatalogSchema := PreviewSchema(awsMachinesDisplay, awsMachines, includeAdditionalParamsInSchema, false, euAccessRestricted, regionParameterIsRequired, modulesEnabled) 377 378 outputPlans := map[string]domain.ServicePlan{ 379 AWSPlanID: defaultServicePlan(AWSPlanID, AWSPlanName, plans, awsCatalogSchema, AWSSchema(awsMachinesDisplay, awsMachines, includeAdditionalParamsInSchema, true, euAccessRestricted, regionParameterIsRequired, false)), 380 GCPPlanID: defaultServicePlan(GCPPlanID, GCPPlanName, plans, gcpSchema, GCPSchema(gcpMachinesDisplay, gcpMachines, includeAdditionalParamsInSchema, true, regionParameterIsRequired, false)), 381 OpenStackPlanID: defaultServicePlan(OpenStackPlanID, OpenStackPlanName, plans, openstackSchema, OpenStackSchema(openStackMachinesDisplay, openStackMachines, includeAdditionalParamsInSchema, true, regionParameterIsRequired, false)), 382 AzurePlanID: defaultServicePlan(AzurePlanID, AzurePlanName, plans, azureSchema, AzureSchema(azureMachinesDisplay, azureMachines, includeAdditionalParamsInSchema, true, euAccessRestricted, regionParameterIsRequired, false)), 383 AzureLitePlanID: defaultServicePlan(AzureLitePlanID, AzureLitePlanName, plans, azureLiteSchema, AzureLiteSchema(azureLiteMachinesDisplay, azureLiteMachines, includeAdditionalParamsInSchema, true, euAccessRestricted, regionParameterIsRequired, false)), 384 FreemiumPlanID: defaultServicePlan(FreemiumPlanID, FreemiumPlanName, plans, freemiumSchema, FreemiumSchema(provider, includeAdditionalParamsInSchema, true, euAccessRestricted, regionParameterIsRequired, false)), 385 TrialPlanID: defaultServicePlan(TrialPlanID, TrialPlanName, plans, trialSchema, TrialSchema(includeAdditionalParamsInSchema, true, false)), 386 OwnClusterPlanID: defaultServicePlan(OwnClusterPlanID, OwnClusterPlanName, plans, ownClusterSchema, OwnClusterSchema(true, false)), 387 PreviewPlanID: defaultServicePlan(PreviewPlanID, PreviewPlanName, plans, previewCatalogSchema, AWSSchema(awsMachinesDisplay, awsMachines, includeAdditionalParamsInSchema, true, euAccessRestricted, regionParameterIsRequired, false)), 388 } 389 390 return outputPlans 391 } 392 393 func defaultServicePlan(id, name string, plans PlansConfig, createParams, updateParams *map[string]interface{}) domain.ServicePlan { 394 servicePlan := domain.ServicePlan{ 395 ID: id, 396 Name: name, 397 Description: defaultDescription(name, plans), 398 Metadata: defaultMetadata(name, plans), 399 Schemas: &domain.ServiceSchemas{ 400 Instance: domain.ServiceInstanceSchema{ 401 Create: domain.Schema{ 402 Parameters: *createParams, 403 }, 404 Update: domain.Schema{ 405 Parameters: *updateParams, 406 }, 407 }, 408 }, 409 } 410 411 return servicePlan 412 } 413 414 func defaultDescription(planName string, plans PlansConfig) string { 415 plan, ok := plans[planName] 416 if !ok || len(plan.Description) == 0 { 417 return strings.ToTitle(planName) 418 } 419 420 return plan.Description 421 } 422 423 func defaultMetadata(planName string, plans PlansConfig) *domain.ServicePlanMetadata { 424 plan, ok := plans[planName] 425 if !ok || len(plan.Metadata.DisplayName) == 0 { 426 return &domain.ServicePlanMetadata{ 427 DisplayName: strings.ToTitle(planName), 428 } 429 } 430 return &domain.ServicePlanMetadata{ 431 DisplayName: plan.Metadata.DisplayName, 432 } 433 } 434 435 func IsTrialPlan(planID string) bool { 436 switch planID { 437 case TrialPlanID: 438 return true 439 default: 440 return false 441 } 442 } 443 444 func IsPreviewPlan(planID string) bool { 445 switch planID { 446 case PreviewPlanID: 447 return true 448 default: 449 return false 450 } 451 } 452 453 func IsAzurePlan(planID string) bool { 454 switch planID { 455 case AzurePlanID, AzureLitePlanID: 456 return true 457 default: 458 return false 459 } 460 } 461 462 func IsFreemiumPlan(planID string) bool { 463 switch planID { 464 case FreemiumPlanID: 465 return true 466 default: 467 return false 468 } 469 } 470 471 func IsOwnClusterPlan(planID string) bool { 472 return planID == OwnClusterPlanID 473 } 474 475 func filter(items *[]interface{}, included map[string]interface{}) interface{} { 476 output := make([]interface{}, 0) 477 for i := 0; i < len(*items); i++ { 478 value := (*items)[i] 479 480 if _, ok := included[value.(string)]; ok { 481 output = append(output, value) 482 } 483 } 484 485 return output 486 }