From 77439cf491db827d8f645b9e91f34b75600973ae Mon Sep 17 00:00:00 2001 From: Gordon Date: Thu, 4 Jan 2024 22:24:52 -0500 Subject: [PATCH 1/5] Fix Glue layer to only look at operational resource paths --- pkg/engine2/path_selection/paths.go | 2 +- pkg/engine2/reconciler/remove_edge.go | 4 +- .../testdata/2_routes.dataflow-viz.yaml | 34 ++- ...elete_namespace_resource.dataflow-viz.yaml | 1 - ...ete_resource_and_iacdeps.dataflow-viz.yaml | 9 +- .../testdata/ecs_rds.dataflow-viz.yaml | 32 +-- pkg/engine2/testdata/ecs_rds.expect.yaml | 12 +- .../testdata/extend_graph.dataflow-viz.yaml | 14 +- .../testdata/k8s_api.dataflow-viz.yaml | 199 +++++------------- pkg/engine2/testdata/k8s_api.expect.yaml | 4 +- .../testdata/lambda_efs.dataflow-viz.yaml | 34 ++- pkg/engine2/testdata/lambda_efs.expect.yaml | 2 +- .../namespace_pathselect.dataflow-viz.yaml | 22 +- .../testdata/remove_path.dataflow-viz.yaml | 39 ++-- pkg/engine2/testdata/remove_path.expect.yaml | 10 +- pkg/engine2/testdata/rename.dataflow-viz.yaml | 9 +- .../testdata/single_lambda.dataflow-viz.yaml | 1 - .../testdata/static_site.dataflow-viz.yaml | 8 +- .../testdata/vpc_import.dataflow-viz.yaml | 5 + .../vpc_import_to_lambda.dataflow-viz.yaml | 8 +- ...ort_wo_subnets_to_lambda.dataflow-viz.yaml | 18 +- ...pc_import_wo_subnets_to_lambda.expect.yaml | 2 +- pkg/engine2/visualizer.go | 160 +++++++------- pkg/graph_addons/path.go | 9 + pkg/graph_addons/walk.go | 28 +-- pkg/knowledge_base2/dynamic_value.go | 16 +- pkg/knowledge_base2/graph.go | 73 +++++-- pkg/visualizer2/vis_graph.go | 17 ++ 28 files changed, 384 insertions(+), 388 deletions(-) diff --git a/pkg/engine2/path_selection/paths.go b/pkg/engine2/path_selection/paths.go index 205968171..0eaea619d 100644 --- a/pkg/engine2/path_selection/paths.go +++ b/pkg/engine2/path_selection/paths.go @@ -13,7 +13,7 @@ import ( func GetPaths( sol solution_context.SolutionContext, source, target construct.ResourceId, - pathValidityChecks func(source, target construct.ResourceId, path []construct.ResourceId) bool, + pathValidityChecks func(source, target construct.ResourceId, path construct.Path) bool, hasPathCheck bool, ) ([]construct.Path, error) { var errs error diff --git a/pkg/engine2/reconciler/remove_edge.go b/pkg/engine2/reconciler/remove_edge.go index 1609fdb2b..0a6019050 100644 --- a/pkg/engine2/reconciler/remove_edge.go +++ b/pkg/engine2/reconciler/remove_edge.go @@ -155,7 +155,7 @@ func findEdgesUsedInOtherPathSelection( continue } paths, err := path_selection.GetPaths(ctx, upstream, target, - func(source, target construct.ResourceId, path []construct.ResourceId) bool { return true }, false) + func(source, target construct.ResourceId, path construct.Path) bool { return true }, false) if err != nil { errs = errors.Join(errs, err) continue @@ -190,7 +190,7 @@ func findEdgesUsedInOtherPathSelection( } paths, err := path_selection.GetPaths(ctx, source, downstream, - func(source, target construct.ResourceId, path []construct.ResourceId) bool { return true }, false) + func(source, target construct.ResourceId, path construct.Path) bool { return true }, false) if err != nil { errs = errors.Join(errs, err) continue diff --git a/pkg/engine2/testdata/2_routes.dataflow-viz.yaml b/pkg/engine2/testdata/2_routes.dataflow-viz.yaml index 7130dda8d..b359fd462 100755 --- a/pkg/engine2/testdata/2_routes.dataflow-viz.yaml +++ b/pkg/engine2/testdata/2_routes.dataflow-viz.yaml @@ -3,6 +3,7 @@ resources: lambda_function/lambda_function_1: children: - aws:ecr_image:lambda_function_1-image + - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_function_1-ExecutionRole parent: rest_api/rest_api_1 tag: big @@ -12,21 +13,14 @@ resources: - aws:ecr_image:lambda_function_0-image - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_function_0-ExecutionRole - - aws:log_group:lambda_function_0-log-group - - aws:log_group:lambda_function_1-log-group parent: rest_api/rest_api_1 tag: big rest_api/rest_api_1: - children: - - aws:api_resource:rest_api_1:lambda0 - - aws:api_resource:rest_api_1:lambda1 - - aws:api_stage:rest_api_1:api_stage-0 - tag: parent - - aws:api_integration:rest_api_1/integ1: children: - aws:api_deployment:rest_api_1:api_deployment-0 + - aws:api_integration:rest_api_1:integ0 + - aws:api_integration:rest_api_1:integ1 - aws:api_method:rest_api_1:integ0-api_method - aws:api_method:rest_api_1:integ1-api_method - aws:api_resource:rest_api_1:api_resource-0 @@ -34,25 +28,21 @@ resources: - aws:api_resource:rest_api_1:lambda0 - aws:api_resource:rest_api_1:lambda1 - aws:api_stage:rest_api_1:api_stage-0 - - aws:lambda_permission:integ0-lambda_function_0 - - aws:lambda_permission:integ1-lambda_function_1 + tag: parent + + aws:api_integration:rest_api_1/integ1: parent: rest_api/rest_api_1 tag: big aws:api_integration:rest_api_1/integ1 -> lambda_function/lambda_function_1: - aws:api_integration:rest_api_1/integ0: - children: - - aws:api_deployment:rest_api_1:api_deployment-0 - - aws:api_method:rest_api_1:integ0-api_method - - aws:api_method:rest_api_1:integ1-api_method - - aws:api_resource:rest_api_1:api_resource-0 - - aws:api_resource:rest_api_1:api_resource-1 - - aws:api_resource:rest_api_1:lambda0 - - aws:api_resource:rest_api_1:lambda1 - - aws:api_stage:rest_api_1:api_stage-0 - - aws:lambda_permission:integ0-lambda_function_0 + path: - aws:lambda_permission:integ1-lambda_function_1 + + aws:api_integration:rest_api_1/integ0: parent: rest_api/rest_api_1 tag: big aws:api_integration:rest_api_1/integ0 -> lambda_function/lambda_function_0: + path: + - aws:lambda_permission:integ0-lambda_function_0 + diff --git a/pkg/engine2/testdata/delete_namespace_resource.dataflow-viz.yaml b/pkg/engine2/testdata/delete_namespace_resource.dataflow-viz.yaml index 6989b1662..48b9543d9 100644 --- a/pkg/engine2/testdata/delete_namespace_resource.dataflow-viz.yaml +++ b/pkg/engine2/testdata/delete_namespace_resource.dataflow-viz.yaml @@ -5,6 +5,5 @@ resources: - aws:ecr_image:lambda_function_2-image - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_function_2-ExecutionRole - - aws:log_group:lambda_function_2-log-group tag: big diff --git a/pkg/engine2/testdata/delete_resource_and_iacdeps.dataflow-viz.yaml b/pkg/engine2/testdata/delete_resource_and_iacdeps.dataflow-viz.yaml index c1861776f..c3a3924f8 100644 --- a/pkg/engine2/testdata/delete_resource_and_iacdeps.dataflow-viz.yaml +++ b/pkg/engine2/testdata/delete_resource_and_iacdeps.dataflow-viz.yaml @@ -1,16 +1,15 @@ provider: aws resources: rest_api/rest_api_0: - children: - - aws:api_stage:rest_api_0:api_stage-0 - tag: parent - - aws:api_integration:rest_api_0/rest_api_0_integration_0: children: - aws:api_deployment:rest_api_0:api_deployment-0 + - aws:api_integration:rest_api_0:rest_api_0_integration_0 - aws:api_method:rest_api_0:rest_api_0_integration_0_method - aws:api_resource:rest_api_0:api_resource-0 - aws:api_stage:rest_api_0:api_stage-0 + tag: parent + + aws:api_integration:rest_api_0/rest_api_0_integration_0: parent: rest_api/rest_api_0 tag: big diff --git a/pkg/engine2/testdata/ecs_rds.dataflow-viz.yaml b/pkg/engine2/testdata/ecs_rds.dataflow-viz.yaml index 405aa42ef..309f404d6 100755 --- a/pkg/engine2/testdata/ecs_rds.dataflow-viz.yaml +++ b/pkg/engine2/testdata/ecs_rds.dataflow-viz.yaml @@ -2,41 +2,47 @@ provider: aws resources: rds_instance/rds-instance-2: children: - - aws:iam_role:ecs_service_0-execution-role - aws:rds_subnet_group:rds_subnet_group-0 - - aws:security_group:vpc-0:rds-instance-2-security_group + - aws:subnet:vpc-0:subnet-0 + - aws:subnet:vpc-0:subnet-1 parent: vpc/vpc-0 tag: big vpc/vpc-0: children: - - aws:ecr_image:ecs_service_0-image - - aws:ecr_repo:ecr_repo-0 - - aws:ecs_task_definition:ecs_service_0 - - aws:elastic_ip:subnet-0-route_table-nat_gateway-elastic_ip - - aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip - - aws:iam_role:ecs_service_0-execution-role - aws:internet_gateway:vpc-0:internet_gateway-0 - - aws:log_group:ecs_service_0-log-group - - aws:nat_gateway:subnet-2:subnet-0-route_table-nat_gateway - - aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway - - aws:rds_subnet_group:rds_subnet_group-0 - aws:route_table:vpc-0:subnet-0-route_table - aws:route_table:vpc-0:subnet-1-route_table - aws:route_table:vpc-0:subnet-2-route_table - aws:route_table:vpc-0:subnet-3-route_table - aws:security_group:vpc-0:ecs_service_0-security_group - aws:security_group:vpc-0:rds-instance-2-security_group + - aws:subnet:vpc-0:subnet-0 + - aws:subnet:vpc-0:subnet-1 + - aws:subnet:vpc-0:subnet-2 + - aws:subnet:vpc-0:subnet-3 tag: parent ecs_service/ecs_service_0: children: + - aws:availability_zone:region-0:availability_zone-0 + - aws:availability_zone:region-0:availability_zone-1 - aws:ecr_image:ecs_service_0-image - aws:ecr_repo:ecr_repo-0 + - aws:ecs_cluster:ecs_cluster-0 - aws:ecs_task_definition:ecs_service_0 + - aws:iam_role:ecs_service_0-execution-role - aws:log_group:ecs_service_0-log-group - - aws:security_group:vpc-0:ecs_service_0-security_group + - aws:region:region-0 + - aws:subnet:vpc-0:subnet-0 + - aws:subnet:vpc-0:subnet-1 + - aws:vpc:vpc-0 parent: vpc/vpc-0 tag: big ecs_service/ecs_service_0 -> rds_instance/rds-instance-2: + path: + - aws:ecs_task_definition:ecs_service_0 + - aws:iam_role:ecs_service_0-execution-role + - aws:security_group:vpc-0:rds-instance-2-security_group + diff --git a/pkg/engine2/testdata/ecs_rds.expect.yaml b/pkg/engine2/testdata/ecs_rds.expect.yaml index 674b9038e..f8d24085d 100755 --- a/pkg/engine2/testdata/ecs_rds.expect.yaml +++ b/pkg/engine2/testdata/ecs_rds.expect.yaml @@ -169,12 +169,6 @@ resources: Protocol: "-1" ToPort: 0 IngressRules: - - CidrBlocks: - - 10.0.128.0/18 - Description: Allow ingress traffic from ip addresses within the subnet subnet-0 - FromPort: 0 - Protocol: "-1" - ToPort: 0 - CidrBlocks: - 10.0.192.0/18 Description: Allow ingress traffic from ip addresses within the subnet subnet-1 @@ -186,6 +180,12 @@ resources: Protocol: "-1" Self: true ToPort: 0 + - CidrBlocks: + - 10.0.128.0/18 + Description: Allow ingress traffic from ip addresses within the subnet subnet-0 + FromPort: 0 + Protocol: "-1" + ToPort: 0 Vpc: aws:vpc:vpc-0 aws:route_table:vpc-0:subnet-0-route_table: Routes: diff --git a/pkg/engine2/testdata/extend_graph.dataflow-viz.yaml b/pkg/engine2/testdata/extend_graph.dataflow-viz.yaml index 2042458ef..f3279bbe5 100755 --- a/pkg/engine2/testdata/extend_graph.dataflow-viz.yaml +++ b/pkg/engine2/testdata/extend_graph.dataflow-viz.yaml @@ -5,12 +5,15 @@ resources: - aws:ecr_image:lambda_function_0-image - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_function_0-ExecutionRole - - aws:log_group:lambda_function_0-log-group parent: rest_api/rest_api_1 tag: big rest_api/rest_api_1: children: + - aws:api_deployment:rest_api_1:api_deployment-0 + - aws:api_integration:rest_api_1:rest_api_1_integration_0 + - aws:api_method:rest_api_1:rest_api_1_integration_0_method + - aws:api_resource:rest_api_1:api_resource-0 - aws:api_stage:rest_api_1:api_stage-0 tag: parent @@ -18,13 +21,10 @@ resources: tag: big aws:api_integration:rest_api_1/rest_api_1_integration_0: - children: - - aws:api_deployment:rest_api_1:api_deployment-0 - - aws:api_method:rest_api_1:rest_api_1_integration_0_method - - aws:api_resource:rest_api_1:api_resource-0 - - aws:api_stage:rest_api_1:api_stage-0 - - aws:lambda_permission:rest_api_1_integration_0_lambda_function_0 parent: rest_api/rest_api_1 tag: big aws:api_integration:rest_api_1/rest_api_1_integration_0 -> lambda_function/lambda_function_0: + path: + - aws:lambda_permission:rest_api_1_integration_0_lambda_function_0 + diff --git a/pkg/engine2/testdata/k8s_api.dataflow-viz.yaml b/pkg/engine2/testdata/k8s_api.dataflow-viz.yaml index 50a2f40a8..203b904e2 100755 --- a/pkg/engine2/testdata/k8s_api.dataflow-viz.yaml +++ b/pkg/engine2/testdata/k8s_api.dataflow-viz.yaml @@ -2,166 +2,92 @@ provider: aws resources: kubernetes:pod:eks_cluster-0/pod2: children: - - aws:ecr_image:pod2-ecr_image - - aws:ecr_repo:ecr_repo-0 + - aws:availability_zone:region-0:availability_zone-0 + - aws:eks_cluster:eks_cluster-0 + - aws:iam_role:ClusterRole-eks_cluster-0 + - aws:region:region-0 + - aws:subnet:vpc-0:subnet-0 + - aws:subnet:vpc-0:subnet-1 + - aws:vpc:vpc-0 parent: eks_cluster/eks_cluster-0 tag: big load_balancer/rest-api-4-integbcc77100: children: - - aws:api_deployment:rest_api_4:api_deployment-0 - - aws:api_method:rest_api_4:rest_api_4_integration_0_method - - aws:api_resource:rest_api_4:api_resource-0 - - aws:api_stage:rest_api_4:api_stage-0 - - aws:eks_add_on:amazon-cloudwatch-observability - - aws:eks_node_group:eks_node_group-0 - - aws:elastic_ip:subnet-0-route_table-nat_gateway-elastic_ip - - aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip - - aws:iam_oidc_provider:eks_cluster-0 - - aws:iam_policy:iam_policy-0 - - aws:iam_role:amazon-cloudwatch-observability-iam_role - - aws:iam_role:aws-load-balancer-controller - - aws:iam_role:eks_node_group-0-iam_role - - aws:iam_role:pod2 - - aws:internet_gateway:vpc-0:internet_gateway-0 - - aws:load_balancer_listener:rest_api_4_integration_0-pod2 - - aws:nat_gateway:subnet-2:subnet-0-route_table-nat_gateway - - aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway - - aws:route_table:vpc-0:subnet-0-route_table - - aws:route_table:vpc-0:subnet-1-route_table - - aws:route_table:vpc-0:subnet-2-route_table - - aws:route_table:vpc-0:subnet-3-route_table - - aws:security_group:vpc-0:eks_cluster-0-security_group - - aws:target_group:rest-api-4-integbcc77100 - - aws:vpc_link:rest_api_4_integration_0-pod2 - - kubernetes:service:eks_cluster-0:restapi4integration0-pod2 - - kubernetes:service_account:eks_cluster-0:aws-load-balancer-controller - - kubernetes:service_account:eks_cluster-0:pod2 - - kubernetes:target_group_binding:eks_cluster-0:restapi4integration0-pod2 + - aws:availability_zone:region-0:availability_zone-0 + - aws:availability_zone:region-0:availability_zone-1 + - aws:region:region-0 + - aws:subnet:vpc-0:subnet-0 + - aws:subnet:vpc-0:subnet-1 + - aws:vpc:vpc-0 parent: eks_cluster/eks_cluster-0 tag: big load_balancer/rest-api-4-integbcc77100 -> kubernetes:pod:eks_cluster-0/pod2: - kubernetes:helm_chart:eks_cluster-0/metricsserver: - children: - - aws:api_deployment:rest_api_4:api_deployment-0 - - aws:api_method:rest_api_4:rest_api_4_integration_0_method - - aws:api_resource:rest_api_4:api_resource-0 - - aws:api_stage:rest_api_4:api_stage-0 - - aws:eks_add_on:amazon-cloudwatch-observability - - aws:eks_node_group:eks_node_group-0 - - aws:elastic_ip:subnet-0-route_table-nat_gateway-elastic_ip - - aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip - - aws:iam_oidc_provider:eks_cluster-0 - - aws:iam_policy:iam_policy-0 - - aws:iam_role:amazon-cloudwatch-observability-iam_role - - aws:iam_role:aws-load-balancer-controller - - aws:iam_role:eks_node_group-0-iam_role - - aws:iam_role:pod2 - - aws:internet_gateway:vpc-0:internet_gateway-0 + path: - aws:load_balancer_listener:rest_api_4_integration_0-pod2 - - aws:nat_gateway:subnet-2:subnet-0-route_table-nat_gateway - - aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway - - aws:route_table:vpc-0:subnet-0-route_table - - aws:route_table:vpc-0:subnet-1-route_table - - aws:route_table:vpc-0:subnet-2-route_table - - aws:route_table:vpc-0:subnet-3-route_table - aws:security_group:vpc-0:eks_cluster-0-security_group - aws:target_group:rest-api-4-integbcc77100 - - aws:vpc_link:rest_api_4_integration_0-pod2 - kubernetes:service:eks_cluster-0:restapi4integration0-pod2 - - kubernetes:service_account:eks_cluster-0:aws-load-balancer-controller - - kubernetes:service_account:eks_cluster-0:pod2 - kubernetes:target_group_binding:eks_cluster-0:restapi4integration0-pod2 + + kubernetes:helm_chart:eks_cluster-0/metricsserver: + children: + - aws:eks_cluster:eks_cluster-0 + - aws:iam_role:ClusterRole-eks_cluster-0 + - aws:subnet:vpc-0:subnet-0 + - aws:subnet:vpc-0:subnet-1 + - aws:vpc:vpc-0 parent: eks_cluster/eks_cluster-0 tag: big kubernetes:helm_chart:eks_cluster-0/aws-load-balancer-controller: children: - - aws:api_deployment:rest_api_4:api_deployment-0 - - aws:api_method:rest_api_4:rest_api_4_integration_0_method - - aws:api_resource:rest_api_4:api_resource-0 - - aws:api_stage:rest_api_4:api_stage-0 - - aws:eks_add_on:amazon-cloudwatch-observability - - aws:eks_node_group:eks_node_group-0 - - aws:elastic_ip:subnet-0-route_table-nat_gateway-elastic_ip - - aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip - - aws:iam_oidc_provider:eks_cluster-0 - - aws:iam_policy:iam_policy-0 - - aws:iam_role:amazon-cloudwatch-observability-iam_role - - aws:iam_role:aws-load-balancer-controller - - aws:iam_role:eks_node_group-0-iam_role - - aws:iam_role:pod2 - - aws:internet_gateway:vpc-0:internet_gateway-0 - - aws:load_balancer_listener:rest_api_4_integration_0-pod2 - - aws:nat_gateway:subnet-2:subnet-0-route_table-nat_gateway - - aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway - - aws:route_table:vpc-0:subnet-0-route_table - - aws:route_table:vpc-0:subnet-1-route_table - - aws:route_table:vpc-0:subnet-2-route_table - - aws:route_table:vpc-0:subnet-3-route_table - - aws:security_group:vpc-0:eks_cluster-0-security_group - - aws:target_group:rest-api-4-integbcc77100 - - aws:vpc_link:rest_api_4_integration_0-pod2 - - kubernetes:service:eks_cluster-0:restapi4integration0-pod2 - - kubernetes:service_account:eks_cluster-0:aws-load-balancer-controller - - kubernetes:service_account:eks_cluster-0:pod2 - - kubernetes:target_group_binding:eks_cluster-0:restapi4integration0-pod2 + - aws:availability_zone:region-0:availability_zone-0 + - aws:eks_cluster:eks_cluster-0 + - aws:iam_role:ClusterRole-eks_cluster-0 + - aws:region:region-0 + - aws:subnet:vpc-0:subnet-0 + - aws:subnet:vpc-0:subnet-1 + - aws:vpc:vpc-0 parent: eks_cluster/eks_cluster-0 tag: big vpc/vpc-0: children: - - aws:api_deployment:rest_api_4:api_deployment-0 - - aws:api_method:rest_api_4:rest_api_4_integration_0_method - - aws:api_resource:rest_api_4:api_resource-0 - - aws:api_stage:rest_api_4:api_stage-0 - - aws:eks_add_on:amazon-cloudwatch-observability - - aws:eks_node_group:eks_node_group-0 - - aws:elastic_ip:subnet-0-route_table-nat_gateway-elastic_ip - - aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip - - aws:iam_oidc_provider:eks_cluster-0 - - aws:iam_policy:iam_policy-0 - - aws:iam_role:amazon-cloudwatch-observability-iam_role - - aws:iam_role:aws-load-balancer-controller - - aws:iam_role:eks_node_group-0-iam_role - - aws:iam_role:pod2 - aws:internet_gateway:vpc-0:internet_gateway-0 - - aws:load_balancer_listener:rest_api_4_integration_0-pod2 - - aws:nat_gateway:subnet-2:subnet-0-route_table-nat_gateway - - aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway - aws:route_table:vpc-0:subnet-0-route_table - aws:route_table:vpc-0:subnet-1-route_table - aws:route_table:vpc-0:subnet-2-route_table - aws:route_table:vpc-0:subnet-3-route_table - aws:security_group:vpc-0:eks_cluster-0-security_group - - aws:target_group:rest-api-4-integbcc77100 - - aws:vpc_link:rest_api_4_integration_0-pod2 - - kubernetes:service:eks_cluster-0:restapi4integration0-pod2 - - kubernetes:service_account:eks_cluster-0:aws-load-balancer-controller - - kubernetes:service_account:eks_cluster-0:pod2 - - kubernetes:target_group_binding:eks_cluster-0:restapi4integration0-pod2 + - aws:subnet:vpc-0:subnet-0 + - aws:subnet:vpc-0:subnet-1 + - aws:subnet:vpc-0:subnet-2 + - aws:subnet:vpc-0:subnet-3 tag: parent rest_api/rest_api_4: children: + - aws:api_deployment:rest_api_4:api_deployment-0 + - aws:api_integration:rest_api_4:rest_api_4_integration_0 + - aws:api_method:rest_api_4:rest_api_4_integration_0_method + - aws:api_resource:rest_api_4:api_resource-0 - aws:api_stage:rest_api_4:api_stage-0 tag: parent eks_cluster/eks_cluster-0: children: - - aws:eks_add_on:amazon-cloudwatch-observability - - aws:eks_add_on:vpc-cni - - aws:eks_node_group:eks_node_group-0 - - aws:iam_oidc_provider:eks_cluster-0 - - aws:iam_policy:iam_policy-0 + - aws:availability_zone:region-0:availability_zone-0 + - aws:availability_zone:region-0:availability_zone-1 - aws:iam_role:ClusterRole-eks_cluster-0 - - aws:iam_role:amazon-cloudwatch-observability-iam_role - - aws:iam_role:aws-load-balancer-controller - - aws:iam_role:eks_node_group-0-iam_role - - aws:iam_role:pod2 - - aws:security_group:vpc-0:eks_cluster-0-security_group - - aws:target_group:rest-api-4-integbcc77100 + - aws:region:region-0 + - aws:subnet:vpc-0:subnet-0 + - aws:subnet:vpc-0:subnet-1 + - aws:vpc:vpc-0 + - kubernetes:helm_chart:eks_cluster-0:aws-load-balancer-controller + - kubernetes:helm_chart:eks_cluster-0:metricsserver + - kubernetes:pod:eks_cluster-0:pod2 - kubernetes:service:eks_cluster-0:restapi4integration0-pod2 - kubernetes:service_account:eks_cluster-0:aws-load-balancer-controller - kubernetes:service_account:eks_cluster-0:pod2 @@ -169,37 +95,10 @@ resources: tag: parent aws:api_integration:rest_api_4/rest_api_4_integration_0: - children: - - aws:api_deployment:rest_api_4:api_deployment-0 - - aws:api_method:rest_api_4:rest_api_4_integration_0_method - - aws:api_resource:rest_api_4:api_resource-0 - - aws:api_stage:rest_api_4:api_stage-0 - - aws:eks_add_on:amazon-cloudwatch-observability - - aws:eks_node_group:eks_node_group-0 - - aws:elastic_ip:subnet-0-route_table-nat_gateway-elastic_ip - - aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip - - aws:iam_oidc_provider:eks_cluster-0 - - aws:iam_policy:iam_policy-0 - - aws:iam_role:amazon-cloudwatch-observability-iam_role - - aws:iam_role:aws-load-balancer-controller - - aws:iam_role:eks_node_group-0-iam_role - - aws:iam_role:pod2 - - aws:internet_gateway:vpc-0:internet_gateway-0 - - aws:load_balancer_listener:rest_api_4_integration_0-pod2 - - aws:nat_gateway:subnet-2:subnet-0-route_table-nat_gateway - - aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway - - aws:route_table:vpc-0:subnet-0-route_table - - aws:route_table:vpc-0:subnet-1-route_table - - aws:route_table:vpc-0:subnet-2-route_table - - aws:route_table:vpc-0:subnet-3-route_table - - aws:security_group:vpc-0:eks_cluster-0-security_group - - aws:target_group:rest-api-4-integbcc77100 - - aws:vpc_link:rest_api_4_integration_0-pod2 - - kubernetes:service:eks_cluster-0:restapi4integration0-pod2 - - kubernetes:service_account:eks_cluster-0:aws-load-balancer-controller - - kubernetes:service_account:eks_cluster-0:pod2 - - kubernetes:target_group_binding:eks_cluster-0:restapi4integration0-pod2 parent: rest_api/rest_api_4 tag: big aws:api_integration:rest_api_4/rest_api_4_integration_0 -> load_balancer/rest-api-4-integbcc77100: + path: + - aws:vpc_link:rest_api_4_integration_0-pod2 + diff --git a/pkg/engine2/testdata/k8s_api.expect.yaml b/pkg/engine2/testdata/k8s_api.expect.yaml index d4ae3b2a6..38fa6ade5 100755 --- a/pkg/engine2/testdata/k8s_api.expect.yaml +++ b/pkg/engine2/testdata/k8s_api.expect.yaml @@ -67,8 +67,8 @@ resources: - aws:iam_oidc_provider:eks_cluster-0#Arn Version: "2012-10-17" ManagedPolicies: - - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy - arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess + - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy aws:rest_api:rest_api_4: BinaryMediaTypes: - application/octet-stream @@ -267,12 +267,12 @@ resources: - ec2.amazonaws.com Version: "2012-10-17" ManagedPolicies: - - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy - arn:aws:iam::aws:policy/AWSCloudMapFullAccess + - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy aws:iam_role:pod2: AssumeRolePolicyDoc: Statement: diff --git a/pkg/engine2/testdata/lambda_efs.dataflow-viz.yaml b/pkg/engine2/testdata/lambda_efs.dataflow-viz.yaml index 90e7b6433..80db6f9e5 100755 --- a/pkg/engine2/testdata/lambda_efs.dataflow-viz.yaml +++ b/pkg/engine2/testdata/lambda_efs.dataflow-viz.yaml @@ -2,25 +2,17 @@ provider: aws resources: efs_file_system/test-efs-fs: children: + - aws:availability_zone:region-0:availability_zone-0 - aws:efs_access_point:test-efs-fs:lambda_test_app-test-efs-fs - aws:efs_mount_target:test-efs-fs:lambda_test_app-test-efs-fs - aws:efs_mount_target:test-efs-fs:subnet-1-test-efs-fs - - aws:iam_role:lambda_test_app-ExecutionRole - - aws:log_group:lambda_test_app-log-group + - aws:region:region-0 parent: vpc/vpc-0 tag: big vpc/vpc-0: children: - - aws:efs_mount_target:test-efs-fs:lambda_test_app-test-efs-fs - - aws:efs_mount_target:test-efs-fs:subnet-1-test-efs-fs - - aws:elastic_ip:lambda_test_app-test-efs-fs-route_table-nat_gateway-elastic_ip - - aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip - - aws:iam_role:lambda_test_app-ExecutionRole - aws:internet_gateway:vpc-0:internet_gateway-0 - - aws:log_group:lambda_test_app-log-group - - aws:nat_gateway:subnet-2:lambda_test_app-test-efs-fs-route_table-nat_gateway - - aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway - aws:route_table:vpc-0:lambda_test_app-test-efs-fs-route_table - aws:route_table:vpc-0:subnet-1-route_table - aws:route_table:vpc-0:subnet-2-route_table @@ -28,14 +20,34 @@ resources: - aws:security_group:vpc-0:lambda_test_app-security_group - aws:security_group:vpc-0:lambda_test_app-test-efs-fs - aws:security_group:vpc-0:subnet-1-test-efs-fs + - aws:subnet:vpc-0:lambda_test_app-test-efs-fs + - aws:subnet:vpc-0:subnet-1 + - aws:subnet:vpc-0:subnet-2 + - aws:subnet:vpc-0:subnet-3 tag: parent lambda_function/lambda_test_app: children: + - aws:availability_zone:region-0:availability_zone-0 + - aws:availability_zone:region-0:availability_zone-1 - aws:ecr_image:lambda_test_app-image - aws:ecr_repo:ecr_repo-0 - - aws:security_group:vpc-0:lambda_test_app-security_group + - aws:iam_role:lambda_test_app-ExecutionRole + - aws:region:region-0 + - aws:subnet:vpc-0:lambda_test_app-test-efs-fs + - aws:subnet:vpc-0:subnet-1 + - aws:vpc:vpc-0 parent: vpc/vpc-0 tag: big lambda_function/lambda_test_app -> efs_file_system/test-efs-fs: + path: + - aws:efs_access_point:test-efs-fs:lambda_test_app-test-efs-fs + - aws:efs_mount_target:test-efs-fs:lambda_test_app-test-efs-fs + - aws:efs_mount_target:test-efs-fs:subnet-1-test-efs-fs + - aws:iam_role:lambda_test_app-ExecutionRole + - aws:security_group:vpc-0:lambda_test_app-test-efs-fs + - aws:security_group:vpc-0:subnet-1-test-efs-fs + - aws:subnet:vpc-0:lambda_test_app-test-efs-fs + - aws:subnet:vpc-0:subnet-1 + diff --git a/pkg/engine2/testdata/lambda_efs.expect.yaml b/pkg/engine2/testdata/lambda_efs.expect.yaml index 7245f91b4..da4038007 100755 --- a/pkg/engine2/testdata/lambda_efs.expect.yaml +++ b/pkg/engine2/testdata/lambda_efs.expect.yaml @@ -62,8 +62,8 @@ resources: - aws:efs_file_system:test-efs-fs#Arn Version: "2012-10-17" ManagedPolicies: - - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole + - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole aws:ecr_repo:ecr_repo-0: ForceDelete: true aws:efs_mount_target:test-efs-fs:lambda_test_app-test-efs-fs: diff --git a/pkg/engine2/testdata/namespace_pathselect.dataflow-viz.yaml b/pkg/engine2/testdata/namespace_pathselect.dataflow-viz.yaml index 3d3727190..faf084d74 100755 --- a/pkg/engine2/testdata/namespace_pathselect.dataflow-viz.yaml +++ b/pkg/engine2/testdata/namespace_pathselect.dataflow-viz.yaml @@ -2,27 +2,29 @@ provider: aws resources: vpc/vpc_1: children: - - aws:elastic_ip:lambda_function_2-vpc_1-route_table-nat_gateway-elastic_ip - - aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip - - aws:iam_role:lambda_function_0-ExecutionRole - - aws:iam_role:lambda_function_2-ExecutionRole - aws:internet_gateway:vpc_1:internet_gateway-0 - - aws:log_group:lambda_function_0-log-group - - aws:log_group:lambda_function_2-log-group - - aws:nat_gateway:subnet-2:lambda_function_2-vpc_1-route_table-nat_gateway - - aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway - aws:route_table:vpc_1:lambda_function_2-vpc_1-route_table - aws:route_table:vpc_1:subnet-1-route_table - aws:route_table:vpc_1:subnet-2-route_table - aws:route_table:vpc_1:subnet-3-route_table - aws:security_group:vpc_1:lambda_function_2-security_group + - aws:subnet:vpc_1:lambda_function_2-vpc_1 + - aws:subnet:vpc_1:subnet-1 + - aws:subnet:vpc_1:subnet-2 + - aws:subnet:vpc_1:subnet-3 tag: parent lambda_function/lambda_function_2: children: + - aws:availability_zone:region-0:availability_zone-0 + - aws:availability_zone:region-0:availability_zone-1 - aws:ecr_image:lambda_function_2-image + - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_function_2-ExecutionRole - - aws:security_group:vpc_1:lambda_function_2-security_group + - aws:region:region-0 + - aws:subnet:vpc_1:lambda_function_2-vpc_1 + - aws:subnet:vpc_1:subnet-1 + - aws:vpc:vpc_1 parent: vpc/vpc_1 tag: big @@ -31,8 +33,6 @@ resources: - aws:ecr_image:lambda_function_0-image - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_function_0-ExecutionRole - - aws:log_group:lambda_function_0-log-group - - aws:log_group:lambda_function_2-log-group parent: vpc/vpc_1 tag: big diff --git a/pkg/engine2/testdata/remove_path.dataflow-viz.yaml b/pkg/engine2/testdata/remove_path.dataflow-viz.yaml index 83e7a6ded..675a439ac 100644 --- a/pkg/engine2/testdata/remove_path.dataflow-viz.yaml +++ b/pkg/engine2/testdata/remove_path.dataflow-viz.yaml @@ -3,22 +3,14 @@ resources: rds_instance/rds-instance-1: children: - aws:rds_subnet_group:rds_subnet_group-0 - - aws:security_group:vpc-0:rds-instance-1-security_group + - aws:subnet:vpc-0:subnet-0 + - aws:subnet:vpc-0:subnet-1 parent: vpc/vpc-0 tag: big vpc/vpc-0: children: - - aws:elastic_ip:subnet-0-route_table-nat_gateway-elastic_ip - - aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip - - aws:iam_role:lambda_function_0-ExecutionRole - - aws:iam_role:lambda_function_3-ExecutionRole - aws:internet_gateway:vpc-0:internet_gateway-0 - - aws:log_group:lambda_function_0-log-group - - aws:log_group:lambda_function_3-log-group - - aws:nat_gateway:subnet-2:subnet-0-route_table-nat_gateway - - aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway - - aws:rds_subnet_group:rds_subnet_group-0 - aws:route_table:vpc-0:subnet-0-route_table - aws:route_table:vpc-0:subnet-1-route_table - aws:route_table:vpc-0:subnet-2-route_table @@ -26,25 +18,44 @@ resources: - aws:security_group:vpc-0:lambda_function_0-security_group - aws:security_group:vpc-0:lambda_function_3-security_group - aws:security_group:vpc-0:rds-instance-1-security_group + - aws:subnet:vpc-0:subnet-0 + - aws:subnet:vpc-0:subnet-1 + - aws:subnet:vpc-0:subnet-2 + - aws:subnet:vpc-0:subnet-3 tag: parent lambda_function/lambda_function_3: children: + - aws:availability_zone:region-0:availability_zone-0 + - aws:availability_zone:region-0:availability_zone-1 - aws:ecr_image:lambda_function_3-image + - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_function_3-ExecutionRole - - aws:security_group:vpc-0:lambda_function_3-security_group + - aws:region:region-0 + - aws:subnet:vpc-0:subnet-0 + - aws:subnet:vpc-0:subnet-1 + - aws:vpc:vpc-0 parent: vpc/vpc-0 tag: big lambda_function/lambda_function_0: children: + - aws:availability_zone:region-0:availability_zone-0 + - aws:availability_zone:region-0:availability_zone-1 - aws:ecr_image:lambda_function_0-image - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_function_0-ExecutionRole - - aws:log_group:lambda_function_0-log-group - - aws:log_group:lambda_function_3-log-group - - aws:security_group:vpc-0:lambda_function_0-security_group + - aws:region:region-0 + - aws:subnet:vpc-0:subnet-0 + - aws:subnet:vpc-0:subnet-1 + - aws:vpc:vpc-0 parent: vpc/vpc-0 tag: big lambda_function/lambda_function_0 -> rds_instance/rds-instance-1: + path: + - aws:iam_role:lambda_function_0-ExecutionRole + - aws:security_group:vpc-0:rds-instance-1-security_group + - aws:subnet:vpc-0:subnet-0 + - aws:subnet:vpc-0:subnet-1 + diff --git a/pkg/engine2/testdata/remove_path.expect.yaml b/pkg/engine2/testdata/remove_path.expect.yaml index 9d4b30dc7..71afa2c34 100644 --- a/pkg/engine2/testdata/remove_path.expect.yaml +++ b/pkg/engine2/testdata/remove_path.expect.yaml @@ -215,6 +215,11 @@ resources: Protocol: "-1" ToPort: 0 IngressRules: + - Description: Allow ingress traffic from within the same security group + FromPort: 0 + Protocol: "-1" + Self: true + ToPort: 0 - CidrBlocks: - 10.0.128.0/18 Description: Allow ingress traffic from ip addresses within the subnet subnet-0 @@ -227,11 +232,6 @@ resources: FromPort: 0 Protocol: "-1" ToPort: 0 - - Description: Allow ingress traffic from within the same security group - FromPort: 0 - Protocol: "-1" - Self: true - ToPort: 0 Vpc: aws:vpc:vpc-0 aws:route_table:vpc-0:subnet-0-route_table: Routes: diff --git a/pkg/engine2/testdata/rename.dataflow-viz.yaml b/pkg/engine2/testdata/rename.dataflow-viz.yaml index 8342b915c..54ce3b18d 100644 --- a/pkg/engine2/testdata/rename.dataflow-viz.yaml +++ b/pkg/engine2/testdata/rename.dataflow-viz.yaml @@ -1,16 +1,17 @@ provider: aws resources: dynamodb_table/dynamodb_table_1: - children: - - aws:iam_role:lambda_function_0-ExecutionRole - - aws:log_group:lambda_function_0-log-group - - aws:log_group:new_lambda_name-log-group tag: big lambda_function/new_lambda_name: children: - aws:ecr_image:lambda_function_0-image - aws:ecr_repo:ecr_repo-0 + - aws:iam_role:lambda_function_0-ExecutionRole tag: big lambda_function/new_lambda_name -> dynamodb_table/dynamodb_table_1: + path: + - aws:SERVICE_API:lambda_function_0-lambda_function_0-log-group + - aws:iam_role:lambda_function_0-ExecutionRole + diff --git a/pkg/engine2/testdata/single_lambda.dataflow-viz.yaml b/pkg/engine2/testdata/single_lambda.dataflow-viz.yaml index ae0fd618c..f96c54cfb 100755 --- a/pkg/engine2/testdata/single_lambda.dataflow-viz.yaml +++ b/pkg/engine2/testdata/single_lambda.dataflow-viz.yaml @@ -5,6 +5,5 @@ resources: - aws:ecr_image:lambda_function_0-image - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_function_0-ExecutionRole - - aws:log_group:lambda_function_0-log-group tag: big diff --git a/pkg/engine2/testdata/static_site.dataflow-viz.yaml b/pkg/engine2/testdata/static_site.dataflow-viz.yaml index 45f661cfb..af6a02102 100755 --- a/pkg/engine2/testdata/static_site.dataflow-viz.yaml +++ b/pkg/engine2/testdata/static_site.dataflow-viz.yaml @@ -1,13 +1,13 @@ provider: aws resources: s3_bucket/s3-bucket-0: - children: - - aws:s3_bucket_policy:s3_bucket_policy-0 tag: big cloudfront_distribution/cloudfront_distribution_1: - children: - - aws:s3_bucket_policy:s3_bucket_policy-0 tag: big cloudfront_distribution/cloudfront_distribution_1 -> s3_bucket/s3-bucket-0: + path: + - aws:cloudfront_origin_access_identity:cloudfront_origin_access_identity-0 + - aws:s3_bucket_policy:s3_bucket_policy-0 + diff --git a/pkg/engine2/testdata/vpc_import.dataflow-viz.yaml b/pkg/engine2/testdata/vpc_import.dataflow-viz.yaml index d9450d94e..ef77d5e59 100644 --- a/pkg/engine2/testdata/vpc_import.dataflow-viz.yaml +++ b/pkg/engine2/testdata/vpc_import.dataflow-viz.yaml @@ -1,5 +1,10 @@ provider: aws resources: vpc/vpc: + children: + - aws:subnet:vpc:subnet1 + - aws:subnet:vpc:subnet2 + - aws:subnet:vpc:subnet3 + - aws:subnet:vpc:subnet4 tag: parent diff --git a/pkg/engine2/testdata/vpc_import_to_lambda.dataflow-viz.yaml b/pkg/engine2/testdata/vpc_import_to_lambda.dataflow-viz.yaml index fe0326893..8b914cf86 100644 --- a/pkg/engine2/testdata/vpc_import_to_lambda.dataflow-viz.yaml +++ b/pkg/engine2/testdata/vpc_import_to_lambda.dataflow-viz.yaml @@ -2,9 +2,8 @@ provider: aws resources: vpc/vpc: children: - - aws:iam_role:lambda_function-ExecutionRole - - aws:log_group:lambda_function-log-group - aws:security_group:vpc:lambda_function-security_group + - aws:subnet:vpc:subnet1 tag: parent lambda_function/lambda_function: @@ -12,8 +11,9 @@ resources: - aws:ecr_image:lambda_function-image - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_function-ExecutionRole - - aws:log_group:lambda_function-log-group - - aws:security_group:vpc:lambda_function-security_group + - aws:subnet:subnet2 + - aws:subnet:vpc:subnet1 + - aws:vpc:vpc parent: vpc/vpc tag: big diff --git a/pkg/engine2/testdata/vpc_import_wo_subnets_to_lambda.dataflow-viz.yaml b/pkg/engine2/testdata/vpc_import_wo_subnets_to_lambda.dataflow-viz.yaml index a44a958aa..0bf05b62f 100644 --- a/pkg/engine2/testdata/vpc_import_wo_subnets_to_lambda.dataflow-viz.yaml +++ b/pkg/engine2/testdata/vpc_import_wo_subnets_to_lambda.dataflow-viz.yaml @@ -2,27 +2,29 @@ provider: aws resources: vpc/vpc: children: - - aws:elastic_ip:lambda_function-vpc-route_table-nat_gateway-elastic_ip - - aws:elastic_ip:subnet-1-route_table-nat_gateway-elastic_ip - - aws:iam_role:lambda_function-ExecutionRole - aws:internet_gateway:vpc:internet_gateway-0 - - aws:log_group:lambda_function-log-group - - aws:nat_gateway:subnet-2:lambda_function-vpc-route_table-nat_gateway - - aws:nat_gateway:subnet-3:subnet-1-route_table-nat_gateway - aws:route_table:vpc:lambda_function-vpc-route_table - aws:route_table:vpc:subnet-1-route_table - aws:route_table:vpc:subnet-2-route_table - aws:route_table:vpc:subnet-3-route_table - aws:security_group:vpc:lambda_function-security_group + - aws:subnet:vpc:lambda_function-vpc + - aws:subnet:vpc:subnet-1 + - aws:subnet:vpc:subnet-2 + - aws:subnet:vpc:subnet-3 tag: parent lambda_function/lambda_function: children: + - aws:availability_zone:region-0:availability_zone-0 + - aws:availability_zone:region-0:availability_zone-1 - aws:ecr_image:lambda_function-image - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_function-ExecutionRole - - aws:log_group:lambda_function-log-group - - aws:security_group:vpc:lambda_function-security_group + - aws:region:region-0 + - aws:subnet:vpc:lambda_function-vpc + - aws:subnet:vpc:subnet-1 + - aws:vpc:vpc parent: vpc/vpc tag: big diff --git a/pkg/engine2/testdata/vpc_import_wo_subnets_to_lambda.expect.yaml b/pkg/engine2/testdata/vpc_import_wo_subnets_to_lambda.expect.yaml index a906cf73d..f90b59f74 100644 --- a/pkg/engine2/testdata/vpc_import_wo_subnets_to_lambda.expect.yaml +++ b/pkg/engine2/testdata/vpc_import_wo_subnets_to_lambda.expect.yaml @@ -40,8 +40,8 @@ resources: - lambda.amazonaws.com Version: "2012-10-17" ManagedPolicies: - - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole + - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole aws:subnet:vpc:lambda_function-vpc: AvailabilityZone: aws:availability_zone:region-0:availability_zone-0 CidrBlock: 10.0.128.0/18 diff --git a/pkg/engine2/visualizer.go b/pkg/engine2/visualizer.go index 93b1cc2d9..3694bf2d3 100644 --- a/pkg/engine2/visualizer.go +++ b/pkg/engine2/visualizer.go @@ -113,17 +113,21 @@ func (e *Engine) GetViewsDag(view View, sol solution_context.SolutionContext) (v case NoRenderTag: continue case ParentIconTag: - continue + parent, err := viewDag.Vertex(id) + if err != nil { + errs = errors.Join(errs, err) + continue + } + if err := e.setChildren(sol, parent); err != nil { + errs = errors.Join(errs, err) + } case BigIconTag: err := e.handleBigIcon(sol, view, undirected, viewDag, id) if err != nil { errs = errors.Join(errs, fmt.Errorf("failed to handle big icon %s: %w", id, err)) } case SmallIconTag: - err := e.handleSmallIcon(view, undirected, viewDag, id) - if err != nil { - errs = errors.Join(errs, fmt.Errorf("failed to handle small icon %s: %w", id, err)) - } + // Small icons don't need special handling, handleBigIcon will look for any relevant small icons to include default: errs = errors.Join(errs, fmt.Errorf("unknown tag %s", tag)) } @@ -132,116 +136,114 @@ func (e *Engine) GetViewsDag(view View, sol solution_context.SolutionContext) (v return viewDag, errs } -// handleSmallIcon finds big icons to attach this resource to. It always adds to big icons for which it is -// in the glue layer. It also adds to the big icon that is closest to the resource (if there is one). -func (e *Engine) handleSmallIcon( +// handleBigIcon sets the parent of the big icon if there is a group it should be added to and +// adds edges to any other big icons based on having the proper connections (network & permissions). +func (e *Engine) handleBigIcon( + sol solution_context.SolutionContext, view View, - g construct.Graph, + undirected construct.Graph, viewDag visualizer.VisGraph, id construct.ResourceId, ) error { - ids, err := construct.TopologicalSort(viewDag) + source, err := viewDag.Vertex(id) if err != nil { return err } - pather, err := construct.ShortestPaths(g, id, construct.DontSkipEdges) + + parent, err := e.findParent(view, undirected, viewDag, id) if err != nil { return err } - glueIds, err := knowledgebase.Downstream(g, e.Kb, id, knowledgebase.ResourceGlueLayer) + source.Parent = parent + + targets, err := construct.TopologicalSort(viewDag) if err != nil { return err } - glue := set.SetOf(glueIds...) - var errs error - var bestParent *visualizer.VisResource - bestParentWeight := math.MaxInt32 - for _, candidate := range ids { - // If the resource is in the glue layer, add it - if glue.Contains(candidate) { - parent, err := viewDag.Vertex(candidate) - if err != nil { - errs = errors.Join(errs, err) - continue - } - parent.Children.Add(id) - } - // Even if it was in the glue layer, continue calculating the best parent so we don't accidentally - // attribute it to a worse parent. - path, err := pather.ShortestPath(candidate) - if errors.Is(err, graph.ErrTargetNotReachable) { + var errs error + for _, target := range targets { + if target == id { continue - } else if err != nil { - errs = errors.Join(errs, err) + } + if tag := GetResourceVizTag(e.Kb, view, target); tag != BigIconTag { continue } - weight, err := graph_addons.PathWeight(g, graph_addons.Path[construct.ResourceId](path)) + + paths, err := visPaths(sol, view, id, target) if err != nil { errs = errors.Join(errs, err) continue } - if weight < bestParentWeight { - bestParentWeight = weight - bestParent, err = viewDag.Vertex(candidate) - if err != nil { - errs = errors.Join(errs, err) - continue + if len(paths) > 0 { + allPathResources := make(set.Set[construct.ResourceId]) + for _, path := range paths { + for _, pathRes := range path[1 : len(path)-1] { + allPathResources.Add(pathRes) + } } + errs = errors.Join(errs, viewDag.AddEdge(id, target, graph.EdgeData(visualizer.VisEdgeData{ + PathResources: allPathResources, + }))) } } if errs != nil { return errs } - if bestParent != nil { - bestParent.Children.Add(id) + + if err := e.setChildren(sol, source); err != nil { + return err } + return nil } -// handleBigIcon sets the parent of the big icon if there is a group it should be added to and -// adds edges to any other big icons based on having the proper connections (network & permissions). -func (e *Engine) handleBigIcon( - sol solution_context.SolutionContext, - view View, - undirected construct.Graph, - viewDag visualizer.VisGraph, - id construct.ResourceId, -) error { - source, err := viewDag.Vertex(id) +func (e *Engine) setChildren(sol solution_context.SolutionContext, v *visualizer.VisResource) error { + glue, err := knowledgebase.Downstream(sol.DataflowGraph(), sol.KnowledgeBase(), v.ID, knowledgebase.ResourceGlueLayer) if err != nil { - return err + return fmt.Errorf("failed to get glue layer for %s: %w", v.ID, err) } - parent, err := e.findParent(view, undirected, viewDag, id) - if err != nil { - return err + for _, glueElem := range glue { + v.Children.Add(glueElem) } - source.Parent = parent - targets, err := construct.TopologicalSort(viewDag) + // After glue, also include any resources whose namespace is this resource + ids, err := construct.TopologicalSort(sol.DataflowGraph()) if err != nil { return err } var errs error - for _, target := range targets { - if target == id { - continue - } - if tag := GetResourceVizTag(e.Kb, view, target); tag != BigIconTag { + for _, id := range ids { + if id.Namespace == "" { continue } - - hasPath, err := hasVisPath(sol, view, id, target) + tmpl, err := sol.KnowledgeBase().GetResourceTemplate(id) if err != nil { errs = errors.Join(errs, err) continue } - if hasPath { - errs = errors.Join(errs, viewDag.AddEdge(id, target)) + for _, p := range tmpl.Properties { + if p.Details().Namespace { + pres, err := sol.RawView().Vertex(id) + if err != nil { + errs = errors.Join(errs, err) + break + } + val, err := pres.GetProperty(p.Details().Path) + if err != nil { + errs = errors.Join(errs, err) + break + } + if val == v.ID { + v.Children.Add(id) + break + } + } } } - return err + + return nil } func (e *Engine) findParent( @@ -297,25 +299,25 @@ candidateLoop: return } -func hasVisPath(sol solution_context.SolutionContext, view View, source, target construct.ResourceId) (bool, error) { +func visPaths(sol solution_context.SolutionContext, view View, source, target construct.ResourceId) ([]construct.Path, error) { srcTemplate, err := sol.KnowledgeBase().GetResourceTemplate(source) if err != nil || srcTemplate == nil { - return false, fmt.Errorf("has path could not find source resource %s: %w", source, err) + return nil, fmt.Errorf("has path could not find source resource %s: %w", source, err) } targetTemplate, err := sol.KnowledgeBase().GetResourceTemplate(target) if err != nil || targetTemplate == nil { - return false, fmt.Errorf("has path could not find target resource %s: %w", target, err) + return nil, fmt.Errorf("has path could not find target resource %s: %w", target, err) } if len(targetTemplate.PathSatisfaction.AsTarget) == 0 || len(srcTemplate.PathSatisfaction.AsSource) == 0 { - return false, nil + return nil, nil } sourceRes, err := sol.RawView().Vertex(source) if err != nil { - return false, fmt.Errorf("has path could not find source resource %s: %w", source, err) + return nil, fmt.Errorf("has path could not find source resource %s: %w", source, err) } targetRes, err := sol.RawView().Vertex(target) if err != nil { - return false, fmt.Errorf("has path could not find target resource %s: %w", target, err) + return nil, fmt.Errorf("has path could not find target resource %s: %w", target, err) } consumed, err := knowledgebase.HasConsumedFromResource( @@ -324,20 +326,20 @@ func hasVisPath(sol solution_context.SolutionContext, view View, source, target solution_context.DynamicCtx(sol), ) if err != nil { - return false, err + return nil, err } if !consumed { - return false, nil + return nil, nil } return checkPaths(sol, view, source, target) } -func checkPaths(sol solution_context.SolutionContext, view View, source, target construct.ResourceId) (bool, error) { +func checkPaths(sol solution_context.SolutionContext, view View, source, target construct.ResourceId) ([]construct.Path, error) { paths, err := path_selection.GetPaths( sol, source, target, - func(source, target construct.ResourceId, path []construct.ResourceId) bool { + func(source, target construct.ResourceId, path construct.Path) bool { for _, res := range path[1 : len(path)-1] { switch GetResourceVizTag(sol.KnowledgeBase(), view, res) { case BigIconTag, ParentIconTag: @@ -347,7 +349,7 @@ func checkPaths(sol solution_context.SolutionContext, view View, source, target } return true }, - true, + false, ) - return len(paths) > 0, err + return paths, err } diff --git a/pkg/graph_addons/path.go b/pkg/graph_addons/path.go index 131a56fe7..b62a60c9c 100644 --- a/pkg/graph_addons/path.go +++ b/pkg/graph_addons/path.go @@ -23,3 +23,12 @@ func PathWeight[K comparable, V any](g graph.Graph[K, V], path Path[K]) (weight } return } + +func (p Path[K]) Contains(k K) bool { + for _, elem := range p { + if elem == k { + return true + } + } + return false +} diff --git a/pkg/graph_addons/walk.go b/pkg/graph_addons/walk.go index f2db9bea8..d44513ffa 100644 --- a/pkg/graph_addons/walk.go +++ b/pkg/graph_addons/walk.go @@ -4,10 +4,9 @@ import ( "errors" "github.com/dominikbraun/graph" - "github.com/klothoplatform/klotho/pkg/set" ) -type WalkGraphFunc[K comparable] func(k K, nerr error) error +type WalkGraphFunc[K comparable] func(p Path[K], nerr error) error var ( StopWalk = errors.New("stop walk") @@ -38,19 +37,24 @@ func walk[K comparable, T any]( f WalkGraphFunc[K], deps map[K]map[K]graph.Edge[K], ) error { - visited := make(set.Set[K]) - var queue []K + var queue []Path[K] + enqueue := func(current Path[K], next K) { + if current.Contains(next) { + // Prevent loops + return + } + queue = append(queue, append(current, next)) + } + startPath := Path[K]{start} for d := range deps[start] { - queue = append(queue, d) + enqueue(startPath, d) } - visited.Add(start) var err error - var current K + var current Path[K] for len(queue) > 0 { current, queue = queue[0], queue[1:] - visited.Add(current) nerr := f(current, err) if errors.Is(nerr, StopWalk) { @@ -61,11 +65,9 @@ func walk[K comparable, T any]( } err = nerr - for d := range deps[current] { - if visited.Contains(d) { - continue - } - queue = append(queue, d) + last := current[len(current)-1] + for d := range deps[last] { + enqueue(current, d) } } return err diff --git a/pkg/knowledge_base2/dynamic_value.go b/pkg/knowledge_base2/dynamic_value.go index 3ef559fa9..e088da606 100644 --- a/pkg/knowledge_base2/dynamic_value.go +++ b/pkg/knowledge_base2/dynamic_value.go @@ -289,7 +289,8 @@ func (ctx DynamicValueContext) upstream(selector any, resource construct.Resourc } var match construct.ResourceId - err = graph_addons.WalkUp(ctx.Graph, resource, func(id construct.ResourceId, nerr error) error { + err = graph_addons.WalkUp(ctx.Graph, resource, func(path graph_addons.Path[construct.ResourceId], nerr error) error { + id := path[len(path)-1] if selId.Matches(id) { match = id return graph_addons.StopWalk @@ -341,12 +342,13 @@ func (ctx DynamicValueContext) LayeredUpstream( return construct.ResourceId{}, err } result := construct.ResourceId{} - wrapper := func(id construct.ResourceId, nerr error) error { + wrapper := func(path graph_addons.Path[construct.ResourceId], nerr error) error { + id := path[len(path)-1] if selId.Matches(id) { result = id return graph_addons.StopWalk } - return f(id, nerr) + return f(path, nerr) } err = graph_addons.WalkUp(ctx.Graph, resource, wrapper) if err != nil { @@ -382,7 +384,8 @@ func (ctx DynamicValueContext) downstream(selector any, resource construct.Resou } var match construct.ResourceId - err = graph_addons.WalkDown(ctx.Graph, resource, func(id construct.ResourceId, nerr error) error { + err = graph_addons.WalkDown(ctx.Graph, resource, func(path graph_addons.Path[construct.ResourceId], nerr error) error { + id := path[len(path)-1] if selId.Matches(id) { match = id return graph_addons.StopWalk @@ -413,12 +416,13 @@ func (ctx DynamicValueContext) LayeredDownstream( return construct.ResourceId{}, err } result := construct.ResourceId{} - wrapper := func(id construct.ResourceId, nerr error) error { + wrapper := func(path graph_addons.Path[construct.ResourceId], nerr error) error { + id := path[len(path)-1] if selId.Matches(id) { result = id return graph_addons.StopWalk } - return f(id, nerr) + return f(path, nerr) } err = graph_addons.WalkDown(ctx.Graph, resource, wrapper) if err != nil { diff --git a/pkg/knowledge_base2/graph.go b/pkg/knowledge_base2/graph.go index 190d83c46..d5d8e17f5 100644 --- a/pkg/knowledge_base2/graph.go +++ b/pkg/knowledge_base2/graph.go @@ -41,7 +41,8 @@ func resourceLocal( rid construct.ResourceId, ids *[]construct.ResourceId, ) graph_addons.WalkGraphFunc[construct.ResourceId] { - return func(id construct.ResourceId, nerr error) error { + return func(path graph_addons.Path[construct.ResourceId], nerr error) error { + id := path[len(path)-1] isSideEffect, err := IsOperationalResourceSideEffect(dag, kb, rid, id) if err != nil { return errors.Join(nerr, err) @@ -60,7 +61,8 @@ func resourceDirect( dag construct.Graph, ids *[]construct.ResourceId, ) graph_addons.WalkGraphFunc[construct.ResourceId] { - return func(id construct.ResourceId, nerr error) error { + return func(path graph_addons.Path[construct.ResourceId], nerr error) error { + id := path[len(path)-1] if ids != nil { (*ids) = append(*ids, id) } @@ -69,17 +71,28 @@ func resourceDirect( } func resourceGlue( + dag construct.Graph, kb TemplateKB, ids *[]construct.ResourceId, ) graph_addons.WalkGraphFunc[construct.ResourceId] { - return func(id construct.ResourceId, nerr error) error { - if GetFunctionality(kb, id) == Unknown { - if ids != nil { - (*ids) = append(*ids, id) - } + return func(path graph_addons.Path[construct.ResourceId], nerr error) error { + if len(path) <= 1 { + // skip source, this shouldn't happen but just in case return nil } - return graph_addons.SkipPath + // Since we're skipping the path if it doesn't match, we only need to check the most recently added (ie, the last) + // resource in the path. + last := path[len(path)-1] + prevLast := path[len(path)-2] + sideEffect, err := IsOperationalResourceSideEffect(dag, kb, prevLast, last) + if err != nil { + return errors.Join(nerr, err) + } + if !sideEffect { + return graph_addons.SkipPath + } + (*ids) = append(*ids, last) + return nil } } @@ -87,7 +100,8 @@ func firstFunctional( kb TemplateKB, ids *[]construct.ResourceId, ) graph_addons.WalkGraphFunc[construct.ResourceId] { - return func(id construct.ResourceId, nerr error) error { + return func(path graph_addons.Path[construct.ResourceId], nerr error) error { + id := path[len(path)-1] if ids != nil { (*ids) = append(*ids, id) } @@ -101,7 +115,8 @@ func firstFunctional( func allDeps( ids *[]construct.ResourceId, ) graph_addons.WalkGraphFunc[construct.ResourceId] { - return func(id construct.ResourceId, nerr error) error { + return func(path graph_addons.Path[construct.ResourceId], nerr error) error { + id := path[len(path)-1] if ids != nil { (*ids) = append(*ids, id) } @@ -157,9 +172,20 @@ func Downstream(dag construct.Graph, kb TemplateKB, rid construct.ResourceId, la case ResourceLocalLayer: f = resourceLocal(dag, kb, rid, &result) case ResourceDirectLayer: - f = resourceDirect(dag, &result) + // use a more performant implementation for direct since we can use the edges directly. + edges, err := dag.Edges() + if err != nil { + return nil, err + } + var ids []construct.ResourceId + for _, edge := range edges { + if edge.Source == rid { + ids = append(ids, edge.Target) + } + } + return ids, nil case ResourceGlueLayer: - f = resourceGlue(kb, &result) + f = resourceGlue(dag, kb, &result) case FirstFunctionalLayer: f = firstFunctional(kb, &result) case AllDepsLayer: @@ -173,7 +199,8 @@ func Downstream(dag construct.Graph, kb TemplateKB, rid construct.ResourceId, la func DownstreamFunctional(dag construct.Graph, kb TemplateKB, resource construct.ResourceId) ([]construct.ResourceId, error) { var result []construct.ResourceId - err := graph_addons.WalkDown(dag, resource, func(id construct.ResourceId, nerr error) error { + err := graph_addons.WalkDown(dag, resource, func(path graph_addons.Path[construct.ResourceId], nerr error) error { + id := path[len(path)-1] if GetFunctionality(kb, id) != Unknown { result = append(result, id) return graph_addons.SkipPath @@ -190,9 +217,20 @@ func Upstream(dag construct.Graph, kb TemplateKB, rid construct.ResourceId, laye case ResourceLocalLayer: f = resourceLocal(dag, kb, rid, &result) case ResourceDirectLayer: - f = resourceDirect(dag, &result) + // use a more performant implementation for direct since we can use the edges directly. + edges, err := dag.Edges() + if err != nil { + return nil, err + } + var ids []construct.ResourceId + for _, edge := range edges { + if edge.Target == rid { + ids = append(ids, edge.Source) + } + } + return ids, nil case ResourceGlueLayer: - f = resourceGlue(kb, &result) + f = resourceGlue(dag, kb, &result) case FirstFunctionalLayer: f = firstFunctional(kb, &result) case AllDepsLayer: @@ -217,7 +255,7 @@ func layerWalkFunc( case ResourceDirectLayer: return resourceDirect(dag, &result), nil case ResourceGlueLayer: - return resourceGlue(kb, &result), nil + return resourceGlue(dag, kb, &result), nil case FirstFunctionalLayer: return firstFunctional(kb, &result), nil case AllDepsLayer: @@ -229,7 +267,8 @@ func layerWalkFunc( func UpstreamFunctional(dag construct.Graph, kb TemplateKB, resource construct.ResourceId) ([]construct.ResourceId, error) { var result []construct.ResourceId - err := graph_addons.WalkUp(dag, resource, func(id construct.ResourceId, nerr error) error { + err := graph_addons.WalkUp(dag, resource, func(path graph_addons.Path[construct.ResourceId], nerr error) error { + id := path[len(path)-1] if GetFunctionality(kb, id) != Unknown { result = append(result, id) return graph_addons.SkipPath diff --git a/pkg/visualizer2/vis_graph.go b/pkg/visualizer2/vis_graph.go index 9eb7fca14..c4abe6eb9 100644 --- a/pkg/visualizer2/vis_graph.go +++ b/pkg/visualizer2/vis_graph.go @@ -2,6 +2,7 @@ package visualizer import ( "errors" + "sort" "github.com/dominikbraun/graph" construct "github.com/klothoplatform/klotho/pkg/construct2" @@ -18,6 +19,10 @@ type ( Children set.Set[construct.ResourceId] } + VisEdgeData struct { + PathResources set.Set[construct.ResourceId] + } + VisGraph graph.Graph[construct.ResourceId, *VisResource] ) @@ -51,3 +56,15 @@ func ConstructToVis(g construct.Graph) (VisGraph, error) { } return vis, errs } + +func (d VisEdgeData) MarshalYAML() (interface{}, error) { + res := d.PathResources.ToSlice() + sort.Sort(construct.SortedIds(res)) + return map[string]any{ + // TODO infacopilot frontend currently just uses 'path' as the colledction of + // additional resources to show in the graph for that edge. We have more information + // we could give, but for compatibility until more is added to the frontend, just flatten + // everything and call it 'path'. + "path": res, + }, nil +} From 88119ac12d0ef47ee7891add6edbe4dd5260a0b3 Mon Sep 17 00:00:00 2001 From: Gordon Date: Fri, 5 Jan 2024 01:45:39 -0500 Subject: [PATCH 2/5] Fix walk overwriting the path causing missed paths --- .../testdata/ecs_rds.dataflow-viz.yaml | 4 ++++ .../testdata/k8s_api.dataflow-viz.yaml | 5 +++++ pkg/engine2/testdata/k8s_api.expect.yaml | 4 ++-- pkg/engine2/testdata/lambda_efs.expect.yaml | 22 +++++++++---------- .../testdata/remove_path.dataflow-viz.yaml | 4 ++++ pkg/engine2/testdata/remove_path.expect.yaml | 2 +- pkg/graph_addons/walk.go | 12 +++++++++- pkg/visualizer2/file.go | 8 ++----- 8 files changed, 40 insertions(+), 21 deletions(-) diff --git a/pkg/engine2/testdata/ecs_rds.dataflow-viz.yaml b/pkg/engine2/testdata/ecs_rds.dataflow-viz.yaml index 309f404d6..2b60f7245 100755 --- a/pkg/engine2/testdata/ecs_rds.dataflow-viz.yaml +++ b/pkg/engine2/testdata/ecs_rds.dataflow-viz.yaml @@ -2,9 +2,13 @@ provider: aws resources: rds_instance/rds-instance-2: children: + - aws:availability_zone:region-0:availability_zone-0 + - aws:availability_zone:region-0:availability_zone-1 - aws:rds_subnet_group:rds_subnet_group-0 + - aws:region:region-0 - aws:subnet:vpc-0:subnet-0 - aws:subnet:vpc-0:subnet-1 + - aws:vpc:vpc-0 parent: vpc/vpc-0 tag: big diff --git a/pkg/engine2/testdata/k8s_api.dataflow-viz.yaml b/pkg/engine2/testdata/k8s_api.dataflow-viz.yaml index 203b904e2..bd2592d7b 100755 --- a/pkg/engine2/testdata/k8s_api.dataflow-viz.yaml +++ b/pkg/engine2/testdata/k8s_api.dataflow-viz.yaml @@ -3,6 +3,7 @@ resources: kubernetes:pod:eks_cluster-0/pod2: children: - aws:availability_zone:region-0:availability_zone-0 + - aws:availability_zone:region-0:availability_zone-1 - aws:eks_cluster:eks_cluster-0 - aws:iam_role:ClusterRole-eks_cluster-0 - aws:region:region-0 @@ -33,8 +34,11 @@ resources: kubernetes:helm_chart:eks_cluster-0/metricsserver: children: + - aws:availability_zone:region-0:availability_zone-0 + - aws:availability_zone:region-0:availability_zone-1 - aws:eks_cluster:eks_cluster-0 - aws:iam_role:ClusterRole-eks_cluster-0 + - aws:region:region-0 - aws:subnet:vpc-0:subnet-0 - aws:subnet:vpc-0:subnet-1 - aws:vpc:vpc-0 @@ -44,6 +48,7 @@ resources: kubernetes:helm_chart:eks_cluster-0/aws-load-balancer-controller: children: - aws:availability_zone:region-0:availability_zone-0 + - aws:availability_zone:region-0:availability_zone-1 - aws:eks_cluster:eks_cluster-0 - aws:iam_role:ClusterRole-eks_cluster-0 - aws:region:region-0 diff --git a/pkg/engine2/testdata/k8s_api.expect.yaml b/pkg/engine2/testdata/k8s_api.expect.yaml index 38fa6ade5..35611b664 100755 --- a/pkg/engine2/testdata/k8s_api.expect.yaml +++ b/pkg/engine2/testdata/k8s_api.expect.yaml @@ -267,12 +267,12 @@ resources: - ec2.amazonaws.com Version: "2012-10-17" ManagedPolicies: - - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore - - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy - arn:aws:iam::aws:policy/AWSCloudMapFullAccess - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy + - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore + - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy aws:iam_role:pod2: AssumeRolePolicyDoc: Statement: diff --git a/pkg/engine2/testdata/lambda_efs.expect.yaml b/pkg/engine2/testdata/lambda_efs.expect.yaml index da4038007..446f91e7c 100755 --- a/pkg/engine2/testdata/lambda_efs.expect.yaml +++ b/pkg/engine2/testdata/lambda_efs.expect.yaml @@ -145,12 +145,6 @@ resources: Protocol: "-1" ToPort: 0 IngressRules: - - CidrBlocks: - - 10.0.192.0/18 - Description: Allow ingress traffic from ip addresses within the subnet lambda_test_app-test-efs-fs - FromPort: 0 - Protocol: "-1" - ToPort: 0 - Description: Allow ingress traffic from within the same security group FromPort: 0 Protocol: "-1" @@ -162,6 +156,12 @@ resources: FromPort: 0 Protocol: "-1" ToPort: 0 + - CidrBlocks: + - 10.0.192.0/18 + Description: Allow ingress traffic from ip addresses within the subnet lambda_test_app-test-efs-fs + FromPort: 0 + Protocol: "-1" + ToPort: 0 Vpc: aws:vpc:vpc-0 aws:security_group:vpc-0:subnet-1-test-efs-fs: EgressRules: @@ -172,16 +172,16 @@ resources: Protocol: "-1" ToPort: 0 IngressRules: - - CidrBlocks: - - 10.0.128.0/18 - Description: Allow ingress traffic from ip addresses within the subnet subnet-1 + - Description: Allow ingress traffic from within the same security group FromPort: 0 Protocol: "-1" + Self: true ToPort: 0 - - Description: Allow ingress traffic from within the same security group + - CidrBlocks: + - 10.0.128.0/18 + Description: Allow ingress traffic from ip addresses within the subnet subnet-1 FromPort: 0 Protocol: "-1" - Self: true ToPort: 0 Vpc: aws:vpc:vpc-0 aws:route_table:vpc-0:subnet-1-route_table: diff --git a/pkg/engine2/testdata/remove_path.dataflow-viz.yaml b/pkg/engine2/testdata/remove_path.dataflow-viz.yaml index 675a439ac..919791390 100644 --- a/pkg/engine2/testdata/remove_path.dataflow-viz.yaml +++ b/pkg/engine2/testdata/remove_path.dataflow-viz.yaml @@ -2,9 +2,13 @@ provider: aws resources: rds_instance/rds-instance-1: children: + - aws:availability_zone:region-0:availability_zone-0 + - aws:availability_zone:region-0:availability_zone-1 - aws:rds_subnet_group:rds_subnet_group-0 + - aws:region:region-0 - aws:subnet:vpc-0:subnet-0 - aws:subnet:vpc-0:subnet-1 + - aws:vpc:vpc-0 parent: vpc/vpc-0 tag: big diff --git a/pkg/engine2/testdata/remove_path.expect.yaml b/pkg/engine2/testdata/remove_path.expect.yaml index 71afa2c34..7d19756c8 100644 --- a/pkg/engine2/testdata/remove_path.expect.yaml +++ b/pkg/engine2/testdata/remove_path.expect.yaml @@ -113,8 +113,8 @@ resources: - aws:rds_instance:rds-instance-1#RdsConnectionArn Version: "2012-10-17" ManagedPolicies: - - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole + - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole aws:ecr_repo:ecr_repo-0: ForceDelete: true aws:elastic_ip:subnet-0-route_table-nat_gateway-elastic_ip: diff --git a/pkg/graph_addons/walk.go b/pkg/graph_addons/walk.go index d44513ffa..92c936925 100644 --- a/pkg/graph_addons/walk.go +++ b/pkg/graph_addons/walk.go @@ -43,7 +43,17 @@ func walk[K comparable, T any]( // Prevent loops return } - queue = append(queue, append(current, next)) + // make a new slice because `append` won't copy if there's capacity + // which causes the latest `append` to overwrite the last element of any previous appends + // (as happens when appending in a loop as we do below). + // x := make([]int, 2, 3); x[0] = 1; x[1] = 2 + // y := append(x, 3) + // z := append(x, 4) + // fmt.Println(y) // [1 2 4] !! + nextPath := make(Path[K], len(current)+1) + copy(nextPath, current) + nextPath[len(nextPath)-1] = next + queue = append(queue, nextPath) } startPath := Path[K]{start} diff --git a/pkg/visualizer2/file.go b/pkg/visualizer2/file.go index fcc49af30..80ec084ab 100644 --- a/pkg/visualizer2/file.go +++ b/pkg/visualizer2/file.go @@ -64,12 +64,8 @@ func (f *File) WriteTo(w io.Writer) (n int64, err error) { props["parent"] = f.KeyFor(res.Parent) } if len(res.Children) > 0 { - childrenIds := res.Children.ToSlice() - sort.Sort(construct.SortedIds(childrenIds)) - children := make([]string, len(childrenIds)) - for i, child := range childrenIds { - children[i] = child.String() - } + children := res.Children.ToSlice() + sort.Sort(construct.SortedIds(children)) props["children"] = children } From 3ed13078ace18a4e166a150bc70b36e027c495a7 Mon Sep 17 00:00:00 2001 From: Gordon Date: Mon, 8 Jan 2024 17:02:12 -0500 Subject: [PATCH 3/5] Add unit test --- pkg/engine2/cli.go | 4 +- pkg/engine2/dot.go | 129 ++++++++++++++++ pkg/engine2/visualizer.go | 112 ++++++++++---- pkg/knowledge_base2/graph.go | 108 +++++++------ pkg/knowledge_base2/graph_test.go | 44 ------ pkg/knowledge_base2/kbtesting/graph_test.go | 162 ++++++++++++++++++++ pkg/knowledge_base2/kbtesting/mock_kb.go | 18 ++- 7 files changed, 446 insertions(+), 131 deletions(-) create mode 100644 pkg/engine2/dot.go delete mode 100644 pkg/knowledge_base2/graph_test.go create mode 100644 pkg/knowledge_base2/kbtesting/graph_test.go diff --git a/pkg/engine2/cli.go b/pkg/engine2/cli.go index 64b773ac8..9fc9105de 100644 --- a/pkg/engine2/cli.go +++ b/pkg/engine2/cli.go @@ -412,14 +412,14 @@ func writeDebugGraphs(sol solution_context.SolutionContext) { wg.Add(2) go func() { defer wg.Done() - err := construct.GraphToSVG(sol.DataflowGraph(), "dataflow") + err := GraphToSVG(sol.KnowledgeBase(), sol.DataflowGraph(), "dataflow") if err != nil { zap.S().Errorf("failed to write dataflow graph: %s", err.Error()) } }() go func() { defer wg.Done() - err := construct.GraphToSVG(sol.DeploymentGraph(), "iac") + err := GraphToSVG(sol.KnowledgeBase(), sol.DeploymentGraph(), "iac") if err != nil { zap.S().Errorf("failed to write iac graph: %s", err.Error()) } diff --git a/pkg/engine2/dot.go b/pkg/engine2/dot.go new file mode 100644 index 000000000..da81474eb --- /dev/null +++ b/pkg/engine2/dot.go @@ -0,0 +1,129 @@ +package engine2 + +import ( + "bytes" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "sort" + + construct "github.com/klothoplatform/klotho/pkg/construct2" + "github.com/klothoplatform/klotho/pkg/dot" + knowledgebase2 "github.com/klothoplatform/klotho/pkg/knowledge_base2" +) + +func dotAttributes(kb knowledgebase2.TemplateKB, r *construct.Resource) map[string]string { + a := make(map[string]string) + a["label"] = r.ID.String() + a["shape"] = "box" + tmpl, _ := kb.GetResourceTemplate(r.ID) + if tmpl != nil { + a["label"] += fmt.Sprintf("\n%v", tmpl.Classification.Is) + } + return a +} + +func dotEdgeAttributes(e construct.ResourceEdge) map[string]string { + a := make(map[string]string) + _ = e.Source.WalkProperties(func(path construct.PropertyPath, nerr error) error { + v := path.Get() + if v == e.Target.ID { + a["label"] = path.String() + return construct.StopWalk + } + return nil + }) + if e.Properties.Weight > 0 { + if a["label"] == "" { + a["label"] = fmt.Sprintf("%d", e.Properties.Weight) + } else { + a["label"] = fmt.Sprintf("%s\n%d", a["label"], e.Properties.Weight) + } + } + return a +} + +func GraphToDOT(kb knowledgebase2.TemplateKB, g construct.Graph, out io.Writer) error { + ids, err := construct.TopologicalSort(g) + if err != nil { + return err + } + nodes, err := construct.ResolveIds(g, ids) + if err != nil { + return err + } + var errs error + printf := func(s string, args ...any) { + _, err := fmt.Fprintf(out, s, args...) + errs = errors.Join(errs, err) + } + printf(`digraph { + rankdir = TB +`) + for _, n := range nodes { + printf(" %q%s\n", n.ID, dot.AttributesToString(dotAttributes(kb, n))) + } + + topoIndex := func(id construct.ResourceId) int { + for i, id2 := range ids { + if id2 == id { + return i + } + } + return -1 + } + edges, err := g.Edges() + if err != nil { + return err + } + sort.Slice(edges, func(i, j int) bool { + ti, tj := topoIndex(edges[i].Source), topoIndex(edges[j].Source) + if ti != tj { + return ti < tj + } + ti, tj = topoIndex(edges[i].Target), topoIndex(edges[j].Target) + return ti < tj + }) + for _, e := range edges { + edge, err := g.Edge(e.Source, e.Target) + if err != nil { + errs = errors.Join(errs, err) + continue + } + printf(" %q -> %q%s\n", e.Source, e.Target, dot.AttributesToString(dotEdgeAttributes(edge))) + } + printf("}\n") + return errs +} + +func GraphToSVG(kb knowledgebase2.TemplateKB, g construct.Graph, prefix string) error { + if debugDir := os.Getenv("KLOTHO_DEBUG_DIR"); debugDir != "" { + prefix = filepath.Join(debugDir, prefix) + } + f, err := os.Create(prefix + ".gv") + if err != nil { + return err + } + defer f.Close() + + dotContent := new(bytes.Buffer) + err = GraphToDOT(kb, g, io.MultiWriter(f, dotContent)) + if err != nil { + return fmt.Errorf("could not render graph to file %s: %v", prefix+".gv", err) + } + + svgContent, err := dot.ExecPan(bytes.NewReader(dotContent.Bytes())) + if err != nil { + return fmt.Errorf("could not run 'dot' for %s: %v", prefix+".gv", err) + } + + svgFile, err := os.Create(prefix + ".gv.svg") + if err != nil { + return fmt.Errorf("could not create file %s: %v", prefix+".gv.svg", err) + } + defer svgFile.Close() + _, err = fmt.Fprint(svgFile, svgContent) + return err +} diff --git a/pkg/engine2/visualizer.go b/pkg/engine2/visualizer.go index 3694bf2d3..834c1345a 100644 --- a/pkg/engine2/visualizer.go +++ b/pkg/engine2/visualizer.go @@ -64,24 +64,24 @@ func GetResourceVizTag(kb knowledgebase.TemplateKB, view View, resource construc func (e *Engine) GetViewsDag(view View, sol solution_context.SolutionContext) (visualizer.VisGraph, error) { viewDag := visualizer.NewVisGraph() - var graph construct.Graph + var resGraph construct.Graph if view == IACView { - graph = sol.DeploymentGraph() + resGraph = sol.DeploymentGraph() } else { - graph = sol.DataflowGraph() + resGraph = sol.DataflowGraph() } undirected := construct.NewGraphWithOptions() - err := undirected.AddVerticesFrom(graph) + err := undirected.AddVerticesFrom(resGraph) if err != nil { return nil, fmt.Errorf("could not copy vertices for undirected: %w", err) } - err = undirected.AddEdgesFrom(graph) + err = undirected.AddEdgesFrom(resGraph) if err != nil { return nil, fmt.Errorf("could not copy edges for undirected: %w", err) } - ids, err := construct.ReverseTopologicalSort(graph) + ids, err := construct.ReverseTopologicalSort(resGraph) if err != nil { return nil, err } @@ -113,13 +113,9 @@ func (e *Engine) GetViewsDag(view View, sol solution_context.SolutionContext) (v case NoRenderTag: continue case ParentIconTag: - parent, err := viewDag.Vertex(id) + err := e.handleParentIcon(sol, view, viewDag, id) if err != nil { - errs = errors.Join(errs, err) - continue - } - if err := e.setChildren(sol, parent); err != nil { - errs = errors.Join(errs, err) + errs = errors.Join(errs, fmt.Errorf("failed to handle parent icon %s: %w", id, err)) } case BigIconTag: err := e.handleBigIcon(sol, view, undirected, viewDag, id) @@ -136,6 +132,30 @@ func (e *Engine) GetViewsDag(view View, sol solution_context.SolutionContext) (v return viewDag, errs } +func (e *Engine) handleParentIcon( + sol solution_context.SolutionContext, + view View, + viewDag visualizer.VisGraph, + id construct.ResourceId, +) error { + this, err := viewDag.Vertex(id) + if err != nil { + return err + } + + parent, err := e.findParent(view, sol, viewDag, id) + if err != nil { + return err + } + this.Parent = parent + + if err := e.setChildren(sol, view, this); err != nil { + return err + } + + return nil +} + // handleBigIcon sets the parent of the big icon if there is a group it should be added to and // adds edges to any other big icons based on having the proper connections (network & permissions). func (e *Engine) handleBigIcon( @@ -145,16 +165,16 @@ func (e *Engine) handleBigIcon( viewDag visualizer.VisGraph, id construct.ResourceId, ) error { - source, err := viewDag.Vertex(id) + this, err := viewDag.Vertex(id) if err != nil { return err } - parent, err := e.findParent(view, undirected, viewDag, id) + parent, err := e.findParent(view, sol, viewDag, id) if err != nil { return err } - source.Parent = parent + this.Parent = parent targets, err := construct.TopologicalSort(viewDag) if err != nil { @@ -191,20 +211,27 @@ func (e *Engine) handleBigIcon( return errs } - if err := e.setChildren(sol, source); err != nil { + if err := e.setChildren(sol, view, this); err != nil { return err } return nil } -func (e *Engine) setChildren(sol solution_context.SolutionContext, v *visualizer.VisResource) error { - glue, err := knowledgebase.Downstream(sol.DataflowGraph(), sol.KnowledgeBase(), v.ID, knowledgebase.ResourceGlueLayer) +func (e *Engine) setChildren(sol solution_context.SolutionContext, view View, v *visualizer.VisResource) error { + glue, err := knowledgebase.Downstream( + sol.DataflowGraph(), + sol.KnowledgeBase(), + v.ID, + knowledgebase.ResourceLocalLayer, + ) if err != nil { return fmt.Errorf("failed to get glue layer for %s: %w", v.ID, err) } for _, glueElem := range glue { - v.Children.Add(glueElem) + if GetResourceVizTag(e.Kb, view, glueElem) == SmallIconTag { + v.Children.Add(glueElem) + } } // After glue, also include any resources whose namespace is this resource @@ -248,26 +275,57 @@ func (e *Engine) setChildren(sol solution_context.SolutionContext, v *visualizer func (e *Engine) findParent( view View, - undirected construct.Graph, + sol solution_context.SolutionContext, viewDag visualizer.VisGraph, id construct.ResourceId, ) (bestParent construct.ResourceId, err error) { - ids, err := construct.TopologicalSort(viewDag) + if id.Namespace != "" { + // namespaced resources' parents is always their namespace resource + tmpl, err := sol.KnowledgeBase().GetResourceTemplate(id) + if err != nil { + return bestParent, err + } + thisRes, err := sol.RawView().Vertex(id) + if err != nil { + return bestParent, err + } + for _, p := range tmpl.Properties { + if !p.Details().Namespace { + continue + } + v, err := thisRes.GetProperty(p.Details().Path) + if err != nil { + return bestParent, fmt.Errorf("failed to get namespace property %s: %w", p.Details().Path, err) + } + if propId, ok := v.(construct.ResourceId); ok { + return propId, nil + } else { + return bestParent, fmt.Errorf("namespace property %s is not a resource id (was: %T)", p.Details().Path, v) + } + } + } + + glue, err := knowledgebase.Downstream( + sol.DataflowGraph(), + sol.KnowledgeBase(), + id, + knowledgebase.ResourceLocalLayer, + ) if err != nil { return } - pather, err := construct.ShortestPaths(undirected, id, construct.DontSkipEdges) + pather, err := construct.ShortestPaths(sol.DataflowGraph(), id, construct.DontSkipEdges) if err != nil { return } bestParentWeight := math.MaxInt32 var errs error candidateLoop: - for _, id := range ids { - if GetResourceVizTag(e.Kb, view, id) != ParentIconTag { + for _, candidate := range glue { + if GetResourceVizTag(e.Kb, view, candidate) != ParentIconTag { continue } - path, err := pather.ShortestPath(id) + path, err := pather.ShortestPath(candidate) if errors.Is(err, graph.ErrTargetNotReachable) { continue } else if err != nil { @@ -285,14 +343,14 @@ candidateLoop: continue candidateLoop } } - weight, err := graph_addons.PathWeight(undirected, graph_addons.Path[construct.ResourceId](path)) + weight, err := graph_addons.PathWeight(sol.DataflowGraph(), graph_addons.Path[construct.ResourceId](path)) if err != nil { errs = errors.Join(errs, err) continue } if weight < bestParentWeight { bestParentWeight = weight - bestParent = id + bestParent = candidate } } err = errs diff --git a/pkg/knowledge_base2/graph.go b/pkg/knowledge_base2/graph.go index d5d8e17f5..841d064cf 100644 --- a/pkg/knowledge_base2/graph.go +++ b/pkg/knowledge_base2/graph.go @@ -12,9 +12,10 @@ import ( type ( // DependencyLayer represents how far away a resource to return for the [Upstream]/[Downstream] methods. - // 1. ResourceLocalLayer (layer 1) represents any unique resources the target resource needs to be operational + // 1. ResourceLocalLayer (layer 1) represents any unique resources the target resource needs to be operational, + // transitively. // 2. ResourceGlueLayer (layer 2) represents all upstream/downstream resources that represent glue. - // This will not include any other functional resources and will stop searching paths + // This will not include any other functional resources and will stopsearching paths // once a functional resource is reached. // 3. FirstFunctionalLayer (layer 3) represents all upstream/downstream resources that represent glue and // the first functional resource in other paths from the target resource. @@ -42,18 +43,23 @@ func resourceLocal( ids *[]construct.ResourceId, ) graph_addons.WalkGraphFunc[construct.ResourceId] { return func(path graph_addons.Path[construct.ResourceId], nerr error) error { - id := path[len(path)-1] - isSideEffect, err := IsOperationalResourceSideEffect(dag, kb, rid, id) + if len(path) <= 1 { + // skip source, this shouldn't happen but just in case + return nil + } + // Since we're skipping the path if it doesn't match, we only need to check the most recently added (ie, the last) + // resource in the path. + last := path[len(path)-1] + prevLast := path[len(path)-2] + sideEffect, err := IsOperationalResourceSideEffect(dag, kb, prevLast, last) if err != nil { return errors.Join(nerr, err) } - if isSideEffect { - if ids != nil { - (*ids) = append(*ids, id) - } - return nil + if !sideEffect { + return graph_addons.SkipPath } - return graph_addons.SkipPath + (*ids) = append(*ids, last) + return nil } } @@ -71,28 +77,18 @@ func resourceDirect( } func resourceGlue( - dag construct.Graph, kb TemplateKB, ids *[]construct.ResourceId, ) graph_addons.WalkGraphFunc[construct.ResourceId] { return func(path graph_addons.Path[construct.ResourceId], nerr error) error { - if len(path) <= 1 { - // skip source, this shouldn't happen but just in case + id := path[len(path)-1] + if GetFunctionality(kb, id) == Unknown { + if ids != nil { + (*ids) = append(*ids, id) + } return nil } - // Since we're skipping the path if it doesn't match, we only need to check the most recently added (ie, the last) - // resource in the path. - last := path[len(path)-1] - prevLast := path[len(path)-2] - sideEffect, err := IsOperationalResourceSideEffect(dag, kb, prevLast, last) - if err != nil { - return errors.Join(nerr, err) - } - if !sideEffect { - return graph_addons.SkipPath - } - (*ids) = append(*ids, last) - return nil + return graph_addons.SkipPath } } @@ -185,7 +181,7 @@ func Downstream(dag construct.Graph, kb TemplateKB, rid construct.ResourceId, la } return ids, nil case ResourceGlueLayer: - f = resourceGlue(dag, kb, &result) + f = resourceGlue(kb, &result) case FirstFunctionalLayer: f = firstFunctional(kb, &result) case AllDepsLayer: @@ -230,7 +226,7 @@ func Upstream(dag construct.Graph, kb TemplateKB, rid construct.ResourceId, laye } return ids, nil case ResourceGlueLayer: - f = resourceGlue(dag, kb, &result) + f = resourceGlue(kb, &result) case FirstFunctionalLayer: f = firstFunctional(kb, &result) case AllDepsLayer: @@ -255,7 +251,7 @@ func layerWalkFunc( case ResourceDirectLayer: return resourceDirect(dag, &result), nil case ResourceGlueLayer: - return resourceGlue(dag, kb, &result), nil + return resourceGlue(kb, &result), nil case FirstFunctionalLayer: return firstFunctional(kb, &result), nil case AllDepsLayer: @@ -316,41 +312,43 @@ func IsOperationalResourceSideEffect(dag construct.Graph, kb TemplateKB, rid, si } } + if !ruleSatisfied { + continue + } + // If the side effect resource fits the rule we then perform 2 more checks // 1. is there a path in the direction of the rule // 2. Is the property set with the resource that we are checking for - if ruleSatisfied { - if step.Direction == DirectionUpstream { - resources, err := graph.ShortestPathStable(dag, sideEffect, rid, construct.ResourceIdLess) - if len(resources) == 0 || err != nil { - continue - } - } else { - resources, err := graph.ShortestPathStable(dag, rid, sideEffect, construct.ResourceIdLess) - if len(resources) == 0 || err != nil { - continue - } + if step.Direction == DirectionUpstream { + resources, err := graph.ShortestPathStable(dag, sideEffect, rid, construct.ResourceIdLess) + if len(resources) == 0 || err != nil { + continue } - - propertyVal, err := resource.GetProperty(property.Details().Path) - if err != nil || propertyVal == nil { + } else { + resources, err := graph.ShortestPathStable(dag, rid, sideEffect, construct.ResourceIdLess) + if len(resources) == 0 || err != nil { continue } - val := reflect.ValueOf(propertyVal) - if val.Kind() == reflect.Array || val.Kind() == reflect.Slice { - for i := 0; i < val.Len(); i++ { - if arrId, ok := val.Index(i).Interface().(construct.ResourceId); ok && arrId == sideEffect { - return true, nil - } - } - } else { - if val.IsZero() { - continue - } - if valId, ok := val.Interface().(construct.ResourceId); ok && valId == sideEffect { + } + + propertyVal, err := resource.GetProperty(property.Details().Path) + if err != nil || propertyVal == nil { + continue + } + val := reflect.ValueOf(propertyVal) + if val.Kind() == reflect.Array || val.Kind() == reflect.Slice { + for i := 0; i < val.Len(); i++ { + if arrId, ok := val.Index(i).Interface().(construct.ResourceId); ok && arrId == sideEffect { return true, nil } } + } else { + if val.IsZero() { + continue + } + if valId, ok := val.Interface().(construct.ResourceId); ok && valId == sideEffect { + return true, nil + } } } diff --git a/pkg/knowledge_base2/graph_test.go b/pkg/knowledge_base2/graph_test.go deleted file mode 100644 index 57ffdb71c..000000000 --- a/pkg/knowledge_base2/graph_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package knowledgebase2 - -// import ( -// "testing" - -// construct "github.com/klothoplatform/klotho/pkg/construct2" -// ) - -// func Test_IsOperationalResourceSideEffect(t *testing.T) { -// type args struct { -// dag construct.Graph -// templates []*ResourceTemplate -// rid construct.ResourceId -// id construct.ResourceId -// } -// tests := []struct { -// name string -// args args -// want bool -// wantErr bool -// }{ -// {}, -// } -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// kb := &KnowledgeBase{} -// for _, template := range tt.args.templates { -// kb.AddResourceTemplate(template) -// } - -// got, err := IsOperationalResourceSideEffect(tt.args.dag, tt.args.kb, tt.args.rid, tt.args.id) -// if (err != nil) != tt.wantErr { -// t.Errorf("IsOperationalResourceSideEffect() error = %v, wantErr %v", err, tt.wantErr) -// return -// } -// if got != tt.want { -// t.Errorf( -// "IsOperationalResourceSideEffect() got = %v, want %v", -// got, tt.want, -// ) -// } -// }) -// } -// } diff --git a/pkg/knowledge_base2/kbtesting/graph_test.go b/pkg/knowledge_base2/kbtesting/graph_test.go new file mode 100644 index 000000000..0852616a1 --- /dev/null +++ b/pkg/knowledge_base2/kbtesting/graph_test.go @@ -0,0 +1,162 @@ +package kbtesting + +import ( + "fmt" + "testing" + + construct "github.com/klothoplatform/klotho/pkg/construct2" + "github.com/klothoplatform/klotho/pkg/construct2/graphtest" + knowledgebase "github.com/klothoplatform/klotho/pkg/knowledge_base2" + "github.com/klothoplatform/klotho/pkg/knowledge_base2/properties" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestDownstream(t *testing.T) { + // The following variables are provided as a convenience for writing tests that all can use the same + // basic structure / parameters + defaultGraph := graphtest.MakeGraph(t, construct.NewGraph(), + "p:t:A -> p:t:B -> p:t:C", // A -> B -> C all within the same operational boundary + "p:t:B -> p:t:D", // B -> D crosses operational boundaries + "p:t:B -> p:t:X -> p:t:Y", // X is a functional boundary + ) + defaultResource := graphtest.ParseId(t, "p:t:A") + defaultKB := func(t *testing.T, kb *MockKB) { + named := func(name string) func(construct.ResourceId) bool { + return func(id construct.ResourceId) bool { + return id.Name == name + } + } + makeOpProperty := func(name string, namespace bool) knowledgebase.Property { + p := &properties.ResourceProperty{} + p.Name = name + p.Path = name + p.Namespace = namespace + p.OperationalRule = &knowledgebase.PropertyRule{ + Step: knowledgebase.OperationalStep{ + Resources: []knowledgebase.ResourceSelector{{Selector: "p:t:" + name}}, + }, + } + return p + } + + kb.On("GetResourceTemplate", mock.MatchedBy(named("A"))).Return(&knowledgebase.ResourceTemplate{ + Classification: knowledgebase.Classification{Is: []string{"compute"}}, + Properties: knowledgebase.Properties{ + "B": makeOpProperty("B", false), + }, + }, nil) + kb.On("GetResourceTemplate", mock.MatchedBy(named("B"))).Return(&knowledgebase.ResourceTemplate{ + Properties: knowledgebase.Properties{ + "C": makeOpProperty("C", false), + "D": &properties.ResourceProperty{}, // not an operational property + }, + }, nil) + kb.On("GetResourceTemplate", mock.MatchedBy(named("X"))).Return(&knowledgebase.ResourceTemplate{ + Classification: knowledgebase.Classification{Is: []string{"compute"}}, + }, nil) + kb.On("GetResourceTemplate", mock.Anything).Return(&knowledgebase.ResourceTemplate{}, nil) + } + setProp := func(idStr string, prop string, resValueStr string) { + id := graphtest.ParseId(t, idStr) + resValue := graphtest.ParseId(t, resValueStr) + r, err := defaultGraph.Vertex(id) + if err != nil { + t.Fatal(fmt.Errorf("error setting property %s.%s = %s: %w", idStr, prop, resValueStr, err)) + } + err = r.SetProperty(prop, resValue) + if err != nil { + t.Fatal(fmt.Errorf("error setting property %s.%s = %s: %w", idStr, prop, resValueStr, err)) + } + } + setProp("p:t:A", "B", "p:t:B") + setProp("p:t:B", "C", "p:t:C") + setProp("p:t:B", "D", "p:t:D") + + tests := []struct { + name string + graph []any + resource string + layer knowledgebase.DependencyLayer + kbmock func(*testing.T, *MockKB) + want []string + wantErr bool + }{ + // Local + { + name: "local", + layer: knowledgebase.ResourceLocalLayer, + want: []string{"p:t:B", "p:t:C"}, + }, + // Direct + { + name: "no downstream", + resource: "p:t:C", + layer: knowledgebase.ResourceDirectLayer, + }, + { + name: "direct downstream", + layer: knowledgebase.ResourceDirectLayer, + want: []string{"p:t:B"}, + }, + // Glue + { + name: "glue", + layer: knowledgebase.ResourceGlueLayer, + want: []string{"p:t:B", "p:t:C", "p:t:D"}, + }, + // Functional + { + name: "functional", + layer: knowledgebase.FirstFunctionalLayer, + want: []string{"p:t:B", "p:t:C", "p:t:D", "p:t:X"}, + }, + // All + { + name: "all", + layer: knowledgebase.AllDepsLayer, + want: []string{"p:t:B", "p:t:C", "p:t:D", "p:t:X", "p:t:Y"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert := assert.New(t) + + var rid construct.ResourceId + if tt.resource != "" { + rid = graphtest.ParseId(t, tt.resource) + } else { + rid = defaultResource + } + + var g construct.Graph + if tt.graph != nil { + g = graphtest.MakeGraph(t, construct.NewGraph(), tt.graph...) + } else { + g = defaultGraph + } + + kb := &MockKB{} + if tt.kbmock != nil { + tt.kbmock(t, kb) + } else { + defaultKB(t, kb) + } + gotIds, err := knowledgebase.Downstream(g, kb, rid, tt.layer) + if tt.wantErr { + assert.Error(err) + return + } else if !assert.NoError(err) { + return + } + var got []string + if gotIds != nil { + got = make([]string, len(tt.want)) + for i, w := range gotIds { + got[i] = w.String() + } + } + assert.ElementsMatch(tt.want, got) + }) + } +} diff --git a/pkg/knowledge_base2/kbtesting/mock_kb.go b/pkg/knowledge_base2/kbtesting/mock_kb.go index 33ca78736..6f7b92745 100644 --- a/pkg/knowledge_base2/kbtesting/mock_kb.go +++ b/pkg/knowledge_base2/kbtesting/mock_kb.go @@ -1,4 +1,4 @@ -package enginetesting +package kbtesting import ( "github.com/dominikbraun/graph" @@ -16,6 +16,11 @@ func (m *MockKB) ListResources() []*knowledgebase.ResourceTemplate { return args.Get(0).([]*knowledgebase.ResourceTemplate) } +func (m *MockKB) GetModel(model string) *knowledgebase.Model { + args := m.Called(model) + return args.Get(0).(*knowledgebase.Model) +} + func (m *MockKB) Edges() ([]graph.Edge[*knowledgebase.ResourceTemplate], error) { args := m.Called() return args.Get(0).([]graph.Edge[*knowledgebase.ResourceTemplate]), args.Error(1) @@ -64,11 +69,18 @@ func (m *MockKB) GetClassification(id construct.ResourceId) knowledgebase.Classi args := m.Called(id) return args.Get(0).(knowledgebase.Classification) } -func (m *MockKB) GetResourcesNamespaceResource(resource *construct.Resource) construct.ResourceId { +func (m *MockKB) GetResourcesNamespaceResource(resource *construct.Resource) (construct.ResourceId, error) { args := m.Called(resource) - return args.Get(0).(construct.ResourceId) + return args.Get(0).(construct.ResourceId), args.Error(1) } func (m *MockKB) GetResourcePropertyType(resource construct.ResourceId, propertyName string) string { args := m.Called(resource, propertyName) return args.String(0) } + +func (m *MockKB) GetPathSatisfactionsFromEdge( + source, target construct.ResourceId, +) ([]knowledgebase.EdgePathSatisfaction, error) { + args := m.Called(source, target) + return args.Get(0).([]knowledgebase.EdgePathSatisfaction), args.Error(1) +} From b1dc862548db43fadd69a2ddc08405f7db7baede Mon Sep 17 00:00:00 2001 From: Gordon Date: Mon, 8 Jan 2024 17:05:06 -0500 Subject: [PATCH 4/5] Regen tests --- .../testdata/2_routes.dataflow-viz.yaml | 2 - .../testdata/ecs_rds.dataflow-viz.yaml | 13 ------- pkg/engine2/testdata/ecs_rds.expect.yaml | 12 +++--- .../testdata/extend_graph.dataflow-viz.yaml | 1 - .../testdata/k8s_api.dataflow-viz.yaml | 37 +------------------ pkg/engine2/testdata/k8s_api.expect.yaml | 12 +++--- .../testdata/lambda_efs.dataflow-viz.yaml | 9 ----- pkg/engine2/testdata/lambda_efs.expect.yaml | 14 +++---- .../namespace_pathselect.dataflow-viz.yaml | 7 ---- .../testdata/remove_path.dataflow-viz.yaml | 18 --------- pkg/engine2/testdata/remove_path.expect.yaml | 2 +- .../vpc_import_to_lambda.dataflow-viz.yaml | 3 -- ...ort_wo_subnets_to_lambda.dataflow-viz.yaml | 6 --- pkg/knowledge_base2/kbtesting/graph_test.go | 8 +++- 14 files changed, 29 insertions(+), 115 deletions(-) diff --git a/pkg/engine2/testdata/2_routes.dataflow-viz.yaml b/pkg/engine2/testdata/2_routes.dataflow-viz.yaml index b359fd462..c36389dc2 100755 --- a/pkg/engine2/testdata/2_routes.dataflow-viz.yaml +++ b/pkg/engine2/testdata/2_routes.dataflow-viz.yaml @@ -5,7 +5,6 @@ resources: - aws:ecr_image:lambda_function_1-image - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_function_1-ExecutionRole - parent: rest_api/rest_api_1 tag: big lambda_function/lambda_function_0: @@ -13,7 +12,6 @@ resources: - aws:ecr_image:lambda_function_0-image - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_function_0-ExecutionRole - parent: rest_api/rest_api_1 tag: big rest_api/rest_api_1: diff --git a/pkg/engine2/testdata/ecs_rds.dataflow-viz.yaml b/pkg/engine2/testdata/ecs_rds.dataflow-viz.yaml index 2b60f7245..9f728f2c0 100755 --- a/pkg/engine2/testdata/ecs_rds.dataflow-viz.yaml +++ b/pkg/engine2/testdata/ecs_rds.dataflow-viz.yaml @@ -2,13 +2,7 @@ provider: aws resources: rds_instance/rds-instance-2: children: - - aws:availability_zone:region-0:availability_zone-0 - - aws:availability_zone:region-0:availability_zone-1 - aws:rds_subnet_group:rds_subnet_group-0 - - aws:region:region-0 - - aws:subnet:vpc-0:subnet-0 - - aws:subnet:vpc-0:subnet-1 - - aws:vpc:vpc-0 parent: vpc/vpc-0 tag: big @@ -29,18 +23,11 @@ resources: ecs_service/ecs_service_0: children: - - aws:availability_zone:region-0:availability_zone-0 - - aws:availability_zone:region-0:availability_zone-1 - aws:ecr_image:ecs_service_0-image - aws:ecr_repo:ecr_repo-0 - - aws:ecs_cluster:ecs_cluster-0 - aws:ecs_task_definition:ecs_service_0 - aws:iam_role:ecs_service_0-execution-role - aws:log_group:ecs_service_0-log-group - - aws:region:region-0 - - aws:subnet:vpc-0:subnet-0 - - aws:subnet:vpc-0:subnet-1 - - aws:vpc:vpc-0 parent: vpc/vpc-0 tag: big diff --git a/pkg/engine2/testdata/ecs_rds.expect.yaml b/pkg/engine2/testdata/ecs_rds.expect.yaml index f8d24085d..674b9038e 100755 --- a/pkg/engine2/testdata/ecs_rds.expect.yaml +++ b/pkg/engine2/testdata/ecs_rds.expect.yaml @@ -169,6 +169,12 @@ resources: Protocol: "-1" ToPort: 0 IngressRules: + - CidrBlocks: + - 10.0.128.0/18 + Description: Allow ingress traffic from ip addresses within the subnet subnet-0 + FromPort: 0 + Protocol: "-1" + ToPort: 0 - CidrBlocks: - 10.0.192.0/18 Description: Allow ingress traffic from ip addresses within the subnet subnet-1 @@ -180,12 +186,6 @@ resources: Protocol: "-1" Self: true ToPort: 0 - - CidrBlocks: - - 10.0.128.0/18 - Description: Allow ingress traffic from ip addresses within the subnet subnet-0 - FromPort: 0 - Protocol: "-1" - ToPort: 0 Vpc: aws:vpc:vpc-0 aws:route_table:vpc-0:subnet-0-route_table: Routes: diff --git a/pkg/engine2/testdata/extend_graph.dataflow-viz.yaml b/pkg/engine2/testdata/extend_graph.dataflow-viz.yaml index f3279bbe5..a43ab65d7 100755 --- a/pkg/engine2/testdata/extend_graph.dataflow-viz.yaml +++ b/pkg/engine2/testdata/extend_graph.dataflow-viz.yaml @@ -5,7 +5,6 @@ resources: - aws:ecr_image:lambda_function_0-image - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_function_0-ExecutionRole - parent: rest_api/rest_api_1 tag: big rest_api/rest_api_1: diff --git a/pkg/engine2/testdata/k8s_api.dataflow-viz.yaml b/pkg/engine2/testdata/k8s_api.dataflow-viz.yaml index bd2592d7b..f1792347d 100755 --- a/pkg/engine2/testdata/k8s_api.dataflow-viz.yaml +++ b/pkg/engine2/testdata/k8s_api.dataflow-viz.yaml @@ -2,26 +2,12 @@ provider: aws resources: kubernetes:pod:eks_cluster-0/pod2: children: - - aws:availability_zone:region-0:availability_zone-0 - - aws:availability_zone:region-0:availability_zone-1 - - aws:eks_cluster:eks_cluster-0 - aws:iam_role:ClusterRole-eks_cluster-0 - - aws:region:region-0 - - aws:subnet:vpc-0:subnet-0 - - aws:subnet:vpc-0:subnet-1 - - aws:vpc:vpc-0 parent: eks_cluster/eks_cluster-0 tag: big load_balancer/rest-api-4-integbcc77100: - children: - - aws:availability_zone:region-0:availability_zone-0 - - aws:availability_zone:region-0:availability_zone-1 - - aws:region:region-0 - - aws:subnet:vpc-0:subnet-0 - - aws:subnet:vpc-0:subnet-1 - - aws:vpc:vpc-0 - parent: eks_cluster/eks_cluster-0 + parent: vpc/vpc-0 tag: big load_balancer/rest-api-4-integbcc77100 -> kubernetes:pod:eks_cluster-0/pod2: @@ -34,27 +20,13 @@ resources: kubernetes:helm_chart:eks_cluster-0/metricsserver: children: - - aws:availability_zone:region-0:availability_zone-0 - - aws:availability_zone:region-0:availability_zone-1 - - aws:eks_cluster:eks_cluster-0 - aws:iam_role:ClusterRole-eks_cluster-0 - - aws:region:region-0 - - aws:subnet:vpc-0:subnet-0 - - aws:subnet:vpc-0:subnet-1 - - aws:vpc:vpc-0 parent: eks_cluster/eks_cluster-0 tag: big kubernetes:helm_chart:eks_cluster-0/aws-load-balancer-controller: children: - - aws:availability_zone:region-0:availability_zone-0 - - aws:availability_zone:region-0:availability_zone-1 - - aws:eks_cluster:eks_cluster-0 - aws:iam_role:ClusterRole-eks_cluster-0 - - aws:region:region-0 - - aws:subnet:vpc-0:subnet-0 - - aws:subnet:vpc-0:subnet-1 - - aws:vpc:vpc-0 parent: eks_cluster/eks_cluster-0 tag: big @@ -83,13 +55,7 @@ resources: eks_cluster/eks_cluster-0: children: - - aws:availability_zone:region-0:availability_zone-0 - - aws:availability_zone:region-0:availability_zone-1 - aws:iam_role:ClusterRole-eks_cluster-0 - - aws:region:region-0 - - aws:subnet:vpc-0:subnet-0 - - aws:subnet:vpc-0:subnet-1 - - aws:vpc:vpc-0 - kubernetes:helm_chart:eks_cluster-0:aws-load-balancer-controller - kubernetes:helm_chart:eks_cluster-0:metricsserver - kubernetes:pod:eks_cluster-0:pod2 @@ -97,6 +63,7 @@ resources: - kubernetes:service_account:eks_cluster-0:aws-load-balancer-controller - kubernetes:service_account:eks_cluster-0:pod2 - kubernetes:target_group_binding:eks_cluster-0:restapi4integration0-pod2 + parent: vpc/vpc-0 tag: parent aws:api_integration:rest_api_4/rest_api_4_integration_0: diff --git a/pkg/engine2/testdata/k8s_api.expect.yaml b/pkg/engine2/testdata/k8s_api.expect.yaml index 35611b664..936705163 100755 --- a/pkg/engine2/testdata/k8s_api.expect.yaml +++ b/pkg/engine2/testdata/k8s_api.expect.yaml @@ -483,12 +483,6 @@ resources: Protocol: "-1" ToPort: 0 IngressRules: - - CidrBlocks: - - 0.0.0.0/0 - Description: Allows ingress traffic from the EKS control plane - FromPort: 9443 - Protocol: TCP - ToPort: 9443 - Description: Allow ingress traffic from within the same security group FromPort: 0 Protocol: "-1" @@ -506,6 +500,12 @@ resources: FromPort: 0 Protocol: "-1" ToPort: 0 + - CidrBlocks: + - 0.0.0.0/0 + Description: Allows ingress traffic from the EKS control plane + FromPort: 9443 + Protocol: TCP + ToPort: 9443 Vpc: aws:vpc:vpc-0 aws:route_table:vpc-0:subnet-0-route_table: Routes: diff --git a/pkg/engine2/testdata/lambda_efs.dataflow-viz.yaml b/pkg/engine2/testdata/lambda_efs.dataflow-viz.yaml index 80db6f9e5..1180a0504 100755 --- a/pkg/engine2/testdata/lambda_efs.dataflow-viz.yaml +++ b/pkg/engine2/testdata/lambda_efs.dataflow-viz.yaml @@ -2,12 +2,9 @@ provider: aws resources: efs_file_system/test-efs-fs: children: - - aws:availability_zone:region-0:availability_zone-0 - aws:efs_access_point:test-efs-fs:lambda_test_app-test-efs-fs - aws:efs_mount_target:test-efs-fs:lambda_test_app-test-efs-fs - aws:efs_mount_target:test-efs-fs:subnet-1-test-efs-fs - - aws:region:region-0 - parent: vpc/vpc-0 tag: big vpc/vpc-0: @@ -28,15 +25,9 @@ resources: lambda_function/lambda_test_app: children: - - aws:availability_zone:region-0:availability_zone-0 - - aws:availability_zone:region-0:availability_zone-1 - aws:ecr_image:lambda_test_app-image - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_test_app-ExecutionRole - - aws:region:region-0 - - aws:subnet:vpc-0:lambda_test_app-test-efs-fs - - aws:subnet:vpc-0:subnet-1 - - aws:vpc:vpc-0 parent: vpc/vpc-0 tag: big diff --git a/pkg/engine2/testdata/lambda_efs.expect.yaml b/pkg/engine2/testdata/lambda_efs.expect.yaml index 446f91e7c..dba34bea6 100755 --- a/pkg/engine2/testdata/lambda_efs.expect.yaml +++ b/pkg/engine2/testdata/lambda_efs.expect.yaml @@ -62,8 +62,8 @@ resources: - aws:efs_file_system:test-efs-fs#Arn Version: "2012-10-17" ManagedPolicies: - - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole + - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole aws:ecr_repo:ecr_repo-0: ForceDelete: true aws:efs_mount_target:test-efs-fs:lambda_test_app-test-efs-fs: @@ -145,6 +145,12 @@ resources: Protocol: "-1" ToPort: 0 IngressRules: + - CidrBlocks: + - 10.0.192.0/18 + Description: Allow ingress traffic from ip addresses within the subnet lambda_test_app-test-efs-fs + FromPort: 0 + Protocol: "-1" + ToPort: 0 - Description: Allow ingress traffic from within the same security group FromPort: 0 Protocol: "-1" @@ -156,12 +162,6 @@ resources: FromPort: 0 Protocol: "-1" ToPort: 0 - - CidrBlocks: - - 10.0.192.0/18 - Description: Allow ingress traffic from ip addresses within the subnet lambda_test_app-test-efs-fs - FromPort: 0 - Protocol: "-1" - ToPort: 0 Vpc: aws:vpc:vpc-0 aws:security_group:vpc-0:subnet-1-test-efs-fs: EgressRules: diff --git a/pkg/engine2/testdata/namespace_pathselect.dataflow-viz.yaml b/pkg/engine2/testdata/namespace_pathselect.dataflow-viz.yaml index faf084d74..de979fd97 100755 --- a/pkg/engine2/testdata/namespace_pathselect.dataflow-viz.yaml +++ b/pkg/engine2/testdata/namespace_pathselect.dataflow-viz.yaml @@ -16,15 +16,9 @@ resources: lambda_function/lambda_function_2: children: - - aws:availability_zone:region-0:availability_zone-0 - - aws:availability_zone:region-0:availability_zone-1 - aws:ecr_image:lambda_function_2-image - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_function_2-ExecutionRole - - aws:region:region-0 - - aws:subnet:vpc_1:lambda_function_2-vpc_1 - - aws:subnet:vpc_1:subnet-1 - - aws:vpc:vpc_1 parent: vpc/vpc_1 tag: big @@ -33,6 +27,5 @@ resources: - aws:ecr_image:lambda_function_0-image - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_function_0-ExecutionRole - parent: vpc/vpc_1 tag: big diff --git a/pkg/engine2/testdata/remove_path.dataflow-viz.yaml b/pkg/engine2/testdata/remove_path.dataflow-viz.yaml index 919791390..7e8af91ca 100644 --- a/pkg/engine2/testdata/remove_path.dataflow-viz.yaml +++ b/pkg/engine2/testdata/remove_path.dataflow-viz.yaml @@ -2,13 +2,7 @@ provider: aws resources: rds_instance/rds-instance-1: children: - - aws:availability_zone:region-0:availability_zone-0 - - aws:availability_zone:region-0:availability_zone-1 - aws:rds_subnet_group:rds_subnet_group-0 - - aws:region:region-0 - - aws:subnet:vpc-0:subnet-0 - - aws:subnet:vpc-0:subnet-1 - - aws:vpc:vpc-0 parent: vpc/vpc-0 tag: big @@ -30,29 +24,17 @@ resources: lambda_function/lambda_function_3: children: - - aws:availability_zone:region-0:availability_zone-0 - - aws:availability_zone:region-0:availability_zone-1 - aws:ecr_image:lambda_function_3-image - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_function_3-ExecutionRole - - aws:region:region-0 - - aws:subnet:vpc-0:subnet-0 - - aws:subnet:vpc-0:subnet-1 - - aws:vpc:vpc-0 parent: vpc/vpc-0 tag: big lambda_function/lambda_function_0: children: - - aws:availability_zone:region-0:availability_zone-0 - - aws:availability_zone:region-0:availability_zone-1 - aws:ecr_image:lambda_function_0-image - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_function_0-ExecutionRole - - aws:region:region-0 - - aws:subnet:vpc-0:subnet-0 - - aws:subnet:vpc-0:subnet-1 - - aws:vpc:vpc-0 parent: vpc/vpc-0 tag: big diff --git a/pkg/engine2/testdata/remove_path.expect.yaml b/pkg/engine2/testdata/remove_path.expect.yaml index 7d19756c8..71afa2c34 100644 --- a/pkg/engine2/testdata/remove_path.expect.yaml +++ b/pkg/engine2/testdata/remove_path.expect.yaml @@ -113,8 +113,8 @@ resources: - aws:rds_instance:rds-instance-1#RdsConnectionArn Version: "2012-10-17" ManagedPolicies: - - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole + - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole aws:ecr_repo:ecr_repo-0: ForceDelete: true aws:elastic_ip:subnet-0-route_table-nat_gateway-elastic_ip: diff --git a/pkg/engine2/testdata/vpc_import_to_lambda.dataflow-viz.yaml b/pkg/engine2/testdata/vpc_import_to_lambda.dataflow-viz.yaml index 8b914cf86..4f5085f59 100644 --- a/pkg/engine2/testdata/vpc_import_to_lambda.dataflow-viz.yaml +++ b/pkg/engine2/testdata/vpc_import_to_lambda.dataflow-viz.yaml @@ -11,9 +11,6 @@ resources: - aws:ecr_image:lambda_function-image - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_function-ExecutionRole - - aws:subnet:subnet2 - - aws:subnet:vpc:subnet1 - - aws:vpc:vpc parent: vpc/vpc tag: big diff --git a/pkg/engine2/testdata/vpc_import_wo_subnets_to_lambda.dataflow-viz.yaml b/pkg/engine2/testdata/vpc_import_wo_subnets_to_lambda.dataflow-viz.yaml index 0bf05b62f..ae4f18879 100644 --- a/pkg/engine2/testdata/vpc_import_wo_subnets_to_lambda.dataflow-viz.yaml +++ b/pkg/engine2/testdata/vpc_import_wo_subnets_to_lambda.dataflow-viz.yaml @@ -16,15 +16,9 @@ resources: lambda_function/lambda_function: children: - - aws:availability_zone:region-0:availability_zone-0 - - aws:availability_zone:region-0:availability_zone-1 - aws:ecr_image:lambda_function-image - aws:ecr_repo:ecr_repo-0 - aws:iam_role:lambda_function-ExecutionRole - - aws:region:region-0 - - aws:subnet:vpc:lambda_function-vpc - - aws:subnet:vpc:subnet-1 - - aws:vpc:vpc parent: vpc/vpc tag: big diff --git a/pkg/knowledge_base2/kbtesting/graph_test.go b/pkg/knowledge_base2/kbtesting/graph_test.go index 0852616a1..2e3bf6916 100644 --- a/pkg/knowledge_base2/kbtesting/graph_test.go +++ b/pkg/knowledge_base2/kbtesting/graph_test.go @@ -99,6 +99,12 @@ func TestDownstream(t *testing.T) { layer: knowledgebase.ResourceDirectLayer, want: []string{"p:t:B"}, }, + { + name: "direct downstream B", + layer: knowledgebase.ResourceDirectLayer, + resource: "p:t:B", + want: []string{"p:t:C", "p:t:D", "p:t:X"}, + }, // Glue { name: "glue", @@ -151,7 +157,7 @@ func TestDownstream(t *testing.T) { } var got []string if gotIds != nil { - got = make([]string, len(tt.want)) + got = make([]string, len(gotIds)) for i, w := range gotIds { got[i] = w.String() } From aacac65d400b685af737b64d0518e5afbb79fe55 Mon Sep 17 00:00:00 2001 From: Gordon Date: Tue, 9 Jan 2024 12:15:23 -0500 Subject: [PATCH 5/5] Roll up parents if not a ParentIconTag --- pkg/engine2/visualizer.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/engine2/visualizer.go b/pkg/engine2/visualizer.go index 834c1345a..c09dc8616 100644 --- a/pkg/engine2/visualizer.go +++ b/pkg/engine2/visualizer.go @@ -298,7 +298,11 @@ func (e *Engine) findParent( return bestParent, fmt.Errorf("failed to get namespace property %s: %w", p.Details().Path, err) } if propId, ok := v.(construct.ResourceId); ok { - return propId, nil + if GetResourceVizTag(e.Kb, view, propId) == ParentIconTag { + return propId, nil + } + // the property isn't shown as a parent (eg. Subnet or ALB Listener), so roll it up to the next parent + return e.findParent(view, sol, viewDag, propId) } else { return bestParent, fmt.Errorf("namespace property %s is not a resource id (was: %T)", p.Details().Path, v) }