github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/cmd/ns-adapter/main.go (about) 1 package main 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "net/http" 8 "os" 9 "strings" 10 11 "github.com/kyma-incubator/compass/components/director/internal/domain/formationconstraint/operators" 12 13 "github.com/kyma-incubator/compass/components/director/internal/domain/formationconstraint" 14 "github.com/kyma-incubator/compass/components/director/internal/domain/formationtemplateconstraintreferences" 15 16 databuilder "github.com/kyma-incubator/compass/components/director/internal/domain/webhook/datainputbuilder" 17 18 "github.com/kyma-incubator/compass/components/director/internal/domain/formationassignment" 19 20 webhookclient "github.com/kyma-incubator/compass/components/director/pkg/webhook_client" 21 22 "github.com/kyma-incubator/compass/components/director/internal/domain/formationtemplate" 23 httputildirector "github.com/kyma-incubator/compass/components/director/pkg/auth" 24 25 "github.com/kyma-incubator/compass/components/director/internal/domain/formation" 26 runtimectx "github.com/kyma-incubator/compass/components/director/internal/domain/runtime_context" 27 "github.com/kyma-incubator/compass/components/director/internal/domain/schema" 28 "github.com/kyma-incubator/compass/components/director/internal/healthz" 29 30 "github.com/kyma-incubator/compass/components/director/internal/authenticator/claims" 31 authmiddleware "github.com/kyma-incubator/compass/components/director/pkg/auth-middleware" 32 33 "github.com/kyma-incubator/compass/components/director/internal/methodnotallowed" 34 35 validation "github.com/go-ozzo/ozzo-validation/v4" 36 "github.com/gorilla/mux" 37 "github.com/kyma-incubator/compass/components/director/internal/domain/api" 38 "github.com/kyma-incubator/compass/components/director/internal/domain/application" 39 "github.com/kyma-incubator/compass/components/director/internal/domain/apptemplate" 40 "github.com/kyma-incubator/compass/components/director/internal/domain/auth" 41 bundleutil "github.com/kyma-incubator/compass/components/director/internal/domain/bundle" 42 "github.com/kyma-incubator/compass/components/director/internal/domain/bundlereferences" 43 "github.com/kyma-incubator/compass/components/director/internal/domain/document" 44 "github.com/kyma-incubator/compass/components/director/internal/domain/eventdef" 45 "github.com/kyma-incubator/compass/components/director/internal/domain/fetchrequest" 46 "github.com/kyma-incubator/compass/components/director/internal/domain/integrationsystem" 47 "github.com/kyma-incubator/compass/components/director/internal/domain/label" 48 "github.com/kyma-incubator/compass/components/director/internal/domain/labeldef" 49 "github.com/kyma-incubator/compass/components/director/internal/domain/runtime" 50 "github.com/kyma-incubator/compass/components/director/internal/domain/scenarioassignment" 51 "github.com/kyma-incubator/compass/components/director/internal/domain/spec" 52 "github.com/kyma-incubator/compass/components/director/internal/domain/tenant" 53 "github.com/kyma-incubator/compass/components/director/internal/domain/version" 54 "github.com/kyma-incubator/compass/components/director/internal/domain/webhook" 55 "github.com/kyma-incubator/compass/components/director/internal/model" 56 "github.com/kyma-incubator/compass/components/director/internal/nsadapter/adapter" 57 "github.com/kyma-incubator/compass/components/director/internal/nsadapter/handler" 58 "github.com/kyma-incubator/compass/components/director/internal/nsadapter/httputil" 59 "github.com/kyma-incubator/compass/components/director/internal/nsadapter/nsmodel" 60 "github.com/kyma-incubator/compass/components/director/internal/uid" 61 "github.com/kyma-incubator/compass/components/director/pkg/accessstrategy" 62 "github.com/kyma-incubator/compass/components/director/pkg/certloader" 63 "github.com/kyma-incubator/compass/components/director/pkg/correlation" 64 directorHandler "github.com/kyma-incubator/compass/components/director/pkg/handler" 65 "github.com/kyma-incubator/compass/components/director/pkg/log" 66 "github.com/kyma-incubator/compass/components/director/pkg/normalizer" 67 "github.com/kyma-incubator/compass/components/director/pkg/persistence" 68 "github.com/kyma-incubator/compass/components/director/pkg/signal" 69 "github.com/kyma-incubator/compass/components/director/pkg/str" 70 "github.com/pkg/errors" 71 "github.com/vrischmann/envconfig" 72 ) 73 74 const appTemplateName = "SAP S/4HANA On-Premise" 75 76 func main() { 77 ctx, cancel := context.WithCancel(context.Background()) 78 79 defer cancel() 80 81 term := make(chan os.Signal) 82 signal.HandleInterrupts(ctx, cancel, term) 83 84 conf := adapter.Configuration{} 85 err := envconfig.InitWithPrefix(&conf, "APP") 86 exitOnError(err, "while reading ns adapter configuration") 87 88 ordWebhookMapping, err := application.UnmarshalMappings(conf.ORDWebhookMappings) 89 exitOnError(err, "failed while unmarshalling ord webhook mappings") 90 91 transact, closeDBConn, err := persistence.Configure(ctx, conf.Database) 92 exitOnError(err, "Error while establishing the connection to the database") 93 defer func() { 94 err := closeDBConn() 95 exitOnError(err, "Error while closing the connection to the database") 96 }() 97 98 certCache, err := certloader.StartCertLoader(ctx, conf.CertLoaderConfig) 99 exitOnError(err, "Failed to initialize certificate loader") 100 101 securedHTTPClient := httputildirector.PrepareHTTPClient(conf.ClientTimeout) 102 mtlsHTTPClient := httputildirector.PrepareMTLSClient(conf.ClientTimeout, certCache, conf.ExternalClientCertSecretName) 103 extSvcMtlsHTTPClient := httputildirector.PrepareMTLSClient(conf.ClientTimeout, certCache, conf.ExtSvcClientCertSecretName) 104 105 uidSvc := uid.NewService() 106 107 tenantConv := tenant.NewConverter() 108 tenantRepo := tenant.NewRepository(tenantConv) 109 110 authConverter := auth.NewConverter() 111 frConverter := fetchrequest.NewConverter(authConverter) 112 versionConverter := version.NewConverter() 113 docConverter := document.NewConverter(frConverter) 114 webhookConverter := webhook.NewConverter(authConverter) 115 specConverter := spec.NewConverter(frConverter) 116 apiConverter := api.NewConverter(versionConverter, specConverter) 117 eventAPIConverter := eventdef.NewConverter(versionConverter, specConverter) 118 labelDefConverter := labeldef.NewConverter() 119 labelConverter := label.NewConverter() 120 intSysConverter := integrationsystem.NewConverter() 121 bundleConverter := bundleutil.NewConverter(authConverter, apiConverter, eventAPIConverter, docConverter) 122 appConverter := application.NewConverter(webhookConverter, bundleConverter) 123 runtimeConverter := runtime.NewConverter(webhookConverter) 124 bundleReferenceConverter := bundlereferences.NewConverter() 125 runtimeContextConverter := runtimectx.NewConverter() 126 formationConv := formation.NewConverter() 127 formationTemplateConverter := formationtemplate.NewConverter(webhookConverter) 128 129 runtimeRepo := runtime.NewRepository(runtimeConverter) 130 applicationRepo := application.NewRepository(appConverter) 131 labelRepo := label.NewRepository(labelConverter) 132 labelDefRepo := labeldef.NewRepository(labelDefConverter) 133 webhookRepo := webhook.NewRepository(webhookConverter) 134 apiRepo := api.NewRepository(apiConverter) 135 eventAPIRepo := eventdef.NewRepository(eventAPIConverter) 136 specRepo := spec.NewRepository(specConverter) 137 docRepo := document.NewRepository(docConverter) 138 fetchRequestRepo := fetchrequest.NewRepository(frConverter) 139 intSysRepo := integrationsystem.NewRepository(intSysConverter) 140 bundleRepo := bundleutil.NewRepository(bundleConverter) 141 bundleReferenceRepo := bundlereferences.NewRepository(bundleReferenceConverter) 142 runtimeContextRepo := runtimectx.NewRepository(runtimeContextConverter) 143 formationRepo := formation.NewRepository(formationConv) 144 formationTemplateRepo := formationtemplate.NewRepository(formationTemplateConverter) 145 146 labelSvc := label.NewLabelService(labelRepo, labelDefRepo, uidSvc) 147 assignmentConv := scenarioassignment.NewConverter() 148 scenarioAssignmentRepo := scenarioassignment.NewRepository(assignmentConv) 149 scenariosSvc := labeldef.NewService(labelDefRepo, labelRepo, scenarioAssignmentRepo, tenantRepo, uidSvc) 150 fetchRequestSvc := fetchrequest.NewService(fetchRequestRepo, &http.Client{Timeout: conf.ClientTimeout}, accessstrategy.NewDefaultExecutorProvider(certCache, conf.ExternalClientCertSecretName, conf.ExtSvcClientCertSecretName)) 151 specSvc := spec.NewService(specRepo, fetchRequestRepo, uidSvc, fetchRequestSvc) 152 bundleReferenceSvc := bundlereferences.NewService(bundleReferenceRepo, uidSvc) 153 apiSvc := api.NewService(apiRepo, uidSvc, specSvc, bundleReferenceSvc) 154 eventAPISvc := eventdef.NewService(eventAPIRepo, uidSvc, specSvc, bundleReferenceSvc) 155 docSvc := document.NewService(docRepo, fetchRequestRepo, uidSvc) 156 bundleSvc := bundleutil.NewService(bundleRepo, apiSvc, eventAPISvc, docSvc, uidSvc) 157 scenarioAssignmentSvc := scenarioassignment.NewService(scenarioAssignmentRepo, scenariosSvc) 158 tntSvc := tenant.NewServiceWithLabels(tenantRepo, uidSvc, labelRepo, labelSvc, tenantConv) 159 webhookClient := webhookclient.NewClient(securedHTTPClient, mtlsHTTPClient, extSvcMtlsHTTPClient) 160 161 appTemplateConverter := apptemplate.NewConverter(appConverter, webhookConverter) 162 appTemplateRepo := apptemplate.NewRepository(appTemplateConverter) 163 appTemplateSvc := apptemplate.NewService(appTemplateRepo, webhookRepo, uidSvc, labelSvc, labelRepo, applicationRepo) 164 165 formationConstraintConverter := formationconstraint.NewConverter() 166 formationConstraintRepo := formationconstraint.NewRepository(formationConstraintConverter) 167 168 formationTemplateConstraintReferencesConverter := formationtemplateconstraintreferences.NewConverter() 169 formationTemplateConstraintReferencesRepo := formationtemplateconstraintreferences.NewRepository(formationTemplateConstraintReferencesConverter) 170 171 formationAssignmentConv := formationassignment.NewConverter() 172 formationAssignmentRepo := formationassignment.NewRepository(formationAssignmentConv) 173 webhookDataInputBuilder := databuilder.NewWebhookDataInputBuilder(applicationRepo, appTemplateRepo, runtimeRepo, runtimeContextRepo, labelRepo) 174 formationConstraintSvc := formationconstraint.NewService(formationConstraintRepo, formationTemplateConstraintReferencesRepo, uidSvc, formationConstraintConverter) 175 constraintEngine := operators.NewConstraintEngine(transact, formationConstraintSvc, tntSvc, scenarioAssignmentSvc, nil, formationRepo, labelRepo, labelSvc, applicationRepo, runtimeContextRepo, formationTemplateRepo, formationAssignmentRepo, conf.RuntimeTypeLabelKey, conf.ApplicationTypeLabelKey) 176 notificationsBuilder := formation.NewNotificationsBuilder(webhookConverter, constraintEngine, conf.RuntimeTypeLabelKey, conf.ApplicationTypeLabelKey) 177 notificationsGenerator := formation.NewNotificationsGenerator(applicationRepo, appTemplateRepo, runtimeRepo, runtimeContextRepo, labelRepo, webhookRepo, webhookDataInputBuilder, notificationsBuilder) 178 notificationSvc := formation.NewNotificationService(tenantRepo, webhookClient, notificationsGenerator, constraintEngine, webhookConverter, formationTemplateRepo) 179 faNotificationSvc := formationassignment.NewFormationAssignmentNotificationService(formationAssignmentRepo, webhookConverter, webhookRepo, tenantRepo, webhookDataInputBuilder, formationRepo, notificationsBuilder, runtimeContextRepo, labelSvc, conf.RuntimeTypeLabelKey, conf.ApplicationTypeLabelKey) 180 formationAssignmentStatusSvc := formationassignment.NewFormationAssignmentStatusService(formationAssignmentRepo, constraintEngine, faNotificationSvc) 181 formationAssignmentSvc := formationassignment.NewService(formationAssignmentRepo, uidSvc, applicationRepo, runtimeRepo, runtimeContextRepo, notificationSvc, faNotificationSvc, labelSvc, formationRepo, formationAssignmentStatusSvc, conf.RuntimeTypeLabelKey, conf.ApplicationTypeLabelKey) 182 formationStatusSvc := formation.NewFormationStatusService(formationRepo, labelDefRepo, scenariosSvc, notificationSvc, constraintEngine) 183 formationSvc := formation.NewService(transact, applicationRepo, labelDefRepo, labelRepo, formationRepo, formationTemplateRepo, labelSvc, uidSvc, scenariosSvc, scenarioAssignmentRepo, scenarioAssignmentSvc, tntSvc, runtimeRepo, runtimeContextRepo, formationAssignmentSvc, faNotificationSvc, notificationSvc, constraintEngine, webhookRepo, formationStatusSvc, conf.RuntimeTypeLabelKey, conf.ApplicationTypeLabelKey) 184 appSvc := application.NewService(&normalizer.DefaultNormalizator{}, nil, applicationRepo, webhookRepo, runtimeRepo, labelRepo, intSysRepo, labelSvc, bundleSvc, uidSvc, formationSvc, conf.SelfRegisterDistinguishLabelKey, ordWebhookMapping) 185 186 err = registerAppTemplate(ctx, transact, appTemplateSvc) 187 exitOnError(err, "while registering application template") 188 189 err = calculateTemplateMappings(ctx, conf, transact) 190 exitOnError(err, "while calculating template mappings") 191 192 h := handler.NewHandler(appSvc, appConverter, appTemplateSvc, tntSvc, transact) 193 194 handlerWithTimeout, err := directorHandler.WithTimeoutWithErrorMessage(h, conf.ServerTimeout, httputil.GetTimeoutMessage()) 195 exitOnError(err, "Failed configuring timeout on handler") 196 197 router := mux.NewRouter() 198 199 router.Use(correlation.AttachCorrelationIDToContext()) 200 router.HandleFunc("/healthz", func(writer http.ResponseWriter, request *http.Request) { 201 writer.WriteHeader(http.StatusOK) 202 }) 203 204 log.C(ctx).Info("Registering readiness endpoint...") 205 schemaRepo := schema.NewRepository() 206 ready := healthz.NewReady(transact, conf.ReadyConfig, schemaRepo) 207 router.HandleFunc("/readyz", healthz.NewReadinessHandler(ready)) 208 209 subrouter := router.PathPrefix("/api").Subrouter() 210 subrouter.Use(authmiddleware.New(http.DefaultClient, conf.JwksEndpoint, conf.AllowJWTSigningNone, "", claims.NewClaimsValidator()).NSAdapterHandler()) 211 subrouter.MethodNotAllowedHandler = methodnotallowed.CreateMethodNotAllowedHandler() 212 subrouter.Methods(http.MethodPut). 213 Path("/v1/notifications"). 214 Handler(handlerWithTimeout) 215 216 setValidationMessages() 217 218 server := &http.Server{ 219 Addr: conf.Address, 220 Handler: router, 221 ReadHeaderTimeout: conf.ReadHeadersTimeout, 222 } 223 ctx, err = log.Configure(ctx, conf.Log) 224 exitOnError(err, "while configuring logger") 225 226 log.C(ctx).Infof("API listening on %s", conf.Address) 227 exitOnError(server.ListenAndServe(), "on starting HTTP server") 228 } 229 230 func registerAppTemplate(ctx context.Context, transact persistence.Transactioner, appTemplateSvc apptemplate.ApplicationTemplateService) error { 231 tx, err := transact.Begin() 232 if err != nil { 233 return errors.Wrap(err, "Error while beginning transaction") 234 } 235 defer transact.RollbackUnlessCommitted(ctx, tx) 236 ctxWithTx := persistence.SaveToContext(ctx, tx) 237 238 appTemplate := model.ApplicationTemplateInput{ 239 Name: appTemplateName, 240 Description: str.Ptr(appTemplateName), 241 ApplicationInputJSON: `{ 242 "name": "{{name}}", 243 "description": "{{description}}", 244 "providerName": "SAP", 245 "labels": {"scc": {"Subaccount":"{{subaccount}}", "LocationID":"{{location-id}}", "Host":"{{host}}"}, "systemType":"{{system-type}}", "systemProtocol": "{{protocol}}" }, 246 "systemNumber": "{{system-number}}", 247 "systemStatus": "{{system-status}}" 248 }`, 249 Placeholders: []model.ApplicationTemplatePlaceholder{ 250 { 251 Name: "name", 252 Description: str.Ptr("name of the system"), 253 }, 254 { 255 Name: "description", 256 Description: str.Ptr("description of the system"), 257 }, 258 { 259 Name: "subaccount", 260 Description: str.Ptr("subaccount to which the scc is connected"), 261 }, 262 { 263 Name: "location-id", 264 Description: str.Ptr("location id of the scc"), 265 }, 266 { 267 Name: "system-type", 268 Description: str.Ptr("type of the system"), 269 }, 270 { 271 Name: "host", 272 Description: str.Ptr("host of the system"), 273 }, 274 { 275 Name: "protocol", 276 Description: str.Ptr("protocol of the system"), 277 }, 278 { 279 Name: "system-number", 280 Description: str.Ptr("unique identification of the system"), 281 }, 282 { 283 Name: "system-status", 284 Description: str.Ptr("describes whether the system is reachable or not"), 285 }, 286 }, 287 AccessLevel: model.GlobalApplicationTemplateAccessLevel, 288 } 289 290 _, err = appTemplateSvc.GetByNameAndRegion(ctxWithTx, appTemplateName, nil) 291 if err != nil { 292 if !strings.Contains(err.Error(), "Object not found") { 293 return errors.Wrap(err, fmt.Sprintf("error while getting application template with name: %s", appTemplateName)) 294 } 295 296 log.C(ctx).Infof("Application Template with name %q not found. Triggering creation...", appTemplateName) 297 templateID, err := appTemplateSvc.Create(ctxWithTx, appTemplate) 298 if err != nil { 299 return errors.Wrap(err, fmt.Sprintf("error while registering application template with name: %s", appTemplateName)) 300 } 301 log.C(ctx).Infof(fmt.Sprintf("Successfully registered application template with id %q and name %q", templateID, appTemplateName)) 302 } 303 304 if err := tx.Commit(); err != nil { 305 return errors.Wrap(err, "while committing transaction") 306 } 307 308 return nil 309 } 310 311 func exitOnError(err error, context string) { 312 if err != nil { 313 wrappedError := errors.Wrap(err, context) 314 log.D().Fatal(wrappedError) 315 } 316 } 317 318 func calculateTemplateMappings(ctx context.Context, cfg adapter.Configuration, transact persistence.Transactioner) error { 319 log.C(ctx).Infof("Starting calculation of template mappings") 320 321 var systemToTemplateMappings []nsmodel.TemplateMapping 322 if err := json.Unmarshal([]byte(cfg.SystemToTemplateMappings), &systemToTemplateMappings); err != nil { 323 return errors.Wrap(err, "failed to read system template mappings") 324 } 325 326 authConverter := auth.NewConverter() 327 versionConverter := version.NewConverter() 328 frConverter := fetchrequest.NewConverter(authConverter) 329 webhookConverter := webhook.NewConverter(authConverter) 330 specConverter := spec.NewConverter(frConverter) 331 apiConverter := api.NewConverter(versionConverter, specConverter) 332 eventAPIConverter := eventdef.NewConverter(versionConverter, specConverter) 333 docConverter := document.NewConverter(frConverter) 334 bundleConverter := bundleutil.NewConverter(authConverter, apiConverter, eventAPIConverter, docConverter) 335 appConverter := application.NewConverter(webhookConverter, bundleConverter) 336 appTemplateConv := apptemplate.NewConverter(appConverter, webhookConverter) 337 appTemplateRepo := apptemplate.NewRepository(appTemplateConv) 338 webhookRepo := webhook.NewRepository(webhookConverter) 339 labelDefConverter := labeldef.NewConverter() 340 labelConverter := label.NewConverter() 341 labelRepo := label.NewRepository(labelConverter) 342 labelDefRepo := labeldef.NewRepository(labelDefConverter) 343 appRepo := application.NewRepository(appConverter) 344 345 uidSvc := uid.NewService() 346 labelSvc := label.NewLabelService(labelRepo, labelDefRepo, uidSvc) 347 appTemplateSvc := apptemplate.NewService(appTemplateRepo, webhookRepo, uidSvc, labelSvc, labelRepo, appRepo) 348 349 tx, err := transact.Begin() 350 if err != nil { 351 return errors.Wrap(err, "failed to begin transaction") 352 } 353 defer transact.RollbackUnlessCommitted(ctx, tx) 354 ctx = persistence.SaveToContext(ctx, tx) 355 356 for index, tm := range systemToTemplateMappings { 357 appTemplate, err := appTemplateSvc.GetByNameAndRegion(ctx, tm.Name, nil) 358 if err != nil { 359 return errors.Wrap(err, fmt.Sprintf("failed to retrieve application template with name %q and empty region", tm.Name)) 360 } 361 systemToTemplateMappings[index].ID = appTemplate.ID 362 } 363 364 err = tx.Commit() 365 if err != nil { 366 return errors.Wrap(err, "failed to commit transaction") 367 } 368 369 nsmodel.Mappings = systemToTemplateMappings 370 log.C(ctx).Infof("Calculation of template mappings finished successfully") 371 return nil 372 } 373 374 func setValidationMessages() { 375 validation.ErrRequired = validation.ErrRequired.SetMessage("the value is required") 376 validation.ErrNotNilRequired = validation.ErrNotNilRequired.SetMessage("the value can not be nil") 377 }