diff --git a/apps/console/internal/app/graph/generated/generated.go b/apps/console/internal/app/graph/generated/generated.go index e4b0b840f..bc2fb473f 100644 --- a/apps/console/internal/app/graph/generated/generated.go +++ b/apps/console/internal/app/graph/generated/generated.go @@ -464,6 +464,7 @@ type ComplexityRoot struct { Enabled func(childComplexity int) int PortMappings func(childComplexity int) int ToDevice func(childComplexity int) int + ToIPAddr func(childComplexity int) int } Github__com___kloudlite___operator___apis___crds___v1__ManagedResourceSpec struct { @@ -765,6 +766,7 @@ type ComplexityRoot struct { CoreInterceptAppOnLocalCluster func(childComplexity int, envName string, appname string, clusterName string, ipAddr string, intercept bool, portMappings []*v1.AppInterceptPortMappings) int CoreInterceptExternalApp func(childComplexity int, envName string, externalAppName string, deviceName string, intercept bool, portMappings []*v1.AppInterceptPortMappings) int CoreRemoveDeviceIntercepts func(childComplexity int, envName string, deviceName string) int + CoreSetupDefaultEnvironment func(childComplexity int) int CoreUpdateApp func(childComplexity int, envName string, app entities.App) int CoreUpdateConfig func(childComplexity int, envName string, config entities.Config) int CoreUpdateEnvironment func(childComplexity int, env entities.Environment) int @@ -1070,6 +1072,7 @@ type MetadataResolver interface { Labels(ctx context.Context, obj *v12.ObjectMeta) (map[string]interface{}, error) } type MutationResolver interface { + CoreSetupDefaultEnvironment(ctx context.Context) (bool, error) CoreCreateEnvironment(ctx context.Context, env entities.Environment) (*entities.Environment, error) CoreUpdateEnvironment(ctx context.Context, env entities.Environment) (*entities.Environment, error) CoreDeleteEnvironment(ctx context.Context, envName string) (bool, error) @@ -2905,6 +2908,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Github__com___kloudlite___operator___apis___crds___v1__Intercept.ToDevice(childComplexity), true + case "Github__com___kloudlite___operator___apis___crds___v1__Intercept.toIPAddr": + if e.complexity.Github__com___kloudlite___operator___apis___crds___v1__Intercept.ToIPAddr == nil { + break + } + + return e.complexity.Github__com___kloudlite___operator___apis___crds___v1__Intercept.ToIPAddr(childComplexity), true + case "Github__com___kloudlite___operator___apis___crds___v1__ManagedResourceSpec.resourceNamePrefix": if e.complexity.Github__com___kloudlite___operator___apis___crds___v1__ManagedResourceSpec.ResourceNamePrefix == nil { break @@ -4418,6 +4428,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.CoreRemoveDeviceIntercepts(childComplexity, args["envName"].(string), args["deviceName"].(string)), true + case "Mutation.core_setupDefaultEnvironment": + if e.complexity.Mutation.CoreSetupDefaultEnvironment == nil { + break + } + + return e.complexity.Mutation.CoreSetupDefaultEnvironment(childComplexity), true + case "Mutation.core_updateApp": if e.complexity.Mutation.CoreUpdateApp == nil { break @@ -6003,6 +6020,7 @@ type Query { } type Mutation { + core_setupDefaultEnvironment: Boolean! @isLoggedInAndVerified @hasAccount core_createEnvironment(env: EnvironmentIn!): Environment @isLoggedInAndVerified @hasAccount core_updateEnvironment(env: EnvironmentIn!): Environment @isLoggedInAndVerified @hasAccount core_deleteEnvironment(envName: String!): Boolean! @isLoggedInAndVerified @hasAccount @@ -6344,6 +6362,7 @@ type Github__com___kloudlite___operator___apis___crds___v1__Intercept @shareable enabled: Boolean! portMappings: [Github__com___kloudlite___operator___apis___crds___v1__AppInterceptPortMappings!] toDevice: String! + toIPAddr: String } type Github__com___kloudlite___operator___apis___crds___v1__ManagedResourceSpec @shareable { @@ -6678,6 +6697,7 @@ input Github__com___kloudlite___operator___apis___crds___v1__InterceptIn { enabled: Boolean! portMappings: [Github__com___kloudlite___operator___apis___crds___v1__AppInterceptPortMappingsIn!] toDevice: String! + toIPAddr: String } input Github__com___kloudlite___operator___apis___crds___v1__ManagedResourceSpecIn { @@ -17928,6 +17948,8 @@ func (ec *executionContext) fieldContext_Github__com___kloudlite___operator___ap return ec.fieldContext_Github__com___kloudlite___operator___apis___crds___v1__Intercept_portMappings(ctx, field) case "toDevice": return ec.fieldContext_Github__com___kloudlite___operator___apis___crds___v1__Intercept_toDevice(ctx, field) + case "toIPAddr": + return ec.fieldContext_Github__com___kloudlite___operator___apis___crds___v1__Intercept_toIPAddr(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type Github__com___kloudlite___operator___apis___crds___v1__Intercept", field.Name) }, @@ -19728,6 +19750,8 @@ func (ec *executionContext) fieldContext_Github__com___kloudlite___operator___ap return ec.fieldContext_Github__com___kloudlite___operator___apis___crds___v1__Intercept_portMappings(ctx, field) case "toDevice": return ec.fieldContext_Github__com___kloudlite___operator___apis___crds___v1__Intercept_toDevice(ctx, field) + case "toIPAddr": + return ec.fieldContext_Github__com___kloudlite___operator___apis___crds___v1__Intercept_toIPAddr(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type Github__com___kloudlite___operator___apis___crds___v1__Intercept", field.Name) }, @@ -20421,6 +20445,47 @@ func (ec *executionContext) fieldContext_Github__com___kloudlite___operator___ap return fc, nil } +func (ec *executionContext) _Github__com___kloudlite___operator___apis___crds___v1__Intercept_toIPAddr(ctx context.Context, field graphql.CollectedField, obj *model.GithubComKloudliteOperatorApisCrdsV1Intercept) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Github__com___kloudlite___operator___apis___crds___v1__Intercept_toIPAddr(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ToIPAddr, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2áš–string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Github__com___kloudlite___operator___apis___crds___v1__Intercept_toIPAddr(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Github__com___kloudlite___operator___apis___crds___v1__Intercept", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _Github__com___kloudlite___operator___apis___crds___v1__ManagedResourceSpec_resourceNamePrefix(ctx context.Context, field graphql.CollectedField, obj *model.GithubComKloudliteOperatorApisCrdsV1ManagedResourceSpec) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Github__com___kloudlite___operator___apis___crds___v1__ManagedResourceSpec_resourceNamePrefix(ctx, field) if err != nil { @@ -28409,6 +28474,76 @@ func (ec *executionContext) fieldContext_Metadata_namespace(_ context.Context, f return fc, nil } +func (ec *executionContext) _Mutation_core_setupDefaultEnvironment(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_core_setupDefaultEnvironment(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().CoreSetupDefaultEnvironment(rctx) + } + directive1 := func(ctx context.Context) (interface{}, error) { + if ec.directives.IsLoggedInAndVerified == nil { + return nil, errors.New("directive isLoggedInAndVerified is not implemented") + } + return ec.directives.IsLoggedInAndVerified(ctx, nil, directive0) + } + directive2 := func(ctx context.Context) (interface{}, error) { + if ec.directives.HasAccount == nil { + return nil, errors.New("directive hasAccount is not implemented") + } + return ec.directives.HasAccount(ctx, nil, directive1) + } + + tmp, err := directive2(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(bool); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be bool`, tmp) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_core_setupDefaultEnvironment(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _Mutation_core_createEnvironment(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Mutation_core_createEnvironment(ctx, field) if err != nil { @@ -43424,7 +43559,7 @@ func (ec *executionContext) unmarshalInputGithub__com___kloudlite___operator___a asMap[k] = v } - fieldsInOrder := [...]string{"enabled", "portMappings", "toDevice"} + fieldsInOrder := [...]string{"enabled", "portMappings", "toDevice", "toIPAddr"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -43452,6 +43587,13 @@ func (ec *executionContext) unmarshalInputGithub__com___kloudlite___operator___a return it, err } it.ToDevice = data + case "toIPAddr": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("toIPAddr")) + data, err := ec.unmarshalOString2áš–string(ctx, v) + if err != nil { + return it, err + } + it.ToIPAddr = data } } @@ -48887,6 +49029,8 @@ func (ec *executionContext) _Github__com___kloudlite___operator___apis___crds___ if out.Values[i] == graphql.Null { out.Invalids++ } + case "toIPAddr": + out.Values[i] = ec._Github__com___kloudlite___operator___apis___crds___v1__Intercept_toIPAddr(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -51443,6 +51587,13 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Mutation") + case "core_setupDefaultEnvironment": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_core_setupDefaultEnvironment(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } case "core_createEnvironment": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { return ec._Mutation_core_createEnvironment(ctx, field) diff --git a/apps/console/internal/app/graph/model/models_gen.go b/apps/console/internal/app/graph/model/models_gen.go index cfd28412a..ad830d79e 100644 --- a/apps/console/internal/app/graph/model/models_gen.go +++ b/apps/console/internal/app/graph/model/models_gen.go @@ -409,12 +409,14 @@ type GithubComKloudliteOperatorApisCrdsV1Intercept struct { Enabled bool `json:"enabled"` PortMappings []*GithubComKloudliteOperatorApisCrdsV1AppInterceptPortMappings `json:"portMappings,omitempty"` ToDevice string `json:"toDevice"` + ToIPAddr *string `json:"toIPAddr,omitempty"` } type GithubComKloudliteOperatorApisCrdsV1InterceptIn struct { Enabled bool `json:"enabled"` PortMappings []*v1.AppInterceptPortMappings `json:"portMappings,omitempty"` ToDevice string `json:"toDevice"` + ToIPAddr *string `json:"toIPAddr,omitempty"` } type GithubComKloudliteOperatorApisCrdsV1ManagedResourceSpec struct { diff --git a/apps/console/internal/app/graph/schema.graphqls b/apps/console/internal/app/graph/schema.graphqls index d509fa652..92f37eca7 100644 --- a/apps/console/internal/app/graph/schema.graphqls +++ b/apps/console/internal/app/graph/schema.graphqls @@ -159,6 +159,7 @@ type Query { } type Mutation { + core_setupDefaultEnvironment: Boolean! @isLoggedInAndVerified @hasAccount core_createEnvironment(env: EnvironmentIn!): Environment @isLoggedInAndVerified @hasAccount core_updateEnvironment(env: EnvironmentIn!): Environment @isLoggedInAndVerified @hasAccount core_deleteEnvironment(envName: String!): Boolean! @isLoggedInAndVerified @hasAccount diff --git a/apps/console/internal/app/graph/schema.resolvers.go b/apps/console/internal/app/graph/schema.resolvers.go index ef08ea747..53619178b 100644 --- a/apps/console/internal/app/graph/schema.resolvers.go +++ b/apps/console/internal/app/graph/schema.resolvers.go @@ -7,9 +7,8 @@ package graph import ( "context" "fmt" - "time" - "github.com/kloudlite/api/pkg/errors" + "time" "github.com/kloudlite/api/apps/console/internal/app/graph/generated" "github.com/kloudlite/api/apps/console/internal/app/graph/model" @@ -70,6 +69,19 @@ func (r *importedManagedResourceResolver) OnlineStatus(ctx context.Context, obj }, nil } +// CoreSetupDefaultEnvironment is the resolver for the core_setupDefaultEnvironment field. +func (r *mutationResolver) CoreSetupDefaultEnvironment(ctx context.Context) (bool, error) { + cc, err := toConsoleContext(ctx) + if err != nil { + return false, errors.NewE(err) + } + + if err := r.Domain.SetupDefaultEnvTemplate(cc); err != nil { + return false, errors.NewE(err) + } + return true, nil +} + // CoreCreateEnvironment is the resolver for the core_createEnvironment field. func (r *mutationResolver) CoreCreateEnvironment(ctx context.Context, env entities.Environment) (*entities.Environment, error) { cc, err := toConsoleContext(ctx) @@ -1095,7 +1107,5 @@ func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResol // Query returns generated.QueryResolver implementation. func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} } -type ( - mutationResolver struct{ *Resolver } - queryResolver struct{ *Resolver } -) +type mutationResolver struct{ *Resolver } +type queryResolver struct{ *Resolver } diff --git a/apps/console/internal/app/graph/struct-to-graphql/common-types.graphqls b/apps/console/internal/app/graph/struct-to-graphql/common-types.graphqls index 8b91086d3..bd7f9f743 100644 --- a/apps/console/internal/app/graph/struct-to-graphql/common-types.graphqls +++ b/apps/console/internal/app/graph/struct-to-graphql/common-types.graphqls @@ -181,6 +181,7 @@ type Github__com___kloudlite___operator___apis___crds___v1__Intercept @shareable enabled: Boolean! portMappings: [Github__com___kloudlite___operator___apis___crds___v1__AppInterceptPortMappings!] toDevice: String! + toIPAddr: String } type Github__com___kloudlite___operator___apis___crds___v1__ManagedResourceSpec @shareable { @@ -515,6 +516,7 @@ input Github__com___kloudlite___operator___apis___crds___v1__InterceptIn { enabled: Boolean! portMappings: [Github__com___kloudlite___operator___apis___crds___v1__AppInterceptPortMappingsIn!] toDevice: String! + toIPAddr: String } input Github__com___kloudlite___operator___apis___crds___v1__ManagedResourceSpecIn { diff --git a/apps/console/internal/domain/api.go b/apps/console/internal/domain/api.go index c6e99ab61..0520d0ffa 100644 --- a/apps/console/internal/domain/api.go +++ b/apps/console/internal/domain/api.go @@ -168,6 +168,7 @@ type Domain interface { ListEnvironments(ctx ConsoleContext, search map[string]repos.MatchFilter, pq repos.CursorPagination) (*repos.PaginatedRecord[*entities.Environment], error) GetEnvironment(ctx ConsoleContext, name string) (*entities.Environment, error) + SetupDefaultEnvTemplate(ctx ConsoleContext) error CreateEnvironment(ctx ConsoleContext, env entities.Environment) (*entities.Environment, error) CloneEnvironment(ctx ConsoleContext, args CloneEnvironmentArgs) (*entities.Environment, error) UpdateEnvironment(ctx ConsoleContext, env entities.Environment) (*entities.Environment, error) diff --git a/apps/console/internal/domain/clone-env-template.go b/apps/console/internal/domain/clone-env-template.go new file mode 100644 index 000000000..50033f714 --- /dev/null +++ b/apps/console/internal/domain/clone-env-template.go @@ -0,0 +1,273 @@ +package domain + +import ( + "github.com/kloudlite/api/apps/console/internal/entities" + fc "github.com/kloudlite/api/apps/console/internal/entities/field-constants" + iamT "github.com/kloudlite/api/apps/iam/types" + "github.com/kloudlite/api/common" + "github.com/kloudlite/api/common/fields" + "github.com/kloudlite/api/grpc-interfaces/kloudlite.io/rpc/iam" + "github.com/kloudlite/api/pkg/errors" + "github.com/kloudlite/api/pkg/repos" + t "github.com/kloudlite/api/pkg/types" + crdsv1 "github.com/kloudlite/operator/apis/crds/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type CloneEnvironmentTemplateArgs struct { + SourceAccountName string + SourceEnvName string + DestinationEnvName string + DisplayName string + EnvRoutingMode crdsv1.EnvironmentRoutingMode +} + +func (d *domain) CloneEnvTemplate(ctx ConsoleContext, args CloneEnvironmentTemplateArgs) (*entities.Environment, error) { + if err := d.canPerformActionInAccount(ctx, iamT.CloneEnvironment); err != nil { + return nil, errors.NewE(err) + } + + srcCtx := NewConsoleContext(ctx, "sys-user:console-resource-updater", args.SourceAccountName) + + sourceEnv, err := d.findEnvironment(srcCtx, args.SourceEnvName) + if err != nil { + return nil, errors.NewE(err) + } + + destEnv := &entities.Environment{ + Environment: crdsv1.Environment{ + TypeMeta: sourceEnv.TypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: args.DestinationEnvName, + Namespace: sourceEnv.Namespace, + }, + Spec: crdsv1.EnvironmentSpec{ + TargetNamespace: d.getEnvironmentTargetNamespace(args.DestinationEnvName), + Routing: &crdsv1.EnvironmentRouting{ + Mode: args.EnvRoutingMode, + }, + }, + }, + AccountName: ctx.AccountName, + ClusterName: "", + ResourceMetadata: common.ResourceMetadata{ + DisplayName: args.DisplayName, + CreatedBy: common.CreatedOrUpdatedBy{ + UserId: ctx.UserId, + UserName: ctx.UserName, + UserEmail: ctx.UserEmail, + }, + LastUpdatedBy: common.CreatedOrUpdatedBy{ + UserId: ctx.UserId, + UserName: ctx.UserName, + UserEmail: ctx.UserEmail, + }, + }, + SyncStatus: t.GenSyncStatus(t.SyncActionApply, 0), + } + + if err := d.k8sClient.ValidateObject(ctx, &destEnv.Environment); err != nil { + return nil, errors.NewE(err) + } + + if _, err := d.iamClient.AddMembership(ctx, &iam.AddMembershipIn{ + UserId: string(ctx.UserId), + ResourceType: string(iamT.ResourceEnvironment), + ResourceRef: iamT.NewResourceRef(ctx.AccountName, iamT.ResourceEnvironment, destEnv.Spec.TargetNamespace), + Role: string(iamT.RoleResourceOwner), + }); err != nil { + d.logger.Errorf(err, "error while adding membership") + } + + destEnv, err = d.environmentRepo.Create(ctx, destEnv) + if err != nil { + return nil, errors.NewE(err) + } + + resCtx := ResourceContext{ + ConsoleContext: ctx, + EnvironmentName: destEnv.Name, + } + + filters := repos.Filter{ + fields.AccountName: args.SourceAccountName, + fields.EnvironmentName: args.SourceEnvName, + } + + apps, err := d.appRepo.Find(srcCtx, repos.Query{ + Filter: filters, + Sort: nil, + }) + if err != nil { + return nil, errors.NewE(err) + } + + externalApps, err := d.externalAppRepo.Find(srcCtx, repos.Query{ + Filter: filters, + Sort: nil, + }) + if err != nil { + return nil, errors.NewE(err) + } + + secrets, err := d.secretRepo.Find(srcCtx, repos.Query{ + Filter: d.secretRepo.MergeMatchFilters(filters, map[string]repos.MatchFilter{ + fc.SecretFor: { + MatchType: repos.MatchTypeExact, + Exact: nil, + }, + }), + Sort: nil, + }) + if err != nil { + return nil, errors.NewE(err) + } + + configs, err := d.configRepo.Find(srcCtx, repos.Query{ + Filter: filters, + Sort: nil, + }) + if err != nil { + return nil, errors.NewE(err) + } + + routers, err := d.routerRepo.Find(srcCtx, repos.Query{ + Filter: filters, + Sort: nil, + }) + if err != nil { + return nil, errors.NewE(err) + } + + mresources, err := d.importedMresRepo.Find(srcCtx, repos.Query{ + Filter: filters, + Sort: nil, + }) + if err != nil { + return nil, errors.NewE(err) + } + + resourceMetadata := func(dn string) common.ResourceMetadata { + return common.ResourceMetadata{ + DisplayName: dn, + CreatedBy: common.CreatedOrUpdatedBy{ + UserId: ctx.UserId, + UserName: ctx.UserName, + UserEmail: ctx.UserEmail, + }, + LastUpdatedBy: common.CreatedOrUpdatedBy{ + UserId: ctx.UserId, + UserName: ctx.UserName, + UserEmail: ctx.UserEmail, + }, + } + } + + objectMeta := func(sourceMeta metav1.ObjectMeta, namespace string) metav1.ObjectMeta { + sourceMeta.Namespace = namespace + return sourceMeta + } + + for i := range apps { + appSpec := apps[i].Spec + appSpec.Intercept = nil + if _, err := d.createAndApplyApp(resCtx, &entities.App{ + App: crdsv1.App{ + TypeMeta: apps[i].TypeMeta, + ObjectMeta: objectMeta(apps[i].ObjectMeta, destEnv.Spec.TargetNamespace), + Spec: appSpec, + }, + AccountName: ctx.AccountName, + EnvironmentName: destEnv.Name, + ResourceMetadata: resourceMetadata(apps[i].DisplayName), + SyncStatus: t.GenSyncStatus(t.SyncActionApply, 0), + }); err != nil { + return nil, err + } + } + + for i := range externalApps { + externalAppSpec := externalApps[i].Spec + externalAppSpec.Intercept = nil + if _, err := d.createAndApplyExternalApp(resCtx, &entities.ExternalApp{ + ExternalApp: crdsv1.ExternalApp{ + TypeMeta: externalApps[i].TypeMeta, + ObjectMeta: objectMeta(externalApps[i].ObjectMeta, destEnv.Spec.TargetNamespace), + Spec: externalAppSpec, + }, + AccountName: ctx.AccountName, + EnvironmentName: destEnv.Name, + ResourceMetadata: resourceMetadata(externalApps[i].DisplayName), + SyncStatus: t.GenSyncStatus(t.SyncActionApply, 0), + }); err != nil { + return nil, err + } + } + + for i := range secrets { + if _, err := d.createAndApplySecret(resCtx, &entities.Secret{ + Secret: corev1.Secret{ + TypeMeta: secrets[i].TypeMeta, + ObjectMeta: objectMeta(secrets[i].ObjectMeta, destEnv.Spec.TargetNamespace), + Immutable: secrets[i].Immutable, + Data: secrets[i].Data, + StringData: secrets[i].StringData, + Type: secrets[i].Type, + }, + AccountName: ctx.AccountName, + EnvironmentName: destEnv.Name, + ResourceMetadata: resourceMetadata(secrets[i].DisplayName), + }); err != nil { + return nil, err + } + } + + for i := range configs { + if _, err := d.createAndApplyConfig(resCtx, &entities.Config{ + ConfigMap: corev1.ConfigMap{ + TypeMeta: configs[i].TypeMeta, + ObjectMeta: objectMeta(configs[i].ObjectMeta, destEnv.Spec.TargetNamespace), + Immutable: configs[i].Immutable, + Data: configs[i].Data, + BinaryData: configs[i].BinaryData, + }, + AccountName: ctx.AccountName, + EnvironmentName: destEnv.Name, + ResourceMetadata: resourceMetadata(configs[i].DisplayName), + }); err != nil { + return nil, err + } + } + + for i := range routers { + if _, err := d.createAndApplyRouter(resCtx, &entities.Router{ + Router: crdsv1.Router{ + TypeMeta: routers[i].TypeMeta, + ObjectMeta: objectMeta(routers[i].ObjectMeta, destEnv.Spec.TargetNamespace), + Spec: routers[i].Spec, + Enabled: routers[i].Enabled, + }, + AccountName: ctx.AccountName, + EnvironmentName: destEnv.Name, + ResourceMetadata: resourceMetadata(routers[i].DisplayName), + }); err != nil { + return nil, err + } + } + + for i := range mresources { + if _, err := d.createAndApplyImportedManagedResource(resCtx, CreateAndApplyImportedManagedResourceArgs{ + ImportedManagedResourceName: mresources[i].Name, + ManagedResourceRefID: mresources[i].ManagedResourceRef.ID, + }); err != nil { + return nil, err + } + } + + // if err := d.syncImagePullSecretsToEnvironment(ctx, args.DestinationEnvName); err != nil { + // return nil, err + // } + + return destEnv, nil +} diff --git a/apps/console/internal/domain/environment.go b/apps/console/internal/domain/environment.go index cb813d269..741b6a539 100644 --- a/apps/console/internal/domain/environment.go +++ b/apps/console/internal/domain/environment.go @@ -2,7 +2,6 @@ package domain import ( "fmt" - "strings" "github.com/kloudlite/api/common/fields" crdsv1 "github.com/kloudlite/operator/apis/crds/v1" @@ -83,6 +82,7 @@ func (d *domain) getClusterAttachedToEnvironment(ctx K8sContext, name string) (* if env == nil { return nil, errors.Newf("no cluster attached to this environment") } + return &env.ClusterName, nil } @@ -134,30 +134,56 @@ func (d *domain) findEnvironmentByTargetNs(ctx ConsoleContext, targetNs string) return w, nil } +func (d *domain) SetupDefaultEnvTemplate(ctx ConsoleContext) error { + if d.envVars.DefaultEnvTemplateAccountName == "" && d.envVars.DefaultEnvTemplateName == "" { + return nil + } + + if _, err := d.CloneEnvTemplate(ctx, CloneEnvironmentTemplateArgs{ + SourceAccountName: d.envVars.DefaultEnvTemplateAccountName, + SourceEnvName: d.envVars.DefaultEnvTemplateName, + DestinationEnvName: d.envVars.DefaultEnvTemplateName, + DisplayName: "Default Environment", + EnvRoutingMode: crdsv1.EnvironmentRoutingModePublic, + }); err != nil { + return errors.NewE(err) + } + + return nil +} + func (d *domain) CreateEnvironment(ctx ConsoleContext, env entities.Environment) (*entities.Environment, error) { if err := d.canPerformActionInAccount(ctx, iamT.CreateEnvironment); err != nil { return nil, errors.NewE(err) } - if strings.TrimSpace(env.ClusterName) == "" { - return nil, fmt.Errorf("clustername must be set while creating environments") + dEnvTempName := d.envVars.DefaultEnvTemplateName + if dEnvTempName != "" && dEnvTempName == env.Name { + return nil, fmt.Errorf("name already reserved by default environment template") } - ownedBy, err := d.infraSvc.GetByokClusterOwnedBy(ctx, ports.IsClusterLabelsIn{ - UserId: string(ctx.UserId), - UserEmail: ctx.UserEmail, - UserName: ctx.UserName, - AccountName: ctx.AccountName, - ClusterName: env.ClusterName, - }) - if err != nil { - return nil, errors.NewE(err) - } + if env.ClusterName != "" { + ownedBy, err := d.infraSvc.GetByokClusterOwnedBy(ctx, ports.IsClusterLabelsIn{ + UserId: string(ctx.UserId), + UserEmail: ctx.UserEmail, + UserName: ctx.UserName, + AccountName: ctx.AccountName, + ClusterName: env.ClusterName, + }) + if err != nil { + return nil, errors.NewE(err) + } + + if ownedBy != "" && ownedBy != string(ctx.UserId) { + return nil, fmt.Errorf("it's owned cluster, but you are not the owner") + } + + if env.Labels == nil { + env.Labels = map[string]string{} + } - if ownedBy != "" && ownedBy != string(ctx.UserId) { - return nil, fmt.Errorf("it's owned cluster, but you are not the owner") + env.Labels[constants.ClusterLabelOwnedBy] = string(ctx.UserId) } - env.Labels[constants.ClusterLabelOwnedBy] = string(ctx.UserId) env.EnsureGVK() if err := d.k8sClient.ValidateObject(ctx, &env.Environment); err != nil { diff --git a/apps/console/internal/domain/resource-mapping.go b/apps/console/internal/domain/resource-mapping.go index f8a51a870..769943fdc 100644 --- a/apps/console/internal/domain/resource-mapping.go +++ b/apps/console/internal/domain/resource-mapping.go @@ -31,7 +31,7 @@ func (d *domain) upsertEnvironmentResourceMapping(ctx ResourceContext, res resou if err != nil { return nil, errors.NewE(err) } - if clusterName == nil { + if clusterName == nil || *clusterName == "" { // silent exit return nil, nil } diff --git a/apps/console/internal/env/env.go b/apps/console/internal/env/env.go index b119c4e5a..c14d88b54 100644 --- a/apps/console/internal/env/env.go +++ b/apps/console/internal/env/env.go @@ -33,6 +33,9 @@ type Env struct { IsDev bool KubernetesApiProxy string `env:"KUBERNETES_API_PROXY" default:"localhost:8080"` + + DefaultEnvTemplateAccountName string `env:"DEFAULT_ENV_TEMPLATE_ACCOUNT_NAME"` + DefaultEnvTemplateName string `env:"DEFAULT_ENV_TEMPLATE_NAME"` } func LoadEnv() (*Env, error) { diff --git a/apps/gateway/supergraph.yml b/apps/gateway/supergraph.yml index 1f403cb6b..c13e1a89a 100644 --- a/apps/gateway/supergraph.yml +++ b/apps/gateway/supergraph.yml @@ -24,7 +24,7 @@ subgraphs: routing_url: http://infra-api:3000/query schema: file: ./schemas/infra-api.schema - iot-console-api: - routing_url: http://iot-console-api:3000/query - schema: - file: ./schemas/iot-console-api.schema + # iot-console-api: + # routing_url: http://iot-console-api:3000/query + # schema: + # file: ./schemas/iot-console-api.schema