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  }