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 }