From 39dc4193b4c491308f3d0c7f74ba36aebd81dc8c Mon Sep 17 00:00:00 2001 From: ffforest Date: Tue, 28 Nov 2023 17:05:56 +0800 Subject: [PATCH] feat: add cluster topology and refactor relationship package --- pkg/apis/cluster/types.go | 13 +- pkg/apis/cluster/v1beta1/types.go | 15 +- .../v1beta1/zz_generated.conversion.go | 36 ++++ .../cluster/v1beta1/zz_generated.deepcopy.go | 32 +++- pkg/apis/cluster/zz_generated.deepcopy.go | 32 +++- pkg/generated/openapi/zz_generated.openapi.go | 59 +++++++ pkg/registry/cluster/storage.go | 68 ++++++++ .../relationship.go | 154 ++++++------------ .../{uniresource => relationship}/types.go | 13 +- pkg/registry/search/uniresource/children.go | 13 +- pkg/registry/search/uniresource/common.go | 119 ++++++++++++++ pkg/registry/search/uniresource/parents.go | 9 +- pkg/registry/search/uniresource/topology.go | 18 +- 13 files changed, 450 insertions(+), 131 deletions(-) rename pkg/registry/search/{uniresource => relationship}/relationship.go (54%) rename pkg/registry/search/{uniresource => relationship}/types.go (76%) create mode 100644 pkg/registry/search/uniresource/common.go diff --git a/pkg/apis/cluster/types.go b/pkg/apis/cluster/types.go index 70b30e76..ee50050e 100644 --- a/pkg/apis/cluster/types.go +++ b/pkg/apis/cluster/types.go @@ -16,7 +16,9 @@ limitations under the License. package cluster -import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) type CredentialType string @@ -54,8 +56,17 @@ type ClusterSpec struct { Finalized *bool } +// +k8s:deepcopy-gen=true + +type ClusterTopology struct { + GroupVersionKind string + Count int + Relationship map[string]string +} + type ClusterStatus struct { Healthy bool + Graph map[string]ClusterTopology } type ClusterAccess struct { diff --git a/pkg/apis/cluster/v1beta1/types.go b/pkg/apis/cluster/v1beta1/types.go index 7936f5ff..74a24689 100644 --- a/pkg/apis/cluster/v1beta1/types.go +++ b/pkg/apis/cluster/v1beta1/types.go @@ -16,7 +16,9 @@ limitations under the License. package v1beta1 -import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) type CredentialType string @@ -57,9 +59,18 @@ type ClusterSpec struct { Finalized *bool `json:"finalized,omitempty"` } +// +k8s:deepcopy-gen=true + +type ClusterTopology struct { + GroupVersionKind string + Count int + Relationship map[string]string +} + type ClusterStatus struct { // +optional - Healthy bool `json:"healthy,omitempty"` + Healthy bool `json:"healthy,omitempty"` + Graph map[string]ClusterTopology `json:"graph,omitempty"` } type ClusterAccess struct { diff --git a/pkg/apis/cluster/v1beta1/zz_generated.conversion.go b/pkg/apis/cluster/v1beta1/zz_generated.conversion.go index 968ef17b..733b3a6f 100644 --- a/pkg/apis/cluster/v1beta1/zz_generated.conversion.go +++ b/pkg/apis/cluster/v1beta1/zz_generated.conversion.go @@ -107,6 +107,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*ClusterTopology)(nil), (*cluster.ClusterTopology)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_ClusterTopology_To_cluster_ClusterTopology(a.(*ClusterTopology), b.(*cluster.ClusterTopology), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*cluster.ClusterTopology)(nil), (*ClusterTopology)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_cluster_ClusterTopology_To_v1beta1_ClusterTopology(a.(*cluster.ClusterTopology), b.(*ClusterTopology), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*X509)(nil), (*cluster.X509)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_X509_To_cluster_X509(a.(*X509), b.(*cluster.X509), scope) }); err != nil { @@ -297,6 +307,7 @@ func Convert_cluster_ClusterSpec_To_v1beta1_ClusterSpec(in *cluster.ClusterSpec, func autoConvert_v1beta1_ClusterStatus_To_cluster_ClusterStatus(in *ClusterStatus, out *cluster.ClusterStatus, s conversion.Scope) error { out.Healthy = in.Healthy + out.Graph = *(*map[string]cluster.ClusterTopology)(unsafe.Pointer(&in.Graph)) return nil } @@ -307,6 +318,7 @@ func Convert_v1beta1_ClusterStatus_To_cluster_ClusterStatus(in *ClusterStatus, o func autoConvert_cluster_ClusterStatus_To_v1beta1_ClusterStatus(in *cluster.ClusterStatus, out *ClusterStatus, s conversion.Scope) error { out.Healthy = in.Healthy + out.Graph = *(*map[string]ClusterTopology)(unsafe.Pointer(&in.Graph)) return nil } @@ -315,6 +327,30 @@ func Convert_cluster_ClusterStatus_To_v1beta1_ClusterStatus(in *cluster.ClusterS return autoConvert_cluster_ClusterStatus_To_v1beta1_ClusterStatus(in, out, s) } +func autoConvert_v1beta1_ClusterTopology_To_cluster_ClusterTopology(in *ClusterTopology, out *cluster.ClusterTopology, s conversion.Scope) error { + out.GroupVersionKind = in.GroupVersionKind + out.Count = in.Count + out.Relationship = *(*map[string]string)(unsafe.Pointer(&in.Relationship)) + return nil +} + +// Convert_v1beta1_ClusterTopology_To_cluster_ClusterTopology is an autogenerated conversion function. +func Convert_v1beta1_ClusterTopology_To_cluster_ClusterTopology(in *ClusterTopology, out *cluster.ClusterTopology, s conversion.Scope) error { + return autoConvert_v1beta1_ClusterTopology_To_cluster_ClusterTopology(in, out, s) +} + +func autoConvert_cluster_ClusterTopology_To_v1beta1_ClusterTopology(in *cluster.ClusterTopology, out *ClusterTopology, s conversion.Scope) error { + out.GroupVersionKind = in.GroupVersionKind + out.Count = in.Count + out.Relationship = *(*map[string]string)(unsafe.Pointer(&in.Relationship)) + return nil +} + +// Convert_cluster_ClusterTopology_To_v1beta1_ClusterTopology is an autogenerated conversion function. +func Convert_cluster_ClusterTopology_To_v1beta1_ClusterTopology(in *cluster.ClusterTopology, out *ClusterTopology, s conversion.Scope) error { + return autoConvert_cluster_ClusterTopology_To_v1beta1_ClusterTopology(in, out, s) +} + func autoConvert_v1beta1_X509_To_cluster_X509(in *X509, out *cluster.X509, s conversion.Scope) error { out.Certificate = *(*[]byte)(unsafe.Pointer(&in.Certificate)) out.PrivateKey = *(*[]byte)(unsafe.Pointer(&in.PrivateKey)) diff --git a/pkg/apis/cluster/v1beta1/zz_generated.deepcopy.go b/pkg/apis/cluster/v1beta1/zz_generated.deepcopy.go index cfe95efe..20c2cade 100644 --- a/pkg/apis/cluster/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/cluster/v1beta1/zz_generated.deepcopy.go @@ -31,7 +31,7 @@ func (in *Cluster) DeepCopyInto(out *Cluster) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) return } @@ -188,6 +188,13 @@ func (in *ClusterSpec) DeepCopy() *ClusterSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterStatus) DeepCopyInto(out *ClusterStatus) { *out = *in + if in.Graph != nil { + in, out := &in.Graph, &out.Graph + *out = make(map[string]ClusterTopology, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } return } @@ -201,6 +208,29 @@ func (in *ClusterStatus) DeepCopy() *ClusterStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterTopology) DeepCopyInto(out *ClusterTopology) { + *out = *in + if in.Relationship != nil { + in, out := &in.Relationship, &out.Relationship + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterTopology. +func (in *ClusterTopology) DeepCopy() *ClusterTopology { + if in == nil { + return nil + } + out := new(ClusterTopology) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *X509) DeepCopyInto(out *X509) { *out = *in diff --git a/pkg/apis/cluster/zz_generated.deepcopy.go b/pkg/apis/cluster/zz_generated.deepcopy.go index 8eabc260..18f977ae 100644 --- a/pkg/apis/cluster/zz_generated.deepcopy.go +++ b/pkg/apis/cluster/zz_generated.deepcopy.go @@ -31,7 +31,7 @@ func (in *Cluster) DeepCopyInto(out *Cluster) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) return } @@ -188,6 +188,13 @@ func (in *ClusterSpec) DeepCopy() *ClusterSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterStatus) DeepCopyInto(out *ClusterStatus) { *out = *in + if in.Graph != nil { + in, out := &in.Graph, &out.Graph + *out = make(map[string]ClusterTopology, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } return } @@ -201,6 +208,29 @@ func (in *ClusterStatus) DeepCopy() *ClusterStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterTopology) DeepCopyInto(out *ClusterTopology) { + *out = *in + if in.Relationship != nil { + in, out := &in.Relationship, &out.Relationship + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterTopology. +func (in *ClusterTopology) DeepCopy() *ClusterTopology { + if in == nil { + return nil + } + out := new(ClusterTopology) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *X509) DeepCopyInto(out *X509) { *out = *in diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go index 5b6d3820..bc3d4865 100644 --- a/pkg/generated/openapi/zz_generated.openapi.go +++ b/pkg/generated/openapi/zz_generated.openapi.go @@ -38,6 +38,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/KusionStack/karbour/pkg/apis/cluster/v1beta1.ClusterProxyOptions": schema_pkg_apis_cluster_v1beta1_ClusterProxyOptions(ref), "github.com/KusionStack/karbour/pkg/apis/cluster/v1beta1.ClusterSpec": schema_pkg_apis_cluster_v1beta1_ClusterSpec(ref), "github.com/KusionStack/karbour/pkg/apis/cluster/v1beta1.ClusterStatus": schema_pkg_apis_cluster_v1beta1_ClusterStatus(ref), + "github.com/KusionStack/karbour/pkg/apis/cluster/v1beta1.ClusterTopology": schema_pkg_apis_cluster_v1beta1_ClusterTopology(ref), "github.com/KusionStack/karbour/pkg/apis/cluster/v1beta1.X509": schema_pkg_apis_cluster_v1beta1_X509(ref), "github.com/KusionStack/karbour/pkg/apis/search/v1beta1.ClusterSyncResourcesCondition": schema_pkg_apis_search_v1beta1_ClusterSyncResourcesCondition(ref), "github.com/KusionStack/karbour/pkg/apis/search/v1beta1.FieldSelector": schema_pkg_apis_search_v1beta1_FieldSelector(ref), @@ -363,7 +364,65 @@ func schema_pkg_apis_cluster_v1beta1_ClusterStatus(ref common.ReferenceCallback) Format: "", }, }, + "graph": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/KusionStack/karbour/pkg/apis/cluster/v1beta1.ClusterTopology"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/KusionStack/karbour/pkg/apis/cluster/v1beta1.ClusterTopology"}, + } +} + +func schema_pkg_apis_cluster_v1beta1_ClusterTopology(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "GroupVersionKind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "Count": { + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int32", + }, + }, + "Relationship": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, }, + Required: []string{"GroupVersionKind", "Count", "Relationship"}, }, }, } diff --git a/pkg/registry/cluster/storage.go b/pkg/registry/cluster/storage.go index df311a04..f0aa1d60 100644 --- a/pkg/registry/cluster/storage.go +++ b/pkg/registry/cluster/storage.go @@ -18,14 +18,23 @@ package cluster import ( "context" + "fmt" + "os" "github.com/KusionStack/karbour/pkg/apis/cluster" + "github.com/KusionStack/karbour/pkg/registry/search/relationship" + "github.com/dominikbraun/graph/draw" + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/apis/meta/internalversion" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/generic" genericregistry "k8s.io/apiserver/pkg/registry/generic/registry" "k8s.io/apiserver/pkg/registry/rest" + "k8s.io/client-go/dynamic" + "k8s.io/klog/v2" "sigs.k8s.io/structured-merge-diff/v4/fieldpath" ) @@ -84,6 +93,10 @@ func (r *StatusREST) New() runtime.Object { return &cluster.Cluster{} } +func (r *StatusREST) NewList() runtime.Object { + return &cluster.ClusterList{} +} + // Destroy cleans up resources on shutdown. func (r *StatusREST) Destroy() { // Given that underlying store is shared with REST, @@ -95,6 +108,31 @@ func (r *StatusREST) Get(ctx context.Context, name string, options *metav1.GetOp return r.Store.Get(ctx, name, options) } +func (r *StatusREST) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) { + rt := &cluster.Cluster{} + client, err := r.BuildDynamicClient(ctx) + if err != nil { + return rt, err + } + graph, rg, _ := relationship.BuildRelationshipGraph(ctx, client, true) + + // Draw graph + file, _ := os.Create("./relationship.gv") + _ = draw.DOT(graph, file) + + m := make(map[string]cluster.ClusterTopology) + for _, rgn := range rg.RelationshipNodes { + rgnMap := rgn.ConvertToMap() + m[rgn.GetHash()] = cluster.ClusterTopology{ + GroupVersionKind: rgn.GetHash(), + Count: rgn.ResourceCount, + Relationship: rgnMap, + } + } + rt.Status.Graph = m + return rt, nil +} + // Update alters the status subset of an object. func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) { // We are explicitly setting forceAllowCreate to false in the call to the underlying storage because @@ -110,3 +148,33 @@ func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set { func (r *StatusREST) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) { return r.Store.ConvertToTable(ctx, object, tableOptions) } + +// BuildDynamicClient returns a dynamic client based on the cluster name in the request +func (r *StatusREST) BuildDynamicClient(ctx context.Context) (*dynamic.DynamicClient, error) { + // Extract the cluster name from context + info, ok := request.RequestInfoFrom(ctx) + if !ok { + return nil, fmt.Errorf("could not retrieve request info from context") + } + klog.Infof("Getting topology for cluster %s...", info.Name) + + // Locate the cluster resource and build config with it + obj, err := r.Store.Get(ctx, info.Name, &metav1.GetOptions{}) + if err != nil { + return nil, err + } + clusterFromContext := obj.(*cluster.Cluster) + klog.Infof("Cluster found: %s", clusterFromContext.Name) + config, err := NewConfigFromCluster(clusterFromContext) + if err != nil { + return nil, errors.Wrapf(err, "failed to create cluster client config %s", clusterFromContext.Name) + } + + // Create the dynamic client + client, err := dynamic.NewForConfig(config) + if err != nil { + return nil, err + } + + return client, nil +} diff --git a/pkg/registry/search/uniresource/relationship.go b/pkg/registry/search/relationship/relationship.go similarity index 54% rename from pkg/registry/search/uniresource/relationship.go rename to pkg/registry/search/relationship/relationship.go index 44f71529..b1182b68 100644 --- a/pkg/registry/search/uniresource/relationship.go +++ b/pkg/registry/search/relationship/relationship.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package uniresource +package relationship import ( "context" @@ -23,25 +23,45 @@ import ( "reflect" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" "k8s.io/klog/v2" + topologyutil "github.com/KusionStack/karbour/pkg/util/topology" "github.com/dominikbraun/graph" "github.com/dominikbraun/graph/draw" yaml "gopkg.in/yaml.v3" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/client-go/dynamic" - - topologyutil "github.com/KusionStack/karbour/pkg/util/topology" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func (r ResourceGraphNode) GetHash() string { - return r.Group + "/" + r.Version + "." + r.Kind + ":" + r.Namespace + "." + r.Name +func (rgn ResourceGraphNode) GetHash() string { + return rgn.Group + "/" + rgn.Version + "." + rgn.Kind + ":" + rgn.Namespace + "." + rgn.Name } -func (r RelationshipGraphNode) GetHash() string { +func (rgn RelationshipGraphNode) GetHash() string { + return rgn.Group + "." + rgn.Version + "." + rgn.Kind +} + +func (r Relationship) GetHash() string { return r.Group + "." + r.Version + "." + r.Kind } +func (rgn RelationshipGraphNode) ConvertToMap() map[string]string { + m := make(map[string]string, 0) + for _, p := range rgn.Parent { + parentHash := p.GetHash() + if _, ok := m[parentHash]; !ok { + m[parentHash] = "parent" + } + } + for _, c := range rgn.Children { + childHash := c.GetHash() + if _, ok := m[childHash]; !ok { + m[childHash] = "child" + } + } + return m +} + // FindNodeByGVK locates the Node by GVK on a RelationshipGraph. Used to locate parent and child nodes when building the relationship graph func (rg RelationshipGraph) FindNodeByGVK(group, version, kind string) (*RelationshipGraphNode, error) { for _, item := range rg.RelationshipNodes { @@ -73,7 +93,7 @@ func FindNodeOnGraph(g graph.Graph[string, RelationshipGraphNode], group, versio } // BuildBuiltinRelationshipGraph returns the relationship graph built from the YAML describing resource relationships -func BuildBuiltinRelationshipGraph() (graph.Graph[string, RelationshipGraphNode], error) { +func BuildBuiltinRelationshipGraph(ctx context.Context, client *dynamic.DynamicClient, countResouces bool) (graph.Graph[string, RelationshipGraphNode], *RelationshipGraph, error) { r := RelationshipGraph{} yamlFile, err := os.ReadFile("relationship.yaml") if err != nil { @@ -97,7 +117,7 @@ func BuildBuiltinRelationshipGraph() (graph.Graph[string, RelationshipGraphNode] // Append the same parent-child relationship to child's parent node if it does not exist already c.ChildNode.Parent, err = InsertIfNotExist(c.ChildNode.Parent, *c, "parent") if err != nil { - return nil, err + return nil, nil, err } } for _, p := range ri.Parent { @@ -109,7 +129,7 @@ func BuildBuiltinRelationshipGraph() (graph.Graph[string, RelationshipGraphNode] // Append the same parent-child relationship to parent's child node if it does not exist already p.ParentNode.Children, err = InsertIfNotExist(p.ParentNode.Children, *p, "child") if err != nil { - return nil, err + return nil, nil, err } } } @@ -122,7 +142,19 @@ func BuildBuiltinRelationshipGraph() (graph.Graph[string, RelationshipGraphNode] // Add Vertices for _, node := range r.RelationshipNodes { klog.Infof("Adding Vertex: %s\n", node.GetHash()) - _ = g.AddVertex(*node) + if countResouces { + resGVR, err := topologyutil.GetGVRFromGVK(schema.GroupVersion{Group: node.Group, Version: node.Version}.String(), node.Kind) + if err != nil { + return nil, nil, err + } + resList, _ := client.Resource(resGVR).List(ctx, metav1.ListOptions{}) + resCount := len(resList.Items) + klog.Infof("Counted resources for Vertex %s: %d\n", node.GetHash(), resCount) + node.ResourceCount = resCount + _ = g.AddVertex(*node, graph.VertexWeight(resCount)) + } else { + _ = g.AddVertex(*node) + } } // Add Edges, requires all vertices to be present for _, node := range r.RelationshipNodes { @@ -147,102 +179,14 @@ func BuildBuiltinRelationshipGraph() (graph.Graph[string, RelationshipGraphNode] file, _ := os.Create("./relationship.gv") _ = draw.DOT(g, file) - return g, nil + return g, &r, nil } -// BuildResourceRelationshipGraph builds the complete relationship graph including the built-in one and customer-specified one -func BuildResourceRelationshipGraph() (graph.Graph[string, RelationshipGraphNode], error) { - res, _ := BuildBuiltinRelationshipGraph() +// BuildRelationshipGraph builds the complete relationship graph including the built-in one and customer-specified one +func BuildRelationshipGraph(ctx context.Context, client *dynamic.DynamicClient, countResouces bool) (graph.Graph[string, RelationshipGraphNode], *RelationshipGraph, error) { + res, rg, _ := BuildBuiltinRelationshipGraph(ctx, client, countResouces) // TODO: Also include customized relationship graph - return res, nil -} - -func GetByJSONPath(relatedResList *unstructured.UnstructuredList, relationshipType string, ctx context.Context, client *dynamic.DynamicClient, obj unstructured.Unstructured, relation *Relationship, relatedGVK schema.GroupVersionKind, objResourceNode ResourceGraphNode, relationshipGraph graph.Graph[string, RelationshipGraphNode], resourceGraph graph.Graph[string, ResourceGraphNode]) (graph.Graph[string, ResourceGraphNode], error) { - klog.Infof("Using direct references to find related resources...\n") - var jpMatch bool - var err error - for _, relatedRes := range relatedResList.Items { - if relation.AutoGenerated { - jpMatch, err = topologyutil.JSONPathMatch(relatedRes, obj, relation.JSONPath) - } else { - jpMatch, err = topologyutil.JSONPathMatch(obj, relatedRes, relation.JSONPath) - } - if jpMatch && err == nil { - klog.Infof("%s resource found for kind %s, name %s based on JSONPath.\n", relationshipType, obj.GetKind(), obj.GetName()) - klog.Infof("%s resource is: kind %s, name %s.\n", relationshipType, relatedRes.GetKind(), relatedRes.GetName()) - klog.Infof("---------------------------------------------------------------------------\n") - rgv, _ := schema.ParseGroupVersion(relatedRes.GetAPIVersion()) - relatedResourceNode := ResourceGraphNode{ - Group: rgv.Group, - Version: rgv.Version, - Kind: relatedRes.GetKind(), - Name: relatedRes.GetName(), - Namespace: relatedRes.GetNamespace(), - } - resourceGraph.AddVertex(relatedResourceNode) - if relationshipType == "parent" { - resourceGraph.AddEdge(relatedResourceNode.GetHash(), objResourceNode.GetHash()) - } else { - resourceGraph.AddEdge(objResourceNode.GetHash(), relatedResourceNode.GetHash()) - } - relatedGVKOnGraph, _ := FindNodeOnGraph(relationshipGraph, relatedGVK.Group, relatedGVK.Version, relatedGVK.Kind) - if relationshipType == "parent" && len(relatedGVKOnGraph.Parent) > 0 { - // repeat for parent resources - for _, parentRelation := range relatedGVKOnGraph.Parent { - resourceGraph, _ = GetParents(ctx, client, relatedRes, parentRelation, relatedRes.GetNamespace(), relatedRes.GetName(), relatedResourceNode, relationshipGraph, resourceGraph) - } - } else if relationshipType == "child" && len(relatedGVKOnGraph.Children) > 0 { - // repeat for child resources - for _, childRelation := range relatedGVKOnGraph.Children { - resourceGraph, _ = GetChildren(ctx, client, relatedRes, childRelation, relatedRes.GetNamespace(), relatedRes.GetName(), relatedResourceNode, relationshipGraph, resourceGraph) - } - } - } - } - return resourceGraph, nil -} - -func GetByLabelSelector(relatedResList *unstructured.UnstructuredList, relationshipType string, ctx context.Context, client *dynamic.DynamicClient, obj unstructured.Unstructured, relation *Relationship, relatedGVK schema.GroupVersionKind, objResourceNode ResourceGraphNode, relationshipGraph graph.Graph[string, RelationshipGraphNode], resourceGraph graph.Graph[string, ResourceGraphNode]) (graph.Graph[string, ResourceGraphNode], error) { - klog.Infof("Using label selectors to find related resources...\n") - var labelsMatch bool - var err error - for _, relatedRes := range relatedResList.Items { - if relationshipType == "parent" { - labelsMatch, err = topologyutil.LabelSelectorsMatch(relatedRes, obj, relation.SelectorPath) - } else { - labelsMatch, err = topologyutil.LabelSelectorsMatch(obj, relatedRes, relation.SelectorPath) - } - if labelsMatch && err == nil { - klog.Infof("%s resource found for kind %s, name %s based on %s.\n", relationshipType, obj.GetKind(), obj.GetName(), relation.SelectorPath) - klog.Infof("%s resource is: kind %s, name %s.\n", relationshipType, relatedRes.GetKind(), relatedRes.GetName()) - klog.Infof("---------------------------------------------------------------------------\n") - rgv, _ := schema.ParseGroupVersion(relatedRes.GetAPIVersion()) - relatedResourceNode := ResourceGraphNode{ - Group: rgv.Group, - Version: rgv.Version, - Kind: relatedRes.GetKind(), - Name: relatedRes.GetName(), - Namespace: relatedRes.GetNamespace(), - } - resourceGraph.AddVertex(relatedResourceNode) - if relationshipType == "parent" { - resourceGraph.AddEdge(relatedResourceNode.GetHash(), objResourceNode.GetHash()) - } else { - resourceGraph.AddEdge(objResourceNode.GetHash(), relatedResourceNode.GetHash()) - } - relatedGVKOnGraph, _ := FindNodeOnGraph(relationshipGraph, relatedGVK.Group, relatedGVK.Version, relatedGVK.Kind) - if relationshipType == "parent" && len(relatedGVKOnGraph.Parent) > 0 { - for _, parentRelation := range relatedGVKOnGraph.Parent { - resourceGraph, _ = GetParents(ctx, client, relatedRes, parentRelation, relatedRes.GetNamespace(), relatedRes.GetName(), relatedResourceNode, relationshipGraph, resourceGraph) - } - } else if relationshipType == "child" && len(relatedGVKOnGraph.Children) > 0 { - for _, childRelation := range relatedGVKOnGraph.Children { - resourceGraph, _ = GetChildren(ctx, client, relatedRes, childRelation, relatedRes.GetNamespace(), relatedRes.GetName(), relatedResourceNode, relationshipGraph, resourceGraph) - } - } - } - } - return resourceGraph, nil + return res, rg, nil } // InsertIfNotExist inserts relation into relationList only if it does not exist already diff --git a/pkg/registry/search/uniresource/types.go b/pkg/registry/search/relationship/types.go similarity index 76% rename from pkg/registry/search/uniresource/types.go rename to pkg/registry/search/relationship/types.go index 40012461..dd62e77b 100644 --- a/pkg/registry/search/uniresource/types.go +++ b/pkg/registry/search/relationship/types.go @@ -12,18 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -package uniresource +package relationship type RelationshipGraph struct { RelationshipNodes []*RelationshipGraphNode `json:"relationship,omitempty" yaml:"relationship,omitempty"` } type RelationshipGraphNode struct { - Group string `json:"group,omitempty" yaml:"group,omitempty"` - Version string `json:"version,omitempty" yaml:"version,omitempty"` - Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` - Parent []*Relationship `json:"parent,omitempty" yaml:"parent,omitempty"` - Children []*Relationship `json:"children,omitempty" yaml:"children,omitempty"` + Group string `json:"group,omitempty" yaml:"group,omitempty"` + Version string `json:"version,omitempty" yaml:"version,omitempty"` + Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` + Parent []*Relationship `json:"parent,omitempty" yaml:"parent,omitempty"` + Children []*Relationship `json:"children,omitempty" yaml:"children,omitempty"` + ResourceCount int `json:"resourceCount,omitempty" yaml:"resource_count,omitempty"` } type Relationship struct { diff --git a/pkg/registry/search/uniresource/children.go b/pkg/registry/search/uniresource/children.go index 44a89c56..c268ba7e 100644 --- a/pkg/registry/search/uniresource/children.go +++ b/pkg/registry/search/uniresource/children.go @@ -23,6 +23,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/klog/v2" + "github.com/KusionStack/karbour/pkg/registry/search/relationship" topologyutil "github.com/KusionStack/karbour/pkg/util/topology" "github.com/dominikbraun/graph" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -31,7 +32,7 @@ import ( ) // GetChildResourcesList returns an *unstructured.UnstructuredList representing all resources that matches the child GVK in the current namespace -func GetChildResourcesList(ctx context.Context, client *dynamic.DynamicClient, childRelation *Relationship, namespace string) (*unstructured.UnstructuredList, error) { +func GetChildResourcesList(ctx context.Context, client *dynamic.DynamicClient, childRelation *relationship.Relationship, namespace string) (*unstructured.UnstructuredList, error) { childAPIVersion := childRelation.Group + "/" + childRelation.Version childRes, err := topologyutil.GetGVRFromGVK(childAPIVersion, childRelation.Kind) if err != nil { @@ -55,7 +56,7 @@ func GetChildResourcesList(ctx context.Context, client *dynamic.DynamicClient, c } // GetChildren returns a graph that includes all of the child resources for the current obj that are described by the childRelation -func GetChildren(ctx context.Context, client *dynamic.DynamicClient, obj unstructured.Unstructured, childRelation *Relationship, namespace, objName string, objResourceNode ResourceGraphNode, relationshipGraph graph.Graph[string, RelationshipGraphNode], resourceGraph graph.Graph[string, ResourceGraphNode]) (graph.Graph[string, ResourceGraphNode], error) { +func GetChildren(ctx context.Context, client *dynamic.DynamicClient, obj unstructured.Unstructured, childRelation *relationship.Relationship, namespace, objName string, objResourceNode relationship.ResourceGraphNode, relationshipGraph graph.Graph[string, relationship.RelationshipGraphNode], resourceGraph graph.Graph[string, relationship.ResourceGraphNode]) (graph.Graph[string, relationship.ResourceGraphNode], error) { if childRelation.Type == "OwnerReference" { // If relationship type is ownerreference, honor that instead of relationship graph gv, _ := schema.ParseGroupVersion(childRelation.Group + "/" + childRelation.Version) @@ -114,11 +115,11 @@ func GetChildren(ctx context.Context, client *dynamic.DynamicClient, obj unstruc } // GetChildrenByOwnerReference returns a graph that includes all of the child resources for the current obj described by their children's OwnerReferences field -func GetChildrenByOwnerReference(childResList *unstructured.UnstructuredList, ctx context.Context, client *dynamic.DynamicClient, obj unstructured.Unstructured, childGVK schema.GroupVersionKind, relationshipGraph graph.Graph[string, RelationshipGraphNode], resourceGraph graph.Graph[string, ResourceGraphNode]) (graph.Graph[string, ResourceGraphNode], error) { +func GetChildrenByOwnerReference(childResList *unstructured.UnstructuredList, ctx context.Context, client *dynamic.DynamicClient, obj unstructured.Unstructured, childGVK schema.GroupVersionKind, relationshipGraph graph.Graph[string, relationship.RelationshipGraphNode], resourceGraph graph.Graph[string, relationship.ResourceGraphNode]) (graph.Graph[string, relationship.ResourceGraphNode], error) { // For ownerreference-identified children, look up all instances of the child GVK and filter by ownerreference klog.Infof("Using OwnerReferences to find children...\n") gv, _ := schema.ParseGroupVersion(obj.GetAPIVersion()) - objResourceNode := ResourceGraphNode{ + objResourceNode := relationship.ResourceGraphNode{ Group: gv.Group, Version: gv.Version, Kind: obj.GetKind(), @@ -132,7 +133,7 @@ func GetChildrenByOwnerReference(childResList *unstructured.UnstructuredList, ct klog.Infof("Child resource is: kind %s, name %s.\n", childRes.GetKind(), childRes.GetName()) klog.Infof("---------------------------------------------------------------------------\n") cgv, _ := schema.ParseGroupVersion(childRes.GetAPIVersion()) - childResourceNode := ResourceGraphNode{ + childResourceNode := relationship.ResourceGraphNode{ Group: cgv.Group, Version: cgv.Version, Kind: childRes.GetKind(), @@ -141,7 +142,7 @@ func GetChildrenByOwnerReference(childResList *unstructured.UnstructuredList, ct } resourceGraph.AddVertex(childResourceNode) resourceGraph.AddEdge(objResourceNode.GetHash(), childResourceNode.GetHash()) - childGVKOnGraph, _ := FindNodeOnGraph(relationshipGraph, childGVK.Group, childGVK.Version, childRes.GetKind()) + childGVKOnGraph, _ := relationship.FindNodeOnGraph(relationshipGraph, childGVK.Group, childGVK.Version, childRes.GetKind()) if len(childGVKOnGraph.Children) > 0 { // repeat for child resources // shorten call stack diff --git a/pkg/registry/search/uniresource/common.go b/pkg/registry/search/uniresource/common.go new file mode 100644 index 00000000..8476effa --- /dev/null +++ b/pkg/registry/search/uniresource/common.go @@ -0,0 +1,119 @@ +/* +Copyright The Karbour Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package uniresource + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/klog/v2" + + "github.com/dominikbraun/graph" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/client-go/dynamic" + + "github.com/KusionStack/karbour/pkg/registry/search/relationship" + topologyutil "github.com/KusionStack/karbour/pkg/util/topology" +) + +func GetByJSONPath(relatedResList *unstructured.UnstructuredList, relationshipType string, ctx context.Context, client *dynamic.DynamicClient, obj unstructured.Unstructured, relation *relationship.Relationship, relatedGVK schema.GroupVersionKind, objResourceNode relationship.ResourceGraphNode, relationshipGraph graph.Graph[string, relationship.RelationshipGraphNode], resourceGraph graph.Graph[string, relationship.ResourceGraphNode]) (graph.Graph[string, relationship.ResourceGraphNode], error) { + klog.Infof("Using direct references to find related resources...\n") + var jpMatch bool + var err error + for _, relatedRes := range relatedResList.Items { + if relation.AutoGenerated { + jpMatch, err = topologyutil.JSONPathMatch(relatedRes, obj, relation.JSONPath) + } else { + jpMatch, err = topologyutil.JSONPathMatch(obj, relatedRes, relation.JSONPath) + } + if jpMatch && err == nil { + klog.Infof("%s resource found for kind %s, name %s based on JSONPath.\n", relationshipType, obj.GetKind(), obj.GetName()) + klog.Infof("%s resource is: kind %s, name %s.\n", relationshipType, relatedRes.GetKind(), relatedRes.GetName()) + klog.Infof("---------------------------------------------------------------------------\n") + rgv, _ := schema.ParseGroupVersion(relatedRes.GetAPIVersion()) + relatedResourceNode := relationship.ResourceGraphNode{ + Group: rgv.Group, + Version: rgv.Version, + Kind: relatedRes.GetKind(), + Name: relatedRes.GetName(), + Namespace: relatedRes.GetNamespace(), + } + resourceGraph.AddVertex(relatedResourceNode) + if relationshipType == "parent" { + resourceGraph.AddEdge(relatedResourceNode.GetHash(), objResourceNode.GetHash()) + } else { + resourceGraph.AddEdge(objResourceNode.GetHash(), relatedResourceNode.GetHash()) + } + relatedGVKOnGraph, _ := relationship.FindNodeOnGraph(relationshipGraph, relatedGVK.Group, relatedGVK.Version, relatedGVK.Kind) + if relationshipType == "parent" && len(relatedGVKOnGraph.Parent) > 0 { + // repeat for parent resources + for _, parentRelation := range relatedGVKOnGraph.Parent { + resourceGraph, _ = GetParents(ctx, client, relatedRes, parentRelation, relatedRes.GetNamespace(), relatedRes.GetName(), relatedResourceNode, relationshipGraph, resourceGraph) + } + } else if relationshipType == "child" && len(relatedGVKOnGraph.Children) > 0 { + // repeat for child resources + for _, childRelation := range relatedGVKOnGraph.Children { + resourceGraph, _ = GetChildren(ctx, client, relatedRes, childRelation, relatedRes.GetNamespace(), relatedRes.GetName(), relatedResourceNode, relationshipGraph, resourceGraph) + } + } + } + } + return resourceGraph, nil +} + +func GetByLabelSelector(relatedResList *unstructured.UnstructuredList, relationshipType string, ctx context.Context, client *dynamic.DynamicClient, obj unstructured.Unstructured, relation *relationship.Relationship, relatedGVK schema.GroupVersionKind, objResourceNode relationship.ResourceGraphNode, relationshipGraph graph.Graph[string, relationship.RelationshipGraphNode], resourceGraph graph.Graph[string, relationship.ResourceGraphNode]) (graph.Graph[string, relationship.ResourceGraphNode], error) { + klog.Infof("Using label selectors to find related resources...\n") + var labelsMatch bool + var err error + for _, relatedRes := range relatedResList.Items { + if relationshipType == "parent" { + labelsMatch, err = topologyutil.LabelSelectorsMatch(relatedRes, obj, relation.SelectorPath) + } else { + labelsMatch, err = topologyutil.LabelSelectorsMatch(obj, relatedRes, relation.SelectorPath) + } + if labelsMatch && err == nil { + klog.Infof("%s resource found for kind %s, name %s based on %s.\n", relationshipType, obj.GetKind(), obj.GetName(), relation.SelectorPath) + klog.Infof("%s resource is: kind %s, name %s.\n", relationshipType, relatedRes.GetKind(), relatedRes.GetName()) + klog.Infof("---------------------------------------------------------------------------\n") + rgv, _ := schema.ParseGroupVersion(relatedRes.GetAPIVersion()) + relatedResourceNode := relationship.ResourceGraphNode{ + Group: rgv.Group, + Version: rgv.Version, + Kind: relatedRes.GetKind(), + Name: relatedRes.GetName(), + Namespace: relatedRes.GetNamespace(), + } + resourceGraph.AddVertex(relatedResourceNode) + if relationshipType == "parent" { + resourceGraph.AddEdge(relatedResourceNode.GetHash(), objResourceNode.GetHash()) + } else { + resourceGraph.AddEdge(objResourceNode.GetHash(), relatedResourceNode.GetHash()) + } + relatedGVKOnGraph, _ := relationship.FindNodeOnGraph(relationshipGraph, relatedGVK.Group, relatedGVK.Version, relatedGVK.Kind) + if relationshipType == "parent" && len(relatedGVKOnGraph.Parent) > 0 { + for _, parentRelation := range relatedGVKOnGraph.Parent { + resourceGraph, _ = GetParents(ctx, client, relatedRes, parentRelation, relatedRes.GetNamespace(), relatedRes.GetName(), relatedResourceNode, relationshipGraph, resourceGraph) + } + } else if relationshipType == "child" && len(relatedGVKOnGraph.Children) > 0 { + for _, childRelation := range relatedGVKOnGraph.Children { + resourceGraph, _ = GetChildren(ctx, client, relatedRes, childRelation, relatedRes.GetNamespace(), relatedRes.GetName(), relatedResourceNode, relationshipGraph, resourceGraph) + } + } + } + } + return resourceGraph, nil +} diff --git a/pkg/registry/search/uniresource/parents.go b/pkg/registry/search/uniresource/parents.go index 3f662b46..a0268f06 100644 --- a/pkg/registry/search/uniresource/parents.go +++ b/pkg/registry/search/uniresource/parents.go @@ -23,6 +23,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/klog/v2" + "github.com/KusionStack/karbour/pkg/registry/search/relationship" topologyutil "github.com/KusionStack/karbour/pkg/util/topology" "github.com/dominikbraun/graph" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -31,7 +32,7 @@ import ( ) // GetParentResourcesList returns an *unstructured.UnstructuredList representing all resources that matches the parent GVK in the current namespace -func GetParentResourcesList(ctx context.Context, client *dynamic.DynamicClient, parentRelation *Relationship, namespace string) (*unstructured.UnstructuredList, error) { +func GetParentResourcesList(ctx context.Context, client *dynamic.DynamicClient, parentRelation *relationship.Relationship, namespace string) (*unstructured.UnstructuredList, error) { parentAPIVersion := parentRelation.Group + "/" + parentRelation.Version parentRes, err := topologyutil.GetGVRFromGVK(parentAPIVersion, parentRelation.Kind) if err != nil { @@ -57,7 +58,7 @@ func GetParentResourcesList(ctx context.Context, client *dynamic.DynamicClient, } // GetParents returns a graph that includes all of the parent resources for the current obj that are described by the parentRelation -func GetParents(ctx context.Context, client *dynamic.DynamicClient, obj unstructured.Unstructured, parentRelation *Relationship, namespace, objName string, objResourceNode ResourceGraphNode, relationshipGraph graph.Graph[string, RelationshipGraphNode], resourceGraph graph.Graph[string, ResourceGraphNode]) (graph.Graph[string, ResourceGraphNode], error) { +func GetParents(ctx context.Context, client *dynamic.DynamicClient, obj unstructured.Unstructured, parentRelation *relationship.Relationship, namespace, objName string, objResourceNode relationship.ResourceGraphNode, relationshipGraph graph.Graph[string, relationship.RelationshipGraphNode], resourceGraph graph.Graph[string, relationship.ResourceGraphNode]) (graph.Graph[string, relationship.ResourceGraphNode], error) { var err error if parentRelation.Type == "OwnerReference" { // If relationship type is ownerreference, honor that instead of relationship graph @@ -95,7 +96,7 @@ func GetParents(ctx context.Context, client *dynamic.DynamicClient, obj unstruct } // GetParentsByOwnerReference returns a graph that includes all of the parent resources for the current obj described by its OwnerReferences field -func GetParentsByOwnerReference(ctx context.Context, client *dynamic.DynamicClient, obj unstructured.Unstructured, objResourceNode ResourceGraphNode, relationshipGraph graph.Graph[string, RelationshipGraphNode], resourceGraph graph.Graph[string, ResourceGraphNode]) (graph.Graph[string, ResourceGraphNode], error) { +func GetParentsByOwnerReference(ctx context.Context, client *dynamic.DynamicClient, obj unstructured.Unstructured, objResourceNode relationship.ResourceGraphNode, relationshipGraph graph.Graph[string, relationship.RelationshipGraphNode], resourceGraph graph.Graph[string, relationship.ResourceGraphNode]) (graph.Graph[string, relationship.ResourceGraphNode], error) { klog.Infof("Using OwnerReferences to find parents...\n") objName := obj.GetName() namespace := obj.GetNamespace() @@ -131,7 +132,7 @@ func GetParentsByOwnerReference(ctx context.Context, client *dynamic.DynamicClie klog.Infof("Parent resource is: kind %s, name %s.\n", parentRes.GetKind(), parentRes.GetName()) klog.Infof("---------------------------------------------------------------------------\n") pgv, _ := schema.ParseGroupVersion(parentRes.GetAPIVersion()) - parentResourceNode := ResourceGraphNode{ + parentResourceNode := relationship.ResourceGraphNode{ Group: pgv.Group, Version: pgv.Version, Kind: parentRes.GetKind(), diff --git a/pkg/registry/search/uniresource/topology.go b/pkg/registry/search/uniresource/topology.go index 43c94320..08eaa001 100644 --- a/pkg/registry/search/uniresource/topology.go +++ b/pkg/registry/search/uniresource/topology.go @@ -31,6 +31,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" + "github.com/KusionStack/karbour/pkg/registry/search/relationship" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/registry/rest" @@ -76,13 +77,20 @@ func (r *TopologyREST) List(ctx context.Context, options *internalversion.ListOp // } // ... klog.Infof("Query string: %s", queryString) - rg, _ := BuildResourceRelationshipGraph() + client, err := r.BuildDynamicClient(ctx) + if err != nil { + return nil, err + } + rg, _, err := relationship.BuildRelationshipGraph(ctx, client, false) + if err != nil { + return nil, err + } res, err := r.Storage.Search(ctx, queryString, storage.SQLPatternType) if err != nil { return nil, err } - ResourceGraphNodeHash := func(rgn ResourceGraphNode) string { + ResourceGraphNodeHash := func(rgn relationship.ResourceGraphNode) string { return rgn.Group + "/" + rgn.Version + "." + rgn.Kind + ":" + rgn.Namespace + "." + rgn.Name } g := graph.New(ResourceGraphNodeHash, graph.Directed(), graph.PreventCycles()) @@ -135,11 +143,11 @@ func (r *TopologyREST) BuildDynamicClient(ctx context.Context) (*dynamic.Dynamic } // GetResourceRelationship returns a full graph that contains all the resources that are related to obj -func (r *TopologyREST) GetResourceRelationship(ctx context.Context, obj unstructured.Unstructured, relationshipGraph graph.Graph[string, RelationshipGraphNode], resourceGraph graph.Graph[string, ResourceGraphNode]) (graph.Graph[string, ResourceGraphNode], error) { +func (r *TopologyREST) GetResourceRelationship(ctx context.Context, obj unstructured.Unstructured, relationshipGraph graph.Graph[string, relationship.RelationshipGraphNode], resourceGraph graph.Graph[string, relationship.ResourceGraphNode]) (graph.Graph[string, relationship.ResourceGraphNode], error) { namespace := obj.GetNamespace() objName := obj.GetName() gv, _ := schema.ParseGroupVersion(obj.GetAPIVersion()) - objResourceNode := ResourceGraphNode{ + objResourceNode := relationship.ResourceGraphNode{ Group: gv.Group, Version: gv.Version, Kind: obj.GetKind(), @@ -152,7 +160,7 @@ func (r *TopologyREST) GetResourceRelationship(ctx context.Context, obj unstruct return resourceGraph, err } - objGVKOnGraph, _ := FindNodeOnGraph(relationshipGraph, gv.Group, gv.Version, obj.GetKind()) + objGVKOnGraph, _ := relationship.FindNodeOnGraph(relationshipGraph, gv.Group, gv.Version, obj.GetKind()) // TODO: process error // Recursively find parents for _, objParent := range objGVKOnGraph.Parent {