github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/cmd/operations-manager/main.go (about) 1 package main 2 3 import ( 4 "context" 5 "crypto/tls" 6 "net/http" 7 "os" 8 "time" 9 10 "github.com/kyma-incubator/compass/components/director/internal/domain/formationconstraint/operators" 11 12 "github.com/kyma-incubator/compass/components/director/internal/domain/api" 13 "github.com/kyma-incubator/compass/components/director/internal/domain/application" 14 "github.com/kyma-incubator/compass/components/director/internal/domain/apptemplate" 15 "github.com/kyma-incubator/compass/components/director/internal/domain/auth" 16 bundleutil "github.com/kyma-incubator/compass/components/director/internal/domain/bundle" 17 "github.com/kyma-incubator/compass/components/director/internal/domain/bundlereferences" 18 "github.com/kyma-incubator/compass/components/director/internal/domain/document" 19 "github.com/kyma-incubator/compass/components/director/internal/domain/eventdef" 20 "github.com/kyma-incubator/compass/components/director/internal/domain/fetchrequest" 21 "github.com/kyma-incubator/compass/components/director/internal/domain/formation" 22 "github.com/kyma-incubator/compass/components/director/internal/domain/formationassignment" 23 "github.com/kyma-incubator/compass/components/director/internal/domain/formationconstraint" 24 "github.com/kyma-incubator/compass/components/director/internal/domain/formationtemplate" 25 "github.com/kyma-incubator/compass/components/director/internal/domain/formationtemplateconstraintreferences" 26 "github.com/kyma-incubator/compass/components/director/internal/domain/integrationsystem" 27 "github.com/kyma-incubator/compass/components/director/internal/domain/label" 28 "github.com/kyma-incubator/compass/components/director/internal/domain/labeldef" 29 "github.com/kyma-incubator/compass/components/director/internal/domain/operation" 30 "github.com/kyma-incubator/compass/components/director/internal/domain/runtime" 31 runtimectx "github.com/kyma-incubator/compass/components/director/internal/domain/runtime_context" 32 "github.com/kyma-incubator/compass/components/director/internal/domain/scenarioassignment" 33 "github.com/kyma-incubator/compass/components/director/internal/domain/spec" 34 "github.com/kyma-incubator/compass/components/director/internal/domain/tenant" 35 "github.com/kyma-incubator/compass/components/director/internal/domain/version" 36 "github.com/kyma-incubator/compass/components/director/internal/domain/webhook" 37 databuilder "github.com/kyma-incubator/compass/components/director/internal/domain/webhook/datainputbuilder" 38 "github.com/kyma-incubator/compass/components/director/internal/features" 39 operationsmanager "github.com/kyma-incubator/compass/components/director/internal/operations_manager" 40 "github.com/kyma-incubator/compass/components/director/internal/uid" 41 "github.com/kyma-incubator/compass/components/director/pkg/accessstrategy" 42 httputil "github.com/kyma-incubator/compass/components/director/pkg/auth" 43 "github.com/kyma-incubator/compass/components/director/pkg/certloader" 44 configprovider "github.com/kyma-incubator/compass/components/director/pkg/config" 45 "github.com/kyma-incubator/compass/components/director/pkg/cronjob" 46 "github.com/kyma-incubator/compass/components/director/pkg/executor" 47 "github.com/kyma-incubator/compass/components/director/pkg/normalizer" 48 "github.com/kyma-incubator/compass/components/director/pkg/retry" 49 webhookclient "github.com/kyma-incubator/compass/components/director/pkg/webhook_client" 50 51 "github.com/kyma-incubator/compass/components/director/internal/domain/schema" 52 "github.com/kyma-incubator/compass/components/director/internal/healthz" 53 54 "github.com/gorilla/mux" 55 "github.com/kyma-incubator/compass/components/director/pkg/correlation" 56 "github.com/kyma-incubator/compass/components/director/pkg/log" 57 "github.com/kyma-incubator/compass/components/director/pkg/persistence" 58 "github.com/kyma-incubator/compass/components/director/pkg/signal" 59 "github.com/pkg/errors" 60 "github.com/vrischmann/envconfig" 61 ) 62 63 type config struct { 64 Address string `envconfig:"default=127.0.0.1:8080"` 65 ShutdownTimeout time.Duration `envconfig:"default=10s"` 66 ReadHeadersTimeout time.Duration `envconfig:"APP_READ_REQUEST_HEADERS_TIMEOUT,default=30s"` 67 ClientTimeout time.Duration `envconfig:"APP_CLIENT_TIMEOUT,default=30s"` 68 69 ORDOpCreationJobSchedulePeriod time.Duration `envconfig:"APP_ORD_OPERATIONS_CREATION_JOB_SCHEDULE_PERIOD,default=168h"` 70 ORDOpDeletionJobSchedulePeriod time.Duration `envconfig:"APP_ORD_OPERATIONS_DELETION_JOB_SCHEDULE_PERIOD,default=24h"` 71 DeleteCompletedOpsOlderThanDays int `envconfig:"APP_DELETE_COMPLETED_OPERATIONS_OLDER_THAN_DAYS,default=5"` 72 DeleteFailedOpsOlderThanDays int `envconfig:"APP_DELETE_FAILED_OPERATIONS_OLDER_THAN_DAYS,default=10"` 73 74 SkipSSLValidation bool `envconfig:"default=false"` 75 ConfigurationFileReload time.Duration `envconfig:"default=1m"` 76 SelfRegisterDistinguishLabelKey string `envconfig:"APP_SELF_REGISTER_DISTINGUISH_LABEL_KEY"` 77 RuntimeTypeLabelKey string `envconfig:"APP_RUNTIME_TYPE_LABEL_KEY,default=runtimeType"` 78 ApplicationTypeLabelKey string `envconfig:"APP_APPLICATION_TYPE_LABEL_KEY,default=applicationType"` 79 80 ORDWebhookMappings string `envconfig:"APP_ORD_WEBHOOK_MAPPINGS"` 81 TenantMappingConfigPath string `envconfig:"APP_TENANT_MAPPING_CONFIG_PATH"` 82 TenantMappingCallbackURL string `envconfig:"APP_TENANT_MAPPING_CALLBACK_URL"` 83 84 ExternalClientCertSecretName string `envconfig:"APP_EXTERNAL_CLIENT_CERT_SECRET_NAME"` 85 ExtSvcClientCertSecretName string `envconfig:"APP_EXT_SVC_CLIENT_CERT_SECRET_NAME"` 86 87 Log *log.Config 88 Database persistence.DatabaseConfig 89 CertLoaderConfig certloader.Config 90 ReadyConfig healthz.ReadyConfig 91 RetryConfig retry.Config 92 ConfigurationFile string 93 Features features.Config 94 ElectionConfig cronjob.ElectionConfig 95 } 96 97 func main() { 98 ctx, cancel := context.WithCancel(context.Background()) 99 defer cancel() 100 101 term := make(chan os.Signal) 102 signal.HandleInterrupts(ctx, cancel, term) 103 104 conf := config{} 105 err := envconfig.InitWithPrefix(&conf, "APP") 106 exitOnError(err, "while reading operations-manager configuration") 107 108 ctx, err = log.Configure(ctx, conf.Log) 109 exitOnError(err, "while configuring logger") 110 111 transact, closeDBConn, err := persistence.Configure(ctx, conf.Database) 112 exitOnError(err, "Error while establishing the connection to the database") 113 defer func() { 114 err := closeDBConn() 115 exitOnError(err, "Error while closing the connection to the database") 116 }() 117 118 router := mux.NewRouter() 119 120 log.C(ctx).Info("Registering health endpoint...") 121 router.Use(correlation.AttachCorrelationIDToContext()) 122 router.HandleFunc("/healthz", func(writer http.ResponseWriter, request *http.Request) { 123 writer.WriteHeader(http.StatusOK) 124 }) 125 126 log.C(ctx).Info("Registering readiness endpoint...") 127 schemaRepo := schema.NewRepository() 128 ready := healthz.NewReady(transact, conf.ReadyConfig, schemaRepo) 129 router.HandleFunc("/readyz", healthz.NewReadinessHandler(ready)) 130 131 cfgProvider := createAndRunConfigProvider(ctx, conf) 132 133 ordWebhookMapping, err := application.UnmarshalMappings(conf.ORDWebhookMappings) 134 exitOnError(err, "failed while unmarshalling ord webhook mappings") 135 136 tenantMappingConfig, err := apptemplate.UnmarshalTenantMappingConfig(conf.TenantMappingConfigPath) 137 exitOnError(err, "Error while loading Tenant mapping config") 138 139 svc, err := createOperationsManagerService(ctx, cfgProvider, transact, ordWebhookMapping, conf, tenantMappingConfig, conf.TenantMappingCallbackURL) 140 if err != nil { 141 exitOnError(err, "failed while creating operations manager service") 142 } 143 144 runMainSrv, shutdownMainSrv := createServer(ctx, conf, router, "main") 145 146 go func() { 147 <-ctx.Done() 148 // Interrupt signal received - shut down the servers 149 shutdownMainSrv() 150 }() 151 152 go func() { 153 if err := startCreateORDOperationsJob(ctx, svc, conf); err != nil { 154 log.C(ctx).WithError(err).Error("Failed to start create ORD operations cronjob. Stopping app...") 155 } 156 cancel() 157 }() 158 159 go func() { 160 if err := startDeleteOldORDOperationsJob(ctx, svc, conf); err != nil { 161 log.C(ctx).WithError(err).Error("Failed to start delete old ORD operations cronjob. Stopping app...") 162 } 163 cancel() 164 }() 165 166 log.C(ctx).Infof("Operations Manager has started") 167 runMainSrv() 168 } 169 170 func startCreateORDOperationsJob(ctx context.Context, opManager *operationsmanager.Service, cfg config) error { 171 job := cronjob.CronJob{ 172 Name: "CreateORDOperations", 173 Fn: func(jobCtx context.Context) { 174 log.C(jobCtx).Infof("Starting creation of ORD operations...") 175 if err := opManager.CreateORDOperations(ctx); err != nil { 176 log.C(jobCtx).WithError(err).Errorf("error occurred while creating Open Resource Discovery operations") 177 } 178 log.C(jobCtx).Infof("Creation of ORD operations finished.") 179 }, 180 SchedulePeriod: cfg.ORDOpCreationJobSchedulePeriod, 181 } 182 return cronjob.RunCronJob(ctx, cfg.ElectionConfig, job) 183 } 184 185 func startDeleteOldORDOperationsJob(ctx context.Context, opManager *operationsmanager.Service, cfg config) error { 186 job := cronjob.CronJob{ 187 Name: "DeleteOldORDOperations", 188 Fn: func(jobCtx context.Context) { 189 log.C(jobCtx).Infof("Starting deletion of old ORD operations...") 190 if err := opManager.DeleteOldOperations(ctx, operationsmanager.OrdAggregationOpType, cfg.DeleteCompletedOpsOlderThanDays, cfg.DeleteFailedOpsOlderThanDays); err != nil { 191 log.C(jobCtx).WithError(err).Errorf("error occurred while deleting old Open Resource Discovery operations") 192 } 193 log.C(jobCtx).Infof("Deletion of old ORD operations finished.") 194 }, 195 SchedulePeriod: cfg.ORDOpDeletionJobSchedulePeriod, 196 } 197 return cronjob.RunCronJob(ctx, cfg.ElectionConfig, job) 198 } 199 200 func createOperationsManagerService(ctx context.Context, cfgProvider *configprovider.Provider, transact persistence.Transactioner, ordWebhookMapping []application.ORDWebhookMapping, conf config, tenantMappingConfig map[string]interface{}, callbackURL string) (*operationsmanager.Service, error) { 201 retryHTTPExecutor := retry.NewHTTPExecutor(&conf.RetryConfig) 202 203 httpClient := &http.Client{ 204 Timeout: conf.ClientTimeout, 205 Transport: &http.Transport{ 206 TLSClientConfig: &tls.Config{ 207 InsecureSkipVerify: conf.SkipSSLValidation, 208 }, 209 }, 210 } 211 212 certCache, err := certloader.StartCertLoader(ctx, conf.CertLoaderConfig) 213 if err != nil { 214 return nil, err 215 } 216 accessStrategyExecutorProviderWithoutTenant := accessstrategy.NewDefaultExecutorProvider(certCache, conf.ExternalClientCertSecretName, conf.ExtSvcClientCertSecretName) 217 218 securedHTTPClient := httputil.PrepareHTTPClientWithSSLValidation(conf.ClientTimeout, conf.SkipSSLValidation) 219 mtlsClient := httputil.PrepareMTLSClient(conf.ClientTimeout, certCache, conf.ExternalClientCertSecretName) 220 extSvcMtlsClient := httputil.PrepareMTLSClient(conf.ClientTimeout, certCache, conf.ExtSvcClientCertSecretName) 221 webhookClient := webhookclient.NewClient(securedHTTPClient, mtlsClient, extSvcMtlsClient) 222 223 opConv := operation.NewConverter() 224 tenantConverter := tenant.NewConverter() 225 authConverter := auth.NewConverter() 226 frConverter := fetchrequest.NewConverter(authConverter) 227 versionConverter := version.NewConverter() 228 docConverter := document.NewConverter(frConverter) 229 webhookConverter := webhook.NewConverter(authConverter) 230 specConverter := spec.NewConverter(frConverter) 231 apiConverter := api.NewConverter(versionConverter, specConverter) 232 eventAPIConverter := eventdef.NewConverter(versionConverter, specConverter) 233 bundleConverter := bundleutil.NewConverter(authConverter, apiConverter, eventAPIConverter, docConverter) 234 appConverter := application.NewConverter(webhookConverter, bundleConverter) 235 runtimeConverter := runtime.NewConverter(webhookConverter) 236 labelConverter := label.NewConverter() 237 intSysConverter := integrationsystem.NewConverter() 238 labelDefConverter := labeldef.NewConverter() 239 formationTemplateConverter := formationtemplate.NewConverter(webhookConverter) 240 formationConstraintConverter := formationconstraint.NewConverter() 241 appTemplateConverter := apptemplate.NewConverter(appConverter, webhookConverter) 242 formationConv := formation.NewConverter() 243 runtimeContextConv := runtimectx.NewConverter() 244 bundleReferenceConv := bundlereferences.NewConverter() 245 assignmentConv := scenarioassignment.NewConverter() 246 formationAssignmentConv := formationassignment.NewConverter() 247 formationTemplateConstraintReferencesConverter := formationtemplateconstraintreferences.NewConverter() 248 249 opRepo := operation.NewRepository(opConv) 250 applicationRepo := application.NewRepository(appConverter) 251 webhookRepo := webhook.NewRepository(webhookConverter) 252 tenantRepo := tenant.NewRepository(tenantConverter) 253 runtimeRepo := runtime.NewRepository(runtimeConverter) 254 labelRepo := label.NewRepository(labelConverter) 255 labelDefRepo := labeldef.NewRepository(labelDefConverter) 256 formationRepo := formation.NewRepository(formationConv) 257 formationTemplateRepo := formationtemplate.NewRepository(formationTemplateConverter) 258 intSysRepo := integrationsystem.NewRepository(intSysConverter) 259 apiRepo := api.NewRepository(apiConverter) 260 specRepo := spec.NewRepository(specConverter) 261 docRepo := document.NewRepository(docConverter) 262 fetchRequestRepo := fetchrequest.NewRepository(frConverter) 263 bundleRepo := bundleutil.NewRepository(bundleConverter) 264 bundleReferenceRepo := bundlereferences.NewRepository(bundleReferenceConv) 265 runtimeContextRepo := runtimectx.NewRepository(runtimeContextConv) 266 eventAPIRepo := eventdef.NewRepository(eventAPIConverter) 267 scenarioAssignmentRepo := scenarioassignment.NewRepository(assignmentConv) 268 formationAssignmentRepo := formationassignment.NewRepository(formationAssignmentConv) 269 formationConstraintRepo := formationconstraint.NewRepository(formationConstraintConverter) 270 formationTemplateConstraintReferencesRepo := formationtemplateconstraintreferences.NewRepository(formationTemplateConstraintReferencesConverter) 271 appTemplateRepo := apptemplate.NewRepository(appTemplateConverter) 272 273 uidSvc := uid.NewService() 274 opSvc := operation.NewService(opRepo, uidSvc) 275 tenantSvc := tenant.NewService(tenantRepo, uidSvc, tenantConverter) 276 webhookSvc := webhook.NewService(webhookRepo, applicationRepo, uidSvc, tenantSvc, tenantMappingConfig, callbackURL) 277 labelSvc := label.NewLabelService(labelRepo, labelDefRepo, uidSvc) 278 fetchRequestSvc := fetchrequest.NewServiceWithRetry(fetchRequestRepo, httpClient, accessStrategyExecutorProviderWithoutTenant, retryHTTPExecutor) 279 bundleReferenceSvc := bundlereferences.NewService(bundleReferenceRepo, uidSvc) 280 specSvc := spec.NewService(specRepo, fetchRequestRepo, uidSvc, fetchRequestSvc) 281 apiSvc := api.NewService(apiRepo, uidSvc, specSvc, bundleReferenceSvc) 282 docSvc := document.NewService(docRepo, fetchRequestRepo, uidSvc) 283 eventAPISvc := eventdef.NewService(eventAPIRepo, uidSvc, specSvc, bundleReferenceSvc) 284 scenariosSvc := labeldef.NewService(labelDefRepo, labelRepo, scenarioAssignmentRepo, tenantRepo, uidSvc) 285 scenarioAssignmentSvc := scenarioassignment.NewService(scenarioAssignmentRepo, scenariosSvc) 286 bundleSvc := bundleutil.NewService(bundleRepo, apiSvc, eventAPISvc, docSvc, uidSvc) 287 tntSvc := tenant.NewServiceWithLabels(tenantRepo, uidSvc, labelRepo, labelSvc, tenantConverter) 288 formationConstraintSvc := formationconstraint.NewService(formationConstraintRepo, formationTemplateConstraintReferencesRepo, uidSvc, formationConstraintConverter) 289 constraintEngine := operators.NewConstraintEngine(transact, formationConstraintSvc, tenantSvc, scenarioAssignmentSvc, nil, formationRepo, labelRepo, labelSvc, applicationRepo, runtimeContextRepo, formationTemplateRepo, formationAssignmentRepo, conf.RuntimeTypeLabelKey, conf.ApplicationTypeLabelKey) 290 webhookDataInputBuilder := databuilder.NewWebhookDataInputBuilder(applicationRepo, appTemplateRepo, runtimeRepo, runtimeContextRepo, labelRepo) 291 notificationsBuilder := formation.NewNotificationsBuilder(webhookConverter, constraintEngine, conf.Features.RuntimeTypeLabelKey, conf.Features.ApplicationTypeLabelKey) 292 notificationsGenerator := formation.NewNotificationsGenerator(applicationRepo, appTemplateRepo, runtimeRepo, runtimeContextRepo, labelRepo, webhookRepo, webhookDataInputBuilder, notificationsBuilder) 293 notificationSvc := formation.NewNotificationService(tenantRepo, webhookClient, notificationsGenerator, constraintEngine, webhookConverter, formationTemplateRepo) 294 faNotificationSvc := formationassignment.NewFormationAssignmentNotificationService(formationAssignmentRepo, webhookConverter, webhookRepo, tenantRepo, webhookDataInputBuilder, formationRepo, notificationsBuilder, runtimeContextRepo, labelSvc, conf.Features.RuntimeTypeLabelKey, conf.Features.ApplicationTypeLabelKey) 295 formationAssignmentStatusSvc := formationassignment.NewFormationAssignmentStatusService(formationAssignmentRepo, constraintEngine, faNotificationSvc) 296 formationAssignmentSvc := formationassignment.NewService(formationAssignmentRepo, uidSvc, applicationRepo, runtimeRepo, runtimeContextRepo, notificationSvc, faNotificationSvc, labelSvc, formationRepo, formationAssignmentStatusSvc, conf.Features.RuntimeTypeLabelKey, conf.Features.ApplicationTypeLabelKey) 297 formationStatusSvc := formation.NewFormationStatusService(formationRepo, labelDefRepo, scenariosSvc, notificationSvc, constraintEngine) 298 formationSvc := formation.NewService(transact, applicationRepo, labelDefRepo, labelRepo, formationRepo, formationTemplateRepo, labelSvc, uidSvc, scenariosSvc, scenarioAssignmentRepo, scenarioAssignmentSvc, tntSvc, runtimeRepo, runtimeContextRepo, formationAssignmentSvc, faNotificationSvc, notificationSvc, constraintEngine, webhookRepo, formationStatusSvc, conf.Features.RuntimeTypeLabelKey, conf.Features.ApplicationTypeLabelKey) 299 appSvc := application.NewService(&normalizer.DefaultNormalizator{}, cfgProvider, applicationRepo, webhookRepo, runtimeRepo, labelRepo, intSysRepo, labelSvc, bundleSvc, uidSvc, formationSvc, conf.SelfRegisterDistinguishLabelKey, ordWebhookMapping) 300 301 ordOpCreator := operationsmanager.NewOperationCreator(operationsmanager.OrdCreatorType, transact, opSvc, webhookSvc, appSvc) 302 return operationsmanager.NewOperationService(transact, opSvc, ordOpCreator), nil 303 } 304 305 func exitOnError(err error, context string) { 306 if err != nil { 307 wrappedError := errors.Wrap(err, context) 308 log.D().Fatal(wrappedError) 309 } 310 } 311 312 func createAndRunConfigProvider(ctx context.Context, cfg config) *configprovider.Provider { 313 provider := configprovider.NewProvider(cfg.ConfigurationFile) 314 err := provider.Load() 315 exitOnError(err, "Error on loading configuration file") 316 executor.NewPeriodic(cfg.ConfigurationFileReload, func(ctx context.Context) { 317 if err := provider.Load(); err != nil { 318 exitOnError(err, "Error from Reloader watch") 319 } 320 log.C(ctx).Infof("Successfully reloaded configuration file.") 321 }).Run(ctx) 322 323 return provider 324 } 325 326 func createServer(ctx context.Context, cfg config, handler http.Handler, name string) (func(), func()) { 327 server := &http.Server{ 328 Addr: cfg.Address, 329 Handler: handler, 330 ReadHeaderTimeout: cfg.ReadHeadersTimeout, 331 } 332 333 runFn := func() { 334 log.C(ctx).Infof("Running %s server on %s...", name, cfg.Address) 335 if err := server.ListenAndServe(); err != http.ErrServerClosed { 336 log.C(ctx).Errorf("%s HTTP server ListenAndServe: %v", name, err) 337 } 338 } 339 340 shutdownFn := func() { 341 ctx, cancel := context.WithTimeout(context.Background(), cfg.ShutdownTimeout) 342 defer cancel() 343 344 log.C(ctx).Infof("Shutting down %s server...", name) 345 if err := server.Shutdown(ctx); err != nil { 346 log.C(ctx).Errorf("%s HTTP server Shutdown: %v", name, err) 347 } 348 } 349 350 return runFn, shutdownFn 351 }