diff --git a/envvar.go b/envvar.go index 38c1982c..d9616ebb 100644 --- a/envvar.go +++ b/envvar.go @@ -48,7 +48,7 @@ func GetSecretFromCache(c kubernetes.Interface, namespace, name, key string) (st } secret, err := c.CoreV1().Secrets(namespace).Get(context.Background(), name, metav1.GetOptions{}) if secret == nil { - return "", fmt.Errorf("Could not get contents of secret %v from namespace %v: %v", name, namespace, err) + return "", fmt.Errorf("could not get contents of secret %v from namespace %v: %w", name, namespace, err) } value, ok := secret.Data[key] @@ -58,7 +58,7 @@ func GetSecretFromCache(c kubernetes.Interface, namespace, name, key string) (st for k := range secret.Data { names = append(names, k) } - return "", fmt.Errorf("Could not find key %v in secret %v (%s)", key, name, strings.Join(names, ", ")) + return "", fmt.Errorf("could not find key %v in secret %v (%s)", key, name, strings.Join(names, ", ")) } envCache.Set(id, string(value), 5*time.Minute) return string(value), nil @@ -71,7 +71,7 @@ func GetConfigMapFromCache(c kubernetes.Interface, namespace, name, key string) } configMap, err := c.CoreV1().ConfigMaps(namespace).Get(context.Background(), name, metav1.GetOptions{}) if configMap == nil { - return "", fmt.Errorf("Could not get contents of configmap %v from namespace %v: %v", name, namespace, err) + return "", fmt.Errorf("could not get contents of configmap %v from namespace %v: %w", name, namespace, err) } value, ok := configMap.Data[key] @@ -80,7 +80,7 @@ func GetConfigMapFromCache(c kubernetes.Interface, namespace, name, key string) for k := range configMap.Data { names = append(names, k) } - return "", fmt.Errorf("Could not find key %v in configmap %v (%s)", key, name, strings.Join(names, ", ")) + return "", fmt.Errorf("could not find key %v in configmap %v (%s)", key, name, strings.Join(names, ", ")) } envCache.Set(id, string(value), 5*time.Minute) return string(value), nil diff --git a/fixtures/dummy/components.go b/fixtures/dummy/components.go index 2d63a6a5..0541d2e1 100644 --- a/fixtures/dummy/components.go +++ b/fixtures/dummy/components.go @@ -1,6 +1,8 @@ package dummy import ( + "fmt" + "github.com/flanksource/duty/models" "github.com/flanksource/duty/types" "github.com/google/uuid" @@ -72,76 +74,99 @@ var ClusterComponent = models.Component{ Type: "KubernetesCluster", Status: types.ComponentStatusHealthy, CreatedAt: DummyCreatedAt, + Tooltip: "Kubernetes Cluster", + Icon: "icon-cluster", } var NodesComponent = models.Component{ ID: uuid.MustParse("018681fe-b27e-7627-72c2-ad18e93f72f4"), Name: "Nodes", + Icon: "icon-kubernetes-node", + Tooltip: "Kubernetes Nodes", ExternalId: "dummy/nodes", Type: "KubernetesNodes", Status: types.ComponentStatusHealthy, ParentId: &ClusterComponent.ID, CreatedAt: DummyCreatedAt, + Path: ClusterComponent.ID.String(), } var NodeA = models.Component{ ID: uuid.MustParse("018681fe-f5aa-37e9-83f7-47b5b0232d5e"), Name: "node-a", + Icon: "icon-kubernetes-node", + Tooltip: "Node A", ExternalId: "dummy/node-a", Type: "KubernetesNode", Status: types.ComponentStatusHealthy, ParentId: &NodesComponent.ID, CreatedAt: DummyCreatedAt, + Path: fmt.Sprintf("%s.%s", ClusterComponent.ID.String(), NodesComponent.ID.String()), } var NodeB = models.Component{ ID: uuid.MustParse("018681ff-227e-4d71-b38e-0693cc862213"), Name: "node-b", + Icon: "icon-kubernetes-node", + Tooltip: "Node B", ExternalId: "dummy/node-b", Type: "KubernetesNode", Status: types.ComponentStatusHealthy, ParentId: &NodesComponent.ID, CreatedAt: DummyCreatedAt, + Path: fmt.Sprintf("%s.%s", ClusterComponent.ID.String(), NodesComponent.ID.String()), } var PodsComponent = models.Component{ ID: uuid.MustParse("018681ff-559f-7183-19d1-7d898b4e1413"), Name: "Pods", + Icon: "icon-kubernetes-pod", + Tooltip: "Kubernetes Pods", ExternalId: "dummy/pods", Type: "KubernetesPods", Status: types.ComponentStatusHealthy, ParentId: &ClusterComponent.ID, CreatedAt: DummyCreatedAt, + Path: ClusterComponent.ID.String(), } var LogisticsAPIPod = models.Component{ ID: uuid.MustParse("018681ff-80ed-d10d-21ef-c74f152b085b"), Name: "logistics-api-574dc95b5d-mp64w", + Icon: "icon-kubernetes-pod", + Tooltip: "Logistic API Pod", ExternalId: "dummy/logistics-api-574dc95b5d-mp64w", Type: "KubernetesPod", Status: types.ComponentStatusHealthy, ParentId: &PodsComponent.ID, CreatedAt: DummyCreatedAt, + Path: fmt.Sprintf("%s.%s", ClusterComponent.ID.String(), PodsComponent.ID.String()), } var LogisticsUIPod = models.Component{ ID: uuid.MustParse("018681ff-b6c1-a14d-2fd4-8c7dac94cddd"), Name: "logistics-ui-676b85b87c-tjjcp", + Icon: "icon-kubernetes-pod", + Tooltip: "Logistic UI Pod", Type: "KubernetesPod", ExternalId: "dummy/logistics-ui-676b85b87c-tjjcp", Status: types.ComponentStatusHealthy, ParentId: &PodsComponent.ID, CreatedAt: DummyCreatedAt, + Path: fmt.Sprintf("%s.%s", ClusterComponent.ID.String(), PodsComponent.ID.String()), } var LogisticsWorkerPod = models.Component{ ID: uuid.MustParse("018681ff-e578-a926-e366-d2dc0646eafa"), Name: "logistics-worker-79cb67d8f5-lr66n", + Icon: "icon-kubernetes-pod", + Tooltip: "Logistic Worker Pod", ExternalId: "dummy/logistics-worker-79cb67d8f5-lr66n", Type: "KubernetesPod", Status: types.ComponentStatusHealthy, ParentId: &PodsComponent.ID, CreatedAt: DummyCreatedAt, + Path: fmt.Sprintf("%s.%s", ClusterComponent.ID.String(), PodsComponent.ID.String()), } var PaymentsAPI = models.Component{ diff --git a/fixtures/expectations/topology_child_tree.json b/fixtures/expectations/topology_child_tree.json index 6994cf59..8fb7854d 100644 --- a/fixtures/expectations/topology_child_tree.json +++ b/fixtures/expectations/topology_child_tree.json @@ -1,19 +1,8 @@ { "components": [ { - "id": "018681fe-f5aa-37e9-83f7-47b5b0232d5e", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/node-a", - "parent_id": "018681fe-b27e-7627-72c2-ad18e93f72f4", - "name": "node-a", - "status": "healthy", - "type": "KubernetesNode", - "summary": { - "healthy": 2 - }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", + "children": ["018681ff-80ed-d10d-21ef-c74f152b085b", "018681ff-b6c1-a14d-2fd4-8c7dac94cddd"], "components": [ { "id": "018681ff-80ed-d10d-21ef-c74f152b085b", @@ -29,9 +18,10 @@ "is_leaf": false, "created_at": "2023-01-01T05:29:00+05:30", "updated_at": "2023-01-01T05:29:00+05:30", - "parents": [ - "018681fe-f5aa-37e9-83f7-47b5b0232d5e" - ] + "icon": "icon-kubernetes-pod", + "tooltip": "Logistic API Pod", + "parents": ["018681fe-f5aa-37e9-83f7-47b5b0232d5e"], + "path": "018681fe-8156-4b91-d178-caf8b3c2818c.018681ff-559f-7183-19d1-7d898b4e1413" }, { "id": "018681ff-b6c1-a14d-2fd4-8c7dac94cddd", @@ -47,15 +37,49 @@ "is_leaf": false, "created_at": "2023-01-01T05:29:00+05:30", "updated_at": "2023-01-01T05:29:00+05:30", - "parents": [ - "018681fe-f5aa-37e9-83f7-47b5b0232d5e" - ] + "icon": "icon-kubernetes-pod", + "tooltip": "Logistic UI Pod", + "parents": ["018681fe-f5aa-37e9-83f7-47b5b0232d5e"], + "path": "018681fe-8156-4b91-d178-caf8b3c2818c.018681ff-559f-7183-19d1-7d898b4e1413" + }, + { + "agent_id": "00000000-0000-0000-0000-000000000000", + "id": "018681ff-80ed-d10d-21ef-c74f152b085b", + "is_leaf": false, + "name": "logistics-api-574dc95b5d-mp64w", + "parent_id": "018681ff-559f-7183-19d1-7d898b4e1413", + "parents": ["018681fe-f5aa-37e9-83f7-47b5b0232d5e"], + "status": "healthy", + "summary": { + "healthy": 1 + }, + "type": "KubernetesPod" + }, + { + "agent_id": "00000000-0000-0000-0000-000000000000", + "id": "018681ff-b6c1-a14d-2fd4-8c7dac94cddd", + "is_leaf": false, + "name": "logistics-ui-676b85b87c-tjjcp", + "parent_id": "018681ff-559f-7183-19d1-7d898b4e1413", + "parents": ["018681fe-f5aa-37e9-83f7-47b5b0232d5e"], + "status": "healthy", + "summary": { + "healthy": 1 + }, + "type": "KubernetesPod" } ], - "children": [ - "018681ff-80ed-d10d-21ef-c74f152b085b", - "018681ff-b6c1-a14d-2fd4-8c7dac94cddd" - ] + "external_id": "dummy/node-a", + "icon": "icon-kubernetes-node", + "id": "018681fe-f5aa-37e9-83f7-47b5b0232d5e", + "is_leaf": false, + "name": "node-a", + "parent_id": "018681fe-b27e-7627-72c2-ad18e93f72f4", + "path": "018681fe-8156-4b91-d178-caf8b3c2818c.018681fe-b27e-7627-72c2-ad18e93f72f4", + "status": "healthy", + "summary": { "healthy": 4 }, + "tooltip": "Node A", + "type": "KubernetesNode" } ], "healthStatuses": [ diff --git a/fixtures/expectations/topology_cluster_component_tree.json b/fixtures/expectations/topology_cluster_component_tree.json new file mode 100644 index 00000000..adb6545e --- /dev/null +++ b/fixtures/expectations/topology_cluster_component_tree.json @@ -0,0 +1,187 @@ +{ + "components": [ + { + "agent_id": "00000000-0000-0000-0000-000000000000", + "components": [ + { + "agent_id": "00000000-0000-0000-0000-000000000000", + "components": [ + { + "agent_id": "00000000-0000-0000-0000-000000000000", + "components": [ + { + "agent_id": "00000000-0000-0000-0000-000000000000", + "id": "018681ff-e578-a926-e366-d2dc0646eafa", + "is_leaf": false, + "name": "logistics-worker-79cb67d8f5-lr66n", + "parent_id": "018681ff-559f-7183-19d1-7d898b4e1413", + "parents": ["018681ff-227e-4d71-b38e-0693cc862213"], + "status": "healthy", + "summary": { + "healthy": 1 + }, + "type": "KubernetesPod" + } + ], + "id": "018681ff-227e-4d71-b38e-0693cc862213", + "is_leaf": false, + "name": "node-b", + "parent_id": "018681fe-b27e-7627-72c2-ad18e93f72f4", + "status": "healthy", + "summary": { + "healthy": 1, + "insights": { + "security": { + "critical": 1 + } + } + }, + "type": "KubernetesNode" + }, + { + "agent_id": "00000000-0000-0000-0000-000000000000", + "components": [ + { + "agent_id": "00000000-0000-0000-0000-000000000000", + "id": "018681ff-80ed-d10d-21ef-c74f152b085b", + "is_leaf": false, + "name": "logistics-api-574dc95b5d-mp64w", + "parent_id": "018681ff-559f-7183-19d1-7d898b4e1413", + "parents": ["018681fe-f5aa-37e9-83f7-47b5b0232d5e"], + "status": "healthy", + "summary": { + "healthy": 1 + }, + "type": "KubernetesPod" + }, + { + "agent_id": "00000000-0000-0000-0000-000000000000", + "id": "018681ff-b6c1-a14d-2fd4-8c7dac94cddd", + "is_leaf": false, + "name": "logistics-ui-676b85b87c-tjjcp", + "parent_id": "018681ff-559f-7183-19d1-7d898b4e1413", + "parents": ["018681fe-f5aa-37e9-83f7-47b5b0232d5e"], + "status": "healthy", + "summary": { + "healthy": 1 + }, + "type": "KubernetesPod" + } + ], + "id": "018681fe-f5aa-37e9-83f7-47b5b0232d5e", + "is_leaf": false, + "name": "node-a", + "parent_id": "018681fe-b27e-7627-72c2-ad18e93f72f4", + "status": "healthy", + "summary": { + "healthy": 2 + }, + "type": "KubernetesNode" + } + ], + "external_id": "dummy/nodes", + "icon": "icon-kubernetes-node", + "id": "018681fe-b27e-7627-72c2-ad18e93f72f4", + "is_leaf": false, + "name": "Nodes", + "parent_id": "018681fe-8156-4b91-d178-caf8b3c2818c", + "path": "018681fe-8156-4b91-d178-caf8b3c2818c", + "status": "healthy", + "summary": { + "healthy": 2, + "insights": { + "security": { + "critical": 1 + } + } + }, + "tooltip": "Kubernetes Nodes", + "type": "KubernetesNodes" + }, + { + "agent_id": "00000000-0000-0000-0000-000000000000", + "components": [ + { + "agent_id": "00000000-0000-0000-0000-000000000000", + "id": "018681ff-80ed-d10d-21ef-c74f152b085b", + "is_leaf": false, + "name": "logistics-api-574dc95b5d-mp64w", + "parent_id": "018681ff-559f-7183-19d1-7d898b4e1413", + "status": "healthy", + "parents": ["018681fe-f5aa-37e9-83f7-47b5b0232d5e"], + "summary": { + "healthy": 1 + }, + "type": "KubernetesPod" + }, + { + "agent_id": "00000000-0000-0000-0000-000000000000", + "id": "018681ff-b6c1-a14d-2fd4-8c7dac94cddd", + "is_leaf": false, + "name": "logistics-ui-676b85b87c-tjjcp", + "parent_id": "018681ff-559f-7183-19d1-7d898b4e1413", + "status": "healthy", + "parents": ["018681fe-f5aa-37e9-83f7-47b5b0232d5e"], + "summary": { + "healthy": 1 + }, + "type": "KubernetesPod" + }, + { + "agent_id": "00000000-0000-0000-0000-000000000000", + "id": "018681ff-e578-a926-e366-d2dc0646eafa", + "is_leaf": false, + "name": "logistics-worker-79cb67d8f5-lr66n", + "parents": ["018681ff-227e-4d71-b38e-0693cc862213"], + "parent_id": "018681ff-559f-7183-19d1-7d898b4e1413", + "status": "healthy", + "summary": { + "healthy": 1 + }, + "type": "KubernetesPod" + } + ], + "external_id": "dummy/pods", + "icon": "icon-kubernetes-pod", + "id": "018681ff-559f-7183-19d1-7d898b4e1413", + "is_leaf": false, + "name": "Pods", + "parent_id": "018681fe-8156-4b91-d178-caf8b3c2818c", + "path": "018681fe-8156-4b91-d178-caf8b3c2818c", + "status": "healthy", + "summary": { + "healthy": 3 + }, + "tooltip": "Kubernetes Pods", + "type": "KubernetesPods" + } + ], + "external_id": "dummy/cluster", + "icon": "icon-cluster", + "id": "018681fe-8156-4b91-d178-caf8b3c2818c", + "is_leaf": false, + "name": "cluster", + "status": "healthy", + "summary": { + "healthy": 2, + "insights": { + "security": { + "critical": 1 + } + } + }, + "tooltip": "Kubernetes Cluster", + "type": "KubernetesCluster" + } + ], + "healthStatuses": ["healthy"], + "tags": null, + "teams": [], + "types": [ + "KubernetesCluster", + "KubernetesNode", + "KubernetesNodes", + "KubernetesPod", + "KubernetesPods" + ] +} diff --git a/fixtures/expectations/topology_depth_1_root_tree.json b/fixtures/expectations/topology_depth_1_root_tree.json index fefd817c..b7c43a1c 100644 --- a/fixtures/expectations/topology_depth_1_root_tree.json +++ b/fixtures/expectations/topology_depth_1_root_tree.json @@ -13,7 +13,6 @@ "owner": "logistics-team", "summary": { "healthy": 1, - "warning": 1, "incidents": { "availability": { "Blocker": 1 @@ -23,7 +22,8 @@ "security": { "critical": 1 } - } + }, + "warning": 1 }, "is_leaf": false, "created_at": "2023-01-01T05:29:00+05:30", @@ -44,6 +44,8 @@ } } }, + "icon": "icon-cluster", + "tooltip": "Kubernetes Cluster", "is_leaf": false, "created_at": "2023-01-01T05:29:00+05:30", "updated_at": "2023-01-01T05:29:00+05:30" diff --git a/fixtures/expectations/topology_depth_2_root_tree.json b/fixtures/expectations/topology_depth_2_root_tree.json index bd0879ef..32889a33 100644 --- a/fixtures/expectations/topology_depth_2_root_tree.json +++ b/fixtures/expectations/topology_depth_2_root_tree.json @@ -1,50 +1,21 @@ { "components": [ { - "id": "018681fc-e54f-bd4f-42be-068a9a69eeb5", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics", - "name": "logistics", - "labels": { - "telemetry": "enabled" - }, - "status": "warning", - "type": "Entity", - "owner": "logistics-team", - "summary": { - "healthy": 1, - "warning": 1, - "incidents": { - "availability": { - "Blocker": 1 - } - }, - "insights": { - "security": { - "critical": 1 - } - } - }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", "components": [ { - "id": "018681fd-5770-336f-227c-259435d7fc6b", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics-api", - "parent_id": "018681fc-e54f-bd4f-42be-068a9a69eeb5", - "name": "logistics-api", - "labels": { - "telemetry": "enabled" + "checks": { + "healthy": 2 }, + "id": "018681fd-5770-336f-227c-259435d7fc6b", + "is_leaf": false, + "labels": { "telemetry": "enabled" }, + "name": "logistics-api", + "parent_id": "018681fc-e54f-bd4f-42be-068a9a69eeb5", "status": "warning", - "type": "Application", - "owner": "logistics-team", - "path": "018681fc-e54f-bd4f-42be-068a9a69eeb5", "summary": { "healthy": 1, - "unhealthy": 1, "incidents": { "availability": { "Blocker": 1 @@ -54,33 +25,48 @@ "security": { "critical": 1 } - } + }, + "unhealthy": 1 }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", - "checks": { - "healthy": 2 - } + "type": "Application" }, { - "id": "018681fd-c1ff-16ee-dff0-8c8796e4263e", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics-ui", - "parent_id": "018681fc-e54f-bd4f-42be-068a9a69eeb5", + "id": "018681fd-c1ff-16ee-dff0-8c8796e4263e", + "is_leaf": false, "name": "logistics-ui", + "parent_id": "018681fc-e54f-bd4f-42be-068a9a69eeb5", "status": "healthy", - "type": "Application", - "owner": "logistics-team", - "path": "018681fc-e54f-bd4f-42be-068a9a69eeb5", "summary": { "healthy": 1 }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30" + "type": "Application" } - ] + ], + "external_id": "dummy/logistics", + "id": "018681fc-e54f-bd4f-42be-068a9a69eeb5", + "is_leaf": false, + "labels": { + "telemetry": "enabled" + }, + "name": "logistics", + "owner": "logistics-team", + "status": "warning", + "summary": { + "healthy": 1, + "incidents": { + "availability": { + "Blocker": 1 + } + }, + "insights": { + "security": { + "critical": 1 + } + }, + "warning": 1 + }, + "type": "Entity" }, { "id": "018681fe-8156-4b91-d178-caf8b3c2818c", @@ -97,14 +83,13 @@ } } }, + "icon": "icon-cluster", + "tooltip": "Kubernetes Cluster", "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", "components": [ { "id": "018681fe-b27e-7627-72c2-ad18e93f72f4", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/nodes", "parent_id": "018681fe-8156-4b91-d178-caf8b3c2818c", "name": "Nodes", "status": "healthy", @@ -117,14 +102,11 @@ } } }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30" + "is_leaf": false }, { "id": "018681ff-559f-7183-19d1-7d898b4e1413", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/pods", "parent_id": "018681fe-8156-4b91-d178-caf8b3c2818c", "name": "Pods", "status": "healthy", @@ -132,36 +114,25 @@ "summary": { "healthy": 3 }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30" + "is_leaf": false } ] }, { - "id": "4643e4de-6215-4c71-9600-9cf69b2cbbee", "agent_id": "ebd4cbf7-267e-48f9-a050-eca12e535ce1", "external_id": "dummy/payments-api", + "id": "4643e4de-6215-4c71-9600-9cf69b2cbbee", + "is_leaf": false, "name": "payments-api", "status": "healthy", - "type": "Application", - "summary": { - "healthy": 1 - }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30" + "summary": { "healthy": 1 }, + "type": "Application" } ], - "healthStatuses": [ - "healthy", - "unhealthy" - ], + "healthStatuses": ["healthy", "unhealthy"], "teams": [], "tags": { - "telemetry": [ - "enabled" - ] + "telemetry": ["enabled"] }, "types": [ "Application", diff --git a/fixtures/expectations/topology_root_tree.json b/fixtures/expectations/topology_root_tree.json index a5ef8794..d51b1d66 100644 --- a/fixtures/expectations/topology_root_tree.json +++ b/fixtures/expectations/topology_root_tree.json @@ -1,96 +1,35 @@ { "components": [ { - "id": "018681fc-e54f-bd4f-42be-068a9a69eeb5", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics", - "name": "logistics", - "labels": { - "telemetry": "enabled" - }, - "status": "warning", - "type": "Entity", - "owner": "logistics-team", - "summary": { - "healthy": 1, - "warning": 1, - "incidents": { - "availability": { - "Blocker": 1 - } - }, - "insights": { - "security": { - "critical": 1 - } - } - }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", "components": [ { - "id": "018681fd-5770-336f-227c-259435d7fc6b", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics-api", - "parent_id": "018681fc-e54f-bd4f-42be-068a9a69eeb5", - "name": "logistics-api", - "labels": { - "telemetry": "enabled" - }, - "status": "warning", - "type": "Application", - "owner": "logistics-team", - "path": "018681fc-e54f-bd4f-42be-068a9a69eeb5", - "summary": { - "healthy": 1, - "unhealthy": 1, - "incidents": { - "availability": { - "Blocker": 1 - } - }, - "insights": { - "security": { - "critical": 1 - } - } - }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", - "checks": { - "healthy": 2 - }, "components": [ { - "id": "018681fe-010a-6647-74ad-58b3a136dfe4", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics-worker", - "parent_id": "018681fd-5770-336f-227c-259435d7fc6b", + "id": "018681fe-010a-6647-74ad-58b3a136dfe4", + "is_leaf": false, "name": "logistics-worker", + "parent_id": "018681fd-5770-336f-227c-259435d7fc6b", "status": "healthy", - "type": "Application", - "path": "018681fc-e54f-bd4f-42be-068a9a69eeb5.018681fd-5770-336f-227c-259435d7fc6b", "summary": { "healthy": 1 }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30" + "type": "Application" }, { - "id": "018681fe-4529-c50f-26fd-530fa9c57319", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics-db", - "parent_id": "018681fd-5770-336f-227c-259435d7fc6b", + "checks": { + "unhealthy": 1 + }, + "id": "018681fe-4529-c50f-26fd-530fa9c57319", + "is_leaf": false, "name": "logistics-db", + "parent_id": "018681fd-5770-336f-227c-259435d7fc6b", "status": "unhealthy", "status_reason": "database not accepting connections", - "type": "Database", - "path": "018681fc-e54f-bd4f-42be-068a9a69eeb5.018681fd-5770-336f-227c-259435d7fc6b", "summary": { - "unhealthy": 1, "incidents": { "availability": { "Blocker": 1 @@ -100,181 +39,174 @@ "security": { "critical": 1 } - } - }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", - "checks": { + }, "unhealthy": 1 - } + }, + "type": "Database" } - ] + ], + "id": "018681fd-5770-336f-227c-259435d7fc6b", + "is_leaf": false, + "labels": { + "telemetry": "enabled" + }, + "name": "logistics-api", + "parent_id": "018681fc-e54f-bd4f-42be-068a9a69eeb5", + "status": "warning", + "checks": { + "healthy": 2 + }, + "summary": { + "healthy": 1, + "incidents": { + "availability": { + "Blocker": 1 + } + }, + "insights": { + "security": { + "critical": 1 + } + }, + "unhealthy": 1 + }, + "type": "Application" }, { - "id": "018681fd-c1ff-16ee-dff0-8c8796e4263e", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics-ui", - "parent_id": "018681fc-e54f-bd4f-42be-068a9a69eeb5", + "id": "018681fd-c1ff-16ee-dff0-8c8796e4263e", + "is_leaf": false, "name": "logistics-ui", + "parent_id": "018681fc-e54f-bd4f-42be-068a9a69eeb5", "status": "healthy", - "type": "Application", - "owner": "logistics-team", - "path": "018681fc-e54f-bd4f-42be-068a9a69eeb5", - "summary": { - "healthy": 1 - }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30" + "summary": {"healthy": 1}, + "type": "Application" } - ] - }, - { - "id": "018681fe-8156-4b91-d178-caf8b3c2818c", - "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/cluster", - "name": "cluster", - "status": "healthy", - "type": "KubernetesCluster", + ], + "external_id": "dummy/logistics", + "id": "018681fc-e54f-bd4f-42be-068a9a69eeb5", + "is_leaf": false, + "labels": { + "telemetry": "enabled" + }, + "name": "logistics", + "owner": "logistics-team", + "status": "warning", "summary": { - "healthy": 2, + "healthy": 1, + "incidents": { + "availability": { + "Blocker": 1 + } + }, "insights": { "security": { "critical": 1 } - } + }, + "warning": 1 }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", + "type": "Entity" + }, + { + "agent_id": "00000000-0000-0000-0000-000000000000", "components": [ { - "id": "018681fe-b27e-7627-72c2-ad18e93f72f4", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/nodes", - "parent_id": "018681fe-8156-4b91-d178-caf8b3c2818c", - "name": "Nodes", - "status": "healthy", - "type": "KubernetesNodes", - "summary": { - "healthy": 2, - "insights": { - "security": { - "critical": 1 - } - } - }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", "components": [ { - "id": "018681fe-f5aa-37e9-83f7-47b5b0232d5e", + "id": "018681ff-227e-4d71-b38e-0693cc862213", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/node-a", "parent_id": "018681fe-b27e-7627-72c2-ad18e93f72f4", - "name": "node-a", + "name": "node-b", "status": "healthy", "type": "KubernetesNode", "summary": { - "healthy": 2 + "healthy": 1, + "insights": { + "security": { + "critical": 1 + } + } }, "is_leaf": false, "created_at": "2023-01-01T05:29:00+05:30", "updated_at": "2023-01-01T05:29:00+05:30", "components": [ { - "id": "018681ff-80ed-d10d-21ef-c74f152b085b", - "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics-api-574dc95b5d-mp64w", - "parent_id": "018681ff-559f-7183-19d1-7d898b4e1413", - "name": "logistics-api-574dc95b5d-mp64w", - "status": "healthy", - "type": "KubernetesPod", - "summary": { - "healthy": 1 - }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", - "parents": [ - "018681fe-f5aa-37e9-83f7-47b5b0232d5e" - ] - }, - { - "id": "018681ff-b6c1-a14d-2fd4-8c7dac94cddd", + "id": "018681ff-e578-a926-e366-d2dc0646eafa", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics-ui-676b85b87c-tjjcp", "parent_id": "018681ff-559f-7183-19d1-7d898b4e1413", - "name": "logistics-ui-676b85b87c-tjjcp", + "name": "logistics-worker-79cb67d8f5-lr66n", + "parents": ["018681ff-227e-4d71-b38e-0693cc862213"], "status": "healthy", "type": "KubernetesPod", "summary": { "healthy": 1 }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", - "parents": [ - "018681fe-f5aa-37e9-83f7-47b5b0232d5e" - ] + "is_leaf": false } - ], - "children": [ - "018681ff-80ed-d10d-21ef-c74f152b085b", - "018681ff-b6c1-a14d-2fd4-8c7dac94cddd" ] }, { - "id": "018681ff-227e-4d71-b38e-0693cc862213", + "id": "018681fe-f5aa-37e9-83f7-47b5b0232d5e", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/node-b", "parent_id": "018681fe-b27e-7627-72c2-ad18e93f72f4", - "name": "node-b", + "name": "node-a", "status": "healthy", "type": "KubernetesNode", "summary": { - "healthy": 1, - "insights": { - "security": { - "critical": 1 - } - } + "healthy": 2 }, "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", "components": [ { - "id": "018681ff-e578-a926-e366-d2dc0646eafa", + "id": "018681ff-80ed-d10d-21ef-c74f152b085b", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics-worker-79cb67d8f5-lr66n", "parent_id": "018681ff-559f-7183-19d1-7d898b4e1413", - "name": "logistics-worker-79cb67d8f5-lr66n", + "name": "logistics-api-574dc95b5d-mp64w", + "status": "healthy", + "type": "KubernetesPod", + "parents": ["018681fe-f5aa-37e9-83f7-47b5b0232d5e"], + "summary": { + "healthy": 1 + }, + "is_leaf": false + }, + { + "id": "018681ff-b6c1-a14d-2fd4-8c7dac94cddd", + "agent_id": "00000000-0000-0000-0000-000000000000", + "parent_id": "018681ff-559f-7183-19d1-7d898b4e1413", + "name": "logistics-ui-676b85b87c-tjjcp", "status": "healthy", + "parents": ["018681fe-f5aa-37e9-83f7-47b5b0232d5e"], "type": "KubernetesPod", "summary": { "healthy": 1 }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", - "parents": [ - "018681ff-227e-4d71-b38e-0693cc862213" - ] + "is_leaf": false } - ], - "children": [ - "018681ff-e578-a926-e366-d2dc0646eafa" ] } - ] + ], + "id": "018681fe-b27e-7627-72c2-ad18e93f72f4", + "is_leaf": false, + "name": "Nodes", + "parent_id": "018681fe-8156-4b91-d178-caf8b3c2818c", + "status": "healthy", + "summary": { + "healthy": 2, + "insights": { + "security": { + "critical": 1 + } + } + }, + "type": "KubernetesNodes" }, { "id": "018681ff-559f-7183-19d1-7d898b4e1413", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/pods", "parent_id": "018681fe-8156-4b91-d178-caf8b3c2818c", "name": "Pods", "status": "healthy", @@ -283,80 +215,71 @@ "healthy": 3 }, "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", "components": [ { - "id": "018681ff-80ed-d10d-21ef-c74f152b085b", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics-api-574dc95b5d-mp64w", - "parent_id": "018681ff-559f-7183-19d1-7d898b4e1413", + "id": "018681ff-80ed-d10d-21ef-c74f152b085b", + "is_leaf": false, "name": "logistics-api-574dc95b5d-mp64w", + "parents": ["018681fe-f5aa-37e9-83f7-47b5b0232d5e"], + "parent_id": "018681ff-559f-7183-19d1-7d898b4e1413", "status": "healthy", - "type": "KubernetesPod", - "summary": { - "healthy": 1 - }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", - "parents": [ - "018681fe-f5aa-37e9-83f7-47b5b0232d5e" - ] + "summary": { "healthy": 1 }, + "type": "KubernetesPod" }, { - "id": "018681ff-b6c1-a14d-2fd4-8c7dac94cddd", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics-ui-676b85b87c-tjjcp", - "parent_id": "018681ff-559f-7183-19d1-7d898b4e1413", + "id": "018681ff-b6c1-a14d-2fd4-8c7dac94cddd", + "is_leaf": false, + "parents": ["018681fe-f5aa-37e9-83f7-47b5b0232d5e"], "name": "logistics-ui-676b85b87c-tjjcp", + "parent_id": "018681ff-559f-7183-19d1-7d898b4e1413", "status": "healthy", - "type": "KubernetesPod", - "summary": { - "healthy": 1 - }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", - "parents": [ - "018681fe-f5aa-37e9-83f7-47b5b0232d5e" - ] + "summary": { "healthy": 1 }, + "type": "KubernetesPod" }, { - "id": "018681ff-e578-a926-e366-d2dc0646eafa", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics-worker-79cb67d8f5-lr66n", - "parent_id": "018681ff-559f-7183-19d1-7d898b4e1413", + "id": "018681ff-e578-a926-e366-d2dc0646eafa", + "is_leaf": false, "name": "logistics-worker-79cb67d8f5-lr66n", + "parents": ["018681ff-227e-4d71-b38e-0693cc862213"], + "parent_id": "018681ff-559f-7183-19d1-7d898b4e1413", "status": "healthy", - "type": "KubernetesPod", - "summary": { - "healthy": 1 - }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", - "parents": [ - "018681ff-227e-4d71-b38e-0693cc862213" - ] + "summary": { "healthy": 1 }, + "type": "KubernetesPod" } ] } - ] + ], + "external_id": "dummy/cluster", + "icon": "icon-cluster", + "id": "018681fe-8156-4b91-d178-caf8b3c2818c", + "is_leaf": false, + "name": "cluster", + "status": "healthy", + "summary": { + "healthy": 2, + "insights": { + "security": { + "critical": 1 + } + } + }, + "tooltip": "Kubernetes Cluster", + "type": "KubernetesCluster" }, { - "id": "4643e4de-6215-4c71-9600-9cf69b2cbbee", "agent_id": "ebd4cbf7-267e-48f9-a050-eca12e535ce1", "external_id": "dummy/payments-api", + "id": "4643e4de-6215-4c71-9600-9cf69b2cbbee", + "is_leaf": false, "name": "payments-api", "status": "healthy", - "type": "Application", "summary": { "healthy": 1 }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30" + "type": "Application" } ], "healthStatuses": [ diff --git a/fixtures/expectations/topology_tree_with_agent_id.json b/fixtures/expectations/topology_tree_with_agent_id.json index 57ed2566..53f9904a 100644 --- a/fixtures/expectations/topology_tree_with_agent_id.json +++ b/fixtures/expectations/topology_tree_with_agent_id.json @@ -10,9 +10,7 @@ "summary": { "healthy": 1 }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30" + "is_leaf": false } ], "healthStatuses": [ diff --git a/fixtures/expectations/topology_tree_with_label_filter.json b/fixtures/expectations/topology_tree_with_label_filter.json index 5e9a59cb..c3d661f0 100644 --- a/fixtures/expectations/topology_tree_with_label_filter.json +++ b/fixtures/expectations/topology_tree_with_label_filter.json @@ -2,26 +2,13 @@ "components": [ { "id": "018681fc-e54f-bd4f-42be-068a9a69eeb5", - "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics", - "name": "logistics", - "labels": { - "telemetry": "enabled" - }, - "status": "healthy", - "type": "Entity", - "owner": "logistics-team", - "summary": { - "healthy": 1 - }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", "components": [ { "id": "018681fd-5770-336f-227c-259435d7fc6b", + "checks": { + "healthy": 2 + }, "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics-api", "parent_id": "018681fc-e54f-bd4f-42be-068a9a69eeb5", "name": "logistics-api", "labels": { @@ -29,32 +16,31 @@ }, "status": "healthy", "type": "Application", - "owner": "logistics-team", - "path": "018681fc-e54f-bd4f-42be-068a9a69eeb5", "summary": { "healthy": 1 }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", - "checks": { - "healthy": 2 - } + "is_leaf": false } - ] + ], + "agent_id": "00000000-0000-0000-0000-000000000000", + "external_id": "dummy/logistics", + "name": "logistics", + "labels": { + "telemetry": "enabled" + }, + "status": "healthy", + "type": "Entity", + "owner": "logistics-team", + "summary": { + "healthy": 1 + }, + "is_leaf": false } ], - "healthStatuses": [ - "healthy" - ], + "healthStatuses": ["healthy"], "teams": [], "tags": { - "telemetry": [ - "enabled" - ] + "telemetry": ["enabled"] }, - "types": [ - "Application", - "Entity" - ] + "types": ["Application", "Entity"] } diff --git a/fixtures/expectations/topology_tree_with_owner_filter.json b/fixtures/expectations/topology_tree_with_owner_filter.json index 37ecfa4c..d503e9ad 100644 --- a/fixtures/expectations/topology_tree_with_owner_filter.json +++ b/fixtures/expectations/topology_tree_with_owner_filter.json @@ -21,7 +21,9 @@ { "id": "018681fd-5770-336f-227c-259435d7fc6b", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics-api", + "checks": { + "healthy": 2 + }, "parent_id": "018681fc-e54f-bd4f-42be-068a9a69eeb5", "name": "logistics-api", "labels": { @@ -29,34 +31,22 @@ }, "status": "healthy", "type": "Application", - "owner": "logistics-team", - "path": "018681fc-e54f-bd4f-42be-068a9a69eeb5", "summary": { "healthy": 1 }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", - "checks": { - "healthy": 2 - } + "is_leaf": false }, { "id": "018681fd-c1ff-16ee-dff0-8c8796e4263e", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics-ui", "parent_id": "018681fc-e54f-bd4f-42be-068a9a69eeb5", "name": "logistics-ui", "status": "healthy", "type": "Application", - "owner": "logistics-team", - "path": "018681fc-e54f-bd4f-42be-068a9a69eeb5", "summary": { "healthy": 1 }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30" + "is_leaf": false } ] } diff --git a/fixtures/expectations/topology_tree_with_status_filter.json b/fixtures/expectations/topology_tree_with_status_filter.json index 088c3045..f2e3c7ef 100644 --- a/fixtures/expectations/topology_tree_with_status_filter.json +++ b/fixtures/expectations/topology_tree_with_status_filter.json @@ -1,96 +1,38 @@ { "components": [ { - "id": "018681fc-e54f-bd4f-42be-068a9a69eeb5", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics", - "name": "logistics", - "labels": { - "telemetry": "enabled" - }, - "status": "warning", - "type": "Entity", - "owner": "logistics-team", - "summary": { - "healthy": 1, - "warning": 1, - "incidents": { - "availability": { - "Blocker": 1 - } - }, - "insights": { - "security": { - "critical": 1 - } - } - }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", "components": [ { - "id": "018681fd-5770-336f-227c-259435d7fc6b", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics-api", - "parent_id": "018681fc-e54f-bd4f-42be-068a9a69eeb5", - "name": "logistics-api", - "labels": { - "telemetry": "enabled" - }, - "status": "warning", - "type": "Application", - "owner": "logistics-team", - "path": "018681fc-e54f-bd4f-42be-068a9a69eeb5", - "summary": { - "healthy": 1, - "unhealthy": 1, - "incidents": { - "availability": { - "Blocker": 1 - } - }, - "insights": { - "security": { - "critical": 1 - } - } - }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", "checks": { "healthy": 2 }, "components": [ { - "id": "018681fe-010a-6647-74ad-58b3a136dfe4", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics-worker", - "parent_id": "018681fd-5770-336f-227c-259435d7fc6b", + "id": "018681fe-010a-6647-74ad-58b3a136dfe4", + "is_leaf": false, "name": "logistics-worker", + "parent_id": "018681fd-5770-336f-227c-259435d7fc6b", "status": "healthy", - "type": "Application", - "path": "018681fc-e54f-bd4f-42be-068a9a69eeb5.018681fd-5770-336f-227c-259435d7fc6b", "summary": { "healthy": 1 }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30" + "type": "Application" }, { - "id": "018681fe-4529-c50f-26fd-530fa9c57319", "agent_id": "00000000-0000-0000-0000-000000000000", - "external_id": "dummy/logistics-db", - "parent_id": "018681fd-5770-336f-227c-259435d7fc6b", + "checks": { + "unhealthy": 1 + }, + "id": "018681fe-4529-c50f-26fd-530fa9c57319", + "is_leaf": false, "name": "logistics-db", + "parent_id": "018681fd-5770-336f-227c-259435d7fc6b", "status": "unhealthy", "status_reason": "database not accepting connections", - "type": "Database", - "path": "018681fc-e54f-bd4f-42be-068a9a69eeb5.018681fd-5770-336f-227c-259435d7fc6b", "summary": { - "unhealthy": 1, "incidents": { "availability": { "Blocker": 1 @@ -100,29 +42,67 @@ "security": { "critical": 1 } - } - }, - "is_leaf": false, - "created_at": "2023-01-01T05:29:00+05:30", - "updated_at": "2023-01-01T05:29:00+05:30", - "checks": { + }, "unhealthy": 1 - } + }, + "type": "Database" } - ] + ], + "id": "018681fd-5770-336f-227c-259435d7fc6b", + "is_leaf": false, + "labels": { + "telemetry": "enabled" + }, + "name": "logistics-api", + "parent_id": "018681fc-e54f-bd4f-42be-068a9a69eeb5", + "status": "warning", + "summary": { + "healthy": 1, + "incidents": { + "availability": { + "Blocker": 1 + } + }, + "insights": { + "security": { + "critical": 1 + } + }, + "unhealthy": 1 + }, + "type": "Application" } - ] + ], + "external_id": "dummy/logistics", + "id": "018681fc-e54f-bd4f-42be-068a9a69eeb5", + "is_leaf": false, + "labels": { + "telemetry": "enabled" + }, + "name": "logistics", + "owner": "logistics-team", + "status": "warning", + "summary": { + "healthy": 1, + "incidents": { + "availability": { + "Blocker": 1 + } + }, + "insights": { + "security": { + "critical": 1 + } + }, + "warning": 1 + }, + "type": "Entity" } ], - "healthStatuses": [ - "healthy", - "unhealthy" - ], + "healthStatuses": ["healthy", "unhealthy"], "teams": [], "tags": { - "telemetry": [ - "enabled" - ] + "telemetry": ["enabled"] }, "types": [ "Application", diff --git a/suite_test.go b/suite_test.go index f8fe83c1..5c035bda 100644 --- a/suite_test.go +++ b/suite_test.go @@ -126,16 +126,16 @@ func parseJQ(v []byte, expr string) ([]byte, error) { return jsonVal, nil } -func matchJSON(a []byte, b []byte, jqExpr *string) { - var valueA, valueB = a, b +func matchJSON(actual []byte, expected []byte, jqExpr *string) { + var valueA, valueB = actual, expected var err error if jqExpr != nil { - valueA, err = parseJQ(a, *jqExpr) + valueA, err = parseJQ(actual, *jqExpr) if err != nil { Expect(err).ToNot(HaveOccurred()) } - valueB, err = parseJQ(b, *jqExpr) + valueB, err = parseJQ(expected, *jqExpr) if err != nil { Expect(err).ToNot(HaveOccurred()) } diff --git a/topology.go b/topology.go index d98f98fe..cf30b0fe 100644 --- a/topology.go +++ b/topology.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "slices" "strings" "github.com/flanksource/commons/collections" @@ -26,7 +27,8 @@ type TopologyOptions struct { Types []string Status []string - // when set to true, only the children (except the immediate children) are returned + // when set to true, only the children (except the immediate children) are returned. + // when set to false, the direct children & the parent itself is fetched. restOfTheChildrenOnly bool } @@ -34,23 +36,33 @@ func (opt TopologyOptions) String() string { return fmt.Sprintf("%#v", opt) } +// selectClause returns the columns that should be selected from the topology view. func (opt TopologyOptions) selectClause() string { if !opt.restOfTheChildrenOnly { return "*" } - return "name, namespace, id, is_leaf, status, status_reason, summary, topology_type, labels, team_names, agent_id, parent_id, type" + // parents, incidents, analysis, checks columns need to fetched to create the topology tree even though they may not be essential to the UI. + return "name, namespace, id, is_leaf, status, status_reason, summary, topology_type, labels, team_names, agent_id, parent_id, type, parents, incidents, analysis, checks" } func (opt TopologyOptions) componentWhereClause() string { s := "WHERE components.deleted_at IS NULL" + if opt.ID != "" { if !opt.restOfTheChildrenOnly { s += " AND (components.id = @id OR components.parent_id = @id)" } else { s += " AND (components.path LIKE @path AND components.id != @id AND components.parent_id != @id)" } + } else { + if !opt.restOfTheChildrenOnly { + s += " AND components.parent_id IS NULL" + } else { + s += " AND components.parent_id IS NOT NULL" + } } + if opt.Owner != "" { s += " AND (components.owner = @owner)" } @@ -77,6 +89,12 @@ func (opt TopologyOptions) componentRelationWhereClause() string { } else { s += " AND (component_relationships.relationship_id = @id OR (parent.path LIKE @path AND parent.parent_id != @id))" } + } else { + if !opt.restOfTheChildrenOnly { + s += " AND component_relationships.component_id = NULL" + } else { + s += " AND component_relationships.component_id IS NOT NULL" + } } return s } @@ -164,11 +182,6 @@ func fetchAllComponents(ctx context.Context, dbpool *pgxpool.Pool, params Topolo } } - // On the home view (id=""), the first query covers all the components. - if params.ID == "" { - return response, nil - } - params.restOfTheChildrenOnly = true query, args = generateQuery(params) rows, err = dbpool.Query(ctx, query, pgx.NamedArgs(args)) @@ -188,13 +201,16 @@ func fetchAllComponents(ctx context.Context, dbpool *pgxpool.Pool, params Topolo } } - response.Components = append(response.Components, nonImmediateChildren.Components...) + if len(nonImmediateChildren.Components) > 0 { + response.Components = append(response.Components, nonImmediateChildren.Components...) + response.HealthStatuses = uniqueAppend(response.HealthStatuses, nonImmediateChildren.HealthStatuses) + response.Teams = uniqueAppend(response.Teams, nonImmediateChildren.Teams) + response.Types = uniqueAppend(response.Types, nonImmediateChildren.Types) + if response.Tags != nil || nonImmediateChildren.Tags != nil { + response.Tags = collections.MergeMap(response.Tags, nonImmediateChildren.Tags) + } + } - // Not sure if we need these from non immediate children - // response.HealthStatuses = append(response.HealthStatuses, nonImmediateChildren.HealthStatuses...) - // response.Teams = append(response.Teams, nonImmediateChildren.Teams...) - // response.Tags = append(response.Tags, nonImmediateChildren.Tags...) - // response.Types = append(response.Types, nonImmediateChildren.Types...) return response, nil } @@ -382,3 +398,29 @@ func GetComponent(ctx context.Context, db *gorm.DB, id string) (*models.Componen return &component, nil } + +// uniqueAppend appends elements of b into a if they are not already present in a. +func uniqueAppend(a, b []string) []string { + if len(a) == 0 { + return b + } + + if len(b) == 0 { + return a + } + + m := make(map[string]struct{}) + for _, v := range a { + m[v] = struct{}{} + } + + for _, v := range b { + if _, ok := m[v]; !ok { + m[v] = struct{}{} + a = append(a, v) + } + } + + slices.Sort(a) + return a +} diff --git a/topology_test.go b/topology_test.go index 0095aa04..c9b50a94 100644 --- a/topology_test.go +++ b/topology_test.go @@ -11,6 +11,7 @@ import ( "github.com/flanksource/duty/types" ginkgo "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "github.com/onsi/gomega/format" ) // For debugging @@ -40,15 +41,20 @@ func testTopologyJSON(opts TopologyOptions, path string) { expected := readTestFile(path) jqExpr := `del(.. | .created_at?, .updated_at?)` - matchJSON([]byte(expected), treeJSON, &jqExpr) + matchJSON(treeJSON, []byte(expected), &jqExpr) } var _ = ginkgo.Describe("Topology behavior", func() { + format.MaxLength = 0 // Do not truncate diffs ginkgo.It("Should create root tree", func() { testTopologyJSON(TopologyOptions{}, "fixtures/expectations/topology_root_tree.json") }) + ginkgo.It("Should fetch minimal details of other children", func() { + testTopologyJSON(TopologyOptions{ID: dummy.ClusterComponent.ID.String(), Depth: 5}, "fixtures/expectations/topology_cluster_component_tree.json") + }) + ginkgo.It("Should create child tree", func() { testTopologyJSON(TopologyOptions{ID: dummy.NodeA.ID.String()}, "fixtures/expectations/topology_child_tree.json") })