Skip to content
This repository has been archived by the owner on Jul 4, 2024. It is now read-only.

Commit

Permalink
Introduce regional subaccount tenants (#1998)
Browse files Browse the repository at this point in the history
* Add new regional subaccount tenants

* Add e2e test

* Bump component versions

* Fix formatting

* Improve decommissioning logic

* Improvements

* Remove unused config in tests

* Fix director URL env var in tests chart

* Return error when trying to create subaccount tenant on global onboarding handler

* Small cleanup

* Fix imports

* Address first part of review comments

* Fix imports

* Create account tenant if missing

* Adjust tenant provisioner tests

* Fix test formatting

* Address comments

* Fix test

* Apply review comments

* Fix tenant fetcher config

* Fix tenant fetcher config for real

* Add lint-local to verify-local

* Revert check for uniqueness
  • Loading branch information
desislavaa authored Sep 10, 2021
1 parent c43fb66 commit 56c31a0
Show file tree
Hide file tree
Showing 31 changed files with 1,303 additions and 434 deletions.
14 changes: 11 additions & 3 deletions chart/compass/charts/tenant-fetcher/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ spec:
value: {{ .Values.global.tenantFetcher.tenantProvider.tenantIdProperty }}
- name: APP_TENANT_PROVIDER_CUSTOMER_ID_PROPERTY
value: {{ .Values.global.tenantFetcher.tenantProvider.customerIdProperty }}
- name: APP_TENANT_PROVIDER_SUBACCOUNT_TENANT_ID_PROPERTY
value: {{ .Values.global.tenantFetcher.tenantProvider.subaccountTenantIdProperty }}
- name: APP_TENANT_PROVIDER_SUBDOMAIN_PROPERTY
value: {{ .Values.global.tenantFetcher.tenantProvider.subdomainProperty }}
- name: APP_TENANT_PROVIDER
Expand All @@ -70,11 +72,17 @@ spec:
- name: APP_ROOT_API
value: "{{ .Values.global.tenantFetcher.prefix }}"
- name: APP_HANDLER_ENDPOINT
value: "{{ .Values.server.handlerEndpoint }}"
value: "{{ .Values.global.tenantFetcher.server.handlerEndpoint }}"
- name: APP_REGIONAL_HANDLER_ENDPOINT
value: "{{ .Values.global.tenantFetcher.server.regionalHandlerEndpoint }}"
- name: APP_DEPENDENCIES_ENDPOINT
value: "{{ .Values.global.tenantFetcher.server.dependenciesEndpoint }}"
- name: APP_TENANT_PATH_PARAM
value: "{{ .Values.global.tenantFetcher.server.tenantPathParam }}"
- name: APP_REGION_PATH_PARAM
value: "{{ .Values.global.tenantFetcher.server.regionPathParam }}"
- name: APP_JWKS_ENDPOINT
value: "{{ .Values.global.tenantFetcher.authentication.jwksEndpoint }}"
- name: APP_TENANT_PATH_PARAM
value: "{{ .Values.server.tenantPathParam }}"
- name: APP_DB_USER
valueFrom:
secretKeyRef:
Expand Down
3 changes: 0 additions & 3 deletions chart/compass/charts/tenant-fetcher/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,3 @@ database:
maxOpenConnections: 2
maxIdleConnections: 1

server:
handlerEndpoint: "/v1/callback/{tenantId}"
tenantPathParam: "tenantId"
2 changes: 2 additions & 0 deletions chart/compass/templates/tenant-fetcher-job.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ spec:
value: {{ $config.endpoints.movedRuntimeByLabel }}
- name: APP_TENANT_PROVIDER
value: {{ $config.providerName }}
- name: APP_TENANTS_REGION
value: {{ $config.tenantsRegion }}
- name: APP_CLIENT_ID
valueFrom:
secretKeyRef:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ spec:
value: {{ .Values.global.tenantFetcher.tenantProvider.tenantIdProperty }}
- name: APP_TENANT_PROVIDER_CUSTOMER_ID_PROPERTY
value: {{ .Values.global.tenantFetcher.tenantProvider.customerIdProperty }}
- name: APP_TENANT_PROVIDER_SUBACCOUNT_TENANT_ID_PROPERTY
value: {{ .Values.global.tenantFetcher.tenantProvider.subaccountTenantIdProperty }}
- name: APP_TENANT_PROVIDER_SUBDOMAIN_PROPERTY
value: {{ .Values.global.tenantFetcher.tenantProvider.subdomainProperty }}
- name: APP_TENANT_PROVIDER
value: "test-provider"
- name: APP_TENANT
value: {{ .Values.global.defaultTenant }}
- name: APP_TENANT_FETCHER_URL
Expand All @@ -54,9 +54,15 @@ spec:
- name: APP_ROOT_API
value: "{{ .Values.global.tenantFetcher.prefix }}"
- name: APP_HANDLER_ENDPOINT
value: "/v1/callback/{tenantId}"
value: "{{ .Values.global.tenantFetcher.server.handlerEndpoint }}"
- name: APP_REGIONAL_HANDLER_ENDPOINT
value: "{{ .Values.global.tenantFetcher.server.regionalHandlerEndpoint }}"
- name: APP_DEPENDENCIES_ENDPOINT
value: "{{ .Values.global.tenantFetcher.server.dependenciesEndpoint }}"
- name: APP_TENANT_PATH_PARAM
value: "tenantId"
value: "{{ .Values.global.tenantFetcher.server.tenantPathParam }}"
- name: APP_REGION_PATH_PARAM
value: "{{ .Values.global.tenantFetcher.server.regionPathParam }}"
- name: DIRECTOR_URL
value: "https://{{ .Values.global.gateway.tls.host }}.{{ .Values.global.ingress.domainName }}{{ .Values.global.director.prefix }}"
- name: APP_DB_NAME
Expand Down
13 changes: 10 additions & 3 deletions chart/compass/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ global:
version: "PR-2003"
director:
dir:
version: "PR-2010"
version: "PR-1998"
gateway:
dir:
version: "PR-2003"
Expand All @@ -87,7 +87,7 @@ global:
version: "PR-41"
schema_migrator:
dir:
version: "PR-2010"
version: "PR-1998"
system_broker:
dir:
version: "PR-2003"
Expand All @@ -104,7 +104,7 @@ global:
version: "PR-42"
e2e_tests:
dir:
version: "PR-2010"
version: "PR-1998"
isLocalEnv: false
oauth2:
host: oauth2
Expand Down Expand Up @@ -349,8 +349,15 @@ global:
tenantProvider:
tenantIdProperty: "tenantId"
customerIdProperty: "customerId"
subaccountTenantIdProperty: "subaccountTenantId"
subdomainProperty: "subdomain"
name: "provider"
server:
handlerEndpoint: "/v1/callback/{tenantId}"
regionalHandlerEndpoint: "/v1/regional/{region}/callback/{tenantId}"
dependenciesEndpoint: "/v1/dependencies"
tenantPathParam: "tenantId"
regionPathParam: "region"

ordService:
host: compass-ord-service.compass-system.svc.cluster.local
Expand Down
3 changes: 2 additions & 1 deletion components/director/cmd/tenantfetcher-job/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type config struct {
Features features.Config

TenantProvider string `envconfig:"APP_TENANT_PROVIDER"`
TenantsRegion string `envconfig:"default=central,APP_TENANTS_REGION"`
MetricsPushEndpoint string `envconfig:"optional,APP_METRICS_PUSH_ENDPOINT"`
MovedRuntimeLabelKey string `envconfig:"default=moved_runtime,APP_MOVED_RUNTIME_LABEL_KEY"`
ClientTimeout time.Duration `envconfig:"default=60s"`
Expand Down Expand Up @@ -117,5 +118,5 @@ func createTenantFetcherSvc(cfg config, transact persistence.Transactioner, kube
eventAPIClient.SetMetricsPusher(metricsPusher)
}

return tenantfetcher.NewService(cfg.QueryConfig, transact, kubeClient, cfg.TenantFieldMapping, cfg.MovedRuntimeByLabelFieldMapping, cfg.TenantProvider, eventAPIClient, tenantStorageSvc, runtimeService, labelDefService, cfg.MovedRuntimeLabelKey, cfg.FullResyncInterval)
return tenantfetcher.NewService(cfg.QueryConfig, transact, kubeClient, cfg.TenantFieldMapping, cfg.MovedRuntimeByLabelFieldMapping, cfg.TenantProvider, cfg.TenantsRegion, eventAPIClient, tenantStorageSvc, runtimeService, labelDefService, cfg.MovedRuntimeLabelKey, cfg.FullResyncInterval)
}
24 changes: 21 additions & 3 deletions components/director/cmd/tenantfetcher-svc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ type config struct {
Handler tenantfetcher.HandlerConfig

Database persistence.DatabaseConfig

SecurityConfig securityConfig
}

type securityConfig struct {
JWKSSyncPeriod time.Duration `envconfig:"default=5m"`
AllowJWTSigningNone bool `envconfig:"APP_ALLOW_JWT_SIGNING_NONE,default=false"`
JwksEndpoint string `envconfig:"APP_JWKS_ENDPOINT"`
SubscriptionCallbackScope string `envconfig:"APP_SUBSCRIPTION_CALLBACK_SCOPE"`
}

func main() {
Expand Down Expand Up @@ -107,7 +116,7 @@ func initAPIHandler(ctx context.Context, cfg config, transact persistence.Transa
router := mainRouter.PathPrefix(cfg.RootAPI).Subrouter()
healthCheckRouter := mainRouter.PathPrefix(cfg.RootAPI).Subrouter()

configureAuthMiddleware(ctx, router, cfg.Handler)
configureAuthMiddleware(ctx, router, cfg.SecurityConfig)

registerHandler(ctx, router, cfg.Handler, transact)

Expand Down Expand Up @@ -159,7 +168,7 @@ func createServer(ctx context.Context, cfg config, handler http.Handler, name st
return runFn, shutdownFn
}

func configureAuthMiddleware(ctx context.Context, router *mux.Router, cfg tenantfetcher.HandlerConfig) {
func configureAuthMiddleware(ctx context.Context, router *mux.Router, cfg securityConfig) {
scopeValidator := claims.NewScopesValidator([]string{cfg.SubscriptionCallbackScope})
middleware := auth.New(cfg.JwksEndpoint, cfg.AllowJWTSigningNone, "", scopeValidator)
router.Use(middleware.Handler())
Expand All @@ -186,14 +195,23 @@ func registerHandler(ctx context.Context, router *mux.Router, cfg tenantfetcher.
tenantRepo := tenant.NewRepository(converter)
tenantSvc := tenant.NewServiceWithLabels(tenantRepo, uidSvc, labelRepo, labelUpsertSvc)

provisioner := tenantfetcher.NewTenantProvisioner(tenantSvc)
provisioner := tenantfetcher.NewTenantProvisioner(tenantSvc, cfg.TenantProvider)
tenantHandler := tenantfetcher.NewTenantsHTTPHandler(provisioner, transact, cfg)

log.C(ctx).Infof("Registering Tenant Onboarding endpoint on %s...", cfg.HandlerEndpoint)
router.HandleFunc(cfg.HandlerEndpoint, tenantHandler.Create).Methods(http.MethodPut)

log.C(ctx).Infof("Registering Tenant Decommissioning endpoint on %s...", cfg.HandlerEndpoint)
router.HandleFunc(cfg.HandlerEndpoint, tenantHandler.DeleteByExternalID).Methods(http.MethodDelete)

log.C(ctx).Infof("Registering Regional Tenant Onboarding endpoint on %s...", cfg.RegionalHandlerEndpoint)
router.HandleFunc(cfg.RegionalHandlerEndpoint, tenantHandler.CreateRegional).Methods(http.MethodPut)

log.C(ctx).Infof("Registering Regional Tenant Decommissioning endpoint on %s...", cfg.RegionalHandlerEndpoint)
router.HandleFunc(cfg.RegionalHandlerEndpoint, tenantHandler.DeleteByExternalID).Methods(http.MethodDelete)

log.C(ctx).Infof("Registering service dependencies endpoint on %s...", cfg.DependenciesEndpoint)
router.HandleFunc(cfg.DependenciesEndpoint, tenantHandler.Dependencies).Methods(http.MethodGet)
}

func newReadinessHandler() func(writer http.ResponseWriter, request *http.Request) {
Expand Down
10 changes: 5 additions & 5 deletions components/director/internal/domain/tenant/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import (

type converter struct{}

// NewConverter missing godoc
// NewConverter returns a new Converter that can later be used to make the conversions between the GraphQL, service, and repository layer representations of a Compass tenant.
func NewConverter() *converter {
return &converter{}
}

// ToEntity missing godoc
// ToEntity converts the provided service-layer representation of a tenant to the repository-layer one tenant.Entity.
func (c *converter) ToEntity(in *model.BusinessTenantMapping) *tenant.Entity {
if in == nil {
return nil
Expand All @@ -30,7 +30,7 @@ func (c *converter) ToEntity(in *model.BusinessTenantMapping) *tenant.Entity {
}
}

// FromEntity missing godoc
// FromEntity converts the provided tenant.Entity repo-layer representation of a tenant to the service-layer representation model.BusinessTenantMapping.
func (c *converter) FromEntity(in *tenant.Entity) *model.BusinessTenantMapping {
if in == nil {
return nil
Expand All @@ -47,7 +47,7 @@ func (c *converter) FromEntity(in *tenant.Entity) *model.BusinessTenantMapping {
}
}

// ToGraphQL missing godoc
// ToGraphQL converts the provided model.BusinessTenantMapping service-layer representation of a tenant to the GraphQL-layer representation graphql.Tenant.
func (c *converter) ToGraphQL(in *model.BusinessTenantMapping) *graphql.Tenant {
if in == nil {
return nil
Expand All @@ -61,7 +61,7 @@ func (c *converter) ToGraphQL(in *model.BusinessTenantMapping) *graphql.Tenant {
}
}

// MultipleToGraphQL missing godoc
// MultipleToGraphQL converts all the provided model.BusinessTenantMapping service-layer representations of a tenant to the GraphQL-layer representations graphql.Tenant.
func (c *converter) MultipleToGraphQL(in []*model.BusinessTenantMapping) []*graphql.Tenant {
tenants := make([]*graphql.Tenant, 0, len(in))
for _, r := range in {
Expand Down
37 changes: 25 additions & 12 deletions components/director/internal/domain/tenant/fixtures_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ import (
)

const (
testExternal = "external"
testID = "foo"
testName = "bar"
testSubdomain = "subdomain"
testProvider = "Compass"
initializedColumn = "initialized"
testExternal = "external"
testID = "foo"
testName = "bar"
testParentID = "parent"
testInternalParentID = "internal-parent"
testTemporaryInternalParentID = "internal-parent-temp"
testSubdomain = "subdomain"
testRegion = "eu-1"
testProvider = "Compass"
initializedColumn = "initialized"
)

var (
Expand All @@ -28,12 +32,16 @@ var (
)

func newModelBusinessTenantMapping(id, name string) *model.BusinessTenantMapping {
return newModelBusinessTenantMappingWithType(id, name, "", tenant.Account)
}

func newModelBusinessTenantMappingWithType(id, name, parent string, tenantType tenant.Type) *model.BusinessTenantMapping {
return &model.BusinessTenantMapping{
ID: id,
Name: name,
ExternalTenant: testExternal,
Parent: "",
Type: tenant.Account,
Parent: parent,
Type: tenantType,
Provider: testProvider,
Status: tenant.Active,
}
Expand Down Expand Up @@ -99,13 +107,18 @@ func fixTenantMappingCreateArgs(ent tenant.Entity) []driver.Value {
return []driver.Value{ent.ID, ent.Name, ent.ExternalTenant, ent.Parent, ent.Type, ent.ProviderName, ent.Status}
}

func newModelBusinessTenantMappingInput(name, subdomain string) model.BusinessTenantMappingInput {
func newModelBusinessTenantMappingInput(name, subdomain, region string) model.BusinessTenantMappingInput {
return newModelBusinessTenantMappingInputWithType(testExternal, name, "", subdomain, region, tenant.Account)
}

func newModelBusinessTenantMappingInputWithType(tenantID, name, parent, subdomain, region string, tenantType tenant.Type) model.BusinessTenantMappingInput {
return model.BusinessTenantMappingInput{
Name: name,
ExternalTenant: testExternal,
ExternalTenant: tenantID,
Subdomain: subdomain,
Parent: "",
Type: string(tenant.Account),
Region: region,
Parent: parent,
Type: tenant.TypeToStr(tenantType),
Provider: testProvider,
}
}
Expand Down
20 changes: 10 additions & 10 deletions components/director/internal/domain/tenant/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ var (
initializedComputedColumn = "initialized"
)

// Converter missing godoc
// Converter converts tenants between the model.BusinessTenantMapping service-layer representation of a tenant and the repo-layer representation tenant.Entity.
//go:generate mockery --name=Converter --output=automock --outpkg=automock --case=underscore
type Converter interface {
ToEntity(in *model.BusinessTenantMapping) *tenant.Entity
Expand All @@ -54,7 +54,7 @@ type pgRepository struct {
conv Converter
}

// NewRepository missing godoc
// NewRepository returns a new entity responsible for repo-layer tenant operations. All of its methods require persistence.PersistenceOp it the provided context.
func NewRepository(conv Converter) *pgRepository {
return &pgRepository{
creator: repo.NewCreator(resource.Tenant, tableName, tableColumns),
Expand All @@ -67,12 +67,12 @@ func NewRepository(conv Converter) *pgRepository {
}
}

// Create missing godoc
// Create adds the provided tenant into the Compass storage.
func (r *pgRepository) Create(ctx context.Context, item model.BusinessTenantMapping) error {
return r.creator.Create(ctx, r.conv.ToEntity(&item))
}

// Get missing godoc
// Get retrieves the active tenant with matching internal ID from the Compass storage.
func (r *pgRepository) Get(ctx context.Context, id string) (*model.BusinessTenantMapping, error) {
var entity tenant.Entity
conditions := repo.Conditions{
Expand All @@ -85,7 +85,7 @@ func (r *pgRepository) Get(ctx context.Context, id string) (*model.BusinessTenan
return r.conv.FromEntity(&entity), nil
}

// GetByExternalTenant missing godoc
// GetByExternalTenant retrieves the active tenant with matching external ID from the Compass storage.
func (r *pgRepository) GetByExternalTenant(ctx context.Context, externalTenant string) (*model.BusinessTenantMapping, error) {
var entity tenant.Entity
conditions := repo.Conditions{
Expand All @@ -97,17 +97,17 @@ func (r *pgRepository) GetByExternalTenant(ctx context.Context, externalTenant s
return r.conv.FromEntity(&entity), nil
}

// Exists missing godoc
// Exists checks if tenant with the provided internal ID exists in the Compass storage.
func (r *pgRepository) Exists(ctx context.Context, id string) (bool, error) {
return r.existQuerierGlobal.ExistsGlobal(ctx, repo.Conditions{repo.NewEqualCondition(idColumn, id)})
}

// ExistsByExternalTenant missing godoc
// ExistsByExternalTenant checks if tenant with the provided external ID exists in the Compass storage.
func (r *pgRepository) ExistsByExternalTenant(ctx context.Context, externalTenant string) (bool, error) {
return r.existQuerierGlobal.ExistsGlobal(ctx, repo.Conditions{repo.NewEqualCondition(externalTenantColumn, externalTenant)})
}

// List missing godoc
// List retrieves all tenants from the Compass storage.
func (r *pgRepository) List(ctx context.Context) ([]*model.BusinessTenantMapping, error) {
var entityCollection tenant.EntityCollection

Expand Down Expand Up @@ -136,7 +136,7 @@ func (r *pgRepository) List(ctx context.Context) ([]*model.BusinessTenantMapping
return items, nil
}

// Update missing godoc
// Update updates the values of tenant with matching internal, and external IDs.
func (r *pgRepository) Update(ctx context.Context, model *model.BusinessTenantMapping) error {
if model == nil {
return apperrors.NewInternalError("model can not be empty")
Expand All @@ -147,7 +147,7 @@ func (r *pgRepository) Update(ctx context.Context, model *model.BusinessTenantMa
return r.updaterGlobal.UpdateSingleGlobal(ctx, entity)
}

// DeleteByExternalTenant missing godoc
// DeleteByExternalTenant removes a tenant with matching external ID from the Compass storage.
func (r *pgRepository) DeleteByExternalTenant(ctx context.Context, externalTenant string) error {
conditions := repo.Conditions{
repo.NewEqualCondition(externalTenantColumn, externalTenant),
Expand Down
Loading

0 comments on commit 56c31a0

Please sign in to comment.