github.com/kyma-project/kyma-environment-broker@v0.0.1/cmd/broker/main.go (about) 1 package main 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "log" 8 "math/rand" 9 "net/http" 10 "os" 11 "regexp" 12 gruntime "runtime" 13 "runtime/pprof" 14 "sort" 15 "time" 16 17 "github.com/kyma-project/kyma-environment-broker/internal/euaccess" 18 19 "code.cloudfoundry.org/lager" 20 "github.com/dlmiddlecote/sqlstats" 21 "github.com/gorilla/handlers" 22 "github.com/gorilla/mux" 23 "github.com/kyma-project/kyma-environment-broker/common/director" 24 "github.com/kyma-project/kyma-environment-broker/common/gardener" 25 "github.com/kyma-project/kyma-environment-broker/common/hyperscaler" 26 orchestrationExt "github.com/kyma-project/kyma-environment-broker/common/orchestration" 27 "github.com/kyma-project/kyma-environment-broker/internal" 28 "github.com/kyma-project/kyma-environment-broker/internal/appinfo" 29 "github.com/kyma-project/kyma-environment-broker/internal/avs" 30 "github.com/kyma-project/kyma-environment-broker/internal/broker" 31 kebConfig "github.com/kyma-project/kyma-environment-broker/internal/config" 32 "github.com/kyma-project/kyma-environment-broker/internal/dashboard" 33 "github.com/kyma-project/kyma-environment-broker/internal/edp" 34 "github.com/kyma-project/kyma-environment-broker/internal/event" 35 "github.com/kyma-project/kyma-environment-broker/internal/events" 36 eventshandler "github.com/kyma-project/kyma-environment-broker/internal/events/handler" 37 "github.com/kyma-project/kyma-environment-broker/internal/health" 38 "github.com/kyma-project/kyma-environment-broker/internal/httputil" 39 "github.com/kyma-project/kyma-environment-broker/internal/ias" 40 "github.com/kyma-project/kyma-environment-broker/internal/kubeconfig" 41 "github.com/kyma-project/kyma-environment-broker/internal/metrics" 42 "github.com/kyma-project/kyma-environment-broker/internal/middleware" 43 "github.com/kyma-project/kyma-environment-broker/internal/notification" 44 "github.com/kyma-project/kyma-environment-broker/internal/orchestration" 45 orchestrate "github.com/kyma-project/kyma-environment-broker/internal/orchestration/handlers" 46 "github.com/kyma-project/kyma-environment-broker/internal/process" 47 "github.com/kyma-project/kyma-environment-broker/internal/process/input" 48 "github.com/kyma-project/kyma-environment-broker/internal/process/provisioning" 49 "github.com/kyma-project/kyma-environment-broker/internal/provider" 50 "github.com/kyma-project/kyma-environment-broker/internal/provisioner" 51 "github.com/kyma-project/kyma-environment-broker/internal/reconciler" 52 "github.com/kyma-project/kyma-environment-broker/internal/runtime" 53 "github.com/kyma-project/kyma-environment-broker/internal/runtime/components" 54 "github.com/kyma-project/kyma-environment-broker/internal/runtimeoverrides" 55 "github.com/kyma-project/kyma-environment-broker/internal/runtimeversion" 56 "github.com/kyma-project/kyma-environment-broker/internal/storage" 57 "github.com/kyma-project/kyma-environment-broker/internal/storage/dbmodel" 58 "github.com/kyma-project/kyma-environment-broker/internal/suspension" 59 "github.com/kyma-project/kyma-environment-broker/internal/swagger" 60 "github.com/prometheus/client_golang/prometheus" 61 "github.com/prometheus/client_golang/prometheus/promhttp" 62 "github.com/sirupsen/logrus" 63 "github.com/vrischmann/envconfig" 64 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 65 "k8s.io/apimachinery/pkg/util/wait" 66 "k8s.io/client-go/dynamic" 67 "k8s.io/client-go/kubernetes/scheme" 68 "k8s.io/client-go/rest" 69 "k8s.io/client-go/tools/clientcmd" 70 "sigs.k8s.io/controller-runtime/pkg/client" 71 "sigs.k8s.io/controller-runtime/pkg/client/apiutil" 72 "sigs.k8s.io/controller-runtime/pkg/client/config" 73 ) 74 75 func init() { 76 rand.Seed(time.Now().UTC().UnixNano()) 77 } 78 79 // Config holds configuration for the whole application 80 type Config struct { 81 // DbInMemory allows to use memory storage instead of the postgres one. 82 // Suitable for development purposes. 83 DbInMemory bool `envconfig:"default=false"` 84 85 // DisableProcessOperationsInProgress allows to disable processing operations 86 // which are in progress on starting application. Set to true if you are 87 // running in a separate testing deployment but with the production DB. 88 DisableProcessOperationsInProgress bool `envconfig:"default=false"` 89 90 // DevelopmentMode if set to true then errors are returned in http 91 // responses, otherwise errors are only logged and generic message 92 // is returned to client. 93 // Currently works only with /info endpoints. 94 DevelopmentMode bool `envconfig:"default=false"` 95 96 // DumpProvisionerRequests enables dumping Provisioner requests. Must be disabled on Production environments 97 // because some data must not be visible in the log file. 98 DumpProvisionerRequests bool `envconfig:"default=false"` 99 100 // OperationTimeout is used to check on a top-level if any operation didn't exceed the time for processing. 101 // It is used for provisioning and deprovisioning operations. 102 OperationTimeout time.Duration `envconfig:"default=24h"` 103 104 Host string `envconfig:"optional"` 105 Port string `envconfig:"default=8080"` 106 StatusPort string `envconfig:"default=8071"` 107 108 Provisioner input.Config 109 Reconciler reconciler.Config 110 Director director.Config 111 Database storage.Config 112 Gardener gardener.Config 113 Kubeconfig kubeconfig.Config 114 115 KymaVersion string 116 EnableOnDemandVersion bool `envconfig:"default=false"` 117 ManagedRuntimeComponentsYAMLFilePath string 118 NewAdditionalRuntimeComponentsYAMLFilePath string 119 SkrOidcDefaultValuesYAMLFilePath string 120 SkrDnsProvidersValuesYAMLFilePath string 121 DefaultRequestRegion string `envconfig:"default=cf-eu10"` 122 UpdateProcessingEnabled bool `envconfig:"default=false"` 123 UpdateSubAccountMovementEnabled bool `envconfig:"default=false"` 124 LifecycleManagerIntegrationDisabled bool `envconfig:"default=true"` 125 ReconcilerIntegrationDisabled bool `envconfig:"default=false"` 126 AvsMaintenanceModeDuringUpgradeAlwaysDisabledGlobalAccountsFilePath string 127 128 Broker broker.Config 129 CatalogFilePath string 130 131 Avs avs.Config 132 IAS ias.Config 133 EDP edp.Config 134 135 Notification notification.Config 136 137 VersionConfig struct { 138 Namespace string 139 Name string 140 } 141 142 KymaDashboardConfig dashboard.Config 143 144 OrchestrationConfig orchestration.Config 145 146 TrialRegionMappingFilePath string 147 148 EuAccessWhitelistedGlobalAccountsFilePath string 149 EuAccessRejectionMessage string `envconfig:"default=Due to limited availability you need to open support ticket before attempting to provision Kyma clusters in EU Access only regions"` 150 151 MaxPaginationPage int `envconfig:"default=100"` 152 153 LogLevel string `envconfig:"default=info"` 154 155 // FreemiumProviders is a list of providers for freemium 156 FreemiumProviders []string `envconfig:"default=aws"` 157 158 DomainName string 159 160 // Enable/disable profiler configuration. The profiler samples will be stored 161 // under /tmp/profiler directory. Based on the deployment strategy, this will be 162 // either ephemeral container filesystem or persistent storage 163 Profiler ProfilerConfig 164 165 Events events.Config 166 167 Provisioning process.StagedManagerConfiguration 168 Deprovisioning process.StagedManagerConfiguration 169 Update process.StagedManagerConfiguration 170 } 171 172 type ProfilerConfig struct { 173 Path string `envconfig:"default=/tmp/profiler"` 174 Sampling time.Duration `envconfig:"default=1s"` 175 Memory bool 176 } 177 178 const ( 179 createRuntimeStageName = "create_runtime" 180 checkKymaStageName = "check_kyma" 181 createKymaResourceStageName = "create_kyma_resource" 182 startStageName = "start" 183 ) 184 185 func periodicProfile(logger lager.Logger, profiler ProfilerConfig) { 186 if profiler.Memory == false { 187 return 188 } 189 logger.Info(fmt.Sprintf("Starting periodic profiler %v", profiler)) 190 if err := os.MkdirAll(profiler.Path, os.ModePerm); err != nil { 191 logger.Error(fmt.Sprintf("Failed to create dir %v for profile storage", profiler.Path), err) 192 } 193 for { 194 profName := fmt.Sprintf("%v/mem-%v.pprof", profiler.Path, time.Now().Unix()) 195 logger.Info(fmt.Sprintf("Creating periodic memory profile %v", profName)) 196 profFile, err := os.Create(profName) 197 if err != nil { 198 logger.Error(fmt.Sprintf("Creating periodic memory profile %v failed", profName), err) 199 } 200 pprof.Lookup("allocs").WriteTo(profFile, 0) 201 gruntime.GC() 202 time.Sleep(profiler.Sampling) 203 } 204 } 205 206 func main() { 207 apiextensionsv1.AddToScheme(scheme.Scheme) 208 ctx, cancel := context.WithCancel(context.Background()) 209 defer cancel() 210 211 // create and fill config 212 var cfg Config 213 err := envconfig.InitWithPrefix(&cfg, "APP") 214 fatalOnError(err) 215 216 // check default Kyma versions 217 err = checkDefaultVersions(cfg.KymaVersion) 218 panicOnError(err) 219 220 cfg.OrchestrationConfig.KymaVersion = cfg.KymaVersion 221 cfg.OrchestrationConfig.KubernetesVersion = cfg.Provisioner.KubernetesVersion 222 223 // create logger 224 logger := lager.NewLogger("kyma-env-broker") 225 226 logger.Info("Starting Kyma Environment Broker") 227 228 logs := logrus.New() 229 logs.SetFormatter(&logrus.JSONFormatter{}) 230 if cfg.LogLevel != "" { 231 l, _ := logrus.ParseLevel(cfg.LogLevel) 232 logs.SetLevel(l) 233 } 234 235 logger.Info("Registering healthz endpoint for health probes") 236 health.NewServer(cfg.Host, cfg.StatusPort, logs).ServeAsync() 237 go periodicProfile(logger, cfg.Profiler) 238 239 logs.Infof("Setting provisioner timeouts: provisioning=%s, deprovisioning=%s", cfg.Provisioner.ProvisioningTimeout, cfg.Provisioner.DeprovisioningTimeout) 240 logs.Infof("Setting reconciler timeout: provisioning=%s", cfg.Reconciler.ProvisioningTimeout) 241 logs.Infof("Setting staged manager configuration: provisioning=%s, deprovisioning=%s, update=%s", cfg.Provisioning, cfg.Deprovisioning, cfg.Update) 242 243 // create provisioner client 244 provisionerClient := provisioner.NewProvisionerClient(cfg.Provisioner.URL, cfg.DumpProvisionerRequests) 245 246 reconcilerClient := reconciler.NewReconcilerClient(http.DefaultClient, logs.WithField("service", "reconciler"), &cfg.Reconciler) 247 248 // create kubernetes client 249 k8sCfg, err := config.GetConfig() 250 fatalOnError(err) 251 cli, err := initClient(k8sCfg) 252 fatalOnError(err) 253 254 // create storage 255 cipher := storage.NewEncrypter(cfg.Database.SecretKey) 256 var db storage.BrokerStorage 257 if cfg.DbInMemory { 258 db = storage.NewMemoryStorage() 259 } else { 260 store, conn, err := storage.NewFromConfig(cfg.Database, cfg.Events, cipher, logs.WithField("service", "storage")) 261 fatalOnError(err) 262 db = store 263 dbStatsCollector := sqlstats.NewStatsCollector("broker", conn) 264 prometheus.MustRegister(dbStatsCollector) 265 } 266 267 // Customer Notification 268 clientHTTPForNotification := httputil.NewClient(60, true) 269 notificationClient := notification.NewClient(clientHTTPForNotification, notification.ClientConfig{ 270 URL: cfg.Notification.Url, 271 }) 272 notificationBuilder := notification.NewBundleBuilder(notificationClient, cfg.Notification) 273 274 // Register disabler. Convention: 275 // {component-name} : {component-disabler-service} 276 // 277 // Using map is intentional - we ensure that component name is not duplicated. 278 optionalComponentsDisablers := runtime.ComponentsDisablers{ 279 components.Kiali: runtime.NewGenericComponentDisabler(components.Kiali), 280 components.Tracing: runtime.NewGenericComponentDisabler(components.Tracing), 281 } 282 optComponentsSvc := runtime.NewOptionalComponentsService(optionalComponentsDisablers) 283 284 disabledComponentsProvider := runtime.NewDisabledComponentsProvider() 285 286 // provides configuration for specified Kyma version and plan 287 configProvider := kebConfig.NewConfigProvider( 288 kebConfig.NewConfigMapReader(ctx, cli, logs, cfg.KymaVersion), 289 kebConfig.NewConfigMapKeysValidator(), 290 kebConfig.NewConfigMapConverter()) 291 componentsProvider := runtime.NewComponentsProvider() 292 gardenerClusterConfig, err := gardener.NewGardenerClusterConfig(cfg.Gardener.KubeconfigPath) 293 fatalOnError(err) 294 cfg.Gardener.DNSProviders, err = gardener.ReadDNSProvidersValuesFromYAML(cfg.SkrDnsProvidersValuesYAMLFilePath) 295 fatalOnError(err) 296 dynamicGardener, err := dynamic.NewForConfig(gardenerClusterConfig) 297 fatalOnError(err) 298 299 gardenerNamespace := fmt.Sprintf("garden-%v", cfg.Gardener.Project) 300 gardenerAccountPool := hyperscaler.NewAccountPool(dynamicGardener, gardenerNamespace) 301 gardenerSharedPool := hyperscaler.NewSharedGardenerAccountPool(dynamicGardener, gardenerNamespace) 302 accountProvider := hyperscaler.NewAccountProvider(gardenerAccountPool, gardenerSharedPool) 303 304 regions, err := provider.ReadPlatformRegionMappingFromFile(cfg.TrialRegionMappingFilePath) 305 fatalOnError(err) 306 logs.Infof("Platform region mapping for trial: %v", regions) 307 308 oidcDefaultValues, err := runtime.ReadOIDCDefaultValuesFromYAML(cfg.SkrOidcDefaultValuesYAMLFilePath) 309 fatalOnError(err) 310 inputFactory, err := input.NewInputBuilderFactory(optComponentsSvc, disabledComponentsProvider, componentsProvider, 311 configProvider, cfg.Provisioner, cfg.KymaVersion, regions, cfg.FreemiumProviders, oidcDefaultValues) 312 fatalOnError(err) 313 314 edpClient := edp.NewClient(cfg.EDP, logs.WithField("service", "edpClient")) 315 316 panicOnError(cfg.Avs.ReadMaintenanceModeDuringUpgradeAlwaysDisabledGAIDsFromYaml( 317 cfg.AvsMaintenanceModeDuringUpgradeAlwaysDisabledGlobalAccountsFilePath)) 318 avsClient, err := avs.NewClient(ctx, cfg.Avs, logs) 319 fatalOnError(err) 320 avsDel := avs.NewDelegator(avsClient, cfg.Avs, db.Operations()) 321 externalEvalAssistant := avs.NewExternalEvalAssistant(cfg.Avs) 322 internalEvalAssistant := avs.NewInternalEvalAssistant(cfg.Avs) 323 externalEvalCreator := provisioning.NewExternalEvalCreator(avsDel, cfg.Avs.ExternalTesterDisabled, externalEvalAssistant) 324 upgradeEvalManager := avs.NewEvaluationManager(avsDel, cfg.Avs) 325 326 // IAS 327 clientHTTPForIAS := httputil.NewClient(60, cfg.IAS.SkipCertVerification) 328 if cfg.IAS.TLSRenegotiationEnable { 329 clientHTTPForIAS = httputil.NewRenegotiationTLSClient(30, cfg.IAS.SkipCertVerification) 330 } 331 iasClient := ias.NewClient(clientHTTPForIAS, ias.ClientConfig{ 332 URL: cfg.IAS.URL, 333 ID: cfg.IAS.UserID, 334 Secret: cfg.IAS.UserSecret, 335 }) 336 bundleBuilder := ias.NewBundleBuilder(iasClient, cfg.IAS) 337 338 // application event broker 339 eventBroker := event.NewPubSub(logs) 340 341 // metrics collectors 342 metrics.RegisterAll(eventBroker, db.Operations(), db.Instances()) 343 metrics.StartOpsMetricService(ctx, db.Operations(), logs) 344 //setup runtime overrides appender 345 runtimeOverrides := runtimeoverrides.NewRuntimeOverrides(ctx, cli) 346 347 // define steps 348 accountVersionMapping := runtimeversion.NewAccountVersionMapping(ctx, cli, cfg.VersionConfig.Namespace, cfg.VersionConfig.Name, logs) 349 runtimeVerConfigurator := runtimeversion.NewRuntimeVersionConfigurator(cfg.KymaVersion, accountVersionMapping, db.RuntimeStates()) 350 351 // run queues 352 provisionManager := process.NewStagedManager(db.Operations(), eventBroker, cfg.OperationTimeout, cfg.Provisioning, logs.WithField("provisioning", "manager")) 353 provisionQueue := NewProvisioningProcessingQueue(ctx, provisionManager, cfg.Provisioning.WorkersAmount, &cfg, db, provisionerClient, inputFactory, 354 avsDel, internalEvalAssistant, externalEvalCreator, runtimeVerConfigurator, 355 runtimeOverrides, edpClient, accountProvider, reconcilerClient, k8sClientProvider, cli, logs) 356 357 deprovisionManager := process.NewStagedManager(db.Operations(), eventBroker, cfg.OperationTimeout, cfg.Deprovisioning, logs.WithField("deprovisioning", "manager")) 358 deprovisionQueue := NewDeprovisioningProcessingQueue(ctx, cfg.Deprovisioning.WorkersAmount, deprovisionManager, &cfg, db, eventBroker, provisionerClient, 359 avsDel, internalEvalAssistant, externalEvalAssistant, bundleBuilder, edpClient, accountProvider, reconcilerClient, 360 k8sClientProvider, cli, configProvider, logs) 361 362 updateManager := process.NewStagedManager(db.Operations(), eventBroker, cfg.OperationTimeout, cfg.Update, logs.WithField("update", "manager")) 363 updateQueue := NewUpdateProcessingQueue(ctx, updateManager, cfg.Update.WorkersAmount, db, inputFactory, provisionerClient, eventBroker, 364 runtimeVerConfigurator, db.RuntimeStates(), componentsProvider, reconcilerClient, cfg, k8sClientProvider, cli, logs) 365 /***/ 366 servicesConfig, err := broker.NewServicesConfigFromFile(cfg.CatalogFilePath) 367 fatalOnError(err) 368 369 // create server 370 router := mux.NewRouter() 371 372 createAPI(router, servicesConfig, inputFactory, &cfg, db, provisionQueue, deprovisionQueue, updateQueue, logger, logs, inputFactory.GetPlanDefaults) 373 374 // create metrics endpoint 375 router.Handle("/metrics", promhttp.Handler()) 376 377 // create SKR kubeconfig endpoint 378 kcBuilder := kubeconfig.NewBuilder(provisionerClient) 379 kcHandler := kubeconfig.NewHandler(db, kcBuilder, cfg.Kubeconfig.AllowOrigins, logs.WithField("service", "kubeconfigHandle")) 380 kcHandler.AttachRoutes(router) 381 382 runtimeLister := orchestration.NewRuntimeLister(db.Instances(), db.Operations(), runtime.NewConverter(cfg.DefaultRequestRegion), logs) 383 runtimeResolver := orchestrationExt.NewGardenerRuntimeResolver(dynamicGardener, gardenerNamespace, runtimeLister, logs) 384 385 kymaQueue := NewKymaOrchestrationProcessingQueue(ctx, db, runtimeOverrides, provisionerClient, eventBroker, inputFactory, nil, time.Minute, runtimeVerConfigurator, runtimeResolver, upgradeEvalManager, &cfg, internalEvalAssistant, reconcilerClient, notificationBuilder, logs, cli, 1) 386 clusterQueue := NewClusterOrchestrationProcessingQueue(ctx, db, provisionerClient, eventBroker, inputFactory, 387 nil, time.Minute, runtimeResolver, upgradeEvalManager, notificationBuilder, logs, cli, cfg, 1) 388 389 // TODO: in case of cluster upgrade the same Azure Zones must be send to the Provisioner 390 orchestrationHandler := orchestrate.NewOrchestrationHandler(db, kymaQueue, clusterQueue, cfg.MaxPaginationPage, logs) 391 392 if !cfg.DisableProcessOperationsInProgress { 393 err = processOperationsInProgressByType(internal.OperationTypeProvision, db.Operations(), provisionQueue, logs) 394 fatalOnError(err) 395 err = processOperationsInProgressByType(internal.OperationTypeDeprovision, db.Operations(), deprovisionQueue, logs) 396 fatalOnError(err) 397 err = processOperationsInProgressByType(internal.OperationTypeUpdate, db.Operations(), updateQueue, logs) 398 fatalOnError(err) 399 err = reprocessOrchestrations(orchestrationExt.UpgradeKymaOrchestration, db.Orchestrations(), db.Operations(), kymaQueue, logs) 400 fatalOnError(err) 401 err = reprocessOrchestrations(orchestrationExt.UpgradeClusterOrchestration, db.Orchestrations(), db.Operations(), clusterQueue, logs) 402 fatalOnError(err) 403 } else { 404 logger.Info("Skipping processing operation in progress on start") 405 } 406 407 // configure templates e.g. {{.domain}} to replace it with the domain name 408 swaggerTemplates := map[string]string{ 409 "domain": cfg.DomainName, 410 } 411 err = swagger.NewTemplate("/swagger", swaggerTemplates).Execute() 412 fatalOnError(err) 413 414 // create /orchestration 415 orchestrationHandler.AttachRoutes(router) 416 417 // create list runtimes endpoint 418 runtimeHandler := runtime.NewHandler(db.Instances(), db.Operations(), db.RuntimeStates(), cfg.MaxPaginationPage, cfg.DefaultRequestRegion) 419 runtimeHandler.AttachRoutes(router) 420 421 router.StrictSlash(true).PathPrefix("/").Handler(http.StripPrefix("/", http.FileServer(http.Dir("/swagger")))) 422 svr := handlers.CustomLoggingHandler(os.Stdout, router, func(writer io.Writer, params handlers.LogFormatterParams) { 423 logs.Infof("Call handled: method=%s url=%s statusCode=%d size=%d", params.Request.Method, params.URL.Path, params.StatusCode, params.Size) 424 }) 425 426 fatalOnError(http.ListenAndServe(cfg.Host+":"+cfg.Port, svr)) 427 } 428 429 func k8sClientProvider(kcfg string) (client.Client, error) { 430 restCfg, err := clientcmd.RESTConfigFromKubeConfig([]byte(kcfg)) 431 if err != nil { 432 return nil, err 433 } 434 435 k8sCli, err := client.New(restCfg, client.Options{ 436 Scheme: scheme.Scheme, 437 }) 438 return k8sCli, err 439 } 440 441 func checkDefaultVersions(versions ...string) error { 442 for _, version := range versions { 443 if !isVersionFollowingSemanticVersioning(version) { 444 return fmt.Errorf("Kyma default versions are not following semantic versioning") 445 } 446 } 447 return nil 448 } 449 450 func isVersionFollowingSemanticVersioning(version string) bool { 451 regexpToMatch := regexp.MustCompile("(^[0-9]+\\.{1}).*") 452 if regexpToMatch.MatchString(version) { 453 return true 454 } 455 return false 456 } 457 458 func createAPI(router *mux.Router, servicesConfig broker.ServicesConfig, planValidator broker.PlanValidator, cfg *Config, db storage.BrokerStorage, provisionQueue, deprovisionQueue, updateQueue *process.Queue, logger lager.Logger, logs logrus.FieldLogger, planDefaults broker.PlanDefaults) { 459 suspensionCtxHandler := suspension.NewContextUpdateHandler(db.Operations(), provisionQueue, deprovisionQueue, logs) 460 461 defaultPlansConfig, err := servicesConfig.DefaultPlansConfig() 462 fatalOnError(err) 463 464 debugSink, err := lager.NewRedactingSink(lager.NewWriterSink(os.Stdout, lager.DEBUG), []string{"instance-details"}, []string{}) 465 fatalOnError(err) 466 logger.RegisterSink(debugSink) 467 errorSink, err := lager.NewRedactingSink(lager.NewWriterSink(os.Stderr, lager.ERROR), []string{"instance-details"}, []string{}) 468 fatalOnError(err) 469 logger.RegisterSink(errorSink) 470 471 //EU Access whitelisting 472 whitelistedGlobalAccountIds, err := euaccess.ReadWhitelistedGlobalAccountIdsFromFile(cfg.EuAccessWhitelistedGlobalAccountsFilePath) 473 fatalOnError(err) 474 logs.Infof("Number of globalAccountIds for EU Access: %d\n", len(whitelistedGlobalAccountIds)) 475 476 // create KymaEnvironmentBroker endpoints 477 kymaEnvBroker := &broker.KymaEnvironmentBroker{ 478 ServicesEndpoint: broker.NewServices(cfg.Broker, servicesConfig, logs), 479 ProvisionEndpoint: broker.NewProvision(cfg.Broker, cfg.Gardener, db.Operations(), db.Instances(), 480 provisionQueue, planValidator, defaultPlansConfig, cfg.EnableOnDemandVersion, 481 planDefaults, whitelistedGlobalAccountIds, cfg.EuAccessRejectionMessage, logs, cfg.KymaDashboardConfig), 482 DeprovisionEndpoint: broker.NewDeprovision(db.Instances(), db.Operations(), deprovisionQueue, logs), 483 UpdateEndpoint: broker.NewUpdate(cfg.Broker, db.Instances(), db.RuntimeStates(), db.Operations(), 484 suspensionCtxHandler, cfg.UpdateProcessingEnabled, cfg.UpdateSubAccountMovementEnabled, updateQueue, defaultPlansConfig, 485 planDefaults, logs, cfg.KymaDashboardConfig), 486 GetInstanceEndpoint: broker.NewGetInstance(cfg.Broker, db.Instances(), db.Operations(), logs), 487 LastOperationEndpoint: broker.NewLastOperation(db.Operations(), logs), 488 BindEndpoint: broker.NewBind(logs), 489 UnbindEndpoint: broker.NewUnbind(logs), 490 GetBindingEndpoint: broker.NewGetBinding(logs), 491 LastBindingOperationEndpoint: broker.NewLastBindingOperation(logs), 492 } 493 494 router.Use(middleware.AddRegionToContext(cfg.DefaultRequestRegion)) 495 router.Use(middleware.AddProviderToContext()) 496 for _, prefix := range []string{ 497 "/oauth/", // oauth2 handled by Ory 498 "/oauth/{region}/", // oauth2 handled by Ory with region 499 } { 500 route := router.PathPrefix(prefix).Subrouter() 501 broker.AttachRoutes(route, kymaEnvBroker, logger) 502 } 503 504 respWriter := httputil.NewResponseWriter(logs, cfg.DevelopmentMode) 505 runtimesInfoHandler := appinfo.NewRuntimeInfoHandler(db.Instances(), db.Operations(), defaultPlansConfig, cfg.DefaultRequestRegion, respWriter) 506 router.Handle("/info/runtimes", runtimesInfoHandler) 507 router.Handle("/events", eventshandler.NewHandler(db.Events(), db.Instances())) 508 } 509 510 // queues all in progress operations by type 511 func processOperationsInProgressByType(opType internal.OperationType, op storage.Operations, queue *process.Queue, log logrus.FieldLogger) error { 512 operations, err := op.GetNotFinishedOperationsByType(opType) 513 if err != nil { 514 return fmt.Errorf("while getting in progress operations from storage: %w", err) 515 } 516 for _, operation := range operations { 517 queue.Add(operation.ID) 518 log.Infof("Resuming the processing of %s operation ID: %s", opType, operation.ID) 519 } 520 return nil 521 } 522 523 func reprocessOrchestrations(orchestrationType orchestrationExt.Type, orchestrationsStorage storage.Orchestrations, operationsStorage storage.Operations, queue *process.Queue, log logrus.FieldLogger) error { 524 if err := processCancelingOrchestrations(orchestrationType, orchestrationsStorage, operationsStorage, queue, log); err != nil { 525 return fmt.Errorf("while processing canceled %s orchestrations: %w", orchestrationType, err) 526 } 527 if err := processOrchestration(orchestrationType, orchestrationExt.InProgress, orchestrationsStorage, queue, log); err != nil { 528 return fmt.Errorf("while processing in progress %s orchestrations: %w", orchestrationType, err) 529 } 530 if err := processOrchestration(orchestrationType, orchestrationExt.Pending, orchestrationsStorage, queue, log); err != nil { 531 return fmt.Errorf("while processing pending %s orchestrations: %w", orchestrationType, err) 532 } 533 if err := processOrchestration(orchestrationType, orchestrationExt.Retrying, orchestrationsStorage, queue, log); err != nil { 534 return fmt.Errorf("while processing retrying %s orchestrations: %w", orchestrationType, err) 535 } 536 return nil 537 } 538 539 func processOrchestration(orchestrationType orchestrationExt.Type, state string, orchestrationsStorage storage.Orchestrations, queue *process.Queue, log logrus.FieldLogger) error { 540 filter := dbmodel.OrchestrationFilter{ 541 Types: []string{string(orchestrationType)}, 542 States: []string{state}, 543 } 544 orchestrations, _, _, err := orchestrationsStorage.List(filter) 545 if err != nil { 546 return fmt.Errorf("while getting %s %s orchestrations from storage: %w", state, orchestrationType, err) 547 } 548 sort.Slice(orchestrations, func(i, j int) bool { 549 return orchestrations[i].CreatedAt.Before(orchestrations[j].CreatedAt) 550 }) 551 552 for _, o := range orchestrations { 553 queue.Add(o.OrchestrationID) 554 log.Infof("Resuming the processing of %s %s orchestration ID: %s", state, orchestrationType, o.OrchestrationID) 555 } 556 return nil 557 } 558 559 // processCancelingOrchestrations reprocess orchestrations with canceling state only when some in progress operations exists 560 // reprocess only one orchestration to not clog up the orchestration queue on start 561 func processCancelingOrchestrations(orchestrationType orchestrationExt.Type, orchestrationsStorage storage.Orchestrations, operationsStorage storage.Operations, queue *process.Queue, log logrus.FieldLogger) error { 562 filter := dbmodel.OrchestrationFilter{ 563 Types: []string{string(orchestrationType)}, 564 States: []string{orchestrationExt.Canceling}, 565 } 566 orchestrations, _, _, err := orchestrationsStorage.List(filter) 567 if err != nil { 568 return fmt.Errorf("while getting canceling %s orchestrations from storage: %w", orchestrationType, err) 569 } 570 sort.Slice(orchestrations, func(i, j int) bool { 571 return orchestrations[i].CreatedAt.Before(orchestrations[j].CreatedAt) 572 }) 573 574 for _, o := range orchestrations { 575 count := 0 576 err = nil 577 if orchestrationType == orchestrationExt.UpgradeKymaOrchestration { 578 _, count, _, err = operationsStorage.ListUpgradeKymaOperationsByOrchestrationID(o.OrchestrationID, dbmodel.OperationFilter{States: []string{orchestrationExt.InProgress}}) 579 } else if orchestrationType == orchestrationExt.UpgradeClusterOrchestration { 580 _, count, _, err = operationsStorage.ListUpgradeClusterOperationsByOrchestrationID(o.OrchestrationID, dbmodel.OperationFilter{States: []string{orchestrationExt.InProgress}}) 581 } 582 if err != nil { 583 return fmt.Errorf("while listing %s operations for orchestration %s: %w", orchestrationType, o.OrchestrationID, err) 584 } 585 586 if count > 0 { 587 log.Infof("Resuming the processing of %s %s orchestration ID: %s", orchestrationExt.Canceling, orchestrationType, o.OrchestrationID) 588 queue.Add(o.OrchestrationID) 589 return nil 590 } 591 } 592 return nil 593 } 594 595 func initClient(cfg *rest.Config) (client.Client, error) { 596 mapper, err := apiutil.NewDiscoveryRESTMapper(cfg) 597 if err != nil { 598 err = wait.Poll(time.Second, time.Minute, func() (bool, error) { 599 mapper, err = apiutil.NewDiscoveryRESTMapper(cfg) 600 if err != nil { 601 return false, nil 602 } 603 return true, nil 604 }) 605 if err != nil { 606 return nil, fmt.Errorf("while waiting for client mapper: %w", err) 607 } 608 } 609 cli, err := client.New(cfg, client.Options{Mapper: mapper}) 610 if err != nil { 611 return nil, fmt.Errorf("while creating a client: %w", err) 612 } 613 return cli, nil 614 } 615 616 func fatalOnError(err error) { 617 if err != nil { 618 log.Fatal(err) 619 } 620 } 621 622 func panicOnError(err error) { 623 if err != nil { 624 panic(err) 625 } 626 } 627 628 func skipForPreviewPlan(operation internal.Operation) bool { 629 return !broker.IsPreviewPlan(operation.ProvisioningParameters.PlanID) 630 }