diff --git a/apps/console/internal/domain/environment.go b/apps/console/internal/domain/environment.go index 669b8d098..f9abf7d67 100644 --- a/apps/console/internal/domain/environment.go +++ b/apps/console/internal/domain/environment.go @@ -187,7 +187,7 @@ func (d *domain) CloneEnvironment(ctx ConsoleContext, projectName string, source }, Spec: crdsv1.EnvironmentSpec{ ProjectName: projectName, - TargetNamespace: fmt.Sprintf("env-%s", destinationEnvName), + TargetNamespace: d.getEnvironmentTargetNamespace(projectName, destinationEnvName), Routing: &crdsv1.EnvironmentRouting{ Mode: envRoutingMode, }, @@ -279,7 +279,7 @@ func (d *domain) CloneEnvironment(ctx ConsoleContext, projectName string, source resourceMetadata := func(dn string) common.ResourceMetadata { return common.ResourceMetadata{ - DisplayName: fmt.Sprintf("clone of %s", dn), + DisplayName: dn, CreatedBy: common.CreatedOrUpdatedBy{ UserId: ctx.UserId, UserName: ctx.UserName, diff --git a/apps/infra/internal/app/graph/generated/generated.go b/apps/infra/internal/app/graph/generated/generated.go index fa3596a7e..ad27988a4 100644 --- a/apps/infra/internal/app/graph/generated/generated.go +++ b/apps/infra/internal/app/graph/generated/generated.go @@ -969,6 +969,7 @@ type ComplexityRoot struct { InfraDeleteHelmRelease func(childComplexity int, clusterName string, releaseName string) int InfraDeleteNodePool func(childComplexity int, clusterName string, poolName string) int InfraDeleteProviderSecret func(childComplexity int, secretName string) int + InfraDeletePv func(childComplexity int, clusterName string, pvName string) int InfraUpdateCluster func(childComplexity int, cluster entities.Cluster) int InfraUpdateClusterManagedService func(childComplexity int, clusterName string, service entities.ClusterManagedService) int InfraUpdateDomainEntry func(childComplexity int, domainEntry entities.DomainEntry) int @@ -1286,6 +1287,7 @@ type MutationResolver interface { InfraCreateHelmRelease(ctx context.Context, clusterName string, release entities.HelmRelease) (*entities.HelmRelease, error) InfraUpdateHelmRelease(ctx context.Context, clusterName string, release entities.HelmRelease) (*entities.HelmRelease, error) InfraDeleteHelmRelease(ctx context.Context, clusterName string, releaseName string) (bool, error) + InfraDeletePv(ctx context.Context, clusterName string, pvName string) (bool, error) } type NamespaceResolver interface { CreationTime(ctx context.Context, obj *entities.Namespace) (string, error) @@ -5331,6 +5333,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.InfraDeleteProviderSecret(childComplexity, args["secretName"].(string)), true + case "Mutation.infra_deletePV": + if e.complexity.Mutation.InfraDeletePv == nil { + break + } + + args, err := ec.field_Mutation_infra_deletePV_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.InfraDeletePv(childComplexity, args["clusterName"].(string), args["pvName"].(string)), true + case "Mutation.infra_updateCluster": if e.complexity.Mutation.InfraUpdateCluster == nil { break @@ -6894,6 +6908,8 @@ type Mutation { infra_createHelmRelease(clusterName: String!, release: HelmReleaseIn!): HelmRelease @isLoggedInAndVerified @hasAccount infra_updateHelmRelease(clusterName: String!, release: HelmReleaseIn!): HelmRelease @isLoggedInAndVerified @hasAccount infra_deleteHelmRelease(clusterName: String!, releaseName: String!): Boolean! @isLoggedInAndVerified @hasAccount + + infra_deletePV(clusterName: String!, pvName: String!): Boolean! @isLoggedInAndVerified @hasAccount } type EncodedValue { @@ -8968,6 +8984,30 @@ func (ec *executionContext) field_Mutation_infra_deleteNodePool_args(ctx context return args, nil } +func (ec *executionContext) field_Mutation_infra_deletePV_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["clusterName"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clusterName")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["clusterName"] = arg0 + var arg1 string + if tmp, ok := rawArgs["pvName"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("pvName")) + arg1, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["pvName"] = arg1 + return args, nil +} + func (ec *executionContext) field_Mutation_infra_deleteProviderSecret_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -35802,6 +35842,87 @@ func (ec *executionContext) fieldContext_Mutation_infra_deleteHelmRelease(ctx co return fc, nil } +func (ec *executionContext) _Mutation_infra_deletePV(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_infra_deletePV(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().InfraDeletePv(rctx, fc.Args["clusterName"].(string), fc.Args["pvName"].(string)) + } + 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_infra_deletePV(ctx 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") + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_infra_deletePV_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return + } + return fc, nil +} + func (ec *executionContext) _Namespace_accountName(ctx context.Context, field graphql.CollectedField, obj *entities.Namespace) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Namespace_accountName(ctx, field) if err != nil { @@ -57154,6 +57275,15 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) return ec._Mutation_infra_deleteHelmRelease(ctx, field) }) + if out.Values[i] == graphql.Null { + invalids++ + } + case "infra_deletePV": + + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_infra_deletePV(ctx, field) + }) + if out.Values[i] == graphql.Null { invalids++ } @@ -61628,7 +61758,7 @@ func (ec *executionContext) marshalN__TypeKind2string(ctx context.Context, sel a return res } -func (ec *executionContext) unmarshalOAny2interface(ctx context.Context, v interface{}) (interface{}, error) { +func (ec *executionContext) unmarshalOAny2interface(ctx context.Context, v interface{}) (any, error) { if v == nil { return nil, nil } @@ -61636,7 +61766,7 @@ func (ec *executionContext) unmarshalOAny2interface(ctx context.Context, v inter return res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) marshalOAny2interface(ctx context.Context, sel ast.SelectionSet, v interface{}) graphql.Marshaler { +func (ec *executionContext) marshalOAny2interface(ctx context.Context, sel ast.SelectionSet, v any) graphql.Marshaler { if v == nil { return graphql.Null } diff --git a/apps/infra/internal/app/graph/schema.graphqls b/apps/infra/internal/app/graph/schema.graphqls index d6ad9a83c..ac2ab170d 100644 --- a/apps/infra/internal/app/graph/schema.graphqls +++ b/apps/infra/internal/app/graph/schema.graphqls @@ -137,6 +137,8 @@ type Mutation { infra_createHelmRelease(clusterName: String!, release: HelmReleaseIn!): HelmRelease @isLoggedInAndVerified @hasAccount infra_updateHelmRelease(clusterName: String!, release: HelmReleaseIn!): HelmRelease @isLoggedInAndVerified @hasAccount infra_deleteHelmRelease(clusterName: String!, releaseName: String!): Boolean! @isLoggedInAndVerified @hasAccount + + infra_deletePV(clusterName: String!, pvName: String!): Boolean! @isLoggedInAndVerified @hasAccount } type EncodedValue { diff --git a/apps/infra/internal/app/graph/schema.resolvers.go b/apps/infra/internal/app/graph/schema.resolvers.go index 13b9059cd..482cb1712 100644 --- a/apps/infra/internal/app/graph/schema.resolvers.go +++ b/apps/infra/internal/app/graph/schema.resolvers.go @@ -7,7 +7,6 @@ package graph import ( "context" "encoding/base64" - "github.com/kloudlite/api/apps/infra/internal/app/graph/generated" "github.com/kloudlite/api/apps/infra/internal/app/graph/model" "github.com/kloudlite/api/apps/infra/internal/domain" @@ -226,6 +225,19 @@ func (r *mutationResolver) InfraDeleteHelmRelease(ctx context.Context, clusterNa return true, nil } +// InfraDeletePv is the resolver for the infra_deletePV field. +func (r *mutationResolver) InfraDeletePv(ctx context.Context, clusterName string, pvName string) (bool, error) { + ictx, err := toInfraContext(ctx) + if err != nil { + return false, errors.NewE(err) + } + + if err := r.Domain.DeletePV(ictx, clusterName, pvName); err != nil { + return false, errors.NewE(err) + } + return true, nil +} + // InfraCheckNameAvailability is the resolver for the infra_checkNameAvailability field. func (r *queryResolver) InfraCheckNameAvailability(ctx context.Context, resType domain.ResType, clusterName *string, name string) (*domain.CheckNameAvailabilityOutput, error) { ictx, err := toInfraContext(ctx) diff --git a/apps/infra/internal/domain/api.go b/apps/infra/internal/domain/api.go index 7a8295220..71b513a7d 100644 --- a/apps/infra/internal/domain/api.go +++ b/apps/infra/internal/domain/api.go @@ -133,6 +133,7 @@ type Domain interface { GetPV(ctx InfraContext, clusterName string, pvName string) (*entities.PersistentVolume, error) OnPVUpdateMessage(ctx InfraContext, clusterName string, pv entities.PersistentVolume, status types.ResourceStatus, opts UpdateAndDeleteOpts) error OnPVDeleteMessage(ctx InfraContext, clusterName string, pv entities.PersistentVolume) error + DeletePV(ctx InfraContext, clusterName string, pvName string) error OnIngressUpdateMessage(ctx InfraContext, clusterName string, ingress networkingv1.Ingress, status types.ResourceStatus, opts UpdateAndDeleteOpts) error OnIngressDeleteMessage(ctx InfraContext, clusterName string, ingress networkingv1.Ingress) error diff --git a/apps/infra/internal/domain/clusters.go b/apps/infra/internal/domain/clusters.go index a6e1cfe92..325688c4a 100644 --- a/apps/infra/internal/domain/clusters.go +++ b/apps/infra/internal/domain/clusters.go @@ -405,6 +405,27 @@ func (d *domain) DeleteCluster(ctx InfraContext, name string) error { return errors.NewE(err) } + filter := repos.Filter{ + fields.AccountName: ctx.AccountName, + fields.ClusterName: name, + } + + npCount, err := d.nodePoolRepo.Count(ctx, filter) + if err != nil { + return errors.NewE(err) + } + if npCount != 0 { + return errors.Newf("delete nodepool first, aborting cluster deletion") + } + + pvCount, err := d.pvRepo.Count(ctx, filter) + if err != nil { + return errors.NewE(err) + } + if pvCount != 0 { + return errors.Newf("delete pvs first, aborting cluster deletion") + } + ucluster, err := d.clusterRepo.Patch( ctx, repos.Filter{ diff --git a/apps/infra/internal/domain/pv.go b/apps/infra/internal/domain/pv.go index 2531435dd..b01e438bf 100644 --- a/apps/infra/internal/domain/pv.go +++ b/apps/infra/internal/domain/pv.go @@ -1,7 +1,9 @@ package domain import ( + iamT "github.com/kloudlite/api/apps/iam/types" "github.com/kloudlite/api/apps/infra/internal/entities" + "github.com/kloudlite/api/common" "github.com/kloudlite/api/common/fields" "github.com/kloudlite/api/pkg/errors" "github.com/kloudlite/api/pkg/repos" @@ -35,6 +37,29 @@ func (d *domain) ListPVs(ctx InfraContext, clusterName string, search map[string return d.pvRepo.FindPaginated(ctx, d.nodePoolRepo.MergeMatchFilters(filter, search), pagination) } +func (d *domain) DeletePV(ctx InfraContext, clusterName string, pvName string) error { + // FIXME: (IAM role binding for DeletePV) + if err := d.canPerformActionInAccount(ctx, iamT.DeleteNodepool); err != nil { + return errors.NewE(err) + } + + upv, err := d.pvRepo.Patch( + ctx, + repos.Filter{ + fields.ClusterName: clusterName, + fields.AccountName: ctx.AccountName, + fields.MetadataName: pvName, + }, + common.PatchForMarkDeletion(), + ) + if err != nil { + return errors.NewE(err) + } + + d.resourceEventPublisher.PublishResourceEvent(ctx, clusterName, ResourceTypeNodePool, upv.Name, PublishUpdate) + return d.resDispatcher.DeleteFromTargetCluster(ctx, clusterName, &upv.PersistentVolume) +} + // OnPVDeleteMessage implements Domain. func (d *domain) OnPVDeleteMessage(ctx InfraContext, clusterName string, pv entities.PersistentVolume) error { if err := d.pvRepo.DeleteOne(ctx, repos.Filter{