github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/process/input/builder.go (about) 1 package input 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/kyma-project/control-plane/components/provisioner/pkg/gqlschema" 8 "github.com/kyma-project/kyma-environment-broker/internal" 9 "github.com/kyma-project/kyma-environment-broker/internal/broker" 10 cloudProvider "github.com/kyma-project/kyma-environment-broker/internal/provider" 11 "github.com/kyma-project/kyma-environment-broker/internal/runtime" 12 ) 13 14 //go:generate mockery --name=ComponentListProvider --output=automock --outpkg=automock --case=underscore 15 //go:generate mockery --name=CreatorForPlan --output=automock --outpkg=automock --case=underscore 16 //go:generate mockery --name=ComponentsDisabler --output=automock --outpkg=automock --case=underscore 17 //go:generate mockery --name=OptionalComponentService --output=automock --outpkg=automock --case=underscore 18 19 type ( 20 OptionalComponentService interface { 21 ExecuteDisablers(components internal.ComponentConfigurationInputList, names ...string) (internal.ComponentConfigurationInputList, error) 22 ComputeComponentsToDisable(optComponentsToKeep []string) []string 23 AddComponentToDisable(name string, disabler runtime.ComponentDisabler) 24 } 25 26 ComponentsDisabler interface { 27 DisableComponents(components internal.ComponentConfigurationInputList) (internal.ComponentConfigurationInputList, error) 28 } 29 30 DisabledComponentsProvider interface { 31 DisabledComponentsPerPlan(planID string) (map[string]struct{}, error) 32 DisabledForAll() map[string]struct{} 33 } 34 35 HyperscalerInputProvider interface { 36 Defaults() *gqlschema.ClusterConfigInput 37 ApplyParameters(input *gqlschema.ClusterConfigInput, params internal.ProvisioningParameters) 38 Profile() gqlschema.KymaProfile 39 Provider() internal.CloudProvider 40 } 41 42 CreatorForPlan interface { 43 IsPlanSupport(planID string) bool 44 CreateProvisionInput(parameters internal.ProvisioningParameters, version internal.RuntimeVersionData) (internal.ProvisionerInputCreator, error) 45 CreateUpgradeInput(parameters internal.ProvisioningParameters, version internal.RuntimeVersionData) (internal.ProvisionerInputCreator, error) 46 CreateUpgradeShootInput(parameters internal.ProvisioningParameters, version internal.RuntimeVersionData) (internal.ProvisionerInputCreator, error) 47 GetPlanDefaults(planID string, platformProvider internal.CloudProvider, parametersProvider *internal.CloudProvider) (*gqlschema.ClusterConfigInput, error) 48 } 49 50 ComponentListProvider interface { 51 AllComponents(kymaVersion internal.RuntimeVersionData, config *internal.ConfigForPlan) ([]internal.KymaComponent, error) 52 } 53 54 ConfigurationProvider interface { 55 ProvideForGivenVersionAndPlan(kymaVersion, planName string) (*internal.ConfigForPlan, error) 56 } 57 ) 58 59 type InputBuilderFactory struct { 60 kymaVersion string 61 config Config 62 optComponentsSvc OptionalComponentService 63 componentsProvider ComponentListProvider 64 disabledComponentsProvider DisabledComponentsProvider 65 configProvider ConfigurationProvider 66 trialPlatformRegionMapping map[string]string 67 enabledFreemiumProviders map[string]struct{} 68 oidcDefaultValues internal.OIDCConfigDTO 69 } 70 71 func NewInputBuilderFactory(optComponentsSvc OptionalComponentService, disabledComponentsProvider DisabledComponentsProvider, 72 componentsListProvider ComponentListProvider, configProvider ConfigurationProvider, 73 config Config, defaultKymaVersion string, trialPlatformRegionMapping map[string]string, 74 enabledFreemiumProviders []string, oidcValues internal.OIDCConfigDTO) (CreatorForPlan, error) { 75 76 freemiumProviders := map[string]struct{}{} 77 for _, p := range enabledFreemiumProviders { 78 freemiumProviders[strings.ToLower(p)] = struct{}{} 79 } 80 81 return &InputBuilderFactory{ 82 kymaVersion: defaultKymaVersion, 83 config: config, 84 optComponentsSvc: optComponentsSvc, 85 componentsProvider: componentsListProvider, 86 disabledComponentsProvider: disabledComponentsProvider, 87 configProvider: configProvider, 88 trialPlatformRegionMapping: trialPlatformRegionMapping, 89 enabledFreemiumProviders: freemiumProviders, 90 oidcDefaultValues: oidcValues, 91 }, nil 92 } 93 94 // SetDefaultTrialProvider is used for testing scenario, when the default trial provider is being changed 95 func (f *InputBuilderFactory) SetDefaultTrialProvider(p internal.CloudProvider) { 96 f.config.DefaultTrialProvider = p 97 } 98 99 func (f *InputBuilderFactory) IsPlanSupport(planID string) bool { 100 switch planID { 101 case broker.AWSPlanID, broker.GCPPlanID, broker.AzurePlanID, broker.FreemiumPlanID, 102 broker.AzureLitePlanID, broker.TrialPlanID, broker.OpenStackPlanID, broker.OwnClusterPlanID, broker.PreviewPlanID: 103 return true 104 default: 105 return false 106 } 107 } 108 109 func (f *InputBuilderFactory) GetPlanDefaults(planID string, platformProvider internal.CloudProvider, parametersProvider *internal.CloudProvider) (*gqlschema.ClusterConfigInput, error) { 110 h, err := f.getHyperscalerProviderForPlanID(planID, platformProvider, parametersProvider) 111 if err != nil { 112 return nil, err 113 } 114 return h.Defaults(), nil 115 } 116 117 func (f *InputBuilderFactory) getHyperscalerProviderForPlanID(planID string, platformProvider internal.CloudProvider, parametersProvider *internal.CloudProvider) (HyperscalerInputProvider, error) { 118 var provider HyperscalerInputProvider 119 switch planID { 120 case broker.GCPPlanID: 121 provider = &cloudProvider.GcpInput{ 122 MultiZone: f.config.MultiZoneCluster, 123 ControlPlaneFailureTolerance: f.config.ControlPlaneFailureTolerance, 124 } 125 case broker.FreemiumPlanID: 126 return f.forFreemiumPlan(platformProvider) 127 case broker.OpenStackPlanID: 128 provider = &cloudProvider.OpenStackInput{ 129 FloatingPoolName: f.config.OpenstackFloatingPoolName, 130 } 131 case broker.AzurePlanID: 132 provider = &cloudProvider.AzureInput{ 133 MultiZone: f.config.MultiZoneCluster, 134 ControlPlaneFailureTolerance: f.config.ControlPlaneFailureTolerance, 135 } 136 case broker.AzureLitePlanID: 137 provider = &cloudProvider.AzureLiteInput{} 138 case broker.TrialPlanID: 139 provider = f.forTrialPlan(parametersProvider) 140 case broker.AWSPlanID: 141 provider = &cloudProvider.AWSInput{ 142 MultiZone: f.config.MultiZoneCluster, 143 ControlPlaneFailureTolerance: f.config.ControlPlaneFailureTolerance, 144 } 145 case broker.OwnClusterPlanID: 146 provider = &cloudProvider.NoHyperscalerInput{} 147 // insert cases for other providers like AWS or GCP 148 case broker.PreviewPlanID: 149 provider = &cloudProvider.AWSInput{ 150 MultiZone: f.config.MultiZoneCluster, 151 ControlPlaneFailureTolerance: f.config.ControlPlaneFailureTolerance, 152 } 153 default: 154 return nil, fmt.Errorf("case with plan %s is not supported", planID) 155 } 156 return provider, nil 157 } 158 159 func (f *InputBuilderFactory) CreateProvisionInput(provisioningParameters internal.ProvisioningParameters, version internal.RuntimeVersionData) (internal.ProvisionerInputCreator, error) { 160 if !f.IsPlanSupport(provisioningParameters.PlanID) { 161 return nil, fmt.Errorf("plan %s in not supported", provisioningParameters.PlanID) 162 } 163 164 planName := broker.PlanNamesMapping[provisioningParameters.PlanID] 165 166 cfg, err := f.configProvider.ProvideForGivenVersionAndPlan(version.Version, planName) 167 if err != nil { 168 return nil, fmt.Errorf("while getting configuration for given version and plan: %w", err) 169 } 170 171 provider, err := f.getHyperscalerProviderForPlanID(provisioningParameters.PlanID, provisioningParameters.PlatformProvider, provisioningParameters.Parameters.Provider) 172 if err != nil { 173 return nil, fmt.Errorf("during creating provision input: %w", err) 174 } 175 176 initInput, err := f.initProvisionRuntimeInput(provider, version, cfg) 177 if err != nil { 178 return nil, fmt.Errorf("while initializing ProvisionRuntimeInput: %w", err) 179 } 180 181 disabledForPlan, err := f.disabledComponentsProvider.DisabledComponentsPerPlan(provisioningParameters.PlanID) 182 if err != nil { 183 return nil, fmt.Errorf("while getting disabled components for plan %s: %w", provisioningParameters.PlanID, err) 184 } 185 disabledComponents := mergeMaps(disabledForPlan, f.disabledComponentsProvider.DisabledForAll()) 186 187 return &RuntimeInput{ 188 provisionRuntimeInput: initInput, 189 overrides: make(map[string][]*gqlschema.ConfigEntryInput, 0), 190 labels: make(map[string]string), 191 globalOverrides: make([]*gqlschema.ConfigEntryInput, 0), 192 config: cfg, 193 hyperscalerInputProvider: provider, 194 optionalComponentsService: f.optComponentsSvc, 195 provisioningParameters: provisioningParameters, 196 componentsDisabler: runtime.NewDisabledComponentsService(disabledComponents), 197 enabledOptionalComponents: map[string]struct{}{}, 198 oidcDefaultValues: f.oidcDefaultValues, 199 trialNodesNumber: f.config.TrialNodesNumber, 200 }, nil 201 } 202 203 func (f *InputBuilderFactory) forTrialPlan(provider *internal.CloudProvider) HyperscalerInputProvider { 204 var trialProvider internal.CloudProvider 205 if provider == nil { 206 trialProvider = f.config.DefaultTrialProvider 207 } else { 208 trialProvider = *provider 209 } 210 211 switch trialProvider { 212 case internal.GCP: 213 return &cloudProvider.GcpTrialInput{ 214 PlatformRegionMapping: f.trialPlatformRegionMapping, 215 } 216 case internal.AWS: 217 return &cloudProvider.AWSTrialInput{ 218 PlatformRegionMapping: f.trialPlatformRegionMapping, 219 } 220 default: 221 return &cloudProvider.AzureTrialInput{ 222 PlatformRegionMapping: f.trialPlatformRegionMapping, 223 } 224 } 225 226 } 227 228 func (f *InputBuilderFactory) provideComponentList(version internal.RuntimeVersionData, config *internal.ConfigForPlan) (internal.ComponentConfigurationInputList, error) { 229 allComponents, err := f.componentsProvider.AllComponents(version, config) 230 if err != nil { 231 return internal.ComponentConfigurationInputList{}, fmt.Errorf("while fetching components for %s Kyma version: %w", version.Version, err) 232 } 233 234 return mapToGQLComponentConfigurationInput(allComponents), nil 235 } 236 237 func (f *InputBuilderFactory) initProvisionRuntimeInput(provider HyperscalerInputProvider, version internal.RuntimeVersionData, config *internal.ConfigForPlan) (gqlschema.ProvisionRuntimeInput, error) { 238 components, err := f.provideComponentList(version, config) 239 if err != nil { 240 return gqlschema.ProvisionRuntimeInput{}, err 241 } 242 243 kymaProfile := provider.Profile() 244 245 provisionInput := gqlschema.ProvisionRuntimeInput{ 246 RuntimeInput: &gqlschema.RuntimeInput{}, 247 ClusterConfig: provider.Defaults(), 248 KymaConfig: &gqlschema.KymaConfigInput{ 249 Profile: &kymaProfile, 250 Version: version.Version, 251 Components: components.DeepCopy(), 252 }, 253 } 254 255 if provisionInput.ClusterConfig.GardenerConfig == nil { 256 provisionInput.ClusterConfig.GardenerConfig = &gqlschema.GardenerConfigInput{} 257 } 258 259 provisionInput.ClusterConfig.GardenerConfig.KubernetesVersion = f.config.KubernetesVersion 260 provisionInput.ClusterConfig.GardenerConfig.EnableKubernetesVersionAutoUpdate = &f.config.AutoUpdateKubernetesVersion 261 provisionInput.ClusterConfig.GardenerConfig.EnableMachineImageVersionAutoUpdate = &f.config.AutoUpdateMachineImageVersion 262 if provisionInput.ClusterConfig.GardenerConfig.Purpose == nil { 263 provisionInput.ClusterConfig.GardenerConfig.Purpose = &f.config.DefaultGardenerShootPurpose 264 } 265 if f.config.MachineImage != "" { 266 provisionInput.ClusterConfig.GardenerConfig.MachineImage = &f.config.MachineImage 267 } 268 if f.config.MachineImageVersion != "" { 269 provisionInput.ClusterConfig.GardenerConfig.MachineImageVersion = &f.config.MachineImageVersion 270 } 271 272 return provisionInput, nil 273 } 274 275 func (f *InputBuilderFactory) CreateUpgradeInput(provisioningParameters internal.ProvisioningParameters, version internal.RuntimeVersionData) (internal.ProvisionerInputCreator, error) { 276 if !f.IsPlanSupport(provisioningParameters.PlanID) { 277 return nil, fmt.Errorf("plan %s in not supported", provisioningParameters.PlanID) 278 } 279 280 planName := broker.PlanNamesMapping[provisioningParameters.PlanID] 281 282 cfg, err := f.configProvider.ProvideForGivenVersionAndPlan(version.Version, planName) 283 if err != nil { 284 return nil, fmt.Errorf("while getting configuration for given version and plan: %w", err) 285 } 286 287 provider, err := f.getHyperscalerProviderForPlanID(provisioningParameters.PlanID, provisioningParameters.PlatformProvider, provisioningParameters.Parameters.Provider) 288 if err != nil { 289 return nil, fmt.Errorf("during createing provision input: %w", err) 290 } 291 292 upgradeKymaInput, err := f.initUpgradeRuntimeInput(version, provider, cfg) 293 if err != nil { 294 return nil, fmt.Errorf("while initializing UpgradeRuntimeInput: %w", err) 295 } 296 297 kymaInput, err := f.initProvisionRuntimeInput(provider, version, cfg) 298 if err != nil { 299 return nil, fmt.Errorf("while initializing RuntimeInput: %w", err) 300 } 301 302 disabledForPlan, err := f.disabledComponentsProvider.DisabledComponentsPerPlan(provisioningParameters.PlanID) 303 if err != nil { 304 return nil, fmt.Errorf("every supported plan should be specified in the disabled components map: %w", err) 305 } 306 disabledComponents := mergeMaps(disabledForPlan, f.disabledComponentsProvider.DisabledForAll()) 307 308 return &RuntimeInput{ 309 provisionRuntimeInput: kymaInput, 310 upgradeRuntimeInput: upgradeKymaInput, 311 overrides: make(map[string][]*gqlschema.ConfigEntryInput, 0), 312 globalOverrides: make([]*gqlschema.ConfigEntryInput, 0), 313 optionalComponentsService: f.optComponentsSvc, 314 componentsDisabler: runtime.NewDisabledComponentsService(disabledComponents), 315 enabledOptionalComponents: map[string]struct{}{}, 316 trialNodesNumber: f.config.TrialNodesNumber, 317 oidcDefaultValues: f.oidcDefaultValues, 318 hyperscalerInputProvider: provider, 319 config: cfg, 320 }, nil 321 } 322 323 func (f *InputBuilderFactory) initUpgradeRuntimeInput(version internal.RuntimeVersionData, provider HyperscalerInputProvider, config *internal.ConfigForPlan) (gqlschema.UpgradeRuntimeInput, error) { 324 if version.Version == "" { 325 return gqlschema.UpgradeRuntimeInput{}, fmt.Errorf("desired runtime version cannot be empty") 326 } 327 328 kymaProfile := provider.Profile() 329 components, err := f.provideComponentList(version, config) 330 if err != nil { 331 return gqlschema.UpgradeRuntimeInput{}, err 332 } 333 334 return gqlschema.UpgradeRuntimeInput{ 335 KymaConfig: &gqlschema.KymaConfigInput{ 336 Profile: &kymaProfile, 337 Version: version.Version, 338 Components: components.DeepCopy(), 339 }, 340 }, nil 341 } 342 343 func mapToGQLComponentConfigurationInput(kymaComponents []internal.KymaComponent) internal.ComponentConfigurationInputList { 344 var input internal.ComponentConfigurationInputList 345 for _, component := range kymaComponents { 346 var sourceURL *string 347 if component.Source != nil { 348 sourceURL = &component.Source.URL 349 } 350 351 input = append(input, &gqlschema.ComponentConfigurationInput{ 352 Component: component.Name, 353 Namespace: component.Namespace, 354 SourceURL: sourceURL, 355 }) 356 } 357 return input 358 } 359 360 func mergeMaps(maps ...map[string]struct{}) map[string]struct{} { 361 res := map[string]struct{}{} 362 for _, m := range maps { 363 for k, v := range m { 364 res[k] = v 365 } 366 } 367 return res 368 } 369 370 func (f *InputBuilderFactory) CreateUpgradeShootInput(provisioningParameters internal.ProvisioningParameters, version internal.RuntimeVersionData) (internal.ProvisionerInputCreator, error) { 371 if !f.IsPlanSupport(provisioningParameters.PlanID) { 372 return nil, fmt.Errorf("plan %s in not supported", provisioningParameters.PlanID) 373 } 374 375 planName := broker.PlanNamesMapping[provisioningParameters.PlanID] 376 377 cfg, err := f.configProvider.ProvideForGivenVersionAndPlan(version.Version, planName) 378 if err != nil { 379 return nil, fmt.Errorf("while getting configuration for given version and plan: %w", err) 380 } 381 382 provider, err := f.getHyperscalerProviderForPlanID(provisioningParameters.PlanID, provisioningParameters.PlatformProvider, provisioningParameters.Parameters.Provider) 383 if err != nil { 384 return nil, fmt.Errorf("during createing provision input: %w", err) 385 } 386 387 input := f.initUpgradeShootInput(provider) 388 return &RuntimeInput{ 389 upgradeShootInput: input, 390 config: cfg, 391 hyperscalerInputProvider: provider, 392 trialNodesNumber: f.config.TrialNodesNumber, 393 oidcDefaultValues: f.oidcDefaultValues, 394 }, nil 395 } 396 397 func (f *InputBuilderFactory) initUpgradeShootInput(provider HyperscalerInputProvider) gqlschema.UpgradeShootInput { 398 input := gqlschema.UpgradeShootInput{ 399 GardenerConfig: &gqlschema.GardenerUpgradeInput{ 400 KubernetesVersion: &f.config.KubernetesVersion, 401 }, 402 } 403 404 if f.config.MachineImage != "" { 405 input.GardenerConfig.MachineImage = &f.config.MachineImage 406 } 407 if f.config.MachineImageVersion != "" { 408 input.GardenerConfig.MachineImageVersion = &f.config.MachineImageVersion 409 } 410 411 // sync with the autoscaler and maintenance settings 412 input.GardenerConfig.MaxSurge = &provider.Defaults().GardenerConfig.MaxSurge 413 input.GardenerConfig.MaxUnavailable = &provider.Defaults().GardenerConfig.MaxUnavailable 414 input.GardenerConfig.EnableKubernetesVersionAutoUpdate = &f.config.AutoUpdateKubernetesVersion 415 input.GardenerConfig.EnableMachineImageVersionAutoUpdate = &f.config.AutoUpdateMachineImageVersion 416 417 return input 418 } 419 420 func (f *InputBuilderFactory) forFreemiumPlan(provider internal.CloudProvider) (HyperscalerInputProvider, error) { 421 if !f.IsFreemiumProviderEnabled(provider) { 422 return nil, fmt.Errorf("freemium provider %s is not enabled", provider) 423 } 424 switch provider { 425 case internal.AWS: 426 return &cloudProvider.AWSFreemiumInput{}, nil 427 case internal.Azure: 428 return &cloudProvider.AzureFreemiumInput{}, nil 429 default: 430 return nil, fmt.Errorf("provider %s is not supported", provider) 431 } 432 } 433 434 func (f *InputBuilderFactory) IsFreemiumProviderEnabled(provider internal.CloudProvider) bool { 435 _, found := f.enabledFreemiumProviders[strings.ToLower(string(provider))] 436 return found 437 }