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  }