github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/runtime/resolver.go (about)

     1  package runtime
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/kyma-incubator/compass/components/director/internal/domain/scenarioassignment"
     9  	"github.com/kyma-incubator/compass/components/director/internal/domain/tenant"
    10  
    11  	"github.com/kyma-incubator/compass/components/director/internal/selfregmanager"
    12  	"github.com/kyma-incubator/compass/components/director/pkg/resource"
    13  
    14  	"github.com/google/uuid"
    15  	dataloader "github.com/kyma-incubator/compass/components/director/internal/dataloaders"
    16  	"github.com/kyma-incubator/compass/components/director/internal/domain/eventing"
    17  	labelPkg "github.com/kyma-incubator/compass/components/director/internal/domain/label"
    18  	"github.com/kyma-incubator/compass/components/director/internal/labelfilter"
    19  	"github.com/kyma-incubator/compass/components/director/internal/model"
    20  	"github.com/kyma-incubator/compass/components/director/internal/timestamp"
    21  	"github.com/kyma-incubator/compass/components/director/pkg/apperrors"
    22  	"github.com/kyma-incubator/compass/components/director/pkg/graphql"
    23  	"github.com/kyma-incubator/compass/components/director/pkg/inputvalidation"
    24  	"github.com/kyma-incubator/compass/components/director/pkg/log"
    25  	pkgmodel "github.com/kyma-incubator/compass/components/director/pkg/model"
    26  	"github.com/kyma-incubator/compass/components/director/pkg/persistence"
    27  	"github.com/kyma-incubator/compass/components/director/pkg/str"
    28  	"github.com/pkg/errors"
    29  )
    30  
    31  // EventingService missing godoc
    32  //go:generate mockery --name=EventingService --output=automock --outpkg=automock --case=underscore --disable-version-string
    33  type EventingService interface {
    34  	GetForRuntime(ctx context.Context, runtimeID uuid.UUID) (*model.RuntimeEventingConfiguration, error)
    35  }
    36  
    37  // OAuth20Service missing godoc
    38  //go:generate mockery --name=OAuth20Service --output=automock --outpkg=automock --case=underscore --disable-version-string
    39  type OAuth20Service interface {
    40  	DeleteMultipleClientCredentials(ctx context.Context, auths []pkgmodel.SystemAuth) error
    41  }
    42  
    43  // RuntimeService missing godoc
    44  //go:generate mockery --name=RuntimeService --output=automock --outpkg=automock --case=underscore --disable-version-string
    45  type RuntimeService interface {
    46  	CreateWithMandatoryLabels(ctx context.Context, in model.RuntimeRegisterInput, id string, mandatoryLabels map[string]interface{}) error
    47  	Update(ctx context.Context, id string, in model.RuntimeUpdateInput) error
    48  	Get(ctx context.Context, id string) (*model.Runtime, error)
    49  	GetByTokenIssuer(ctx context.Context, issuer string) (*model.Runtime, error)
    50  	GetByFilters(ctx context.Context, filters []*labelfilter.LabelFilter) (*model.Runtime, error)
    51  	Delete(ctx context.Context, id string) error
    52  	List(ctx context.Context, filter []*labelfilter.LabelFilter, pageSize int, cursor string) (*model.RuntimePage, error)
    53  	SetLabel(ctx context.Context, label *model.LabelInput) error
    54  	GetLabel(ctx context.Context, runtimeID string, key string) (*model.Label, error)
    55  	ListLabels(ctx context.Context, runtimeID string) (map[string]*model.Label, error)
    56  	DeleteLabel(ctx context.Context, runtimeID string, key string) error
    57  	UnsafeExtractModifiableLabels(labels map[string]interface{}) (map[string]interface{}, error)
    58  }
    59  
    60  // ScenarioAssignmentService missing godoc
    61  //go:generate mockery --name=ScenarioAssignmentService --output=automock --outpkg=automock --case=underscore --disable-version-string
    62  type ScenarioAssignmentService interface {
    63  	GetForScenarioName(ctx context.Context, scenarioName string) (model.AutomaticScenarioAssignment, error)
    64  }
    65  
    66  //go:generate mockery --exported --name=formationService --output=automock --outpkg=automock --case=underscore --disable-version-string
    67  type formationService interface {
    68  	MergeScenariosFromInputLabelsAndAssignments(ctx context.Context, inputLabels map[string]interface{}, runtimeID string) ([]interface{}, error)
    69  	AssignFormation(ctx context.Context, tnt, objectID string, objectType graphql.FormationObjectType, formation model.Formation) (*model.Formation, error)
    70  	UnassignFormation(ctx context.Context, tnt, objectID string, objectType graphql.FormationObjectType, formation model.Formation) (*model.Formation, error)
    71  	DeleteAutomaticScenarioAssignment(ctx context.Context, in model.AutomaticScenarioAssignment) error
    72  }
    73  
    74  // RuntimeConverter missing godoc
    75  //go:generate mockery --name=RuntimeConverter --output=automock --outpkg=automock --case=underscore --disable-version-string
    76  type RuntimeConverter interface {
    77  	ToGraphQL(in *model.Runtime) *graphql.Runtime
    78  	MultipleToGraphQL(in []*model.Runtime) []*graphql.Runtime
    79  	RegisterInputFromGraphQL(in graphql.RuntimeRegisterInput) (model.RuntimeRegisterInput, error)
    80  	UpdateInputFromGraphQL(in graphql.RuntimeUpdateInput) model.RuntimeUpdateInput
    81  }
    82  
    83  // SystemAuthConverter missing godoc
    84  //go:generate mockery --name=SystemAuthConverter --output=automock --outpkg=automock --case=underscore --disable-version-string
    85  type SystemAuthConverter interface {
    86  	ToGraphQL(in *pkgmodel.SystemAuth) (graphql.SystemAuth, error)
    87  }
    88  
    89  // SystemAuthService missing godoc
    90  //go:generate mockery --name=SystemAuthService --output=automock --outpkg=automock --case=underscore --disable-version-string
    91  type SystemAuthService interface {
    92  	ListForObject(ctx context.Context, objectType pkgmodel.SystemAuthReferenceObjectType, objectID string) ([]pkgmodel.SystemAuth, error)
    93  }
    94  
    95  // BundleInstanceAuthService missing godoc
    96  //go:generate mockery --name=BundleInstanceAuthService --output=automock --outpkg=automock --case=underscore --disable-version-string
    97  type BundleInstanceAuthService interface {
    98  	ListByRuntimeID(ctx context.Context, runtimeID string) ([]*model.BundleInstanceAuth, error)
    99  	Update(ctx context.Context, instanceAuth *model.BundleInstanceAuth) error
   100  }
   101  
   102  // SelfRegisterManager missing godoc
   103  //go:generate mockery --name=SelfRegisterManager --output=automock --outpkg=automock --case=underscore --disable-version-string
   104  type SelfRegisterManager interface {
   105  	PrepareForSelfRegistration(ctx context.Context, resourceType resource.Type, labels map[string]interface{}, id string, validate func() error) (map[string]interface{}, error)
   106  	CleanupSelfRegistration(ctx context.Context, selfRegisterLabelValue, region string) error
   107  	GetSelfRegDistinguishingLabelKey() string
   108  }
   109  
   110  // SubscriptionService is responsible for service layer operations for subscribing a tenant to a runtime
   111  //go:generate mockery --name=SubscriptionService --output=automock --outpkg=automock --case=underscore --disable-version-string
   112  type SubscriptionService interface {
   113  	SubscribeTenantToRuntime(ctx context.Context, providerID, subaccountTenantID, providerSubaccountID, consumerTenantID, region, subscriptionAppName string) (bool, error)
   114  	UnsubscribeTenantFromRuntime(ctx context.Context, providerID, subaccountTenantID, providerSubaccountID, consumerTenantID, region string) (bool, error)
   115  }
   116  
   117  // TenantFetcher calls an API which fetches details for the given tenant from an external tenancy service, stores the tenant in the Compass DB and returns 200 OK if the tenant was successfully created.
   118  //go:generate mockery --name=TenantFetcher --output=automock --outpkg=automock --case=underscore --disable-version-string
   119  type TenantFetcher interface {
   120  	FetchOnDemand(tenant, parentTenant string) error
   121  }
   122  
   123  // RuntimeContextService missing godoc
   124  //go:generate mockery --name=RuntimeContextService --output=automock --outpkg=automock --case=underscore --disable-version-string
   125  type RuntimeContextService interface {
   126  	GetForRuntime(ctx context.Context, id, runtimeID string) (*model.RuntimeContext, error)
   127  	ListByRuntimeIDs(ctx context.Context, runtimeIDs []string, pageSize int, cursor string) ([]*model.RuntimeContextPage, error)
   128  	ListAllForRuntime(ctx context.Context, runtimeID string) ([]*model.RuntimeContext, error)
   129  	Delete(ctx context.Context, id string) error
   130  }
   131  
   132  // RuntimeContextConverter missing godoc
   133  //go:generate mockery --name=RuntimeContextConverter --output=automock --outpkg=automock --case=underscore --disable-version-string
   134  type RuntimeContextConverter interface {
   135  	ToGraphQL(in *model.RuntimeContext) *graphql.RuntimeContext
   136  	MultipleToGraphQL(in []*model.RuntimeContext) []*graphql.RuntimeContext
   137  }
   138  
   139  // WebhookService missing godoc
   140  //go:generate mockery --name=WebhookService --output=automock --outpkg=automock --case=underscore --disable-version-string
   141  type WebhookService interface {
   142  	ListForRuntime(ctx context.Context, runtimeID string) ([]*model.Webhook, error)
   143  	Create(ctx context.Context, owningResourceID string, in model.WebhookInput, objectType model.WebhookReferenceObjectType) (string, error)
   144  }
   145  
   146  // WebhookConverter missing godoc
   147  //go:generate mockery --name=WebhookConverter --output=automock --outpkg=automock --case=underscore --disable-version-string
   148  type WebhookConverter interface {
   149  	MultipleToGraphQL(in []*model.Webhook) ([]*graphql.Webhook, error)
   150  	MultipleInputFromGraphQL(in []*graphql.WebhookInput) ([]*model.WebhookInput, error)
   151  }
   152  
   153  // Resolver missing godoc
   154  type Resolver struct {
   155  	transact                  persistence.Transactioner
   156  	runtimeService            RuntimeService
   157  	scenarioAssignmentService ScenarioAssignmentService
   158  	sysAuthSvc                SystemAuthService
   159  	converter                 RuntimeConverter
   160  	sysAuthConv               SystemAuthConverter
   161  	oAuth20Svc                OAuth20Service
   162  	eventingSvc               EventingService
   163  	bundleInstanceAuthSvc     BundleInstanceAuthService
   164  	selfRegManager            SelfRegisterManager
   165  	uidService                uidService
   166  	subscriptionSvc           SubscriptionService
   167  	runtimeContextService     RuntimeContextService
   168  	runtimeContextConverter   RuntimeContextConverter
   169  	webhookService            WebhookService
   170  	webhookConverter          WebhookConverter
   171  	fetcher                   TenantFetcher
   172  	formationSvc              formationService
   173  }
   174  
   175  // NewResolver missing godoc
   176  func NewResolver(transact persistence.Transactioner, runtimeService RuntimeService, scenarioAssignmentService ScenarioAssignmentService,
   177  	sysAuthSvc SystemAuthService, oAuthSvc OAuth20Service, conv RuntimeConverter, sysAuthConv SystemAuthConverter,
   178  	eventingSvc EventingService, bundleInstanceAuthSvc BundleInstanceAuthService, selfRegManager SelfRegisterManager,
   179  	uidService uidService, subscriptionSvc SubscriptionService, runtimeContextService RuntimeContextService, runtimeContextConverter RuntimeContextConverter, webhookService WebhookService, webhookConverter WebhookConverter, fetcher TenantFetcher, formationSvc formationService) *Resolver {
   180  	return &Resolver{
   181  		transact:                  transact,
   182  		runtimeService:            runtimeService,
   183  		scenarioAssignmentService: scenarioAssignmentService,
   184  		sysAuthSvc:                sysAuthSvc,
   185  		oAuth20Svc:                oAuthSvc,
   186  		converter:                 conv,
   187  		sysAuthConv:               sysAuthConv,
   188  		eventingSvc:               eventingSvc,
   189  		bundleInstanceAuthSvc:     bundleInstanceAuthSvc,
   190  		selfRegManager:            selfRegManager,
   191  		uidService:                uidService,
   192  		subscriptionSvc:           subscriptionSvc,
   193  		runtimeContextService:     runtimeContextService,
   194  		runtimeContextConverter:   runtimeContextConverter,
   195  		webhookService:            webhookService,
   196  		webhookConverter:          webhookConverter,
   197  		fetcher:                   fetcher,
   198  		formationSvc:              formationSvc,
   199  	}
   200  }
   201  
   202  // Runtimes missing godoc
   203  // TODO: Proper error handling
   204  func (r *Resolver) Runtimes(ctx context.Context, filter []*graphql.LabelFilter, first *int, after *graphql.PageCursor) (*graphql.RuntimePage, error) {
   205  	labelFilter := labelfilter.MultipleFromGraphQL(filter)
   206  	var cursor string
   207  	if after != nil {
   208  		cursor = string(*after)
   209  	}
   210  
   211  	tx, err := r.transact.Begin()
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   216  
   217  	ctx = persistence.SaveToContext(ctx, tx)
   218  
   219  	if first == nil {
   220  		return nil, apperrors.NewInvalidDataError("missing required parameter 'first'")
   221  	}
   222  
   223  	runtimesPage, err := r.runtimeService.List(ctx, labelFilter, *first, cursor)
   224  	if err != nil {
   225  		return nil, err
   226  	}
   227  
   228  	if err = tx.Commit(); err != nil {
   229  		return nil, err
   230  	}
   231  
   232  	gqlRuntimes := r.converter.MultipleToGraphQL(runtimesPage.Data)
   233  
   234  	return &graphql.RuntimePage{
   235  		Data:       gqlRuntimes,
   236  		TotalCount: runtimesPage.TotalCount,
   237  		PageInfo: &graphql.PageInfo{
   238  			StartCursor: graphql.PageCursor(runtimesPage.PageInfo.StartCursor),
   239  			EndCursor:   graphql.PageCursor(runtimesPage.PageInfo.EndCursor),
   240  			HasNextPage: runtimesPage.PageInfo.HasNextPage,
   241  		},
   242  	}, nil
   243  }
   244  
   245  // Runtime missing godoc
   246  func (r *Resolver) Runtime(ctx context.Context, id string) (*graphql.Runtime, error) {
   247  	tx, err := r.transact.Begin()
   248  	if err != nil {
   249  		return nil, err
   250  	}
   251  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   252  
   253  	ctx = persistence.SaveToContext(ctx, tx)
   254  
   255  	runtime, err := r.runtimeService.Get(ctx, id)
   256  	if err != nil {
   257  		if apperrors.IsNotFoundError(err) {
   258  			return nil, tx.Commit()
   259  		}
   260  		return nil, err
   261  	}
   262  
   263  	if err = tx.Commit(); err != nil {
   264  		return nil, err
   265  	}
   266  
   267  	return r.converter.ToGraphQL(runtime), nil
   268  }
   269  
   270  // RuntimeByTokenIssuer returns a Runtime by a token issuer
   271  func (r *Resolver) RuntimeByTokenIssuer(ctx context.Context, issuer string) (*graphql.Runtime, error) {
   272  	tx, err := r.transact.Begin()
   273  	if err != nil {
   274  		return nil, err
   275  	}
   276  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   277  
   278  	ctx = persistence.SaveToContext(ctx, tx)
   279  
   280  	runtime, err := r.runtimeService.GetByTokenIssuer(ctx, issuer)
   281  	if err != nil {
   282  		if apperrors.IsNotFoundError(err) {
   283  			return nil, tx.Commit()
   284  		}
   285  		return nil, err
   286  	}
   287  
   288  	if err = tx.Commit(); err != nil {
   289  		return nil, err
   290  	}
   291  
   292  	return r.converter.ToGraphQL(runtime), nil
   293  }
   294  
   295  // RegisterRuntime missing godoc
   296  func (r *Resolver) RegisterRuntime(ctx context.Context, in graphql.RuntimeRegisterInput) (*graphql.Runtime, error) {
   297  	convertedIn, err := r.converter.RegisterInputFromGraphQL(in)
   298  	if err != nil {
   299  		return nil, err
   300  	}
   301  
   302  	if convertedIn.Labels, err = r.runtimeService.UnsafeExtractModifiableLabels(convertedIn.Labels); err != nil {
   303  		return nil, err
   304  	}
   305  
   306  	id := r.uidService.Generate()
   307  
   308  	validate := func() error { return nil }
   309  
   310  	selfRegLabels, err := r.selfRegManager.PrepareForSelfRegistration(ctx, resource.Runtime, convertedIn.Labels, id, validate)
   311  	if err != nil {
   312  		return nil, err
   313  	}
   314  
   315  	if saVal, ok := in.Labels[scenarioassignment.SubaccountIDKey]; ok {
   316  		sa, err := convertLabelValue(saVal)
   317  		if err != nil {
   318  			return nil, errors.Wrapf(err, "while converting %s label", scenarioassignment.SubaccountIDKey)
   319  		}
   320  
   321  		parentTenant, err := tenant.LoadFromContext(ctx)
   322  		if err != nil {
   323  			return nil, err
   324  		}
   325  		if err := r.fetcher.FetchOnDemand(sa, parentTenant); err != nil {
   326  			return nil, errors.Wrapf(err, "while trying to create if not exists subaccount %s", sa)
   327  		}
   328  	}
   329  
   330  	tx, err := r.transact.Begin()
   331  	if err != nil {
   332  		return nil, err
   333  	}
   334  
   335  	defer func() {
   336  		didRollback := r.transact.RollbackUnlessCommitted(ctx, tx)
   337  		if didRollback {
   338  			labelVal := str.CastOrEmpty(convertedIn.Labels[r.selfRegManager.GetSelfRegDistinguishingLabelKey()])
   339  			if labelVal != "" {
   340  				label, ok := selfRegLabels[selfregmanager.RegionLabel].(string)
   341  				if !ok {
   342  					log.C(ctx).Errorf("An error occurred while casting region label value to string")
   343  				} else {
   344  					r.cleanupAndLogOnError(ctx, id, label)
   345  				}
   346  			}
   347  		}
   348  	}()
   349  
   350  	ctx = persistence.SaveToContext(ctx, tx)
   351  
   352  	if err = r.checkProviderRuntimeExistence(ctx, selfRegLabels); err != nil {
   353  		return nil, err
   354  	}
   355  
   356  	log.C(ctx).Debugf("Creating a Runtime with name %q and id %q", in.Name, id)
   357  	if err = r.runtimeService.CreateWithMandatoryLabels(ctx, convertedIn, id, selfRegLabels); err != nil {
   358  		return nil, err
   359  	}
   360  
   361  	runtime, err := r.runtimeService.Get(ctx, id)
   362  	if err != nil {
   363  		return nil, err
   364  	}
   365  
   366  	if err = tx.Commit(); err != nil {
   367  		return nil, err
   368  	}
   369  
   370  	return r.converter.ToGraphQL(runtime), nil
   371  }
   372  
   373  // UpdateRuntime missing godoc
   374  func (r *Resolver) UpdateRuntime(ctx context.Context, id string, in graphql.RuntimeUpdateInput) (*graphql.Runtime, error) {
   375  	convertedIn := r.converter.UpdateInputFromGraphQL(in)
   376  
   377  	tx, err := r.transact.Begin()
   378  	if err != nil {
   379  		return nil, err
   380  	}
   381  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   382  
   383  	ctx = persistence.SaveToContext(ctx, tx)
   384  
   385  	log.C(ctx).Debugf("Updating a Runtime with id %q", id)
   386  	err = r.runtimeService.Update(ctx, id, convertedIn)
   387  	if err != nil {
   388  		return nil, err
   389  	}
   390  
   391  	runtime, err := r.runtimeService.Get(ctx, id)
   392  	if err != nil {
   393  		return nil, err
   394  	}
   395  
   396  	if err = tx.Commit(); err != nil {
   397  		return nil, err
   398  	}
   399  
   400  	return r.converter.ToGraphQL(runtime), nil
   401  }
   402  
   403  // DeleteRuntime missing godoc
   404  func (r *Resolver) DeleteRuntime(ctx context.Context, id string) (*graphql.Runtime, error) {
   405  	tx, err := r.transact.Begin()
   406  	if err != nil {
   407  		return nil, err
   408  	}
   409  	defer func() {
   410  		r.transact.RollbackUnlessCommitted(ctx, tx)
   411  	}()
   412  
   413  	ctx = persistence.SaveToContext(ctx, tx)
   414  
   415  	runtime, err := r.runtimeService.Get(ctx, id)
   416  	if err != nil {
   417  		return nil, err
   418  	}
   419  
   420  	bundleInstanceAuths, err := r.bundleInstanceAuthSvc.ListByRuntimeID(ctx, runtime.ID)
   421  	if err != nil {
   422  		return nil, err
   423  	}
   424  
   425  	_, err = r.runtimeService.GetLabel(ctx, runtime.ID, r.selfRegManager.GetSelfRegDistinguishingLabelKey())
   426  	if err != nil {
   427  		if !apperrors.IsNotFoundError(err) {
   428  			return nil, errors.Wrapf(err, "while getting self register info label")
   429  		}
   430  	} else {
   431  		regionLabel, err := r.runtimeService.GetLabel(ctx, runtime.ID, selfregmanager.RegionLabel)
   432  		if err != nil {
   433  			return nil, errors.Wrapf(err, "while getting region label")
   434  		}
   435  
   436  		// Committing transaction as the cleanup sends request to external service
   437  		if err = tx.Commit(); err != nil {
   438  			return nil, err
   439  		}
   440  
   441  		regionValue, ok := regionLabel.Value.(string)
   442  		if !ok {
   443  			return nil, errors.Wrap(err, "while casting region label value to string")
   444  		}
   445  
   446  		log.C(ctx).Infof("Executing clean-up for self-registered runtime with id %q", runtime.ID)
   447  		if err := r.selfRegManager.CleanupSelfRegistration(ctx, runtime.ID, regionValue); err != nil {
   448  			return nil, errors.Wrap(err, "An error occurred during cleanup of self-registered runtime: ")
   449  		}
   450  
   451  		tx, err = r.transact.Begin()
   452  		if err != nil {
   453  			return nil, err
   454  		}
   455  		ctx = persistence.SaveToContext(ctx, tx)
   456  	}
   457  
   458  	currentTimestamp := timestamp.DefaultGenerator
   459  	for _, auth := range bundleInstanceAuths {
   460  		if auth.Status.Condition != model.BundleInstanceAuthStatusConditionUnused {
   461  			if err := auth.SetDefaultStatus(model.BundleInstanceAuthStatusConditionUnused, currentTimestamp()); err != nil {
   462  				log.C(ctx).WithError(err).Errorf("while update bundle instance auth status condition: %v", err)
   463  				return nil, err
   464  			}
   465  			if err := r.bundleInstanceAuthSvc.Update(ctx, auth); err != nil {
   466  				log.C(ctx).WithError(err).Errorf("Unable to update bundle instance auth with ID: %s for corresponding bundle with ID: %s: %v", auth.ID, auth.BundleID, err)
   467  				return nil, err
   468  			}
   469  		}
   470  	}
   471  
   472  	auths, err := r.sysAuthSvc.ListForObject(ctx, pkgmodel.RuntimeReference, runtime.ID)
   473  	if err != nil {
   474  		return nil, err
   475  	}
   476  
   477  	if err = r.deleteAssociatedScenarioAssignments(ctx, runtime.ID); err != nil {
   478  		return nil, err
   479  	}
   480  
   481  	deletedRuntime := r.converter.ToGraphQL(runtime)
   482  
   483  	log.C(ctx).Debugf("Deleting a Runtime with id %q", id)
   484  	if err = r.runtimeService.Delete(ctx, id); err != nil {
   485  		return nil, err
   486  	}
   487  
   488  	if err = r.oAuth20Svc.DeleteMultipleClientCredentials(ctx, auths); err != nil {
   489  		return nil, err
   490  	}
   491  
   492  	if err = tx.Commit(); err != nil {
   493  		return nil, err
   494  	}
   495  
   496  	return deletedRuntime, nil
   497  }
   498  
   499  // GetLabel missing godoc
   500  func (r *Resolver) GetLabel(ctx context.Context, runtimeID string, key string) (*graphql.Labels, error) {
   501  	if runtimeID == "" {
   502  		return nil, apperrors.NewInternalError("Runtime cannot be empty")
   503  	}
   504  	if key == "" {
   505  		return nil, apperrors.NewInternalError("Runtime label key cannot be empty")
   506  	}
   507  
   508  	tx, err := r.transact.Begin()
   509  	if err != nil {
   510  		return nil, err
   511  	}
   512  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   513  
   514  	ctx = persistence.SaveToContext(ctx, tx)
   515  
   516  	label, err := r.runtimeService.GetLabel(ctx, runtimeID, key)
   517  	if err != nil {
   518  		if apperrors.IsNotFoundError(err) {
   519  			return nil, tx.Commit()
   520  		}
   521  		return nil, err
   522  	}
   523  
   524  	if err = tx.Commit(); err != nil {
   525  		return nil, err
   526  	}
   527  
   528  	resultLabels := make(map[string]interface{})
   529  	resultLabels[key] = label.Value
   530  
   531  	var gqlLabels graphql.Labels = resultLabels
   532  	return &gqlLabels, nil
   533  }
   534  
   535  // SetRuntimeLabel missing godoc
   536  func (r *Resolver) SetRuntimeLabel(ctx context.Context, runtimeID string, key string, value interface{}) (*graphql.Label, error) {
   537  	// TODO: Use @validation directive on input type instead, after resolving https://github.com/kyma-incubator/compass/issues/515
   538  	gqlLabel := graphql.LabelInput{Key: key, Value: value}
   539  	if err := inputvalidation.Validate(&gqlLabel); err != nil {
   540  		return nil, errors.Wrap(err, "validation error for type LabelInput")
   541  	}
   542  
   543  	tx, err := r.transact.Begin()
   544  	if err != nil {
   545  		return nil, err
   546  	}
   547  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   548  
   549  	ctx = persistence.SaveToContext(ctx, tx)
   550  
   551  	err = r.runtimeService.SetLabel(ctx, &model.LabelInput{
   552  		Key:        key,
   553  		Value:      value,
   554  		ObjectType: model.RuntimeLabelableObject,
   555  		ObjectID:   runtimeID,
   556  	})
   557  	if err != nil {
   558  		return nil, err
   559  	}
   560  
   561  	label, err := r.runtimeService.GetLabel(ctx, runtimeID, key)
   562  	if err != nil {
   563  		return nil, errors.Wrapf(err, "while getting label with key: [%s]", key)
   564  	}
   565  
   566  	if err = tx.Commit(); err != nil {
   567  		return nil, err
   568  	}
   569  
   570  	return &graphql.Label{
   571  		Key:   label.Key,
   572  		Value: label.Value,
   573  	}, nil
   574  }
   575  
   576  // DeleteRuntimeLabel missing godoc
   577  func (r *Resolver) DeleteRuntimeLabel(ctx context.Context, runtimeID string, key string) (*graphql.Label, error) {
   578  	tx, err := r.transact.Begin()
   579  	if err != nil {
   580  		return nil, err
   581  	}
   582  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   583  
   584  	ctx = persistence.SaveToContext(ctx, tx)
   585  
   586  	label, err := r.runtimeService.GetLabel(ctx, runtimeID, key)
   587  	if err != nil {
   588  		return nil, err
   589  	}
   590  
   591  	if err = r.runtimeService.DeleteLabel(ctx, runtimeID, key); err != nil {
   592  		return nil, err
   593  	}
   594  
   595  	if err = tx.Commit(); err != nil {
   596  		return nil, err
   597  	}
   598  
   599  	return &graphql.Label{
   600  		Key:   key,
   601  		Value: label.Value,
   602  	}, nil
   603  }
   604  
   605  // Webhooks missing godoc
   606  func (r *Resolver) Webhooks(ctx context.Context, obj *graphql.Runtime) ([]*graphql.Webhook, error) {
   607  	if obj == nil {
   608  		return nil, apperrors.NewInternalError("Runtime cannot be empty")
   609  	}
   610  
   611  	tx, err := r.transact.Begin()
   612  	if err != nil {
   613  		return nil, err
   614  	}
   615  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   616  
   617  	ctx = persistence.SaveToContext(ctx, tx)
   618  
   619  	webhooks, err := r.webhookService.ListForRuntime(ctx, obj.ID)
   620  	if err != nil {
   621  		return nil, err
   622  	}
   623  
   624  	if err = tx.Commit(); err != nil {
   625  		return nil, err
   626  	}
   627  
   628  	gqlWebhooks, err := r.webhookConverter.MultipleToGraphQL(webhooks)
   629  	if err != nil {
   630  		return nil, err
   631  	}
   632  
   633  	return gqlWebhooks, nil
   634  }
   635  
   636  // Labels missing godoc
   637  func (r *Resolver) Labels(ctx context.Context, obj *graphql.Runtime, key *string) (graphql.Labels, error) {
   638  	if obj == nil {
   639  		return nil, apperrors.NewInternalError("Runtime cannot be empty")
   640  	}
   641  
   642  	tx, err := r.transact.Begin()
   643  	if err != nil {
   644  		return nil, err
   645  	}
   646  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   647  
   648  	ctx = persistence.SaveToContext(ctx, tx)
   649  
   650  	itemMap, err := r.runtimeService.ListLabels(ctx, obj.ID)
   651  	if err != nil {
   652  		if strings.Contains(err.Error(), "doesn't exist") { // TODO: Use custom error and check its type
   653  			return nil, tx.Commit()
   654  		}
   655  		return nil, err
   656  	}
   657  
   658  	err = tx.Commit()
   659  	if err != nil {
   660  		return nil, err
   661  	}
   662  
   663  	resultLabels := make(map[string]interface{})
   664  
   665  	for _, label := range itemMap {
   666  		if key == nil || label.Key == *key {
   667  			resultLabels[label.Key] = label.Value
   668  		}
   669  	}
   670  
   671  	var gqlLabels graphql.Labels = resultLabels
   672  	return gqlLabels, nil
   673  }
   674  
   675  // RuntimeContexts retrieves a page of RuntimeContexts for the specified Runtime
   676  func (r *Resolver) RuntimeContexts(ctx context.Context, obj *graphql.Runtime, first *int, after *graphql.PageCursor) (*graphql.RuntimeContextPage, error) {
   677  	param := dataloader.ParamRuntimeContext{ID: obj.ID, Ctx: ctx, First: first, After: after}
   678  	return dataloader.RuntimeContextFor(ctx).RuntimeContextByID.Load(param)
   679  }
   680  
   681  // RuntimeContextsDataLoader retrieves a page of RuntimeContexts for each Runtime ID in the keys argument
   682  func (r *Resolver) RuntimeContextsDataLoader(keys []dataloader.ParamRuntimeContext) ([]*graphql.RuntimeContextPage, []error) {
   683  	if len(keys) == 0 {
   684  		return nil, []error{apperrors.NewInternalError("No Runtimes found")}
   685  	}
   686  
   687  	ctx := keys[0].Ctx
   688  	runtimeIDs := make([]string, 0, len(keys))
   689  	for _, key := range keys {
   690  		runtimeIDs = append(runtimeIDs, key.ID)
   691  	}
   692  
   693  	var cursor string
   694  	if keys[0].After != nil {
   695  		cursor = string(*keys[0].After)
   696  	}
   697  
   698  	if keys[0].First == nil {
   699  		return nil, []error{apperrors.NewInvalidDataError("missing required parameter 'first'")}
   700  	}
   701  
   702  	tx, err := r.transact.Begin()
   703  	if err != nil {
   704  		return nil, []error{err}
   705  	}
   706  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   707  
   708  	ctx = persistence.SaveToContext(ctx, tx)
   709  
   710  	runtimeContextPages, err := r.runtimeContextService.ListByRuntimeIDs(ctx, runtimeIDs, *keys[0].First, cursor)
   711  	if err != nil {
   712  		return nil, []error{err}
   713  	}
   714  
   715  	gqlRtmCtxs := make([]*graphql.RuntimeContextPage, 0, len(runtimeContextPages))
   716  	for _, page := range runtimeContextPages {
   717  		rtmCtxs := r.runtimeContextConverter.MultipleToGraphQL(page.Data)
   718  
   719  		gqlRtmCtxs = append(gqlRtmCtxs, &graphql.RuntimeContextPage{Data: rtmCtxs, TotalCount: page.TotalCount, PageInfo: &graphql.PageInfo{
   720  			StartCursor: graphql.PageCursor(page.PageInfo.StartCursor),
   721  			EndCursor:   graphql.PageCursor(page.PageInfo.EndCursor),
   722  			HasNextPage: page.PageInfo.HasNextPage,
   723  		}})
   724  	}
   725  
   726  	err = tx.Commit()
   727  	if err != nil {
   728  		return nil, []error{err}
   729  	}
   730  
   731  	return gqlRtmCtxs, nil
   732  }
   733  
   734  // RuntimeContext missing godoc
   735  func (r *Resolver) RuntimeContext(ctx context.Context, obj *graphql.Runtime, id string) (*graphql.RuntimeContext, error) {
   736  	if obj == nil {
   737  		return nil, apperrors.NewInternalError("Runtime cannot be empty")
   738  	}
   739  
   740  	tx, err := r.transact.Begin()
   741  	if err != nil {
   742  		return nil, err
   743  	}
   744  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   745  
   746  	ctx = persistence.SaveToContext(ctx, tx)
   747  
   748  	runtimeContext, err := r.runtimeContextService.GetForRuntime(ctx, id, obj.ID)
   749  	if err != nil {
   750  		if apperrors.IsNotFoundError(err) {
   751  			return nil, tx.Commit()
   752  		}
   753  		return nil, err
   754  	}
   755  
   756  	err = tx.Commit()
   757  	if err != nil {
   758  		return nil, err
   759  	}
   760  
   761  	return r.runtimeContextConverter.ToGraphQL(runtimeContext), nil
   762  }
   763  
   764  // Auths missing godoc
   765  func (r *Resolver) Auths(ctx context.Context, obj *graphql.Runtime) ([]*graphql.RuntimeSystemAuth, error) {
   766  	if obj == nil {
   767  		return nil, apperrors.NewInternalError("Runtime cannot be empty")
   768  	}
   769  
   770  	tx, err := r.transact.Begin()
   771  	if err != nil {
   772  		return nil, err
   773  	}
   774  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   775  
   776  	ctx = persistence.SaveToContext(ctx, tx)
   777  
   778  	sysAuths, err := r.sysAuthSvc.ListForObject(ctx, pkgmodel.RuntimeReference, obj.ID)
   779  	if err != nil {
   780  		return nil, err
   781  	}
   782  
   783  	err = tx.Commit()
   784  	if err != nil {
   785  		return nil, err
   786  	}
   787  
   788  	out := make([]*graphql.RuntimeSystemAuth, 0, len(sysAuths))
   789  	for _, sa := range sysAuths {
   790  		c, err := r.sysAuthConv.ToGraphQL(&sa)
   791  		if err != nil {
   792  			return nil, err
   793  		}
   794  		out = append(out, c.(*graphql.RuntimeSystemAuth))
   795  	}
   796  
   797  	return out, nil
   798  }
   799  
   800  // EventingConfiguration missing godoc
   801  func (r *Resolver) EventingConfiguration(ctx context.Context, obj *graphql.Runtime) (*graphql.RuntimeEventingConfiguration, error) {
   802  	if obj == nil {
   803  		return nil, apperrors.NewInternalError("Runtime cannot be empty")
   804  	}
   805  
   806  	runtimeID, err := uuid.Parse(obj.ID)
   807  	if err != nil {
   808  		return nil, errors.Wrap(err, "while parsing runtime ID as UUID")
   809  	}
   810  
   811  	tx, err := r.transact.Begin()
   812  	if err != nil {
   813  		return nil, errors.Wrap(err, "while opening the transaction")
   814  	}
   815  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   816  
   817  	ctx = persistence.SaveToContext(ctx, tx)
   818  
   819  	eventingCfg, err := r.eventingSvc.GetForRuntime(ctx, runtimeID)
   820  	if err != nil {
   821  		return nil, errors.Wrap(err, "while fetching eventing configuration for runtime")
   822  	}
   823  
   824  	if err = tx.Commit(); err != nil {
   825  		return nil, errors.Wrap(err, "while committing the transaction")
   826  	}
   827  
   828  	return eventing.RuntimeEventingConfigurationToGraphQL(eventingCfg), nil
   829  }
   830  
   831  // deleteAssociatedScenarioAssignments ensures that scenario assignments which are responsible for creation of certain runtime labels are deleted,
   832  // if runtime doesn't have the scenarios label or is part of a scenario for which no scenario assignment exists => noop
   833  func (r *Resolver) deleteAssociatedScenarioAssignments(ctx context.Context, runtimeID string) error {
   834  	scenariosLbl, err := r.runtimeService.GetLabel(ctx, runtimeID, model.ScenariosKey)
   835  	notFound := apperrors.IsNotFoundError(err)
   836  	if err != nil && !notFound {
   837  		return err
   838  	}
   839  
   840  	if notFound {
   841  		return nil
   842  	}
   843  
   844  	scenarios, err := labelPkg.ValueToStringsSlice(scenariosLbl.Value)
   845  	if err != nil {
   846  		return err
   847  	}
   848  
   849  	for _, scenario := range scenarios {
   850  		scenarioAssignment, err := r.scenarioAssignmentService.GetForScenarioName(ctx, scenario)
   851  		notFound := apperrors.IsNotFoundError(err)
   852  		if err != nil && !notFound {
   853  			return err
   854  		}
   855  
   856  		if notFound {
   857  			continue
   858  		}
   859  
   860  		if err := r.formationSvc.DeleteAutomaticScenarioAssignment(ctx, scenarioAssignment); err != nil {
   861  			return err
   862  		}
   863  	}
   864  
   865  	return nil
   866  }
   867  
   868  func (r *Resolver) checkProviderRuntimeExistence(ctx context.Context, labels map[string]interface{}) error {
   869  	distinguishLabelKey := r.selfRegManager.GetSelfRegDistinguishingLabelKey()
   870  	regionLabelKey := selfregmanager.RegionLabel
   871  
   872  	distinguishLabelValue, distinguishLabelExists := labels[distinguishLabelKey]
   873  	region, regionExists := labels[regionLabelKey]
   874  
   875  	if distinguishLabelExists && regionExists {
   876  		filters := []*labelfilter.LabelFilter{
   877  			labelfilter.NewForKeyWithQuery(distinguishLabelKey, fmt.Sprintf("\"%s\"", distinguishLabelValue)),
   878  			labelfilter.NewForKeyWithQuery(regionLabelKey, fmt.Sprintf("\"%s\"", region)),
   879  		}
   880  
   881  		log.C(ctx).Infof("Getting runtime for labels %q: %q and %q: %q", regionLabelKey, region, distinguishLabelKey, distinguishLabelValue)
   882  		_, err := r.runtimeService.GetByFilters(ctx, filters)
   883  		if err != nil {
   884  			if apperrors.IsNotFoundError(err) {
   885  				return nil
   886  			}
   887  			return errors.Wrap(err, fmt.Sprintf("failed to get runtime for labels %q: %q and %q: %q", regionLabelKey, region, distinguishLabelKey, distinguishLabelValue))
   888  		}
   889  
   890  		log.C(ctx).Errorf("Cannot have more than one runtime with labels %q: %q and %q: %q", regionLabelKey, region, distinguishLabelKey, distinguishLabelValue)
   891  		return errors.New(fmt.Sprintf("cannot have more than one runtime with labels %q: %q and %q: %q", regionLabelKey, region, distinguishLabelKey, distinguishLabelValue))
   892  	}
   893  	return nil
   894  }
   895  
   896  func (r *Resolver) cleanupAndLogOnError(ctx context.Context, runtimeID, region string) {
   897  	if err := r.selfRegManager.CleanupSelfRegistration(ctx, runtimeID, region); err != nil {
   898  		log.C(ctx).Errorf("An error occurred during cleanup of self-registered runtime: %v", err)
   899  	}
   900  }