github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/cmd/ordaggregator/main.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"encoding/json"
     7  	"net/http"
     8  	"os"
     9  	"time"
    10  
    11  	"github.com/kyma-incubator/compass/components/director/internal/domain/apptemplateversion"
    12  	"github.com/kyma-incubator/compass/components/director/internal/domain/formationconstraint/operators"
    13  	directorTime "github.com/kyma-incubator/compass/components/director/pkg/time"
    14  
    15  	"github.com/kyma-incubator/compass/components/director/internal/authenticator/claims"
    16  	auth_middleware "github.com/kyma-incubator/compass/components/director/pkg/auth-middleware"
    17  
    18  	"github.com/kyma-incubator/compass/components/director/internal/model"
    19  
    20  	"github.com/gorilla/mux"
    21  	"github.com/kyma-incubator/compass/components/director/pkg/apperrors"
    22  	"github.com/kyma-incubator/compass/components/director/pkg/correlation"
    23  	"github.com/kyma-incubator/compass/components/director/pkg/cronjob"
    24  	timeouthandler "github.com/kyma-incubator/compass/components/director/pkg/handler"
    25  	"github.com/kyma-incubator/compass/components/director/pkg/signal"
    26  
    27  	"github.com/kyma-incubator/compass/components/director/internal/domain/formationconstraint"
    28  	"github.com/kyma-incubator/compass/components/director/internal/domain/formationtemplateconstraintreferences"
    29  
    30  	databuilder "github.com/kyma-incubator/compass/components/director/internal/domain/webhook/datainputbuilder"
    31  
    32  	"github.com/kyma-incubator/compass/components/director/internal/domain/formationassignment"
    33  
    34  	"github.com/kyma-incubator/compass/components/director/internal/domain/apptemplate"
    35  
    36  	httputil "github.com/kyma-incubator/compass/components/director/pkg/auth"
    37  	httputilpkg "github.com/kyma-incubator/compass/components/director/pkg/http"
    38  	webhookclient "github.com/kyma-incubator/compass/components/director/pkg/webhook_client"
    39  
    40  	"github.com/kyma-incubator/compass/components/director/pkg/retry"
    41  
    42  	"github.com/kyma-incubator/compass/components/director/internal/domain/formationtemplate"
    43  
    44  	"github.com/kyma-incubator/compass/components/director/internal/domain/formation"
    45  	runtimectx "github.com/kyma-incubator/compass/components/director/internal/domain/runtime_context"
    46  	"github.com/kyma-incubator/compass/components/director/pkg/certloader"
    47  
    48  	"github.com/kyma-incubator/compass/components/director/pkg/accessstrategy"
    49  
    50  	"github.com/kyma-incubator/compass/components/director/internal/domain/tenant"
    51  
    52  	"github.com/kyma-incubator/compass/components/director/internal/domain/scenarioassignment"
    53  
    54  	"github.com/kyma-incubator/compass/components/director/internal/domain/bundlereferences"
    55  
    56  	"github.com/kyma-incubator/compass/components/director/internal/domain/api"
    57  	"github.com/kyma-incubator/compass/components/director/internal/domain/application"
    58  	"github.com/kyma-incubator/compass/components/director/internal/domain/auth"
    59  	bundleutil "github.com/kyma-incubator/compass/components/director/internal/domain/bundle"
    60  	"github.com/kyma-incubator/compass/components/director/internal/domain/document"
    61  	"github.com/kyma-incubator/compass/components/director/internal/domain/eventdef"
    62  	"github.com/kyma-incubator/compass/components/director/internal/domain/fetchrequest"
    63  	"github.com/kyma-incubator/compass/components/director/internal/domain/integrationsystem"
    64  	"github.com/kyma-incubator/compass/components/director/internal/domain/label"
    65  	"github.com/kyma-incubator/compass/components/director/internal/domain/labeldef"
    66  	"github.com/kyma-incubator/compass/components/director/internal/domain/ordvendor"
    67  	ordpackage "github.com/kyma-incubator/compass/components/director/internal/domain/package"
    68  	"github.com/kyma-incubator/compass/components/director/internal/domain/product"
    69  	"github.com/kyma-incubator/compass/components/director/internal/domain/runtime"
    70  	"github.com/kyma-incubator/compass/components/director/internal/domain/spec"
    71  	"github.com/kyma-incubator/compass/components/director/internal/domain/tombstone"
    72  	"github.com/kyma-incubator/compass/components/director/internal/domain/version"
    73  	"github.com/kyma-incubator/compass/components/director/internal/domain/webhook"
    74  	"github.com/kyma-incubator/compass/components/director/internal/features"
    75  	ord "github.com/kyma-incubator/compass/components/director/internal/open_resource_discovery"
    76  	"github.com/kyma-incubator/compass/components/director/internal/uid"
    77  	configprovider "github.com/kyma-incubator/compass/components/director/pkg/config"
    78  	"github.com/kyma-incubator/compass/components/director/pkg/executor"
    79  	"github.com/kyma-incubator/compass/components/director/pkg/log"
    80  	"github.com/kyma-incubator/compass/components/director/pkg/normalizer"
    81  	"github.com/kyma-incubator/compass/components/director/pkg/persistence"
    82  	"github.com/pkg/errors"
    83  	"github.com/vrischmann/envconfig"
    84  )
    85  
    86  type config struct {
    87  	Address           string `envconfig:"default=127.0.0.1:8080"`
    88  	AggregatorRootAPI string `envconfig:"APP_ROOT_API,default=/ord-aggregator"`
    89  
    90  	ServerTimeout   time.Duration `envconfig:"default=110s"`
    91  	ShutdownTimeout time.Duration `envconfig:"default=10s"`
    92  
    93  	SecurityConfig securityConfig
    94  	Database       persistence.DatabaseConfig
    95  	Log            log.Config
    96  	Features       features.Config
    97  
    98  	ConfigurationFile       string
    99  	ConfigurationFileReload time.Duration `envconfig:"default=1m"`
   100  
   101  	ClientTimeout     time.Duration `envconfig:"default=120s"`
   102  	SkipSSLValidation bool          `envconfig:"default=false"`
   103  
   104  	RetryConfig                    retry.Config
   105  	CertLoaderConfig               certloader.Config
   106  	GlobalRegistryConfig           ord.GlobalRegistryConfig
   107  	ElectionConfig                 cronjob.ElectionConfig
   108  	ORDAggregatorJobSchedulePeriod time.Duration `envconfig:"APP_ORD_AGGREGATOR_JOB_SCHEDULE_PERIOD,default=60m"`
   109  	IsORDAggregatorJobScheduleable bool          `envconfig:"APP_ORD_AGGREGATOR_JOB_IS_SCHEDULABLE,default=true"`
   110  
   111  	MaxParallelWebhookProcessors       int `envconfig:"APP_MAX_PARALLEL_WEBHOOK_PROCESSORS,default=1"`
   112  	MaxParallelDocumentsPerApplication int `envconfig:"APP_MAX_PARALLEL_DOCUMENTS_PER_APPLICATION"`
   113  	MaxParallelSpecificationProcessors int `envconfig:"APP_MAX_PARALLEL_SPECIFICATION_PROCESSORS,default=100"`
   114  
   115  	OrdWebhookPartialProcessURL     string `envconfig:"optional,APP_ORD_WEBHOOK_PARTIAL_PROCESS_URL"`
   116  	OrdWebhookPartialProcessMaxDays int    `envconfig:"APP_ORD_WEBHOOK_PARTIAL_PROCESS_MAX_DAYS,default=0"`
   117  	OrdWebhookPartialProcessing     bool   `envconfig:"APP_ORD_WEBHOOK_PARTIAL_PROCESSING,default=false"`
   118  
   119  	SelfRegisterDistinguishLabelKey string `envconfig:"APP_SELF_REGISTER_DISTINGUISH_LABEL_KEY"`
   120  
   121  	ORDWebhookMappings string `envconfig:"APP_ORD_WEBHOOK_MAPPINGS"`
   122  
   123  	ExternalClientCertSecretName string `envconfig:"APP_EXTERNAL_CLIENT_CERT_SECRET_NAME"`
   124  	ExtSvcClientCertSecretName   string `envconfig:"APP_EXT_SVC_CLIENT_CERT_SECRET_NAME"`
   125  
   126  	TenantMappingConfigPath                  string `envconfig:"APP_TENANT_MAPPING_CONFIG_PATH"`
   127  	TenantMappingCallbackURL                 string `envconfig:"APP_TENANT_MAPPING_CALLBACK_URL"`
   128  	CredentialExchangeStrategyTenantMappings string `envconfig:"APP_CREDENTIAL_EXCHANGE_STRATEGY_TENANT_MAPPINGS"`
   129  
   130  	MetricsConfig ord.MetricsConfig
   131  }
   132  
   133  type securityConfig struct {
   134  	JwksEndpoint        string        `envconfig:"APP_JWKS_ENDPOINT"`
   135  	JWKSSyncPeriod      time.Duration `envconfig:"default=5m"`
   136  	AllowJWTSigningNone bool          `envconfig:"APP_ALLOW_JWT_SIGNING_NONE,default=false"`
   137  	AggregatorSyncScope string        `envconfig:"APP_ORD_AGGREGATOR_SYNC_SCOPE,default=ord_aggregator:sync"`
   138  }
   139  
   140  // TenantService is responsible for service-layer tenant operations
   141  type TenantService interface {
   142  	GetTenantByExternalID(ctx context.Context, id string) (*model.BusinessTenantMapping, error)
   143  }
   144  
   145  func main() {
   146  	ctx, cancel := context.WithCancel(context.Background())
   147  	defer cancel()
   148  
   149  	term := make(chan os.Signal)
   150  	signal.HandleInterrupts(ctx, cancel, term)
   151  
   152  	cfg := config{}
   153  	err := envconfig.InitWithPrefix(&cfg, "APP")
   154  	exitOnError(err, "Error while loading app config")
   155  
   156  	tenantMappingConfig, err := apptemplate.UnmarshalTenantMappingConfig(cfg.TenantMappingConfigPath)
   157  	exitOnError(err, "Error while loading Tenant mapping config")
   158  
   159  	credentialExchangeStrategyTenantMappings, err := unmarshalMappings(cfg.CredentialExchangeStrategyTenantMappings)
   160  	exitOnError(err, "Error while loading Credential Exchange Strategy Tenant Mappings")
   161  
   162  	ctx, err = log.Configure(ctx, &cfg.Log)
   163  	exitOnError(err, "Error while configuring logger")
   164  
   165  	ordWebhookMapping, err := application.UnmarshalMappings(cfg.ORDWebhookMappings)
   166  	exitOnError(err, "failed while unmarshalling ord webhook mappings")
   167  
   168  	cfgProvider := createAndRunConfigProvider(ctx, cfg)
   169  
   170  	transact, closeFunc, err := persistence.Configure(ctx, cfg.Database)
   171  	exitOnError(err, "Error while establishing the connection to the database")
   172  
   173  	defer func() {
   174  		err := closeFunc()
   175  		exitOnError(err, "Error while closing the connection to the database")
   176  	}()
   177  
   178  	certCache, err := certloader.StartCertLoader(ctx, cfg.CertLoaderConfig)
   179  	exitOnError(err, "Failed to initialize certificate loader")
   180  
   181  	httpClient := &http.Client{
   182  		Timeout: cfg.ClientTimeout,
   183  		Transport: &http.Transport{
   184  			TLSClientConfig: &tls.Config{
   185  				InsecureSkipVerify: cfg.SkipSSLValidation,
   186  			},
   187  		},
   188  	}
   189  
   190  	securedHTTPClient := httputil.PrepareHTTPClientWithSSLValidation(cfg.ClientTimeout, cfg.SkipSSLValidation)
   191  	mtlsClient := httputil.PrepareMTLSClient(cfg.ClientTimeout, certCache, cfg.ExternalClientCertSecretName)
   192  	extSvcMtlsClient := httputil.PrepareMTLSClient(cfg.ClientTimeout, certCache, cfg.ExtSvcClientCertSecretName)
   193  
   194  	accessStrategyExecutorProviderWithTenant := accessstrategy.NewExecutorProviderWithTenant(certCache, ctxTenantProvider, cfg.ExternalClientCertSecretName, cfg.ExtSvcClientCertSecretName)
   195  	accessStrategyExecutorProviderWithoutTenant := accessstrategy.NewDefaultExecutorProvider(certCache, cfg.ExternalClientCertSecretName, cfg.ExtSvcClientCertSecretName)
   196  	retryHTTPExecutor := retry.NewHTTPExecutor(&cfg.RetryConfig)
   197  
   198  	ordAggregator := createORDAggregatorSvc(cfgProvider, cfg, transact, httpClient, securedHTTPClient, mtlsClient, extSvcMtlsClient, accessStrategyExecutorProviderWithTenant, accessStrategyExecutorProviderWithoutTenant, retryHTTPExecutor, ordWebhookMapping, tenantMappingConfig, cfg.TenantMappingCallbackURL, credentialExchangeStrategyTenantMappings)
   199  
   200  	jwtHTTPClient := &http.Client{
   201  		Transport: httputilpkg.NewCorrelationIDTransport(httputilpkg.NewHTTPTransportWrapper(http.DefaultTransport.(*http.Transport))),
   202  		CheckRedirect: func(req *http.Request, via []*http.Request) error {
   203  			return http.ErrUseLastResponse
   204  		},
   205  	}
   206  
   207  	handler := initHandler(ctx, jwtHTTPClient, ordAggregator, cfg, transact)
   208  	runMainSrv, shutdownMainSrv := createServer(ctx, cfg, handler, "main")
   209  
   210  	go func() {
   211  		<-ctx.Done()
   212  		// Interrupt signal received - shut down the servers
   213  		shutdownMainSrv()
   214  	}()
   215  
   216  	if cfg.IsORDAggregatorJobScheduleable {
   217  		go func() {
   218  			if err := startSyncORDDocumentsJob(ctx, ordAggregator, cfg); err != nil {
   219  				log.C(ctx).WithError(err).Error("Failed to start sync ORD documents cronjob. Stopping app...")
   220  			}
   221  			cancel()
   222  		}()
   223  	}
   224  
   225  	runMainSrv()
   226  }
   227  
   228  func startSyncORDDocumentsJob(ctx context.Context, ordAggregator *ord.Service, cfg config) error {
   229  	resyncJob := cronjob.CronJob{
   230  		Name: "SyncORDDocuments",
   231  		Fn: func(jobCtx context.Context) {
   232  			log.C(jobCtx).Infof("Starting ORD documents aggregation...")
   233  			if err := ordAggregator.SyncORDDocuments(ctx, cfg.MetricsConfig); err != nil {
   234  				log.C(jobCtx).WithError(err).Errorf("error occurred while syncing Open Resource Discovery Documents")
   235  			}
   236  			log.C(jobCtx).Infof("ORD documents aggregation finished.")
   237  		},
   238  		SchedulePeriod: cfg.ORDAggregatorJobSchedulePeriod,
   239  	}
   240  	return cronjob.RunCronJob(ctx, cfg.ElectionConfig, resyncJob)
   241  }
   242  
   243  func ctxTenantProvider(ctx context.Context) (string, error) {
   244  	localTenantID, err := tenant.LoadLocalTenantIDFromContext(ctx)
   245  	if err != nil {
   246  		return "", err
   247  	}
   248  
   249  	return localTenantID, nil
   250  }
   251  
   252  func createORDAggregatorSvc(cfgProvider *configprovider.Provider, config config, transact persistence.Transactioner, httpClient, securedHTTPClient, mtlsClient, extSvcMtlsClient *http.Client, accessStrategyExecutorProviderWithTenant *accessstrategy.Provider, accessStrategyExecutorProviderWithoutTenant *accessstrategy.Provider, retryHTTPExecutor *retry.HTTPExecutor, ordWebhookMapping []application.ORDWebhookMapping, tenantMappingConfig map[string]interface{}, tenantMappingCallbackURL string, credentialExchangeStrategyTenantMappings map[string]ord.CredentialExchangeStrategyTenantMapping) *ord.Service {
   253  	authConverter := auth.NewConverter()
   254  	frConverter := fetchrequest.NewConverter(authConverter)
   255  	versionConverter := version.NewConverter()
   256  	docConverter := document.NewConverter(frConverter)
   257  	webhookConverter := webhook.NewConverter(authConverter)
   258  	specConverter := spec.NewConverter(frConverter)
   259  	apiConverter := api.NewConverter(versionConverter, specConverter)
   260  	eventAPIConverter := eventdef.NewConverter(versionConverter, specConverter)
   261  	labelDefConverter := labeldef.NewConverter()
   262  	labelConverter := label.NewConverter()
   263  	intSysConverter := integrationsystem.NewConverter()
   264  	bundleConverter := bundleutil.NewConverter(authConverter, apiConverter, eventAPIConverter, docConverter)
   265  	appConverter := application.NewConverter(webhookConverter, bundleConverter)
   266  	appTemplateConverter := apptemplate.NewConverter(appConverter, webhookConverter)
   267  	pkgConverter := ordpackage.NewConverter()
   268  	productConverter := product.NewConverter()
   269  	vendorConverter := ordvendor.NewConverter()
   270  	tombstoneConverter := tombstone.NewConverter()
   271  	runtimeConverter := runtime.NewConverter(webhookConverter)
   272  	bundleReferenceConv := bundlereferences.NewConverter()
   273  	runtimeContextConv := runtimectx.NewConverter()
   274  	formationConv := formation.NewConverter()
   275  	formationTemplateConverter := formationtemplate.NewConverter(webhookConverter)
   276  	formationConstraintConverter := formationconstraint.NewConverter()
   277  	formationTemplateConstraintReferencesConverter := formationtemplateconstraintreferences.NewConverter()
   278  	assignmentConv := scenarioassignment.NewConverter()
   279  	tenantConverter := tenant.NewConverter()
   280  	appTemplateVersionConv := apptemplateversion.NewConverter()
   281  
   282  	runtimeRepo := runtime.NewRepository(runtimeConverter)
   283  	applicationRepo := application.NewRepository(appConverter)
   284  	appTemplateRepo := apptemplate.NewRepository(appTemplateConverter)
   285  	labelRepo := label.NewRepository(labelConverter)
   286  	labelDefRepo := labeldef.NewRepository(labelDefConverter)
   287  	webhookRepo := webhook.NewRepository(webhookConverter)
   288  	apiRepo := api.NewRepository(apiConverter)
   289  	eventAPIRepo := eventdef.NewRepository(eventAPIConverter)
   290  	specRepo := spec.NewRepository(specConverter)
   291  	docRepo := document.NewRepository(docConverter)
   292  	fetchRequestRepo := fetchrequest.NewRepository(frConverter)
   293  	intSysRepo := integrationsystem.NewRepository(intSysConverter)
   294  	bundleRepo := bundleutil.NewRepository(bundleConverter)
   295  	pkgRepo := ordpackage.NewRepository(pkgConverter)
   296  	productRepo := product.NewRepository(productConverter)
   297  	vendorRepo := ordvendor.NewRepository(vendorConverter)
   298  	tombstoneRepo := tombstone.NewRepository(tombstoneConverter)
   299  	bundleReferenceRepo := bundlereferences.NewRepository(bundleReferenceConv)
   300  	runtimeContextRepo := runtimectx.NewRepository(runtimeContextConv)
   301  	formationRepo := formation.NewRepository(formationConv)
   302  	formationTemplateRepo := formationtemplate.NewRepository(formationTemplateConverter)
   303  	formationConstraintRepo := formationconstraint.NewRepository(formationConstraintConverter)
   304  	formationTemplateConstraintReferencesRepo := formationtemplateconstraintreferences.NewRepository(formationTemplateConstraintReferencesConverter)
   305  	scenarioAssignmentRepo := scenarioassignment.NewRepository(assignmentConv)
   306  	tenantRepo := tenant.NewRepository(tenantConverter)
   307  	appTemplateVersionRepo := apptemplateversion.NewRepository(appTemplateVersionConv)
   308  
   309  	timeSvc := directorTime.NewService()
   310  	uidSvc := uid.NewService()
   311  	labelSvc := label.NewLabelService(labelRepo, labelDefRepo, uidSvc)
   312  	scenariosSvc := labeldef.NewService(labelDefRepo, labelRepo, scenarioAssignmentRepo, tenantRepo, uidSvc)
   313  	fetchRequestSvc := fetchrequest.NewServiceWithRetry(fetchRequestRepo, httpClient, accessStrategyExecutorProviderWithoutTenant, retryHTTPExecutor)
   314  	specSvc := spec.NewService(specRepo, fetchRequestRepo, uidSvc, fetchRequestSvc)
   315  	bundleReferenceSvc := bundlereferences.NewService(bundleReferenceRepo, uidSvc)
   316  	apiSvc := api.NewService(apiRepo, uidSvc, specSvc, bundleReferenceSvc)
   317  	eventAPISvc := eventdef.NewService(eventAPIRepo, uidSvc, specSvc, bundleReferenceSvc)
   318  	tenantSvc := tenant.NewService(tenantRepo, uidSvc, tenantConverter)
   319  	webhookSvc := webhook.NewService(webhookRepo, applicationRepo, uidSvc, tenantSvc, tenantMappingConfig, tenantMappingCallbackURL)
   320  	docSvc := document.NewService(docRepo, fetchRequestRepo, uidSvc)
   321  	bundleSvc := bundleutil.NewService(bundleRepo, apiSvc, eventAPISvc, docSvc, uidSvc)
   322  	scenarioAssignmentSvc := scenarioassignment.NewService(scenarioAssignmentRepo, scenariosSvc)
   323  	tntSvc := tenant.NewServiceWithLabels(tenantRepo, uidSvc, labelRepo, labelSvc, tenantConverter)
   324  	webhookClient := webhookclient.NewClient(securedHTTPClient, mtlsClient, extSvcMtlsClient)
   325  	formationAssignmentConv := formationassignment.NewConverter()
   326  	formationAssignmentRepo := formationassignment.NewRepository(formationAssignmentConv)
   327  	webhookDataInputBuilder := databuilder.NewWebhookDataInputBuilder(applicationRepo, appTemplateRepo, runtimeRepo, runtimeContextRepo, labelRepo)
   328  	formationConstraintSvc := formationconstraint.NewService(formationConstraintRepo, formationTemplateConstraintReferencesRepo, uidSvc, formationConstraintConverter)
   329  	constraintEngine := operators.NewConstraintEngine(transact, formationConstraintSvc, tenantSvc, scenarioAssignmentSvc, nil, formationRepo, labelRepo, labelSvc, applicationRepo, runtimeContextRepo, formationTemplateRepo, formationAssignmentRepo, config.Features.RuntimeTypeLabelKey, config.Features.ApplicationTypeLabelKey)
   330  	notificationsBuilder := formation.NewNotificationsBuilder(webhookConverter, constraintEngine, config.Features.RuntimeTypeLabelKey, config.Features.ApplicationTypeLabelKey)
   331  	notificationsGenerator := formation.NewNotificationsGenerator(applicationRepo, appTemplateRepo, runtimeRepo, runtimeContextRepo, labelRepo, webhookRepo, webhookDataInputBuilder, notificationsBuilder)
   332  	notificationSvc := formation.NewNotificationService(tenantRepo, webhookClient, notificationsGenerator, constraintEngine, webhookConverter, formationTemplateRepo)
   333  	faNotificationSvc := formationassignment.NewFormationAssignmentNotificationService(formationAssignmentRepo, webhookConverter, webhookRepo, tenantRepo, webhookDataInputBuilder, formationRepo, notificationsBuilder, runtimeContextRepo, labelSvc, config.Features.RuntimeTypeLabelKey, config.Features.ApplicationTypeLabelKey)
   334  	formationAssignmentStatusSvc := formationassignment.NewFormationAssignmentStatusService(formationAssignmentRepo, constraintEngine, faNotificationSvc)
   335  	formationAssignmentSvc := formationassignment.NewService(formationAssignmentRepo, uidSvc, applicationRepo, runtimeRepo, runtimeContextRepo, notificationSvc, faNotificationSvc, labelSvc, formationRepo, formationAssignmentStatusSvc, config.Features.RuntimeTypeLabelKey, config.Features.ApplicationTypeLabelKey)
   336  	formationStatusSvc := formation.NewFormationStatusService(formationRepo, labelDefRepo, scenariosSvc, notificationSvc, constraintEngine)
   337  	formationSvc := formation.NewService(transact, applicationRepo, labelDefRepo, labelRepo, formationRepo, formationTemplateRepo, labelSvc, uidSvc, scenariosSvc, scenarioAssignmentRepo, scenarioAssignmentSvc, tntSvc, runtimeRepo, runtimeContextRepo, formationAssignmentSvc, faNotificationSvc, notificationSvc, constraintEngine, webhookRepo, formationStatusSvc, config.Features.RuntimeTypeLabelKey, config.Features.ApplicationTypeLabelKey)
   338  	appSvc := application.NewService(&normalizer.DefaultNormalizator{}, cfgProvider, applicationRepo, webhookRepo, runtimeRepo, labelRepo, intSysRepo, labelSvc, bundleSvc, uidSvc, formationSvc, config.SelfRegisterDistinguishLabelKey, ordWebhookMapping)
   339  	packageSvc := ordpackage.NewService(pkgRepo, uidSvc)
   340  	productSvc := product.NewService(productRepo, uidSvc)
   341  	vendorSvc := ordvendor.NewService(vendorRepo, uidSvc)
   342  	tombstoneSvc := tombstone.NewService(tombstoneRepo, uidSvc)
   343  	appTemplateSvc := apptemplate.NewService(appTemplateRepo, webhookRepo, uidSvc, labelSvc, labelRepo, applicationRepo)
   344  	appTemplateVersionSvc := apptemplateversion.NewService(appTemplateVersionRepo, appTemplateSvc, uidSvc, timeSvc)
   345  
   346  	clientConfig := ord.NewClientConfig(config.MaxParallelDocumentsPerApplication)
   347  
   348  	ordClientWithTenantExecutor := ord.NewClient(clientConfig, httpClient, accessStrategyExecutorProviderWithTenant)
   349  	ordClientWithoutTenantExecutor := ord.NewClient(clientConfig, httpClient, accessStrategyExecutorProviderWithoutTenant)
   350  
   351  	globalRegistrySvc := ord.NewGlobalRegistryService(transact, config.GlobalRegistryConfig, vendorSvc, productSvc, ordClientWithoutTenantExecutor, credentialExchangeStrategyTenantMappings)
   352  
   353  	ordConfig := ord.NewServiceConfig(config.MaxParallelWebhookProcessors, config.MaxParallelSpecificationProcessors, config.OrdWebhookPartialProcessMaxDays, config.OrdWebhookPartialProcessURL, config.OrdWebhookPartialProcessing, credentialExchangeStrategyTenantMappings)
   354  	return ord.NewAggregatorService(ordConfig, transact, appSvc, webhookSvc, bundleSvc, bundleReferenceSvc, apiSvc, eventAPISvc, specSvc, fetchRequestSvc, packageSvc, productSvc, vendorSvc, tombstoneSvc, tenantSvc, globalRegistrySvc, ordClientWithTenantExecutor, webhookConverter, appTemplateVersionSvc, appTemplateSvc)
   355  }
   356  
   357  func createAndRunConfigProvider(ctx context.Context, cfg config) *configprovider.Provider {
   358  	provider := configprovider.NewProvider(cfg.ConfigurationFile)
   359  	err := provider.Load()
   360  	exitOnError(err, "Error on loading configuration file")
   361  	executor.NewPeriodic(cfg.ConfigurationFileReload, func(ctx context.Context) {
   362  		if err := provider.Load(); err != nil {
   363  			exitOnError(err, "Error from Reloader watch")
   364  		}
   365  		log.C(ctx).Infof("Successfully reloaded configuration file.")
   366  	}).Run(ctx)
   367  
   368  	return provider
   369  }
   370  
   371  func exitOnError(err error, context string) {
   372  	if err != nil {
   373  		wrappedError := errors.Wrap(err, context)
   374  		log.D().Fatal(wrappedError)
   375  	}
   376  }
   377  
   378  func createServer(ctx context.Context, cfg config, handler http.Handler, name string) (func(), func()) {
   379  	handlerWithTimeout, err := timeouthandler.WithTimeout(handler, cfg.ServerTimeout)
   380  	exitOnError(err, "Error while configuring ord aggregator handler")
   381  
   382  	srv := &http.Server{
   383  		Addr:              cfg.Address,
   384  		Handler:           handlerWithTimeout,
   385  		ReadHeaderTimeout: cfg.ServerTimeout,
   386  	}
   387  
   388  	runFn := func() {
   389  		log.C(ctx).Infof("Running %s server on %s...", name, cfg.Address)
   390  		if err := srv.ListenAndServe(); err != http.ErrServerClosed {
   391  			log.C(ctx).Errorf("%s HTTP server ListenAndServe: %v", name, err)
   392  		}
   393  	}
   394  
   395  	shutdownFn := func() {
   396  		ctx, cancel := context.WithTimeout(context.Background(), cfg.ShutdownTimeout)
   397  		defer cancel()
   398  
   399  		log.C(ctx).Infof("Shutting down %s server...", name)
   400  		if err := srv.Shutdown(ctx); err != nil {
   401  			log.C(ctx).Errorf("%s HTTP server Shutdown: %v", name, err)
   402  		}
   403  	}
   404  
   405  	return runFn, shutdownFn
   406  }
   407  
   408  func initHandler(ctx context.Context, httpClient *http.Client, svc *ord.Service, cfg config, transact persistence.Transactioner) http.Handler {
   409  	const (
   410  		healthzEndpoint   = "/healthz"
   411  		readyzEndpoint    = "/readyz"
   412  		aggregateEndpoint = "/aggregate"
   413  	)
   414  	logger := log.C(ctx)
   415  
   416  	mainRouter := mux.NewRouter()
   417  	mainRouter.Use(correlation.AttachCorrelationIDToContext(), log.RequestLogger(
   418  		cfg.AggregatorRootAPI+healthzEndpoint, cfg.AggregatorRootAPI+readyzEndpoint))
   419  
   420  	handler := ord.NewORDAggregatorHTTPHandler(svc, cfg.MetricsConfig)
   421  	apiRouter := mainRouter.PathPrefix(cfg.AggregatorRootAPI).Subrouter()
   422  	configureAuthMiddleware(ctx, httpClient, apiRouter, cfg, cfg.SecurityConfig.AggregatorSyncScope)
   423  	configureTenantContextMiddleware(apiRouter, transact)
   424  	apiRouter.HandleFunc(aggregateEndpoint, handler.AggregateORDData).Methods(http.MethodPost)
   425  
   426  	healthCheckRouter := mainRouter.PathPrefix(cfg.AggregatorRootAPI).Subrouter()
   427  	logger.Infof("Registering readiness endpoint...")
   428  	healthCheckRouter.HandleFunc(readyzEndpoint, newReadinessHandler())
   429  	logger.Infof("Registering liveness endpoint...")
   430  	healthCheckRouter.HandleFunc(healthzEndpoint, newReadinessHandler())
   431  
   432  	return mainRouter
   433  }
   434  
   435  func newReadinessHandler() func(writer http.ResponseWriter, request *http.Request) {
   436  	return func(writer http.ResponseWriter, request *http.Request) {
   437  		writer.WriteHeader(http.StatusOK)
   438  	}
   439  }
   440  
   441  func configureAuthMiddleware(ctx context.Context, httpClient *http.Client, router *mux.Router, cfg config, requiredScopes ...string) {
   442  	scopeValidator := claims.NewScopesValidator(requiredScopes)
   443  	middleware := auth_middleware.New(httpClient, cfg.SecurityConfig.JwksEndpoint, cfg.SecurityConfig.AllowJWTSigningNone, "", scopeValidator)
   444  	router.Use(middleware.Handler())
   445  
   446  	log.C(ctx).Infof("JWKS synchronization enabled. Sync period: %v", cfg.SecurityConfig.JWKSSyncPeriod)
   447  	periodicExecutor := executor.NewPeriodic(cfg.SecurityConfig.JWKSSyncPeriod, func(ctx context.Context) {
   448  		if err := middleware.SynchronizeJWKS(ctx); err != nil {
   449  			log.C(ctx).WithError(err).Errorf("An error has occurred while synchronizing JWKS: %v", err)
   450  		}
   451  	})
   452  	go periodicExecutor.Run(ctx)
   453  }
   454  
   455  func configureTenantContextMiddleware(apiRouter *mux.Router, transact persistence.Transactioner) {
   456  	tenantConverter := tenant.NewConverter()
   457  	tenantRepo := tenant.NewRepository(tenantConverter)
   458  	uidSvc := uid.NewService()
   459  	tenantSvc := tenant.NewService(tenantRepo, uidSvc, tenantConverter)
   460  
   461  	apiRouter.Use(tenantContextHandler(transact, tenantSvc))
   462  }
   463  
   464  func tenantContextHandler(transact persistence.Transactioner, tenantSvc TenantService) func(next http.Handler) http.Handler {
   465  	return func(next http.Handler) http.Handler {
   466  		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   467  			ctx := r.Context()
   468  
   469  			tx, err := transact.Begin()
   470  			if err != nil {
   471  				apperrors.WriteAppError(ctx, w, err, http.StatusInternalServerError)
   472  				return
   473  			}
   474  			defer transact.RollbackUnlessCommitted(ctx, tx)
   475  
   476  			ctx = persistence.SaveToContext(ctx, tx)
   477  
   478  			tntFromCtx, err := tenant.LoadFromContext(ctx)
   479  			if err != nil {
   480  				apperrors.WriteAppError(ctx, w, err, http.StatusUnauthorized)
   481  				return
   482  			}
   483  
   484  			tnt, err := tenantSvc.GetTenantByExternalID(ctx, tntFromCtx)
   485  			if err != nil {
   486  				apperrors.WriteAppError(ctx, w, err, http.StatusInternalServerError)
   487  				return
   488  			}
   489  
   490  			if err := tx.Commit(); err != nil {
   491  				apperrors.WriteAppError(ctx, w, err, http.StatusInternalServerError)
   492  				return
   493  			}
   494  
   495  			ctx = tenant.SaveToContext(ctx, tnt.ID, tnt.ExternalTenant)
   496  
   497  			next.ServeHTTP(w, r.WithContext(ctx))
   498  		})
   499  	}
   500  }
   501  
   502  func unmarshalMappings(tenantMappings string) (map[string]ord.CredentialExchangeStrategyTenantMapping, error) {
   503  	var mappingsFromEnv map[string]ord.CredentialExchangeStrategyTenantMapping
   504  	if err := json.Unmarshal([]byte(tenantMappings), &mappingsFromEnv); err != nil {
   505  		return nil, errors.Wrap(err, "while unmarshalling tenant mappings")
   506  	}
   507  
   508  	return mappingsFromEnv, nil
   509  }