From 30cea9a6ec31523032b5d8edb6a2f9a09c907792 Mon Sep 17 00:00:00 2001 From: Calvinaud Date: Fri, 6 Sep 2024 10:04:38 +0200 Subject: [PATCH] Seperate call to retrieve namespace list and retrieve KubeObject for only a single namespace (#4680) * Create a seperate function to get Namespace in subscriber Signed-off-by: Calvin Audier * Update graphql server to include KubeNamespace call (model not generated) Signed-off-by: Calvin Audier * Update graphqls to include KubeNamespace model Update objectmodel and subscriber to incldue KubeNamespace Signed-off-by: Calvin Audier * Fix issue in graphqls and adding KubeNamespace type. Regenerating model with gqlgen. Signed-off-by: Calvin Audier * Update model to includude missing field in KubeNamespaceData Rename getKubeNamespace function that didn't match graphqls operation in Subscription Add missing function for the graphql server to retrieve list of namespace Signed-off-by: Calvin Audier * Add remaining function in k8s pkg and requests to return list of namespace Signed-off-by: Calvin Audier * Update graphql/subscriber to transform KubeObject to not be a array since subscriber will only return one element. Signed-off-by: Calvin Audier * Update web server to seperate call for KubeObject and KubeNamespace. Signed-off-by: Calvin Audier * Fix import with goimports Signed-off-by: Calvin Audier * Reverting upgrade of webpack-dev-server so it's compatible with github workflow Signed-off-by: Calvin Audier * Run gofmt with correct version. Signed-off-by: Calvin Audier * Bumping ubi-minimal:8.8 to 8.10 to fix some HIGH CVE severity detected by trivy. Signed-off-by: Calvin Audier * Updating chaos_infrastructure mock to include KubeNamespace. Fix a comment Signed-off-by: Calvin Audier * Fix issue from Codacy Removing some unused code Signed-off-by: Calvin Audier * Fix codacy issue: - Add some trailing comma - Trying transform subscription function to resolve "non-arrow functions are forbidden" - Add null check Signed-off-by: Calvin Audier * Continue fix Codacy issue - Adding trailing comma - Re-ordering by alphabetic order some parameters Signed-off-by: Calvin Audier --------- Signed-off-by: Calvin Audier Signed-off-by: Calvinaud Co-authored-by: Namkyu Park <53862866+namkyu1999@users.noreply.github.com> Co-authored-by: Saranya Jena Signed-off-by: andoriyaprashant --- .../shared/chaos_infrastructure.graphqls | 74 +- .../graph/chaos_infrastructure.resolvers.go | 23 + .../server/graph/generated/generated.go | 653 +++++++++++++++++- .../graphql/server/graph/model/models_gen.go | 40 +- .../pkg/chaos_experiment/handler/handler.go | 28 +- .../model/mocks/service.go | 5 + .../pkg/chaos_infrastructure/service.go | 28 +- .../server/pkg/chaos_infrastructure/types.go | 4 + .../graphql/server/pkg/data-store/store.go | 2 + chaoscenter/subscriber/pkg/k8s/defination.go | 5 +- chaoscenter/subscriber/pkg/k8s/objects.go | 118 +++- .../subscriber/pkg/requests/webhook.go | 15 + .../subscriber/pkg/types/kubeobject.go | 11 + chaoscenter/web/Dockerfile | 2 +- .../api/core/infrastructures/getKubeObject.ts | 58 +- .../TargetApplicationTab.tsx | 62 +- .../controllers/TargetApplicationTab/types.ts | 5 +- .../TargetApplication/TargetApplication.tsx | 31 +- chaoscenter/web/yarn.lock | 88 ++- 19 files changed, 1098 insertions(+), 154 deletions(-) diff --git a/chaoscenter/graphql/definitions/shared/chaos_infrastructure.graphqls b/chaoscenter/graphql/definitions/shared/chaos_infrastructure.graphqls index d0c2b4b5a5c..0ffc9552666 100644 --- a/chaoscenter/graphql/definitions/shared/chaos_infrastructure.graphqls +++ b/chaoscenter/graphql/definitions/shared/chaos_infrastructure.graphqls @@ -358,11 +358,11 @@ type KubeObjectResponse { """ Type of the Kubernetes object """ - kubeObj: [KubeObject]! + kubeObj: KubeObject! } """ -KubeObject consists of the namespace and the available resources in the same +KubeObject consists of the available resources in a namespace """ type KubeObject { """ @@ -404,16 +404,75 @@ input KubeObjectRequest { GVR Request """ kubeObjRequest: KubeGVRRequest + """ + Namespace in which the Kubernetes object is present + """ + namespace: String! objectType: String! workloads: [Workload] } +""" +Defines details for fetching Kubernetes namespace data +""" +input KubeNamespaceRequest { + """ + ID of the infra + """ + infraID: ID! +} + +""" +Define name in the infra (not really useful at the moment but maybe we will need other field later) +""" +type KubeNamespace{ + """ + Name of the namespace + """ + name: String! +} + + + input KubeGVRRequest { group: String! version: String! resource: String! } +""" +Response received for querying Kubernetes Namespaces +""" +type KubeNamespaceResponse { + """ + ID of the infra in which the Kubernetes namespace is present + """ + infraID: ID! + """ + List of the Kubernetes namespace + """ + kubeNamespace: [KubeNamespace]! +} + +""" +Defines the details of Kubernetes namespace +""" +input KubeNamespaceData { + """ + Unique request ID for fetching Kubernetes namespace details + """ + requestID: ID! + """ + ID of the infra in which the Kubernetes namespace is present + """ + infraID: InfraIdentity! + """ + List of KubeNamespace return by subscriber + """ + kubeNamespace: String! +} + + """ Defines the details of Kubernetes object """ @@ -638,6 +697,12 @@ extend type Mutation { """ # authorized directive not required kubeObj(request: KubeObjectData!): String! + + """ + Receives kubernetes namespace data from subscriber + """ + # authorized directive not required + kubeNamespace(request: KubeNamespaceData!): String! } extend type Subscription { @@ -663,4 +728,9 @@ extend type Subscription { Returns a kubernetes object given an input """ getKubeObject(request: KubeObjectRequest!): KubeObjectResponse! + + """ + Returns a kubernetes namespaces given an input + """ + getKubeNamespace(request: KubeNamespaceRequest!): KubeNamespaceResponse! } diff --git a/chaoscenter/graphql/server/graph/chaos_infrastructure.resolvers.go b/chaoscenter/graphql/server/graph/chaos_infrastructure.resolvers.go index 7e4540509eb..59977440584 100644 --- a/chaoscenter/graphql/server/graph/chaos_infrastructure.resolvers.go +++ b/chaoscenter/graphql/server/graph/chaos_infrastructure.resolvers.go @@ -116,6 +116,11 @@ func (r *mutationResolver) KubeObj(ctx context.Context, request model.KubeObject return r.chaosInfrastructureService.KubeObj(request, *data_store.Store) } +// KubeNamespace is the resolver for the kubeNamespace field. +func (r *mutationResolver) KubeNamespace(ctx context.Context, request model.KubeNamespaceData) (string, error) { + return r.chaosInfrastructureService.KubeNamespace(request, *data_store.Store) +} + // GetInfra is the resolver for the getInfra field. func (r *queryResolver) GetInfra(ctx context.Context, projectID string, infraID string) (*model.Infra, error) { logFields := logrus.Fields{ @@ -350,6 +355,24 @@ func (r *subscriptionResolver) GetKubeObject(ctx context.Context, request model. return kubeObjData, nil } +// GetKubeNamespace is the resolver for the getKubeNamespace field. +func (r *subscriptionResolver) GetKubeNamespace(ctx context.Context, request model.KubeNamespaceRequest) (<-chan *model.KubeNamespaceResponse, error) { + logrus.Print("NEW NAMESPACE REQUEST", request.InfraID) + kubeNamespaceData := make(chan *model.KubeNamespaceResponse) + reqID := uuid.New() + data_store.Store.Mutex.Lock() + data_store.Store.KubeNamespaceData[reqID.String()] = kubeNamespaceData + data_store.Store.Mutex.Unlock() + go func() { + <-ctx.Done() + logrus.Println("Closed KubeNamespace Listener") + delete(data_store.Store.KubeNamespaceData, reqID.String()) + }() + go r.chaosExperimentHandler.GetKubeNamespaceData(reqID.String(), request, *data_store.Store) + + return kubeNamespaceData, nil +} + // Subscription returns generated.SubscriptionResolver implementation. func (r *Resolver) Subscription() generated.SubscriptionResolver { return &subscriptionResolver{r} } diff --git a/chaoscenter/graphql/server/graph/generated/generated.go b/chaoscenter/graphql/server/graph/generated/generated.go index a219cbcb630..8a501d3e4da 100644 --- a/chaoscenter/graphql/server/graph/generated/generated.go +++ b/chaoscenter/graphql/server/graph/generated/generated.go @@ -400,6 +400,15 @@ type ComplexityRoot struct { Version func(childComplexity int) int } + KubeNamespace struct { + Name func(childComplexity int) int + } + + KubeNamespaceResponse struct { + InfraID func(childComplexity int) int + KubeNamespace func(childComplexity int) int + } + KubeObject struct { Data func(childComplexity int) int Namespace func(childComplexity int) int @@ -499,6 +508,7 @@ type ComplexityRoot struct { GenerateSSHKey func(childComplexity int) int GetManifestWithInfraID func(childComplexity int, projectID string, infraID string, accessKey string) int GitopsNotifier func(childComplexity int, clusterInfo model.InfraIdentity, experimentID string) int + KubeNamespace func(childComplexity int, request model.KubeNamespaceData) int KubeObj func(childComplexity int, request model.KubeObjectData) int PodLog func(childComplexity int, request model.PodLog) int RegisterInfra func(childComplexity int, projectID string, request model.RegisterInfraRequest) int @@ -696,10 +706,11 @@ type ComplexityRoot struct { } Subscription struct { - GetInfraEvents func(childComplexity int, projectID string) int - GetKubeObject func(childComplexity int, request model.KubeObjectRequest) int - GetPodLog func(childComplexity int, request model.PodLogRequest) int - InfraConnect func(childComplexity int, request model.InfraIdentity) int + GetInfraEvents func(childComplexity int, projectID string) int + GetKubeNamespace func(childComplexity int, request model.KubeNamespaceRequest) int + GetKubeObject func(childComplexity int, request model.KubeObjectRequest) int + GetPodLog func(childComplexity int, request model.PodLogRequest) int + InfraConnect func(childComplexity int, request model.InfraIdentity) int } UserDetails struct { @@ -729,6 +740,7 @@ type MutationResolver interface { GetManifestWithInfraID(ctx context.Context, projectID string, infraID string, accessKey string) (string, error) PodLog(ctx context.Context, request model.PodLog) (string, error) KubeObj(ctx context.Context, request model.KubeObjectData) (string, error) + KubeNamespace(ctx context.Context, request model.KubeNamespaceData) (string, error) AddChaosHub(ctx context.Context, projectID string, request model.CreateChaosHubRequest) (*model.ChaosHub, error) AddRemoteChaosHub(ctx context.Context, projectID string, request model.CreateRemoteChaosHub) (*model.ChaosHub, error) SaveChaosHub(ctx context.Context, projectID string, request model.CreateChaosHubRequest) (*model.ChaosHub, error) @@ -788,6 +800,7 @@ type SubscriptionResolver interface { InfraConnect(ctx context.Context, request model.InfraIdentity) (<-chan *model.InfraActionResponse, error) GetPodLog(ctx context.Context, request model.PodLogRequest) (<-chan *model.PodLogResponse, error) GetKubeObject(ctx context.Context, request model.KubeObjectRequest) (<-chan *model.KubeObjectResponse, error) + GetKubeNamespace(ctx context.Context, request model.KubeNamespaceRequest) (<-chan *model.KubeNamespaceResponse, error) } type executableSchema struct { @@ -2573,6 +2586,27 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.K8SProbe.Version(childComplexity), true + case "KubeNamespace.name": + if e.complexity.KubeNamespace.Name == nil { + break + } + + return e.complexity.KubeNamespace.Name(childComplexity), true + + case "KubeNamespaceResponse.infraID": + if e.complexity.KubeNamespaceResponse.InfraID == nil { + break + } + + return e.complexity.KubeNamespaceResponse.InfraID(childComplexity), true + + case "KubeNamespaceResponse.kubeNamespace": + if e.complexity.KubeNamespaceResponse.KubeNamespace == nil { + break + } + + return e.complexity.KubeNamespaceResponse.KubeNamespace(childComplexity), true + case "KubeObject.data": if e.complexity.KubeObject.Data == nil { break @@ -3097,6 +3131,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.GitopsNotifier(childComplexity, args["clusterInfo"].(model.InfraIdentity), args["experimentID"].(string)), true + case "Mutation.kubeNamespace": + if e.complexity.Mutation.KubeNamespace == nil { + break + } + + args, err := ec.field_Mutation_kubeNamespace_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.KubeNamespace(childComplexity, args["request"].(model.KubeNamespaceData)), true + case "Mutation.kubeObj": if e.complexity.Mutation.KubeObj == nil { break @@ -4272,6 +4318,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Subscription.GetInfraEvents(childComplexity, args["projectID"].(string)), true + case "Subscription.getKubeNamespace": + if e.complexity.Subscription.GetKubeNamespace == nil { + break + } + + args, err := ec.field_Subscription_getKubeNamespace_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Subscription.GetKubeNamespace(childComplexity, args["request"].(model.KubeNamespaceRequest)), true + case "Subscription.getKubeObject": if e.complexity.Subscription.GetKubeObject == nil { break @@ -4377,6 +4435,8 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputInfraIdentity, ec.unmarshalInputK8SProbeRequest, ec.unmarshalInputKubeGVRRequest, + ec.unmarshalInputKubeNamespaceData, + ec.unmarshalInputKubeNamespaceRequest, ec.unmarshalInputKubeObjectData, ec.unmarshalInputKubeObjectRequest, ec.unmarshalInputKubernetesCMDProbeRequest, @@ -5698,11 +5758,11 @@ type KubeObjectResponse { """ Type of the Kubernetes object """ - kubeObj: [KubeObject]! + kubeObj: KubeObject! } """ -KubeObject consists of the namespace and the available resources in the same +KubeObject consists of the available resources in a namespace """ type KubeObject { """ @@ -5744,16 +5804,75 @@ input KubeObjectRequest { GVR Request """ kubeObjRequest: KubeGVRRequest + """ + Namespace in which the Kubernetes object is present + """ + namespace: String! objectType: String! workloads: [Workload] } +""" +Defines details for fetching Kubernetes namespace data +""" +input KubeNamespaceRequest { + """ + ID of the infra + """ + infraID: ID! +} + +""" +Define name in the infra (not really useful at the moment but maybe we will need other field later) +""" +type KubeNamespace{ + """ + Name of the namespace + """ + name: String! +} + + + input KubeGVRRequest { group: String! version: String! resource: String! } +""" +Response received for querying Kubernetes Namespaces +""" +type KubeNamespaceResponse { + """ + ID of the infra in which the Kubernetes namespace is present + """ + infraID: ID! + """ + List of the Kubernetes namespace + """ + kubeNamespace: [KubeNamespace]! +} + +""" +Defines the details of Kubernetes namespace +""" +input KubeNamespaceData { + """ + Unique request ID for fetching Kubernetes namespace details + """ + requestID: ID! + """ + ID of the infra in which the Kubernetes namespace is present + """ + infraID: InfraIdentity! + """ + List of KubeNamespace return by subscriber + """ + kubeNamespace: String! +} + + """ Defines the details of Kubernetes object """ @@ -5978,6 +6097,12 @@ extend type Mutation { """ # authorized directive not required kubeObj(request: KubeObjectData!): String! + + """ + Receives kubernetes namespace data from subscriber + """ + # authorized directive not required + kubeNamespace(request: KubeNamespaceData!): String! } extend type Subscription { @@ -6003,6 +6128,11 @@ extend type Subscription { Returns a kubernetes object given an input """ getKubeObject(request: KubeObjectRequest!): KubeObjectResponse! + + """ + Returns a kubernetes namespaces given an input + """ + getKubeNamespace(request: KubeNamespaceRequest!): KubeNamespaceResponse! } `, BuiltIn: false}, {Name: "../../../definitions/shared/chaoshub.graphqls", Input: `enum AuthType { @@ -8647,6 +8777,21 @@ func (ec *executionContext) field_Mutation_gitopsNotifier_args(ctx context.Conte return args, nil } +func (ec *executionContext) field_Mutation_kubeNamespace_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 model.KubeNamespaceData + if tmp, ok := rawArgs["request"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("request")) + arg0, err = ec.unmarshalNKubeNamespaceData2githubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐKubeNamespaceData(ctx, tmp) + if err != nil { + return nil, err + } + } + args["request"] = arg0 + return args, nil +} + func (ec *executionContext) field_Mutation_kubeObj_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -9757,6 +9902,21 @@ func (ec *executionContext) field_Subscription_getInfraEvents_args(ctx context.C return args, nil } +func (ec *executionContext) field_Subscription_getKubeNamespace_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 model.KubeNamespaceRequest + if tmp, ok := rawArgs["request"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("request")) + arg0, err = ec.unmarshalNKubeNamespaceRequest2githubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐKubeNamespaceRequest(ctx, tmp) + if err != nil { + return nil, err + } + } + args["request"] = arg0 + return args, nil +} + func (ec *executionContext) field_Subscription_getKubeObject_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -21157,6 +21317,142 @@ func (ec *executionContext) fieldContext_K8SProbe_operation(ctx context.Context, return fc, nil } +func (ec *executionContext) _KubeNamespace_name(ctx context.Context, field graphql.CollectedField, obj *model.KubeNamespace) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeNamespace_name(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.Name, nil + }) + 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.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeNamespace_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeNamespace", + 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) _KubeNamespaceResponse_infraID(ctx context.Context, field graphql.CollectedField, obj *model.KubeNamespaceResponse) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeNamespaceResponse_infraID(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.InfraID, nil + }) + 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.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeNamespaceResponse_infraID(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeNamespaceResponse", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeNamespaceResponse_kubeNamespace(ctx context.Context, field graphql.CollectedField, obj *model.KubeNamespaceResponse) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeNamespaceResponse_kubeNamespace(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.KubeNamespace, nil + }) + 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.([]*model.KubeNamespace) + fc.Result = res + return ec.marshalNKubeNamespace2ᚕᚖgithubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐKubeNamespace(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeNamespaceResponse_kubeNamespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeNamespaceResponse", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext_KubeNamespace_name(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type KubeNamespace", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _KubeObject_namespace(ctx context.Context, field graphql.CollectedField, obj *model.KubeObject) (ret graphql.Marshaler) { fc, err := ec.fieldContext_KubeObject_namespace(ctx, field) if err != nil { @@ -21321,9 +21617,9 @@ func (ec *executionContext) _KubeObjectResponse_kubeObj(ctx context.Context, fie } return graphql.Null } - res := resTmp.([]*model.KubeObject) + res := resTmp.(*model.KubeObject) fc.Result = res - return ec.marshalNKubeObject2ᚕᚖgithubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐKubeObject(ctx, field.Selections, res) + return ec.marshalNKubeObject2ᚖgithubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐKubeObject(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_KubeObjectResponse_kubeObj(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -24108,6 +24404,61 @@ func (ec *executionContext) fieldContext_Mutation_kubeObj(ctx context.Context, f return fc, nil } +func (ec *executionContext) _Mutation_kubeNamespace(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_kubeNamespace(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 ec.resolvers.Mutation().KubeNamespace(rctx, fc.Args["request"].(model.KubeNamespaceData)) + }) + 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.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_kubeNamespace(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 String 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_kubeNamespace_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Mutation_addChaosHub(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Mutation_addChaosHub(ctx, field) if err != nil { @@ -33041,6 +33392,81 @@ func (ec *executionContext) fieldContext_Subscription_getKubeObject(ctx context. return fc, nil } +func (ec *executionContext) _Subscription_getKubeNamespace(ctx context.Context, field graphql.CollectedField) (ret func(ctx context.Context) graphql.Marshaler) { + fc, err := ec.fieldContext_Subscription_getKubeNamespace(ctx, field) + if err != nil { + return nil + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Subscription().GetKubeNamespace(rctx, fc.Args["request"].(model.KubeNamespaceRequest)) + }) + if err != nil { + ec.Error(ctx, err) + return nil + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return nil + } + return func(ctx context.Context) graphql.Marshaler { + select { + case res, ok := <-resTmp.(<-chan *model.KubeNamespaceResponse): + if !ok { + return nil + } + return graphql.WriterFunc(func(w io.Writer) { + w.Write([]byte{'{'}) + graphql.MarshalString(field.Alias).MarshalGQL(w) + w.Write([]byte{':'}) + ec.marshalNKubeNamespaceResponse2ᚖgithubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐKubeNamespaceResponse(ctx, field.Selections, res).MarshalGQL(w) + w.Write([]byte{'}'}) + }) + case <-ctx.Done(): + return nil + } + } +} + +func (ec *executionContext) fieldContext_Subscription_getKubeNamespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Subscription", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "infraID": + return ec.fieldContext_KubeNamespaceResponse_infraID(ctx, field) + case "kubeNamespace": + return ec.fieldContext_KubeNamespaceResponse_kubeNamespace(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type KubeNamespaceResponse", field.Name) + }, + } + 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_Subscription_getKubeNamespace_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _UserDetails_userID(ctx context.Context, field graphql.CollectedField, obj *model.UserDetails) (ret graphql.Marshaler) { fc, err := ec.fieldContext_UserDetails_userID(ctx, field) if err != nil { @@ -36681,6 +37107,74 @@ func (ec *executionContext) unmarshalInputKubeGVRRequest(ctx context.Context, ob return it, nil } +func (ec *executionContext) unmarshalInputKubeNamespaceData(ctx context.Context, obj interface{}) (model.KubeNamespaceData, error) { + var it model.KubeNamespaceData + asMap := map[string]interface{}{} + for k, v := range obj.(map[string]interface{}) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"requestID", "infraID", "kubeNamespace"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "requestID": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("requestID")) + data, err := ec.unmarshalNID2string(ctx, v) + if err != nil { + return it, err + } + it.RequestID = data + case "infraID": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("infraID")) + data, err := ec.unmarshalNInfraIdentity2ᚖgithubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐInfraIdentity(ctx, v) + if err != nil { + return it, err + } + it.InfraID = data + case "kubeNamespace": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("kubeNamespace")) + data, err := ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + it.KubeNamespace = data + } + } + + return it, nil +} + +func (ec *executionContext) unmarshalInputKubeNamespaceRequest(ctx context.Context, obj interface{}) (model.KubeNamespaceRequest, error) { + var it model.KubeNamespaceRequest + asMap := map[string]interface{}{} + for k, v := range obj.(map[string]interface{}) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"infraID"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "infraID": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("infraID")) + data, err := ec.unmarshalNID2string(ctx, v) + if err != nil { + return it, err + } + it.InfraID = data + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputKubeObjectData(ctx context.Context, obj interface{}) (model.KubeObjectData, error) { var it model.KubeObjectData asMap := map[string]interface{}{} @@ -36729,7 +37223,7 @@ func (ec *executionContext) unmarshalInputKubeObjectRequest(ctx context.Context, asMap[k] = v } - fieldsInOrder := [...]string{"infraID", "kubeObjRequest", "objectType", "workloads"} + fieldsInOrder := [...]string{"infraID", "kubeObjRequest", "namespace", "objectType", "workloads"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -36750,6 +37244,13 @@ func (ec *executionContext) unmarshalInputKubeObjectRequest(ctx context.Context, return it, err } it.KubeObjRequest = data + case "namespace": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("namespace")) + data, err := ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + it.Namespace = data case "objectType": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("objectType")) data, err := ec.unmarshalNString2string(ctx, v) @@ -40507,6 +41008,89 @@ func (ec *executionContext) _K8SProbe(ctx context.Context, sel ast.SelectionSet, return out } +var kubeNamespaceImplementors = []string{"KubeNamespace"} + +func (ec *executionContext) _KubeNamespace(ctx context.Context, sel ast.SelectionSet, obj *model.KubeNamespace) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, kubeNamespaceImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("KubeNamespace") + case "name": + out.Values[i] = ec._KubeNamespace_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var kubeNamespaceResponseImplementors = []string{"KubeNamespaceResponse"} + +func (ec *executionContext) _KubeNamespaceResponse(ctx context.Context, sel ast.SelectionSet, obj *model.KubeNamespaceResponse) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, kubeNamespaceResponseImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("KubeNamespaceResponse") + case "infraID": + out.Values[i] = ec._KubeNamespaceResponse_infraID(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "kubeNamespace": + out.Values[i] = ec._KubeNamespaceResponse_kubeNamespace(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var kubeObjectImplementors = []string{"KubeObject"} func (ec *executionContext) _KubeObject(ctx context.Context, sel ast.SelectionSet, obj *model.KubeObject) graphql.Marshaler { @@ -41196,6 +41780,13 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { out.Invalids++ } + case "kubeNamespace": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_kubeNamespace(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } case "addChaosHub": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { return ec._Mutation_addChaosHub(ctx, field) @@ -43099,6 +43690,8 @@ func (ec *executionContext) _Subscription(ctx context.Context, sel ast.Selection return ec._Subscription_getPodLog(ctx, fields[0]) case "getKubeObject": return ec._Subscription_getKubeObject(ctx, fields[0]) + case "getKubeNamespace": + return ec._Subscription_getKubeNamespace(ctx, fields[0]) default: panic("unknown field " + strconv.Quote(fields[0].Name)) } @@ -44401,7 +44994,7 @@ func (ec *executionContext) marshalNInt2int(ctx context.Context, sel ast.Selecti return res } -func (ec *executionContext) marshalNKubeObject2ᚕᚖgithubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐKubeObject(ctx context.Context, sel ast.SelectionSet, v []*model.KubeObject) graphql.Marshaler { +func (ec *executionContext) marshalNKubeNamespace2ᚕᚖgithubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐKubeNamespace(ctx context.Context, sel ast.SelectionSet, v []*model.KubeNamespace) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup isLen1 := len(v) == 1 @@ -44425,7 +45018,7 @@ func (ec *executionContext) marshalNKubeObject2ᚕᚖgithubᚗcomᚋlitmuschaos if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalOKubeObject2ᚖgithubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐKubeObject(ctx, sel, v[i]) + ret[i] = ec.marshalOKubeNamespace2ᚖgithubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐKubeNamespace(ctx, sel, v[i]) } if isLen1 { f(i) @@ -44439,6 +45032,40 @@ func (ec *executionContext) marshalNKubeObject2ᚕᚖgithubᚗcomᚋlitmuschaos return ret } +func (ec *executionContext) unmarshalNKubeNamespaceData2githubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐKubeNamespaceData(ctx context.Context, v interface{}) (model.KubeNamespaceData, error) { + res, err := ec.unmarshalInputKubeNamespaceData(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) unmarshalNKubeNamespaceRequest2githubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐKubeNamespaceRequest(ctx context.Context, v interface{}) (model.KubeNamespaceRequest, error) { + res, err := ec.unmarshalInputKubeNamespaceRequest(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNKubeNamespaceResponse2githubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐKubeNamespaceResponse(ctx context.Context, sel ast.SelectionSet, v model.KubeNamespaceResponse) graphql.Marshaler { + return ec._KubeNamespaceResponse(ctx, sel, &v) +} + +func (ec *executionContext) marshalNKubeNamespaceResponse2ᚖgithubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐKubeNamespaceResponse(ctx context.Context, sel ast.SelectionSet, v *model.KubeNamespaceResponse) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._KubeNamespaceResponse(ctx, sel, v) +} + +func (ec *executionContext) marshalNKubeObject2ᚖgithubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐKubeObject(ctx context.Context, sel ast.SelectionSet, v *model.KubeObject) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._KubeObject(ctx, sel, v) +} + func (ec *executionContext) unmarshalNKubeObjectData2githubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐKubeObjectData(ctx context.Context, v interface{}) (model.KubeObjectData, error) { res, err := ec.unmarshalInputKubeObjectData(ctx, v) return res, graphql.ErrorOnPath(ctx, err) @@ -45990,11 +46617,11 @@ func (ec *executionContext) unmarshalOKubeGVRRequest2ᚖgithubᚗcomᚋlitmuscha return &res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) marshalOKubeObject2ᚖgithubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐKubeObject(ctx context.Context, sel ast.SelectionSet, v *model.KubeObject) graphql.Marshaler { +func (ec *executionContext) marshalOKubeNamespace2ᚖgithubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐKubeNamespace(ctx context.Context, sel ast.SelectionSet, v *model.KubeNamespace) graphql.Marshaler { if v == nil { return graphql.Null } - return ec._KubeObject(ctx, sel, v) + return ec._KubeNamespace(ctx, sel, v) } func (ec *executionContext) marshalOKubernetesCMDProbe2ᚖgithubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐKubernetesCMDProbe(ctx context.Context, sel ast.SelectionSet, v *model.KubernetesCMDProbe) graphql.Marshaler { diff --git a/chaoscenter/graphql/server/graph/model/models_gen.go b/chaoscenter/graphql/server/graph/model/models_gen.go index b37057af075..9734945dfb1 100644 --- a/chaoscenter/graphql/server/graph/model/models_gen.go +++ b/chaoscenter/graphql/server/graph/model/models_gen.go @@ -1159,7 +1159,37 @@ type KubeGVRRequest struct { Resource string `json:"resource"` } -// KubeObject consists of the namespace and the available resources in the same +// Define name in the infra (not really useful at the moment but maybe we will need other field later) +type KubeNamespace struct { + // Name of the namespace + Name string `json:"name"` +} + +// Defines the details of Kubernetes namespace +type KubeNamespaceData struct { + // Unique request ID for fetching Kubernetes namespace details + RequestID string `json:"requestID"` + // ID of the infra in which the Kubernetes namespace is present + InfraID *InfraIdentity `json:"infraID"` + // List of KubeNamespace return by subscriber + KubeNamespace string `json:"kubeNamespace"` +} + +// Defines details for fetching Kubernetes namespace data +type KubeNamespaceRequest struct { + // ID of the infra + InfraID string `json:"infraID"` +} + +// Response received for querying Kubernetes Namespaces +type KubeNamespaceResponse struct { + // ID of the infra in which the Kubernetes namespace is present + InfraID string `json:"infraID"` + // List of the Kubernetes namespace + KubeNamespace []*KubeNamespace `json:"kubeNamespace"` +} + +// KubeObject consists of the available resources in a namespace type KubeObject struct { // Namespace of the resource Namespace string `json:"namespace"` @@ -1183,8 +1213,10 @@ type KubeObjectRequest struct { InfraID string `json:"infraID"` // GVR Request KubeObjRequest *KubeGVRRequest `json:"kubeObjRequest,omitempty"` - ObjectType string `json:"objectType"` - Workloads []*Workload `json:"workloads,omitempty"` + // Namespace in which the Kubernetes object is present + Namespace string `json:"namespace"` + ObjectType string `json:"objectType"` + Workloads []*Workload `json:"workloads,omitempty"` } // Response received for querying Kubernetes Object @@ -1192,7 +1224,7 @@ type KubeObjectResponse struct { // ID of the infra in which the Kubernetes object is present InfraID string `json:"infraID"` // Type of the Kubernetes object - KubeObj []*KubeObject `json:"kubeObj"` + KubeObj *KubeObject `json:"kubeObj"` } // Defines the CMD probe properties diff --git a/chaoscenter/graphql/server/pkg/chaos_experiment/handler/handler.go b/chaoscenter/graphql/server/pkg/chaos_experiment/handler/handler.go index c2ff92baa35..ee51be6d05f 100644 --- a/chaoscenter/graphql/server/pkg/chaos_experiment/handler/handler.go +++ b/chaoscenter/graphql/server/pkg/chaos_experiment/handler/handler.go @@ -1234,7 +1234,33 @@ func (c *ChaosExperimentHandler) GetKubeObjData(reqID string, kubeObject model.K } else if reqChan, ok := r.KubeObjectData[reqID]; ok { resp := model.KubeObjectResponse{ InfraID: kubeObject.InfraID, - KubeObj: []*model.KubeObject{}, + KubeObj: &model.KubeObject{}, + } + reqChan <- &resp + close(reqChan) + } +} + +func (c *ChaosExperimentHandler) GetKubeNamespaceData(reqID string, kubeNamespace model.KubeNamespaceRequest, r store.StateData) { + reqType := "namespace" + data, err := json.Marshal(kubeNamespace) + if err != nil { + logrus.Print("ERROR WHILE MARSHALLING POD DETAILS") + } + externalData := string(data) + payload := model.InfraActionResponse{ + Action: &model.ActionPayload{ + RequestID: reqID, + RequestType: reqType, + ExternalData: &externalData, + }, + } + if clusterChan, ok := r.ConnectedInfra[kubeNamespace.InfraID]; ok { + clusterChan <- &payload + } else if reqChan, ok := r.KubeNamespaceData[reqID]; ok { + resp := model.KubeNamespaceResponse{ + InfraID: kubeNamespace.InfraID, + KubeNamespace: []*model.KubeNamespace{}, } reqChan <- &resp close(reqChan) diff --git a/chaoscenter/graphql/server/pkg/chaos_infrastructure/model/mocks/service.go b/chaoscenter/graphql/server/pkg/chaos_infrastructure/model/mocks/service.go index c20b5242771..fb3ac94bf0f 100644 --- a/chaoscenter/graphql/server/pkg/chaos_infrastructure/model/mocks/service.go +++ b/chaoscenter/graphql/server/pkg/chaos_infrastructure/model/mocks/service.go @@ -83,6 +83,11 @@ func (s *InfraService) KubeObj(request model.KubeObjectData, r store.StateData) return args.String(0), args.Error(1) } +func (s *InfraService) KubeNamespace(request model.KubeNamespaceData, r store.StateData) (string, error) { + args := s.Called(request, r) + return args.String(0), args.Error(1) +} + func (s *InfraService) UpdateInfra(query bson.D, update bson.D) error { args := s.Called(query, update) return args.Error(0) diff --git a/chaoscenter/graphql/server/pkg/chaos_infrastructure/service.go b/chaoscenter/graphql/server/pkg/chaos_infrastructure/service.go index 4548e081615..8c9663d241f 100644 --- a/chaoscenter/graphql/server/pkg/chaos_infrastructure/service.go +++ b/chaoscenter/graphql/server/pkg/chaos_infrastructure/service.go @@ -50,6 +50,7 @@ type Service interface { GetVersionDetails() (*model.InfraVersionDetails, error) QueryServerVersion(ctx context.Context) (*model.ServerVersionResponse, error) PodLog(request model.PodLog, r store.StateData) (string, error) + KubeNamespace(request model.KubeNamespaceData, r store.StateData) (string, error) KubeObj(request model.KubeObjectData, r store.StateData) (string, error) UpdateInfra(query bson.D, update bson.D) error GetDBInfra(infraID string) (dbChaosInfra.ChaosInfra, error) @@ -988,7 +989,7 @@ func (in *infraService) KubeObj(request model.KubeObjectData, r store.StateData) return "", err } if reqChan, ok := r.KubeObjectData[request.RequestID]; ok { - var kubeObjData []*model.KubeObject + var kubeObjData *model.KubeObject err = json.Unmarshal([]byte(request.KubeObj), &kubeObjData) if err != nil { return "", fmt.Errorf("failed to unmarshal kubeObj data %w", err) @@ -1005,6 +1006,31 @@ func (in *infraService) KubeObj(request model.KubeObjectData, r store.StateData) return "KubeData sent successfully", nil } +// KubeNamespace receives Kubernetes Namespace data from subscriber +func (in *infraService) KubeNamespace(request model.KubeNamespaceData, r store.StateData) (string, error) { + _, err := in.VerifyInfra(*request.InfraID) + if err != nil { + log.Print("Error", err) + return "", err + } + if reqChan, ok := r.KubeNamespaceData[request.RequestID]; ok { + var kubeNamespaceData []*model.KubeNamespace + err = json.Unmarshal([]byte(request.KubeNamespace), &kubeNamespaceData) + if err != nil { + return "", fmt.Errorf("failed to unmarshal kubeNamespace data %w", err) + } + + resp := model.KubeNamespaceResponse{ + InfraID: request.InfraID.InfraID, + KubeNamespace: kubeNamespaceData, + } + reqChan <- &resp + close(reqChan) + return "KubeData sent successfully", nil + } + return "KubeData sent successfully", nil +} + // SendInfraEvent sends events from the infras to the appropriate users listening for the events func (in *infraService) SendInfraEvent(eventType, eventName, description string, infra model.Infra, r store.StateData) { newEvent := model.InfraEventResponse{ diff --git a/chaoscenter/graphql/server/pkg/chaos_infrastructure/types.go b/chaoscenter/graphql/server/pkg/chaos_infrastructure/types.go index 8a3d8ead071..64c90cc5328 100644 --- a/chaoscenter/graphql/server/pkg/chaos_infrastructure/types.go +++ b/chaoscenter/graphql/server/pkg/chaos_infrastructure/types.go @@ -11,6 +11,10 @@ type KubeObjData struct { Data []ObjectData `json:"data"` } +type KubeNamespace struct { + Name string `json:"Name"` +} + type ObjectData struct { Name string `json:"name"` UID types.UID `json:"uid"` diff --git a/chaoscenter/graphql/server/pkg/data-store/store.go b/chaoscenter/graphql/server/pkg/data-store/store.go index f101298ebe9..41336b021f7 100644 --- a/chaoscenter/graphql/server/pkg/data-store/store.go +++ b/chaoscenter/graphql/server/pkg/data-store/store.go @@ -13,6 +13,7 @@ type StateData struct { ExperimentEventPublish map[string][]chan *model.ExperimentRun ExperimentLog map[string]chan *model.PodLogResponse KubeObjectData map[string]chan *model.KubeObjectResponse + KubeNamespaceData map[string]chan *model.KubeNamespaceResponse Mutex *sync.Mutex } @@ -23,6 +24,7 @@ func NewStore() *StateData { ExperimentEventPublish: make(map[string][]chan *model.ExperimentRun), ExperimentLog: make(map[string]chan *model.PodLogResponse), KubeObjectData: make(map[string]chan *model.KubeObjectResponse), + KubeNamespaceData: make(map[string]chan *model.KubeNamespaceResponse), Mutex: &sync.Mutex{}, } } diff --git a/chaoscenter/subscriber/pkg/k8s/defination.go b/chaoscenter/subscriber/pkg/k8s/defination.go index 68b2a24883f..2adac87f32c 100644 --- a/chaoscenter/subscriber/pkg/k8s/defination.go +++ b/chaoscenter/subscriber/pkg/k8s/defination.go @@ -18,10 +18,13 @@ type SubscriberK8s interface { CreatePodLog(podLog types.PodLogRequest) (types.PodLog, error) SendPodLogs(infraData map[string]string, podLog types.PodLogRequest) GenerateLogPayload(cid, accessKey, version string, podLog types.PodLogRequest) ([]byte, error) - GetKubernetesObjects(request types.KubeObjRequest) ([]*types.KubeObject, error) + GetKubernetesNamespaces(request types.KubeNamespaceRequest) ([]*types.KubeNamespace, error) + GetKubernetesObjects(request types.KubeObjRequest) (*types.KubeObject, error) GetObjectDataByNamespace(namespace string, dynamicClient dynamic.Interface, resourceType schema.GroupVersionResource) ([]types.ObjectData, error) GenerateKubeObject(cid string, accessKey, version string, kubeobjectrequest types.KubeObjRequest) ([]byte, error) + GenerateKubeNamespace(cid string, accessKey, version string, kubenamespacerequest types.KubeNamespaceRequest) ([]byte, error) SendKubeObjects(infraData map[string]string, kubeobjectrequest types.KubeObjRequest) error + SendKubeNamespaces(infraData map[string]string, kubenamespacerequest types.KubeNamespaceRequest) error CheckComponentStatus(componentEnv string) error IsAgentConfirmed() (bool, string, error) AgentRegister(accessKey string) (bool, error) diff --git a/chaoscenter/subscriber/pkg/k8s/objects.go b/chaoscenter/subscriber/pkg/k8s/objects.go index 143ea9f3785..d23bd51466f 100644 --- a/chaoscenter/subscriber/pkg/k8s/objects.go +++ b/chaoscenter/subscriber/pkg/k8s/objects.go @@ -23,63 +23,72 @@ var ( InfraScope = os.Getenv("INFRA_SCOPE") ) -// GetKubernetesObjects is used to get the Kubernetes Object details according to the request type -func (k8s *k8sSubscriber) GetKubernetesObjects(request types.KubeObjRequest) ([]*types.KubeObject, error) { - conf, err := k8s.GetKubeConfig() - if err != nil { - return nil, err - } - clientset, err := kubernetes.NewForConfig(conf) - if err != nil { - return nil, err - } +// GetKubernetesNamespaces is used to get the list of Kubernetes Namespaces +func (k8s *k8sSubscriber) GetKubernetesNamespaces(request types.KubeNamespaceRequest) ([]*types.KubeNamespace, error) { - resourceType := schema.GroupVersionResource{ - Group: request.KubeGVRRequest.Group, - Version: request.KubeGVRRequest.Version, - Resource: request.KubeGVRRequest.Resource, - } - _, dynamicClient, err := k8s.GetDynamicAndDiscoveryClient() - if err != nil { - return nil, err - } - var ObjData []*types.KubeObject + var namespaceData []*types.KubeNamespace if strings.ToLower(InfraScope) == "namespace" { - dataList, err := k8s.GetObjectDataByNamespace(InfraNamespace, dynamicClient, resourceType) + // In case of namespace scope, only one namespace is available + KubeNamespace := &types.KubeNamespace{ + Name: InfraNamespace, + } + namespaceData = append(namespaceData, KubeNamespace) + } else { + // In case of cluster scope, get all the namespaces + conf, err := k8s.GetKubeConfig() if err != nil { return nil, err } - KubeObj := &types.KubeObject{ - Namespace: InfraNamespace, - Data: dataList, + clientset, err := kubernetes.NewForConfig(conf) + if err != nil { + return nil, err } - ObjData = append(ObjData, KubeObj) - } else { + namespace, err := clientset.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{}) if err != nil { return nil, err } - if len(namespace.Items) > 0 { for _, namespace := range namespace.Items { - podList, err := k8s.GetObjectDataByNamespace(namespace.GetName(), dynamicClient, resourceType) - if err != nil { - return nil, err - } - KubeObj := &types.KubeObject{ - Namespace: namespace.GetName(), - Data: podList, + + KubeNamespace := &types.KubeNamespace{ + Name: namespace.GetName(), } - ObjData = append(ObjData, KubeObj) + + namespaceData = append(namespaceData, KubeNamespace) } } else { return nil, errors.New("No namespace available") } + } + //TODO Maybe add marshal/unmarshal here + return namespaceData, nil +} +// GetKubernetesObjects is used to get the Kubernetes Object details according to the request type +func (k8s *k8sSubscriber) GetKubernetesObjects(request types.KubeObjRequest) (*types.KubeObject, error) { + resourceType := schema.GroupVersionResource{ + Group: request.KubeGVRRequest.Group, + Version: request.KubeGVRRequest.Version, + Resource: request.KubeGVRRequest.Resource, } - kubeData, _ := json.Marshal(ObjData) - var kubeObjects []*types.KubeObject + _, dynamicClient, err := k8s.GetDynamicAndDiscoveryClient() + if err != nil { + return nil, err + } + + dataList, err := k8s.GetObjectDataByNamespace(request.Namespace, dynamicClient, resourceType) + if err != nil { + return nil, err + } + KubeObj := &types.KubeObject{ + Namespace: InfraNamespace, + Data: dataList, + } + + kubeData, _ := json.Marshal(KubeObj) + var kubeObjects *types.KubeObject err = json.Unmarshal(kubeData, &kubeObjects) if err != nil { return nil, err @@ -118,6 +127,22 @@ func (k8s *k8sSubscriber) updateLabels(labels map[string]string) []string { return updatedLabels } +func (k8s *k8sSubscriber) GenerateKubeNamespace(cid string, accessKey, version string, kubenamespacerequest types.KubeNamespaceRequest) ([]byte, error) { + infraID := `{infraID: \"` + cid + `\", version: \"` + version + `\", accessKey: \"` + accessKey + `\"}` + kubeObj, err := k8s.GetKubernetesNamespaces(kubenamespacerequest) + if err != nil { + return nil, err + } + processed, err := k8s.gqlSubscriberServer.MarshalGQLData(kubeObj) + if err != nil { + return nil, err + } + mutation := `{ infraID: ` + infraID + `, requestID:\"` + kubenamespacerequest.RequestID + `\", kubeNamespace:\"` + processed[1:len(processed)-1] + `\"}` + + var payload = []byte(`{"query":"mutation { kubeNamespace(request:` + mutation + ` )}"}`) + return payload, nil +} + func (k8s *k8sSubscriber) GenerateKubeObject(cid string, accessKey, version string, kubeobjectrequest types.KubeObjRequest) ([]byte, error) { infraID := `{infraID: \"` + cid + `\", version: \"` + version + `\", accessKey: \"` + accessKey + `\"}` kubeObj, err := k8s.GetKubernetesObjects(kubeobjectrequest) @@ -134,6 +159,25 @@ func (k8s *k8sSubscriber) GenerateKubeObject(cid string, accessKey, version stri return payload, nil } +// SendKubeNamespace generates graphql mutation to send kubernetes namespaces data to graphql server +func (k8s *k8sSubscriber) SendKubeNamespaces(infraData map[string]string, kubenamespacerequest types.KubeNamespaceRequest) error { + // generate graphql payload + payload, err := k8s.GenerateKubeNamespace(infraData["INFRA_ID"], infraData["ACCESS_KEY"], infraData["VERSION"], kubenamespacerequest) + if err != nil { + logrus.WithError(err).Print("Error while getting KubeObject Data") + return err + } + + body, err := k8s.gqlSubscriberServer.SendRequest(infraData["SERVER_ADDR"], payload) + if err != nil { + logrus.Print(err.Error()) + return err + } + + logrus.Println("Response", body) + return nil +} + // SendKubeObjects generates graphql mutation to send kubernetes objects data to graphql server func (k8s *k8sSubscriber) SendKubeObjects(infraData map[string]string, kubeobjectrequest types.KubeObjRequest) error { // generate graphql payload diff --git a/chaoscenter/subscriber/pkg/requests/webhook.go b/chaoscenter/subscriber/pkg/requests/webhook.go index 1407d6e44f4..cafea2d921a 100644 --- a/chaoscenter/subscriber/pkg/requests/webhook.go +++ b/chaoscenter/subscriber/pkg/requests/webhook.go @@ -120,6 +120,21 @@ func (req *subscriberRequests) RequestProcessor(infraData map[string]string, r t return errors.New("error getting kubernetes object data: " + err.Error()) } } + if strings.Index("kubenamespace kubenamespaces", strings.ToLower(r.Payload.Data.InfraConnect.Action.RequestType)) >= 0 { + KubeNamespaceRequest := types.KubeNamespaceRequest{ + RequestID: r.Payload.Data.InfraConnect.Action.RequestID, + } + + err := json.Unmarshal([]byte(r.Payload.Data.InfraConnect.Action.ExternalData), &KubeNamespaceRequest) + if err != nil { + return errors.New("failed to json unmarshal: " + err.Error()) + } + + err = req.subscriberK8s.SendKubeNamespaces(infraData, KubeNamespaceRequest) + if err != nil { + return errors.New("error getting kubernetes namespace data: " + err.Error()) + } + } if strings.ToLower(r.Payload.Data.InfraConnect.Action.RequestType) == "logs" { podRequest := types.PodLogRequest{ RequestID: r.Payload.Data.InfraConnect.Action.RequestID, diff --git a/chaoscenter/subscriber/pkg/types/kubeobject.go b/chaoscenter/subscriber/pkg/types/kubeobject.go index e862cc0b875..baa917a76e5 100644 --- a/chaoscenter/subscriber/pkg/types/kubeobject.go +++ b/chaoscenter/subscriber/pkg/types/kubeobject.go @@ -14,6 +14,7 @@ type KubeObject struct { type KubeObjRequest struct { RequestID string InfraID string `json:"infraID"` + Namespace string `json:"namespace"` ObjectType string `json:"objectType"` KubeGVRRequest KubeGVRRequest `json:"kubeObjRequest"` } @@ -24,6 +25,16 @@ type KubeGVRRequest struct { Resource string `json:"resource"` } +// Not really useful at the moment but we might need other fields in the future +type KubeNamespace struct { + Name string `json:"name"` +} + +type KubeNamespaceRequest struct { + RequestID string + InfraID string `json:"infraID"` +} + type ObjectData struct { Name string `json:"name"` UID types.UID `json:"uid"` diff --git a/chaoscenter/web/Dockerfile b/chaoscenter/web/Dockerfile index 3c4c117f211..616d4e608a3 100644 --- a/chaoscenter/web/Dockerfile +++ b/chaoscenter/web/Dockerfile @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi8/ubi-minimal:8.8 +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.10 RUN microdnf module enable nginx:1.20 RUN microdnf install nginx RUN microdnf update --refresh --best --noplugins --setopt=install_weak_deps=0 diff --git a/chaoscenter/web/src/api/core/infrastructures/getKubeObject.ts b/chaoscenter/web/src/api/core/infrastructures/getKubeObject.ts index b3129ae1dff..54deef519fc 100644 --- a/chaoscenter/web/src/api/core/infrastructures/getKubeObject.ts +++ b/chaoscenter/web/src/api/core/infrastructures/getKubeObject.ts @@ -12,13 +12,14 @@ export interface KubeObjRequest { infraID: string; objectType: string; kubeObjRequest?: KubeGVRRequest; + namespace: string; }; } export interface KubeObjResponse { getKubeObject: { infraID: string; - kubeObj: Array; + kubeObj: KubeObj; }; } @@ -32,13 +33,30 @@ interface KubeObjData { name: string; } -export function kubeObjectSubscription({ +interface KubeNamespace { + name: string; +} + +export interface KubeNamespaceRequest { + request: { + infraID: string; + }; +} + +export interface KubeNamespaceResponse { + getKubeNamespace: { + infraID: string; + kubeNamespace: Array; + }; +} + +export const kubeObjectSubscription = ({ request, ...options }: GqlAPISubscriptionRequest): GqlAPISubscriptionResponse< KubeObjResponse, KubeObjRequest -> { +> => { const { data, loading, error } = useSubscription( gql` subscription getKubeObject($request: KubeObjectRequest!) { @@ -59,6 +77,7 @@ export function kubeObjectSubscription({ request: { infraID: request.infraID, kubeObjRequest: request.kubeObjRequest, + namespace: request.namespace, objectType: request.objectType } }, @@ -67,4 +86,35 @@ export function kubeObjectSubscription({ ); return { data, loading, error }; -} +}; + +export const kubeNamespaceSubscription = ({ + request, + ...options +}: GqlAPISubscriptionRequest): GqlAPISubscriptionResponse< + KubeNamespaceResponse, + KubeNamespaceRequest +> => { + const { data, loading, error } = useSubscription( + gql` + subscription getKubeNamespace($request: KubeNamespaceRequest!) { + getKubeNamespace(request: $request) { + infraID + kubeNamespace { + name + } + } + } + `, + { + variables: { + request: { + infraID: request.infraID + } + }, + ...options + } + ); + + return { data, loading, error }; +}; diff --git a/chaoscenter/web/src/controllers/TargetApplicationTab/TargetApplicationTab.tsx b/chaoscenter/web/src/controllers/TargetApplicationTab/TargetApplicationTab.tsx index bf7089b2add..059482d2e3b 100644 --- a/chaoscenter/web/src/controllers/TargetApplicationTab/TargetApplicationTab.tsx +++ b/chaoscenter/web/src/controllers/TargetApplicationTab/TargetApplicationTab.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { KubeGVRRequest, kubeObjectSubscription } from '@api/core'; +import { KubeGVRRequest, kubeObjectSubscription, kubeNamespaceSubscription } from '@api/core'; import type { ChaosEngine, FaultData } from '@models'; import { TargetApplicationTab } from '@views/ExperimentCreationFaultConfiguration/Tabs'; import type { AppInfoData, TargetApplicationData } from './types'; @@ -16,17 +16,26 @@ export default function TargetApplicationTabController({ infrastructureID, setFaultData }: TargetApplicationControllerProps): React.ReactElement { - const [appInfoData, setAppInfoData] = React.useState([]); + const [namespaceData, setNamespaceData] = React.useState([]); + const [appInfoData, setAppInfoData] = React.useState({ appLabel: [] }); const [targetApp, setTargetApp] = React.useState({ ...engineCR?.spec?.appinfo }); const [selectedGVR, setSelectedGVR] = React.useState(); - const { data: result, loading } = kubeObjectSubscription({ + const { data: resultNamespace, loading: loadingNamespace } = kubeNamespaceSubscription({ + request: { + infraID: infrastructureID ?? '' + }, + shouldResubscribe: true, + skip: targetApp?.appkind === undefined || selectedGVR === undefined + }); + const { data: resultObject, loading: loadingObject } = kubeObjectSubscription({ shouldResubscribe: true, - skip: targetApp?.appkind === undefined || selectedGVR === undefined, + skip: targetApp?.appns === undefined || targetApp?.appns === '', request: { infraID: infrastructureID ?? '', kubeObjRequest: selectedGVR, + namespace: targetApp?.appns ?? '', objectType: 'kubeobject' } }); @@ -37,51 +46,44 @@ export default function TargetApplicationTabController({ if (data.resource === targetApp?.appkind) { setSelectedGVR({ group: data.group, - version: data.version, - resource: `${data.resource}s` + resource: `${data.resource}s`, + version: data.version }); } }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [targetApp?.appkind]); - /** - * UseEffect to filter the labels according to the namespace provided - * Required to populate the appLabels dropdown - */ React.useEffect(() => { - if (result?.getKubeObject) { - const appInfo: AppInfoData[] = []; - const kubeData = result.getKubeObject.kubeObj; - kubeData.forEach(obj => { - const applabels: string[] = []; - obj.data.forEach(objData => { - if (objData.labels) { - applabels.push(...objData.labels.filter(() => obj.namespace === targetApp?.appns)); - } - }); - /** - * Push these labels corresponding to their namespaces - */ - appInfo.push({ - namespace: obj.namespace, - appLabel: applabels - }); - }); + if (resultNamespace?.getKubeNamespace) { + setNamespaceData(resultNamespace.getKubeNamespace.kubeNamespace.map(data => data.name)); + } + }, [resultNamespace?.getKubeNamespace, targetApp?.appkind]); + React.useEffect(() => { + if (resultObject?.getKubeObject) { + const applabels: string[] = []; + resultObject.getKubeObject.kubeObj.data.forEach(objData => { + if (objData.labels) { + applabels.push(...objData.labels); + } + }); + const appInfo: AppInfoData = { appLabel: applabels }; setAppInfoData(appInfo); } - }, [result?.getKubeObject, targetApp?.appns]); + }, [resultObject?.getKubeObject, targetApp?.appns]); return ( ); } diff --git a/chaoscenter/web/src/controllers/TargetApplicationTab/types.ts b/chaoscenter/web/src/controllers/TargetApplicationTab/types.ts index 85f3123fefc..3fd0c0819a8 100644 --- a/chaoscenter/web/src/controllers/TargetApplicationTab/types.ts +++ b/chaoscenter/web/src/controllers/TargetApplicationTab/types.ts @@ -4,7 +4,10 @@ export interface TargetApplicationData { applabel?: string; } +export interface NamespaceData { + namespace: string[]; +} + export interface AppInfoData { - namespace: string; appLabel: string[]; } diff --git a/chaoscenter/web/src/views/ExperimentCreationFaultConfiguration/Tabs/TargetApplication/TargetApplication.tsx b/chaoscenter/web/src/views/ExperimentCreationFaultConfiguration/Tabs/TargetApplication/TargetApplication.tsx index 0f9bba022b0..048959330cb 100644 --- a/chaoscenter/web/src/views/ExperimentCreationFaultConfiguration/Tabs/TargetApplication/TargetApplication.tsx +++ b/chaoscenter/web/src/views/ExperimentCreationFaultConfiguration/Tabs/TargetApplication/TargetApplication.tsx @@ -7,24 +7,28 @@ import { useStrings } from '@strings'; import type { AppInfoData, TargetApplicationData } from '@controllers/TargetApplicationTab/types'; interface TargetApplicationViewProps { - appInfoData: AppInfoData[]; + appInfoData: AppInfoData; + namespaceData: string[]; targetApp: TargetApplicationData | undefined; setTargetApp: React.Dispatch>; engineCR: ChaosEngine | undefined; setFaultData: React.Dispatch>; // getKubeObjectLazyQueryFunction: LazyQueryFunction; infrastructureID: string | undefined; - loading: boolean; + loadingNamespace: boolean; + loadingObject: boolean; } export default function TargetApplicationTab({ appInfoData, + namespaceData, targetApp, setTargetApp, engineCR, setFaultData, // getKubeObjectLazyQueryFunction, - loading + loadingNamespace, + loadingObject }: TargetApplicationViewProps): React.ReactElement { const { getString } = useStrings(); @@ -36,18 +40,19 @@ export default function TargetApplicationTab({ } function getAppNamespaceItems(): SelectOption[] { - return appInfoData.map(data => ({ - label: data.namespace, - value: data.namespace + if (loadingNamespace) return []; + return namespaceData.map(data => ({ + label: data, + value: data })); } function getAppLabelItems(): SelectOption[] { - if (loading) return []; - const filteredAppInfo = appInfoData.filter(data => data.namespace === targetApp?.appns)[0]; - return filteredAppInfo?.appLabel.map(label => ({ - label: label, - value: label + if (loadingObject) return []; + // const filteredAppInfo = appInfoData.filter(data => data.namespace === targetApp?.appns)[0]; + return appInfoData?.appLabel.map(data => ({ + label: data, + value: data })); } @@ -66,8 +71,8 @@ export default function TargetApplicationTab({ onChange={selectedItem => { setTargetApp({ appkind: selectedItem.label, - appns: '', - applabel: '' + applabel: '', + appns: '' }); if (engineCR?.spec?.appinfo?.appkind !== undefined) engineCR.spec.appinfo.appkind = selectedItem.label; setFaultData(faultData => { diff --git a/chaoscenter/web/yarn.lock b/chaoscenter/web/yarn.lock index 9b27ff4f341..05ba4fc1373 100644 --- a/chaoscenter/web/yarn.lock +++ b/chaoscenter/web/yarn.lock @@ -1051,16 +1051,16 @@ "@types/node" "*" "@types/bonjour@^3.5.9": - version "3.5.10" - resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.10.tgz#0f6aadfe00ea414edc86f5d106357cda9701e275" - integrity sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw== + version "3.5.13" + resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.13.tgz#adf90ce1a105e81dd1f9c61fdc5afda1bfb92956" + integrity sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ== dependencies: "@types/node" "*" "@types/connect-history-api-fallback@^1.3.5": - version "1.3.5" - resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae" - integrity sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw== + version "1.5.4" + resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3" + integrity sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw== dependencies: "@types/express-serve-static-core" "*" "@types/node" "*" @@ -1324,11 +1324,6 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2" integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q== -"@types/mime@*": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.4.tgz#2198ac274de6017b44d941e00261d5bc6a0e0a45" - integrity sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw== - "@types/mime@^1": version "1.3.2" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" @@ -1435,10 +1430,10 @@ "@types/scheduler" "*" csstype "^3.0.2" -"@types/retry@^0.12.0": - version "0.12.1" - resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.1.tgz#d8f1c0d0dc23afad6dc16a9e993a0865774b4065" - integrity sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g== +"@types/retry@0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" + integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== "@types/scheduler@*": version "0.16.2" @@ -1459,9 +1454,9 @@ "@types/node" "*" "@types/serve-index@^1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278" - integrity sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg== + version "1.9.4" + resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.4.tgz#e6ae13d5053cb06ed36392110b4f9a49ac4ec898" + integrity sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug== dependencies: "@types/express" "*" @@ -1474,18 +1469,18 @@ "@types/node" "*" "@types/serve-static@^1.13.10": - version "1.15.5" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.5.tgz#15e67500ec40789a1e8c9defc2d32a896f05b033" - integrity sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ== + version "1.15.7" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714" + integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== dependencies: "@types/http-errors" "*" - "@types/mime" "*" "@types/node" "*" + "@types/send" "*" "@types/sockjs@^0.3.33": - version "0.3.33" - resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f" - integrity sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw== + version "0.3.36" + resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" + integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== dependencies: "@types/node" "*" @@ -4408,9 +4403,9 @@ fs-monkey@1.0.3: integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q== fs-monkey@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.5.tgz#fe450175f0db0d7ea758102e1d84096acb925788" - integrity sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew== + version "1.0.6" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.6.tgz#8ead082953e88d992cf3ff844faa907b26756da2" + integrity sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg== fs.realpath@^1.0.0: version "1.0.0" @@ -4703,9 +4698,9 @@ html-encoding-sniffer@^2.0.1: whatwg-encoding "^1.0.5" html-entities@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.2.tgz#760b404685cb1d794e4f4b744332e3b00dcfe488" - integrity sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ== + version "2.5.2" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f" + integrity sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA== html-escaper@^2.0.0: version "2.0.2" @@ -4947,9 +4942,9 @@ ipaddr.js@1.9.1: integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== ipaddr.js@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0" - integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== + version "2.2.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" + integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== is-arguments@^1.0.4: version "1.1.1" @@ -6491,9 +6486,9 @@ onetime@^5.1.0, onetime@^5.1.2: mimic-fn "^2.1.0" open@^8.0.9: - version "8.4.0" - resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" - integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q== + version "8.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== dependencies: define-lazy-prop "^2.0.0" is-docker "^2.1.1" @@ -6589,11 +6584,11 @@ p-map@^4.0.0: aggregate-error "^3.0.0" p-retry@^4.5.0: - version "4.6.1" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.1.tgz#8fcddd5cdf7a67a0911a9cf2ef0e5df7f602316c" - integrity sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA== + version "4.6.2" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" + integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== dependencies: - "@types/retry" "^0.12.0" + "@types/retry" "0.12.0" retry "^0.13.1" p-try@^1.0.0: @@ -8523,7 +8518,7 @@ webpack-cli@^4.9.0: rechoir "^0.7.0" webpack-merge "^5.7.3" -webpack-dev-middleware@^5.3.1: +webpack-dev-middleware@^5.3.4: version "5.3.4" resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz#eb7b39281cbce10e104eb2b8bf2b63fce49a3517" integrity sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q== @@ -8535,9 +8530,9 @@ webpack-dev-middleware@^5.3.1: schema-utils "^4.0.0" webpack-dev-server@^4.15.1: - version "4.15.1" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz#8944b29c12760b3a45bdaa70799b17cb91b03df7" - integrity sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA== + version "4.15.2" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz#9e0c70a42a012560860adb186986da1248333173" + integrity sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g== dependencies: "@types/bonjour" "^3.5.9" "@types/connect-history-api-fallback" "^1.3.5" @@ -8567,7 +8562,7 @@ webpack-dev-server@^4.15.1: serve-index "^1.9.1" sockjs "^0.3.24" spdy "^4.0.2" - webpack-dev-middleware "^5.3.1" + webpack-dev-middleware "^5.3.4" ws "^8.13.0" webpack-merge@^5.7.3: @@ -8745,6 +8740,7 @@ ws@^8.13.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"