From 78401ee79c491cb10124e16105814f37e298c321 Mon Sep 17 00:00:00 2001 From: shirim Date: Thu, 12 Oct 2023 18:47:54 +0300 Subject: [PATCH 01/42] structs and main functionality of https://github.com/np-guard/vpc-network-config-analyzer/issues/163 --- pkg/common/connectionset.go | 17 +++++++++ pkg/vpcmodel/output.go | 1 + pkg/vpcmodel/semanticDiffSubnets.go | 53 +++++++++++++++++++++++++++++ pkg/vpcmodel/subnetsConnectivity.go | 4 ++- 4 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 pkg/vpcmodel/semanticDiffSubnets.go diff --git a/pkg/common/connectionset.go b/pkg/common/connectionset.go index 079e80e22..4ee9972d8 100644 --- a/pkg/common/connectionset.go +++ b/pkg/common/connectionset.go @@ -524,6 +524,23 @@ func (conn *ConnectionSet) ResponseConnection() *ConnectionSet { return res } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///// Diff between ConnectionSet + +// Connection A minus Connection B +type ConnectionSetDiff struct { + ConnectionSet + statefullDiff *ConnectionSet // connection element which is stateful only in A +} + +func (conn *ConnectionSet) ConnectionSet1Minus2(other *ConnectionSet) *ConnectionSet { + // todo: use func (conn *ConnectionSet) Subtract(other *ConnectionSet) *ConnectionSet and note the + // special case in which the connection differs only one being stateful and the other not + return nil +} + +// ToDo up to here in connectionSet + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* diff --git a/pkg/vpcmodel/output.go b/pkg/vpcmodel/output.go index ff3512030..48eff9eae 100644 --- a/pkg/vpcmodel/output.go +++ b/pkg/vpcmodel/output.go @@ -60,6 +60,7 @@ func NewOutputGenerator(c *CloudConfig, grouping bool, uc OutputUseCase, archOnl res.subnetsConn = subnetsConn } } + // todo: add for diff return res, nil } diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go new file mode 100644 index 000000000..dff446ed9 --- /dev/null +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -0,0 +1,53 @@ +package vpcmodel + +import "github.com/np-guard/vpc-network-config-analyzer/pkg/common" + +type SubnetsDiff map[EndpointElem]map[EndpointElem]*common.ConnectionSetDiff + +type diffBetweenSubnets struct { + subnet1Connectivity VPCsubnetConnectivity + subnet2Connectivity VPCsubnetConnectivity + subnet1Minus2 SubnetsDiff + subnet2Minus1 SubnetsDiff + + GroupedSubnet1Minus2 *GroupConnLines + GroupedSubnet1Minus1 *GroupConnLines +} + +type DiffType = int + +const ( + NoDiff DiffType = iota + MissingSrcEP + MissingDstEP + MissingSrcDstEP + MissingConnection + ChangedConnection +) + +func (c *CloudConfig) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets, error) { + return nil, nil +} + +// generates from subnet1Connectivity.AllowedConnsCombined and subnet2Connectivity.AllowedConnsCombined +// Two equivalent subnetConnectivity objects s.t. any (src1, dst1) of subnet1Connectivity and +// (src2, dst2) of subnet2Connectivity are either: +// 1. src1 disjoint src2 or dst1 disjoint dst2 +// 2. src1 = src2 and dst1 = dst2 +// +// todo: use DisjointIPBlocks(set1, set2 []*IPBlock) []*IPBlock of ipBlock.go +func (d *diffBetweenSubnets) getConnectivesWithSameIpBlocks() (*subnetConnectivity, *subnetConnectivity) { + return nil, nil +} + +func (connectivity *subnetConnectivity) subnetConnectivitySubtract(other subnetConnectivity) DiffType { + + return NoDiff +} + +// todo: instead of adding functionality to grouping, I plan to have more generic connectivity items that will be grouped +// encode the SubnetsDiff into this generic item as well as the other entities we are grouping +// and then decode in the printing +// the idea is to use instead of *common.ConnectionSet in the grouped entity a string which will encode the connection +// and also the diff where relevant +// this will requires some rewriting in the existing grouping functionality and the way it provides service to subnetsConnectivity and nodesConnectivity diff --git a/pkg/vpcmodel/subnetsConnectivity.go b/pkg/vpcmodel/subnetsConnectivity.go index 8a054ab5f..657b16869 100644 --- a/pkg/vpcmodel/subnetsConnectivity.go +++ b/pkg/vpcmodel/subnetsConnectivity.go @@ -7,12 +7,14 @@ import ( "fmt" ) +type subnetConnectivity map[EndpointElem]map[EndpointElem]*common.ConnectionSet + // VPCsubnetConnectivity captures allowed connectivity for subnets, considering nacl and pgw resources type VPCsubnetConnectivity struct { // computed for each node (subnet), by iterating its ConnectivityResult for all relevant VPC resources that capture it AllowedConns map[EndpointElem]*ConfigBasedConnectivityResults // combined connectivity - considering both ingress and egress per connection - AllowedConnsCombined map[EndpointElem]map[EndpointElem]*common.ConnectionSet + AllowedConnsCombined subnetConnectivity CloudConfig *CloudConfig // grouped connectivity result GroupedConnectivity *GroupConnLines From 9a4f947f97e6fe9e6f026508899b360afcbff739 Mon Sep 17 00:00:00 2001 From: shirim Date: Sun, 15 Oct 2023 16:58:52 +0300 Subject: [PATCH 02/42] Highlevel code and structs --- pkg/common/connectionset.go | 2 +- pkg/vpcmodel/semanticDiffSubnets.go | 102 ++++++++++++++++++++++------ 2 files changed, 82 insertions(+), 22 deletions(-) diff --git a/pkg/common/connectionset.go b/pkg/common/connectionset.go index 4ee9972d8..dc2bfb7d6 100644 --- a/pkg/common/connectionset.go +++ b/pkg/common/connectionset.go @@ -530,7 +530,7 @@ func (conn *ConnectionSet) ResponseConnection() *ConnectionSet { // Connection A minus Connection B type ConnectionSetDiff struct { ConnectionSet - statefullDiff *ConnectionSet // connection element which is stateful only in A + StatefullDiff *ConnectionSet // connection element which is stateful only in A } func (conn *ConnectionSet) ConnectionSet1Minus2(other *ConnectionSet) *ConnectionSet { diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index dff446ed9..37d7cf626 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -2,19 +2,7 @@ package vpcmodel import "github.com/np-guard/vpc-network-config-analyzer/pkg/common" -type SubnetsDiff map[EndpointElem]map[EndpointElem]*common.ConnectionSetDiff - -type diffBetweenSubnets struct { - subnet1Connectivity VPCsubnetConnectivity - subnet2Connectivity VPCsubnetConnectivity - subnet1Minus2 SubnetsDiff - subnet2Minus1 SubnetsDiff - - GroupedSubnet1Minus2 *GroupConnLines - GroupedSubnet1Minus1 *GroupConnLines -} - -type DiffType = int +// ToDo: go over structs specifically * and lack of const ( NoDiff DiffType = iota @@ -25,8 +13,49 @@ const ( ChangedConnection ) -func (c *CloudConfig) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets, error) { - return nil, nil +type connectionDiff struct { + common.ConnectionSetDiff + DiffType +} + +type SubnetsDiff map[EndpointElem]map[EndpointElem]*connectionDiff + +type configsForDiff struct { + config1 *CloudConfig + config2 *CloudConfig +} + +type diffBetweenSubnets struct { + subnet1Subtract2 SubnetsDiff + subnet2Subtract1 SubnetsDiff + + GroupedSubnet1Minus2 *GroupConnLines + GroupedSubnet1Minus1 *GroupConnLines +} + +type DiffType = int + +func (configs configsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets, error) { + // 1. compute connectivity for each of the subnets + subnetsConn1, err := configs.config1.GetSubnetsConnectivity(true, grouping) + if err != nil { + return nil, nil + } + subnetsConn2, err := configs.config2.GetSubnetsConnectivity(true, grouping) + if err != nil { + return nil, nil + } + + // 2. Computes delta in both directions + subnet1Aligned, subnet2Aligned := subnetsConn1.AllowedConnsCombined.getConnectivesWithSameIpBlocks(subnetsConn2.AllowedConnsCombined) + subnet1Subtract2 := configs.subnetConnectivitySubtract(subnet1Aligned, subnet2Aligned) + subnet2Subtract1 := configs.subnetConnectivitySubtract(subnet2Aligned, subnet1Aligned) + + // 3. ToDo: grouping, see comment at the end of this file + + res := &diffBetweenSubnets{subnet1Subtract2: subnet1Subtract2, + subnet2Subtract1: subnet2Subtract1} + return res, nil } // generates from subnet1Connectivity.AllowedConnsCombined and subnet2Connectivity.AllowedConnsCombined @@ -34,15 +63,46 @@ func (c *CloudConfig) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets, error) // (src2, dst2) of subnet2Connectivity are either: // 1. src1 disjoint src2 or dst1 disjoint dst2 // 2. src1 = src2 and dst1 = dst2 -// -// todo: use DisjointIPBlocks(set1, set2 []*IPBlock) []*IPBlock of ipBlock.go -func (d *diffBetweenSubnets) getConnectivesWithSameIpBlocks() (*subnetConnectivity, *subnetConnectivity) { - return nil, nil +func (connectivity subnetConnectivity) getConnectivesWithSameIpBlocks(other subnetConnectivity) (subnetConnectivity, subnetConnectivity) { + // todo: use DisjointIPBlocks(set1, set2 []*IPBlock) []*IPBlock of ipBlock.go + return connectivity, other } -func (connectivity *subnetConnectivity) subnetConnectivitySubtract(other subnetConnectivity) DiffType { +// assumption: any connection from connectivity and "other" have src (dst) which are either disjoint or equal +func (configs configsForDiff) subnetConnectivitySubtract(connectivity subnetConnectivity, other subnetConnectivity) SubnetsDiff { + connectivitySubtract := map[EndpointElem]map[EndpointElem]*connectionDiff{} + for src, endpointConns := range connectivity { + for dst, conns := range endpointConns { + if conns.IsEmpty() { + continue + } + + if _, ok := connectivitySubtract[src]; !ok { + connectivitySubtract[src] = map[EndpointElem]*connectionDiff{} + } + if otherSrc, existSrc := other[src]; existSrc { + if otherSrcDst, existDst := otherSrc[dst]; existDst { + diffConnection := conns.Subtract(otherSrcDst) + if diffConnection.IsEmpty() { + continue // no diff + } + connectionSetDiff := common.ConnectionSetDiff{ + *diffConnection, + nil, + } + diffConnectionWithType := &connectionDiff{ + connectionSetDiff, + ChangedConnection, + } + connectivitySubtract[src][dst] = diffConnectionWithType + } + // todo: if src, dst do not exist as node see if src missing in config, dst missing or both missing + // todo: if src and dst both present then call ConnectionSet1Minus2 + } + } + } - return NoDiff + return nil } // todo: instead of adding functionality to grouping, I plan to have more generic connectivity items that will be grouped From 15970f47e916bc120f0cf7c0c9f9b743ad728d73 Mon Sep 17 00:00:00 2001 From: shirim Date: Mon, 16 Oct 2023 13:49:45 +0300 Subject: [PATCH 03/42] subnetConnectivitySubtract code; still needs to fill inside functionality code --- pkg/common/connectionset.go | 10 +++- pkg/vpcmodel/semanticDiffSubnets.go | 81 ++++++++++++++++++++++------- 2 files changed, 69 insertions(+), 22 deletions(-) diff --git a/pkg/common/connectionset.go b/pkg/common/connectionset.go index dc2bfb7d6..320ebf1a3 100644 --- a/pkg/common/connectionset.go +++ b/pkg/common/connectionset.go @@ -533,10 +533,16 @@ type ConnectionSetDiff struct { StatefullDiff *ConnectionSet // connection element which is stateful only in A } -func (conn *ConnectionSet) ConnectionSet1Minus2(other *ConnectionSet) *ConnectionSet { +func (conn *ConnectionSet) SubtractWithStateful(other *ConnectionSet) ConnectionSetDiff { // todo: use func (conn *ConnectionSet) Subtract(other *ConnectionSet) *ConnectionSet and note the // special case in which the connection differs only one being stateful and the other not - return nil + diff := ConnectionSetDiff{ConnectionSet{}, + nil} + return diff +} + +func (connn *ConnectionSetDiff) isEmpty() bool { + return false } // ToDo up to here in connectionSet diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 37d7cf626..734bcec76 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -1,6 +1,8 @@ package vpcmodel -import "github.com/np-guard/vpc-network-config-analyzer/pkg/common" +import ( + "github.com/np-guard/vpc-network-config-analyzer/pkg/common" +) // ToDo: go over structs specifically * and lack of @@ -53,16 +55,41 @@ func (configs configsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets // 3. ToDo: grouping, see comment at the end of this file - res := &diffBetweenSubnets{subnet1Subtract2: subnet1Subtract2, + res := &diffBetweenSubnets{ + subnet1Subtract2: subnet1Subtract2, subnet2Subtract1: subnet2Subtract1} return res, nil } +// for a given EndpointElem (representing a subnet or an external ip) in config return the EndpointElem representing the +// subnet/external address in otherConfig or nil if the subnet does not exist in the other config. +func (config *CloudConfig) getEndpointElemInOtherConfig(other *CloudConfig, ep EndpointElem) *EndpointElem { + if ep.IsExternal() { + for _, node := range other.Nodes { + if node.Name() == ep.Name() { + res := EndpointElem(node) + return &res + } + } + } else { + for _, nodeSet := range other.NodeSets { + if nodeSet.Name() == ep.Name() { + res := EndpointElem(nodeSet) + return &res + } + } + } + return nil +} + // generates from subnet1Connectivity.AllowedConnsCombined and subnet2Connectivity.AllowedConnsCombined // Two equivalent subnetConnectivity objects s.t. any (src1, dst1) of subnet1Connectivity and // (src2, dst2) of subnet2Connectivity are either: // 1. src1 disjoint src2 or dst1 disjoint dst2 // 2. src1 = src2 and dst1 = dst2 +// What is done here is repartitioning the ipBlocks so that the above will hold +// +// todo: verify that the returns objects indeed have exactly the same ipBlocks func (connectivity subnetConnectivity) getConnectivesWithSameIpBlocks(other subnetConnectivity) (subnetConnectivity, subnetConnectivity) { // todo: use DisjointIPBlocks(set1, set2 []*IPBlock) []*IPBlock of ipBlock.go return connectivity, other @@ -76,32 +103,46 @@ func (configs configsForDiff) subnetConnectivitySubtract(connectivity subnetConn if conns.IsEmpty() { continue } - if _, ok := connectivitySubtract[src]; !ok { connectivitySubtract[src] = map[EndpointElem]*connectionDiff{} } - if otherSrc, existSrc := other[src]; existSrc { - if otherSrcDst, existDst := otherSrc[dst]; existDst { - diffConnection := conns.Subtract(otherSrcDst) - if diffConnection.IsEmpty() { - continue // no diff - } - connectionSetDiff := common.ConnectionSetDiff{ - *diffConnection, - nil, - } - diffConnectionWithType := &connectionDiff{ - connectionSetDiff, - ChangedConnection, + srcInOther := configs.config1.getEndpointElemInOtherConfig(configs.config2, src) + dstInOther := configs.config1.getEndpointElemInOtherConfig(configs.config2, dst) + if srcInOther != nil && dstInOther != nil { + if otherSrc, ok := other[*srcInOther]; ok { + if otherSrcDst, ok := otherSrc[*dstInOther]; ok { + subtractConn := conns.SubtractWithStateful(otherSrcDst) + if subtractConn.IsEmpty() { + continue // no diff + } + diffConnectionWithType := &connectionDiff{ + subtractConn, + ChangedConnection, + } + connectivitySubtract[src][dst] = diffConnectionWithType + continue } - connectivitySubtract[src][dst] = diffConnectionWithType } - // todo: if src, dst do not exist as node see if src missing in config, dst missing or both missing - // todo: if src and dst both present then call ConnectionSet1Minus2 } + var diff DiffType + if srcInOther == nil && dstInOther == nil { + diff = MissingSrcDstEP + } else if srcInOther == nil { + diff = MissingSrcEP + } else { + diff = MissingDstEP + } + emptyConnection := common.NewConnectionSet(false) + diffConnectionWithType := &connectionDiff{ + common.ConnectionSetDiff{ + *emptyConnection, + nil, + }, + diff, + } + connectivitySubtract[src][dst] = diffConnectionWithType } } - return nil } From 94000632bd2ce5a203fd7d38f31f1827a6f1aa25 Mon Sep 17 00:00:00 2001 From: shirim Date: Tue, 17 Oct 2023 09:56:49 +0300 Subject: [PATCH 04/42] Redefined made the connection set diff and added todos --- pkg/common/connectionset.go | 29 ++++++----------------------- pkg/vpcmodel/semanticDiffSubnets.go | 17 ++++++++++------- 2 files changed, 16 insertions(+), 30 deletions(-) diff --git a/pkg/common/connectionset.go b/pkg/common/connectionset.go index 320ebf1a3..c78ba58cd 100644 --- a/pkg/common/connectionset.go +++ b/pkg/common/connectionset.go @@ -191,6 +191,12 @@ func (conn *ConnectionSet) isAllConnectionsWithoutAllowAll() bool { return conn.connectionProperties.Equals(getAllPropertiesObject()) } +// Subtract +// ToDo: Subtract seems to ignore IsStateful: +// 1. is the delta connection stateful +// 2. connectionProperties is identical but conn stateful while other is not +// the 2nd item can be computed here, with enhancement to relevant structure +// the 1st can not since we do not know where exactly the statefullness came from func (conn *ConnectionSet) Subtract(other *ConnectionSet) *ConnectionSet { if conn.IsEmpty() || other.IsEmpty() { return conn @@ -524,29 +530,6 @@ func (conn *ConnectionSet) ResponseConnection() *ConnectionSet { return res } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///// Diff between ConnectionSet - -// Connection A minus Connection B -type ConnectionSetDiff struct { - ConnectionSet - StatefullDiff *ConnectionSet // connection element which is stateful only in A -} - -func (conn *ConnectionSet) SubtractWithStateful(other *ConnectionSet) ConnectionSetDiff { - // todo: use func (conn *ConnectionSet) Subtract(other *ConnectionSet) *ConnectionSet and note the - // special case in which the connection differs only one being stateful and the other not - diff := ConnectionSetDiff{ConnectionSet{}, - nil} - return diff -} - -func (connn *ConnectionSetDiff) isEmpty() bool { - return false -} - -// ToDo up to here in connectionSet - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 734bcec76..66f9e26c0 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -16,7 +16,7 @@ const ( ) type connectionDiff struct { - common.ConnectionSetDiff + *common.ConnectionSet DiffType } @@ -95,6 +95,7 @@ func (connectivity subnetConnectivity) getConnectivesWithSameIpBlocks(other subn return connectivity, other } +// Subtract one subnetConnectivity from the other // assumption: any connection from connectivity and "other" have src (dst) which are either disjoint or equal func (configs configsForDiff) subnetConnectivitySubtract(connectivity subnetConnectivity, other subnetConnectivity) SubnetsDiff { connectivitySubtract := map[EndpointElem]map[EndpointElem]*connectionDiff{} @@ -111,7 +112,13 @@ func (configs configsForDiff) subnetConnectivitySubtract(connectivity subnetConn if srcInOther != nil && dstInOther != nil { if otherSrc, ok := other[*srcInOther]; ok { if otherSrcDst, ok := otherSrc[*dstInOther]; ok { - subtractConn := conns.SubtractWithStateful(otherSrcDst) + // ToDo: current missing stateful: + // todo 1. is the delta connection stateful + // todo 2. connectionProperties is identical but conn stateful while other is not + // the 2nd item can be computed by conns.Subtract, with enhancement to relevant structure + // the 1st can not since we do not know where exactly the statefullness came from + // we might need to repeat the statefullness computation for the delta connection + subtractConn := conns.Subtract(otherSrcDst) if subtractConn.IsEmpty() { continue // no diff } @@ -132,12 +139,8 @@ func (configs configsForDiff) subnetConnectivitySubtract(connectivity subnetConn } else { diff = MissingDstEP } - emptyConnection := common.NewConnectionSet(false) diffConnectionWithType := &connectionDiff{ - common.ConnectionSetDiff{ - *emptyConnection, - nil, - }, + nil, diff, } connectivitySubtract[src][dst] = diffConnectionWithType From bf18d014d213381c21a4fea6709ceb99ba97bae6 Mon Sep 17 00:00:00 2001 From: shirim Date: Tue, 17 Oct 2023 13:47:32 +0300 Subject: [PATCH 05/42] Minor reorgs --- pkg/vpcmodel/semanticDiffSubnets.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 66f9e26c0..dae10a441 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -27,6 +27,11 @@ type configsForDiff struct { config2 *CloudConfig } +type subnetConfigConnectivity struct { + config *CloudConfig + subnetConnectivity subnetConnectivity +} + type diffBetweenSubnets struct { subnet1Subtract2 SubnetsDiff subnet2Subtract1 SubnetsDiff @@ -50,8 +55,10 @@ func (configs configsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets // 2. Computes delta in both directions subnet1Aligned, subnet2Aligned := subnetsConn1.AllowedConnsCombined.getConnectivesWithSameIpBlocks(subnetsConn2.AllowedConnsCombined) - subnet1Subtract2 := configs.subnetConnectivitySubtract(subnet1Aligned, subnet2Aligned) - subnet2Subtract1 := configs.subnetConnectivitySubtract(subnet2Aligned, subnet1Aligned) + subnetConfigConnectivity1 := subnetConfigConnectivity{configs.config1, subnet1Aligned} + subnetConfigConnectivity2 := subnetConfigConnectivity{configs.config2, subnet2Aligned} + subnet1Subtract2 := subnetConfigConnectivity1.subnetConnectivitySubtract(&subnetConfigConnectivity2) + subnet2Subtract1 := subnetConfigConnectivity2.subnetConnectivitySubtract(&subnetConfigConnectivity1) // 3. ToDo: grouping, see comment at the end of this file @@ -97,9 +104,9 @@ func (connectivity subnetConnectivity) getConnectivesWithSameIpBlocks(other subn // Subtract one subnetConnectivity from the other // assumption: any connection from connectivity and "other" have src (dst) which are either disjoint or equal -func (configs configsForDiff) subnetConnectivitySubtract(connectivity subnetConnectivity, other subnetConnectivity) SubnetsDiff { +func (subnetConfConnectivity *subnetConfigConnectivity) subnetConnectivitySubtract(other *subnetConfigConnectivity) SubnetsDiff { connectivitySubtract := map[EndpointElem]map[EndpointElem]*connectionDiff{} - for src, endpointConns := range connectivity { + for src, endpointConns := range subnetConfConnectivity.subnetConnectivity { for dst, conns := range endpointConns { if conns.IsEmpty() { continue @@ -107,10 +114,10 @@ func (configs configsForDiff) subnetConnectivitySubtract(connectivity subnetConn if _, ok := connectivitySubtract[src]; !ok { connectivitySubtract[src] = map[EndpointElem]*connectionDiff{} } - srcInOther := configs.config1.getEndpointElemInOtherConfig(configs.config2, src) - dstInOther := configs.config1.getEndpointElemInOtherConfig(configs.config2, dst) + srcInOther := subnetConfConnectivity.config.getEndpointElemInOtherConfig(other.config, src) + dstInOther := subnetConfConnectivity.config.getEndpointElemInOtherConfig(other.config, dst) if srcInOther != nil && dstInOther != nil { - if otherSrc, ok := other[*srcInOther]; ok { + if otherSrc, ok := other.subnetConnectivity[*srcInOther]; ok { if otherSrcDst, ok := otherSrc[*dstInOther]; ok { // ToDo: current missing stateful: // todo 1. is the delta connection stateful From 625fa41df87f09aed6a9cde241a15e4321ad2e11 Mon Sep 17 00:00:00 2001 From: shirim Date: Tue, 17 Oct 2023 14:53:01 +0300 Subject: [PATCH 06/42] Export SubnetConnectivityMap and enable external creation for unit test --- pkg/vpcmodel/semanticDiffSubnets.go | 8 ++++---- pkg/vpcmodel/subnetsConnectivity.go | 8 ++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index dae10a441..56ed1885e 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -29,7 +29,7 @@ type configsForDiff struct { type subnetConfigConnectivity struct { config *CloudConfig - subnetConnectivity subnetConnectivity + subnetConnectivity SubnetConnectivityMap } type diffBetweenSubnets struct { @@ -90,19 +90,19 @@ func (config *CloudConfig) getEndpointElemInOtherConfig(other *CloudConfig, ep E } // generates from subnet1Connectivity.AllowedConnsCombined and subnet2Connectivity.AllowedConnsCombined -// Two equivalent subnetConnectivity objects s.t. any (src1, dst1) of subnet1Connectivity and +// Two equivalent SubnetConnectivityMap objects s.t. any (src1, dst1) of subnet1Connectivity and // (src2, dst2) of subnet2Connectivity are either: // 1. src1 disjoint src2 or dst1 disjoint dst2 // 2. src1 = src2 and dst1 = dst2 // What is done here is repartitioning the ipBlocks so that the above will hold // // todo: verify that the returns objects indeed have exactly the same ipBlocks -func (connectivity subnetConnectivity) getConnectivesWithSameIpBlocks(other subnetConnectivity) (subnetConnectivity, subnetConnectivity) { +func (connectivity SubnetConnectivityMap) getConnectivesWithSameIpBlocks(other SubnetConnectivityMap) (SubnetConnectivityMap, SubnetConnectivityMap) { // todo: use DisjointIPBlocks(set1, set2 []*IPBlock) []*IPBlock of ipBlock.go return connectivity, other } -// Subtract one subnetConnectivity from the other +// Subtract one SubnetConnectivityMap from the other // assumption: any connection from connectivity and "other" have src (dst) which are either disjoint or equal func (subnetConfConnectivity *subnetConfigConnectivity) subnetConnectivitySubtract(other *subnetConfigConnectivity) SubnetsDiff { connectivitySubtract := map[EndpointElem]map[EndpointElem]*connectionDiff{} diff --git a/pkg/vpcmodel/subnetsConnectivity.go b/pkg/vpcmodel/subnetsConnectivity.go index 657b16869..ce497ca40 100644 --- a/pkg/vpcmodel/subnetsConnectivity.go +++ b/pkg/vpcmodel/subnetsConnectivity.go @@ -7,14 +7,14 @@ import ( "fmt" ) -type subnetConnectivity map[EndpointElem]map[EndpointElem]*common.ConnectionSet +type SubnetConnectivityMap map[EndpointElem]map[EndpointElem]*common.ConnectionSet // VPCsubnetConnectivity captures allowed connectivity for subnets, considering nacl and pgw resources type VPCsubnetConnectivity struct { // computed for each node (subnet), by iterating its ConnectivityResult for all relevant VPC resources that capture it AllowedConns map[EndpointElem]*ConfigBasedConnectivityResults // combined connectivity - considering both ingress and egress per connection - AllowedConnsCombined subnetConnectivity + AllowedConnsCombined SubnetConnectivityMap CloudConfig *CloudConfig // grouped connectivity result GroupedConnectivity *GroupConnLines @@ -26,6 +26,10 @@ const ( errUnexpectedTypePeerNode = "unexpected type for peerNode in computeAllowedConnsCombined" ) +func NewSubnetConnectivityMap() SubnetConnectivityMap { + return SubnetConnectivityMap{} +} + func subnetConnLine(subnet string, conn *common.ConnectionSet) string { return fmt.Sprintf("%s : %s\n", subnet, conn.String()) } From d2fbe4e9be637a44359fbf957717606459dc437b Mon Sep 17 00:00:00 2001 From: shirim Date: Wed, 18 Oct 2023 09:25:07 +0300 Subject: [PATCH 07/42] Added grouping subnet unittesting as preliminery stage to writing unit testing for semantic diff connectivity --- pkg/vpcmodel/grouping_test.go | 37 +++++++++++++++++++++++++++++ pkg/vpcmodel/subnetsConnectivity.go | 7 ++++++ 2 files changed, 44 insertions(+) diff --git a/pkg/vpcmodel/grouping_test.go b/pkg/vpcmodel/grouping_test.go index 3d51baa49..6c3e3c067 100644 --- a/pkg/vpcmodel/grouping_test.go +++ b/pkg/vpcmodel/grouping_test.go @@ -371,3 +371,40 @@ func TestConfigSelfLoopCliqueLace(t *testing.T) { fmt.Println(groupingStr) fmt.Println("done") } +func configSubnetSelfLoop() (*CloudConfig, *VPCsubnetConnectivity) { + res := &CloudConfig{Nodes: []Node{}} + res.Nodes = append(res.Nodes, + &mockNetIntf{cidr: "10.0.20.5/32", name: "vsi1"}, + &mockNetIntf{cidr: "10.3.20.6/32", name: "vsi2"}, + &mockNetIntf{cidr: "10.7.20.7/32", name: "vsi3"}) + + res.NodeSets = append(res.NodeSets, &mockSubnet{"10.0.20.0/22", "subnet1", []Node{res.Nodes[0]}}) + res.NodeSets = append(res.NodeSets, &mockSubnet{"10.3.20.0/22", "subnet2", []Node{res.Nodes[1]}}) + res.NodeSets = append(res.NodeSets, &mockSubnet{"10.7.20.0/22", "subnet3", []Node{res.Nodes[2]}}) + + res1 := &VPCsubnetConnectivity{AllowedConnsCombined: NewSubnetConnectivityMap()} + res1.AllowedConnsCombined.updateAllowedSubnetConnsMap(res.NodeSets[0], res.NodeSets[1], common.NewConnectionSet(true)) + res1.AllowedConnsCombined.updateAllowedSubnetConnsMap(res.NodeSets[0], res.NodeSets[2], common.NewConnectionSet(true)) + res1.AllowedConnsCombined.updateAllowedSubnetConnsMap(res.NodeSets[1], res.NodeSets[0], common.NewConnectionSet(true)) + res1.AllowedConnsCombined.updateAllowedSubnetConnsMap(res.NodeSets[1], res.NodeSets[2], common.NewConnectionSet(true)) + res1.AllowedConnsCombined.updateAllowedSubnetConnsMap(res.NodeSets[2], res.NodeSets[0], common.NewConnectionSet(true)) + res1.AllowedConnsCombined.updateAllowedSubnetConnsMap(res.NodeSets[2], res.NodeSets[1], common.NewConnectionSet(true)) + + return res, res1 +} + +func TestSubnetSelfLoop(t *testing.T) { + c, s := configSubnetSelfLoop() + res := &GroupConnLines{c: c, s: s, + srcToDst: newGroupingConnections(), dstToSrc: newGroupingConnections(), + groupedEndpointsElemsMap: make(map[string]*groupedEndpointsElems), + groupedExternalNodesMap: make(map[string]*groupedExternalNodes)} + res.groupExternalAddressesForSubnets() + res.groupInternalSrcOrDst(false, false) + res.groupInternalSrcOrDst(true, false) + groupingStr := res.String() + require.Equal(t, "subnet1,subnet2,subnet3 => subnet1,subnet2,subnet3 : All Connections\n\n"+ + "connections are stateful unless marked with *\n", groupingStr) + fmt.Println(groupingStr) + fmt.Println("done") +} diff --git a/pkg/vpcmodel/subnetsConnectivity.go b/pkg/vpcmodel/subnetsConnectivity.go index ce497ca40..1007cfbf1 100644 --- a/pkg/vpcmodel/subnetsConnectivity.go +++ b/pkg/vpcmodel/subnetsConnectivity.go @@ -314,3 +314,10 @@ func (c *CloudConfig) GetConnectivityOutputPerEachSubnetSeparately() string { } return "" } + +func (subnetConnMap SubnetConnectivityMap) updateAllowedSubnetConnsMap(src, dst NodeSet, conn *common.ConnectionSet) { + if _, ok := subnetConnMap[src]; !ok { + subnetConnMap[src] = map[EndpointElem]*common.ConnectionSet{} + } + subnetConnMap[src][dst] = conn +} From 823503ed5a1bc838f324ae6d3b264af0e31c0170 Mon Sep 17 00:00:00 2001 From: shirim Date: Wed, 18 Oct 2023 10:07:02 +0300 Subject: [PATCH 08/42] exporting functionality for unit test; SubnetConnectivitySubtract should return the diff --- pkg/vpcmodel/semanticDiffSubnets.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 56ed1885e..a1ef014d7 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -22,12 +22,12 @@ type connectionDiff struct { type SubnetsDiff map[EndpointElem]map[EndpointElem]*connectionDiff -type configsForDiff struct { +type ConfigsForDiff struct { config1 *CloudConfig config2 *CloudConfig } -type subnetConfigConnectivity struct { +type SubnetConfigConnectivity struct { config *CloudConfig subnetConnectivity SubnetConnectivityMap } @@ -42,7 +42,7 @@ type diffBetweenSubnets struct { type DiffType = int -func (configs configsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets, error) { +func (configs ConfigsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets, error) { // 1. compute connectivity for each of the subnets subnetsConn1, err := configs.config1.GetSubnetsConnectivity(true, grouping) if err != nil { @@ -55,10 +55,10 @@ func (configs configsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets // 2. Computes delta in both directions subnet1Aligned, subnet2Aligned := subnetsConn1.AllowedConnsCombined.getConnectivesWithSameIpBlocks(subnetsConn2.AllowedConnsCombined) - subnetConfigConnectivity1 := subnetConfigConnectivity{configs.config1, subnet1Aligned} - subnetConfigConnectivity2 := subnetConfigConnectivity{configs.config2, subnet2Aligned} - subnet1Subtract2 := subnetConfigConnectivity1.subnetConnectivitySubtract(&subnetConfigConnectivity2) - subnet2Subtract1 := subnetConfigConnectivity2.subnetConnectivitySubtract(&subnetConfigConnectivity1) + subnetConfigConnectivity1 := SubnetConfigConnectivity{configs.config1, subnet1Aligned} + subnetConfigConnectivity2 := SubnetConfigConnectivity{configs.config2, subnet2Aligned} + subnet1Subtract2 := subnetConfigConnectivity1.SubnetConnectivitySubtract(&subnetConfigConnectivity2) + subnet2Subtract1 := subnetConfigConnectivity2.SubnetConnectivitySubtract(&subnetConfigConnectivity1) // 3. ToDo: grouping, see comment at the end of this file @@ -104,7 +104,7 @@ func (connectivity SubnetConnectivityMap) getConnectivesWithSameIpBlocks(other S // Subtract one SubnetConnectivityMap from the other // assumption: any connection from connectivity and "other" have src (dst) which are either disjoint or equal -func (subnetConfConnectivity *subnetConfigConnectivity) subnetConnectivitySubtract(other *subnetConfigConnectivity) SubnetsDiff { +func (subnetConfConnectivity *SubnetConfigConnectivity) SubnetConnectivitySubtract(other *SubnetConfigConnectivity) SubnetsDiff { connectivitySubtract := map[EndpointElem]map[EndpointElem]*connectionDiff{} for src, endpointConns := range subnetConfConnectivity.subnetConnectivity { for dst, conns := range endpointConns { @@ -153,7 +153,7 @@ func (subnetConfConnectivity *subnetConfigConnectivity) subnetConnectivitySubtra connectivitySubtract[src][dst] = diffConnectionWithType } } - return nil + return connectivitySubtract } // todo: instead of adding functionality to grouping, I plan to have more generic connectivity items that will be grouped From 769eb7a442020e692175f907b53e378c9c7d7986 Mon Sep 17 00:00:00 2001 From: shirim Date: Wed, 18 Oct 2023 10:18:48 +0300 Subject: [PATCH 09/42] semantic diff simple unit test --- pkg/vpcmodel/diffSubnets_test.go | 65 ++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 pkg/vpcmodel/diffSubnets_test.go diff --git a/pkg/vpcmodel/diffSubnets_test.go b/pkg/vpcmodel/diffSubnets_test.go new file mode 100644 index 000000000..bed1c0594 --- /dev/null +++ b/pkg/vpcmodel/diffSubnets_test.go @@ -0,0 +1,65 @@ +package vpcmodel + +import ( + "fmt" + "testing" + + "github.com/np-guard/vpc-network-config-analyzer/pkg/common" +) + +// simple diff: +// cfg1 has subnet1, subnet2, subnet3 +// subnet1 -> subnet2 +// subnet2 -> subnet3 +// subnet3 -> subnet2 +// cfg2 has subnet2, subnet3, subnet4 +// subnet2 -> subnet3 +// subnet3 -> subnet4 +// subnet3 -> subnet2 +// expected diff: +// + subnet1 -> subnet2 missing src +// + subnet2 -> subnet3 missing connection +// - subnet3 -> subnet4 missing dst + +func configSimpleSubnetSubtract() (*SubnetConfigConnectivity, *SubnetConfigConnectivity) { + cfg1 := &CloudConfig{Nodes: []Node{}} + cfg1.Nodes = append(cfg1.Nodes, + &mockNetIntf{cidr: "10.0.20.5/32", name: "vsi1"}, + &mockNetIntf{cidr: "10.3.20.6/32", name: "vsi2"}, + &mockNetIntf{cidr: "10.7.20.7/32", name: "vsi3"}) + + cfg1.NodeSets = append(cfg1.NodeSets, &mockSubnet{"10.0.20.0/22", "subnet1", []Node{cfg1.Nodes[0]}}) + cfg1.NodeSets = append(cfg1.NodeSets, &mockSubnet{"10.3.20.0/22", "subnet2", []Node{cfg1.Nodes[1]}}) + cfg1.NodeSets = append(cfg1.NodeSets, &mockSubnet{"10.7.20.0/22", "subnet3", []Node{cfg1.Nodes[2]}}) + + cfg2 := &CloudConfig{Nodes: []Node{}} + cfg2.Nodes = append(cfg1.Nodes, + &mockNetIntf{cidr: "10.0.20.5/32", name: "vsi1"}, + &mockNetIntf{cidr: "10.3.20.6/32", name: "vsi2"}, + &mockNetIntf{cidr: "10.9.20.7/32", name: "vsi4"}) + + subnetConnMap1 := &VPCsubnetConnectivity{AllowedConnsCombined: NewSubnetConnectivityMap()} + subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[0], cfg1.NodeSets[1], common.NewConnectionSet(true)) + subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[1], cfg1.NodeSets[2], common.NewConnectionSet(true)) + subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[2], cfg1.NodeSets[1], common.NewConnectionSet(true)) + + subnetConnMap2 := &VPCsubnetConnectivity{AllowedConnsCombined: NewSubnetConnectivityMap()} + subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[0], cfg1.NodeSets[1], common.NewConnectionSet(true)) + subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[1], cfg1.NodeSets[2], common.NewConnectionSet(true)) + subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[1], cfg1.NodeSets[0], common.NewConnectionSet(true)) + + subnetConfigConn1 := &SubnetConfigConnectivity{cfg1, subnetConnMap1.AllowedConnsCombined} + subnetConfigConn2 := &SubnetConfigConnectivity{cfg2, subnetConnMap2.AllowedConnsCombined} + + return subnetConfigConn1, subnetConfigConn2 +} + +func TestSimpleSubnetSubtract(t *testing.T) { + subnetConfigConn1, subnetConfigConn2 := configSimpleSubnetSubtract() + + subnet1Subtract2 := subnetConfigConn1.SubnetConnectivitySubtract(subnetConfigConn2) + subnet2Subtract1 := subnetConfigConn2.SubnetConnectivitySubtract(subnetConfigConn1) + + fmt.Printf("subnet1Subtract2 is %v\nsubnet2Subtract1 is %v\n", subnet1Subtract2, subnet2Subtract1) + +} From 5a652cc55893dcc48e6cdd5601811543b484ffa3 Mon Sep 17 00:00:00 2001 From: shirim Date: Wed, 18 Oct 2023 11:41:32 +0300 Subject: [PATCH 10/42] improved semantic diff computation --- pkg/vpcmodel/semanticDiffSubnets.go | 39 ++++++++++++++++------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index a1ef014d7..78d25c2da 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -70,6 +70,8 @@ func (configs ConfigsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets // for a given EndpointElem (representing a subnet or an external ip) in config return the EndpointElem representing the // subnet/external address in otherConfig or nil if the subnet does not exist in the other config. +// ToDo: this is done based on names only at the moment. Perhaps take into account other factors such as cidr? +// ToDo: instead of performing this search each time, use a map created once func (config *CloudConfig) getEndpointElemInOtherConfig(other *CloudConfig, ep EndpointElem) *EndpointElem { if ep.IsExternal() { for _, node := range other.Nodes { @@ -114,6 +116,7 @@ func (subnetConfConnectivity *SubnetConfigConnectivity) SubnetConnectivitySubtra if _, ok := connectivitySubtract[src]; !ok { connectivitySubtract[src] = map[EndpointElem]*connectionDiff{} } + diffConnectionWithType := &connectionDiff{nil, NoDiff} srcInOther := subnetConfConnectivity.config.getEndpointElemInOtherConfig(other.config, src) dstInOther := subnetConfConnectivity.config.getEndpointElemInOtherConfig(other.config, dst) if srcInOther != nil && dstInOther != nil { @@ -129,26 +132,21 @@ func (subnetConfConnectivity *SubnetConfigConnectivity) SubnetConnectivitySubtra if subtractConn.IsEmpty() { continue // no diff } - diffConnectionWithType := &connectionDiff{ - subtractConn, - ChangedConnection, - } - connectivitySubtract[src][dst] = diffConnectionWithType - continue + diffConnectionWithType.ConnectionSet = subtractConn + diffConnectionWithType.DiffType = ChangedConnection } } - } - var diff DiffType - if srcInOther == nil && dstInOther == nil { - diff = MissingSrcDstEP - } else if srcInOther == nil { - diff = MissingSrcEP - } else { - diff = MissingDstEP - } - diffConnectionWithType := &connectionDiff{ - nil, - diff, + if diffConnectionWithType.DiffType != ChangedConnection { + diffConnectionWithType.DiffType = MissingConnection + } + } else { // srcInOther == nil || dstInOther == nil + if srcInOther == nil && dstInOther == nil { + diffConnectionWithType.DiffType = MissingSrcDstEP + } else if srcInOther == nil { + diffConnectionWithType.DiffType = MissingSrcEP + } else { + diffConnectionWithType.DiffType = MissingDstEP + } } connectivitySubtract[src][dst] = diffConnectionWithType } @@ -156,6 +154,11 @@ func (subnetConfConnectivity *SubnetConfigConnectivity) SubnetConnectivitySubtra return connectivitySubtract } +// +//func (subnetDiff *SubnetsDiff) String() string { +// +//} + // todo: instead of adding functionality to grouping, I plan to have more generic connectivity items that will be grouped // encode the SubnetsDiff into this generic item as well as the other entities we are grouping // and then decode in the printing From f7359086307f89d78c8d5f35d79bd68b9bd7652b Mon Sep 17 00:00:00 2001 From: shirim Date: Wed, 18 Oct 2023 14:11:46 +0300 Subject: [PATCH 11/42] fixed a bug/typo, added ad-hoc printing functionality --- pkg/vpcmodel/semanticDiffSubnets.go | 60 ++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 78d25c2da..7756ad583 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -1,11 +1,14 @@ package vpcmodel import ( + "fmt" "github.com/np-guard/vpc-network-config-analyzer/pkg/common" ) // ToDo: go over structs specifically * and lack of +type DiffType = int + const ( NoDiff DiffType = iota MissingSrcEP @@ -17,7 +20,7 @@ const ( type connectionDiff struct { *common.ConnectionSet - DiffType + diff DiffType } type SubnetsDiff map[EndpointElem]map[EndpointElem]*connectionDiff @@ -40,8 +43,6 @@ type diffBetweenSubnets struct { GroupedSubnet1Minus1 *GroupConnLines } -type DiffType = int - func (configs ConfigsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets, error) { // 1. compute connectivity for each of the subnets subnetsConn1, err := configs.config1.GetSubnetsConnectivity(true, grouping) @@ -133,19 +134,19 @@ func (subnetConfConnectivity *SubnetConfigConnectivity) SubnetConnectivitySubtra continue // no diff } diffConnectionWithType.ConnectionSet = subtractConn - diffConnectionWithType.DiffType = ChangedConnection + diffConnectionWithType.diff = ChangedConnection } } - if diffConnectionWithType.DiffType != ChangedConnection { - diffConnectionWithType.DiffType = MissingConnection + if diffConnectionWithType.diff != ChangedConnection { + diffConnectionWithType.diff = MissingConnection } } else { // srcInOther == nil || dstInOther == nil if srcInOther == nil && dstInOther == nil { - diffConnectionWithType.DiffType = MissingSrcDstEP + diffConnectionWithType.diff = MissingSrcDstEP } else if srcInOther == nil { - diffConnectionWithType.DiffType = MissingSrcEP + diffConnectionWithType.diff = MissingSrcEP } else { - diffConnectionWithType.DiffType = MissingDstEP + diffConnectionWithType.diff = MissingDstEP } } connectivitySubtract[src][dst] = diffConnectionWithType @@ -154,10 +155,43 @@ func (subnetConfConnectivity *SubnetConfigConnectivity) SubnetConnectivitySubtra return connectivitySubtract } -// -//func (subnetDiff *SubnetsDiff) String() string { -// -//} +// ToDo: likely the current printing functionality will no longer be needed once the grouping is added +// anyways the diff print will be worked on before the final merge +func (subnetDiff *SubnetsDiff) EnhancedString(thisMinusOther bool) string { + var diffDirection, printDiff string + if thisMinusOther { + diffDirection = "--" + } else { + diffDirection = "++" + } + for src, endpointConnDiff := range *subnetDiff { + for dst, connDiff := range endpointConnDiff { + var connectionSetDiff string + if connDiff.ConnectionSet != nil { + connectionSetDiff = connDiff.ConnectionSet.EnhancedString() + } + printDiff += fmt.Sprintf("%s %s => %s : %s %s\n", diffDirection, src.Name(), dst.Name(), + connectionSetDiff, diffDecription(connDiff.diff)) + } + } + return printDiff +} + +func diffDecription(diff DiffType) string { + switch diff { + case MissingSrcEP: + return "missing source" + case MissingDstEP: + return "missing destination" + case MissingSrcDstEP: + return "missing source and destination" + case MissingConnection: + return "missing connection" + case ChangedConnection: + return "changed connection" + } + return "" +} // todo: instead of adding functionality to grouping, I plan to have more generic connectivity items that will be grouped // encode the SubnetsDiff into this generic item as well as the other entities we are grouping From 20b4e98fe900da59b0049e6dae4c990dbe2173d8 Mon Sep 17 00:00:00 2001 From: shirim Date: Wed, 18 Oct 2023 14:12:21 +0300 Subject: [PATCH 12/42] unit test written for current functionality --- pkg/vpcmodel/diffSubnets_test.go | 76 +++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/pkg/vpcmodel/diffSubnets_test.go b/pkg/vpcmodel/diffSubnets_test.go index bed1c0594..23f6b5d6c 100644 --- a/pkg/vpcmodel/diffSubnets_test.go +++ b/pkg/vpcmodel/diffSubnets_test.go @@ -2,51 +2,66 @@ package vpcmodel import ( "fmt" + "github.com/stretchr/testify/require" + "strings" "testing" "github.com/np-guard/vpc-network-config-analyzer/pkg/common" ) // simple diff: -// cfg1 has subnet1, subnet2, subnet3 +// cfg1 has subnet0, subnet1, subnet2, subnet3 +// subnet0 -> subnet1 // subnet1 -> subnet2 +// subnet3 -> subnet1 // subnet2 -> subnet3 // subnet3 -> subnet2 // cfg2 has subnet2, subnet3, subnet4 -// subnet2 -> subnet3 -// subnet3 -> subnet4 // subnet3 -> subnet2 -// expected diff: -// + subnet1 -> subnet2 missing src -// + subnet2 -> subnet3 missing connection -// - subnet3 -> subnet4 missing dst +// subnet3 -> subnet4 + +// expected diff cfg1 subtract cfg2: +// cfg1 subtract cfg2 +// subnet0 -> subnet1 missing src and dst +// subnet1 -> subnet2 missing src +// subnet3 -> subnet1 missing dst +// subnet2 -> subnet3 missing connection +// +// cfg2 subtract cfg1 +// subnet1 subtract subnet2: +// subnet3 -> subnet4 missing dst func configSimpleSubnetSubtract() (*SubnetConfigConnectivity, *SubnetConfigConnectivity) { - cfg1 := &CloudConfig{Nodes: []Node{}} + cfg1 := &CloudConfig{Nodes: []Node{}, NodeSets: []NodeSet{}} cfg1.Nodes = append(cfg1.Nodes, - &mockNetIntf{cidr: "10.0.20.5/32", name: "vsi1"}, - &mockNetIntf{cidr: "10.3.20.6/32", name: "vsi2"}, - &mockNetIntf{cidr: "10.7.20.7/32", name: "vsi3"}) + &mockNetIntf{cidr: "10.0.20.5/32", name: "vsi1-1"}, + &mockNetIntf{cidr: "10.3.20.6/32", name: "vsi1-2"}, + &mockNetIntf{cidr: "10.7.20.7/32", name: "vsi1-3"}) - cfg1.NodeSets = append(cfg1.NodeSets, &mockSubnet{"10.0.20.0/22", "subnet1", []Node{cfg1.Nodes[0]}}) - cfg1.NodeSets = append(cfg1.NodeSets, &mockSubnet{"10.3.20.0/22", "subnet2", []Node{cfg1.Nodes[1]}}) - cfg1.NodeSets = append(cfg1.NodeSets, &mockSubnet{"10.7.20.0/22", "subnet3", []Node{cfg1.Nodes[2]}}) + cfg1.NodeSets = append(cfg1.NodeSets, &mockSubnet{"10.0.20.0/22", "subnet0", []Node{cfg1.Nodes[0]}}) + cfg1.NodeSets = append(cfg1.NodeSets, &mockSubnet{"10.1.20.0/22", "subnet1", []Node{cfg1.Nodes[0]}}) + cfg1.NodeSets = append(cfg1.NodeSets, &mockSubnet{"10.2.20.0/22", "subnet2", []Node{cfg1.Nodes[1]}}) + cfg1.NodeSets = append(cfg1.NodeSets, &mockSubnet{"10.3.20.0/22", "subnet3", []Node{cfg1.Nodes[2]}}) - cfg2 := &CloudConfig{Nodes: []Node{}} + cfg2 := &CloudConfig{Nodes: []Node{}, NodeSets: []NodeSet{}} cfg2.Nodes = append(cfg1.Nodes, - &mockNetIntf{cidr: "10.0.20.5/32", name: "vsi1"}, - &mockNetIntf{cidr: "10.3.20.6/32", name: "vsi2"}, - &mockNetIntf{cidr: "10.9.20.7/32", name: "vsi4"}) + &mockNetIntf{cidr: "10.3.20.5/32", name: "vsi2-1"}, + &mockNetIntf{cidr: "10.7.20.6/32", name: "vsi2-2"}, + &mockNetIntf{cidr: "10.9.20.7/32", name: "vsi2-3"}) + cfg2.NodeSets = append(cfg2.NodeSets, &mockSubnet{"10.2.20.0/22", "subnet2", []Node{cfg2.Nodes[0]}}) + cfg2.NodeSets = append(cfg2.NodeSets, &mockSubnet{"10.3.20.0/22", "subnet3", []Node{cfg2.Nodes[1]}}) + cfg2.NodeSets = append(cfg2.NodeSets, &mockSubnet{"10.4.20.0/22", "subnet4", []Node{cfg2.Nodes[2]}}) subnetConnMap1 := &VPCsubnetConnectivity{AllowedConnsCombined: NewSubnetConnectivityMap()} subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[0], cfg1.NodeSets[1], common.NewConnectionSet(true)) subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[1], cfg1.NodeSets[2], common.NewConnectionSet(true)) - subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[2], cfg1.NodeSets[1], common.NewConnectionSet(true)) + subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[3], cfg1.NodeSets[1], common.NewConnectionSet(true)) + subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[2], cfg1.NodeSets[3], common.NewConnectionSet(true)) + subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[3], cfg1.NodeSets[2], common.NewConnectionSet(true)) subnetConnMap2 := &VPCsubnetConnectivity{AllowedConnsCombined: NewSubnetConnectivityMap()} - subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[0], cfg1.NodeSets[1], common.NewConnectionSet(true)) - subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[1], cfg1.NodeSets[2], common.NewConnectionSet(true)) - subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[1], cfg1.NodeSets[0], common.NewConnectionSet(true)) + subnetConnMap2.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg2.NodeSets[1], cfg2.NodeSets[0], common.NewConnectionSet(true)) + subnetConnMap2.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg2.NodeSets[1], cfg2.NodeSets[2], common.NewConnectionSet(true)) subnetConfigConn1 := &SubnetConfigConnectivity{cfg1, subnetConnMap1.AllowedConnsCombined} subnetConfigConn2 := &SubnetConfigConnectivity{cfg2, subnetConnMap2.AllowedConnsCombined} @@ -56,10 +71,19 @@ func configSimpleSubnetSubtract() (*SubnetConfigConnectivity, *SubnetConfigConne func TestSimpleSubnetSubtract(t *testing.T) { subnetConfigConn1, subnetConfigConn2 := configSimpleSubnetSubtract() - subnet1Subtract2 := subnetConfigConn1.SubnetConnectivitySubtract(subnetConfigConn2) - subnet2Subtract1 := subnetConfigConn2.SubnetConnectivitySubtract(subnetConfigConn1) - - fmt.Printf("subnet1Subtract2 is %v\nsubnet2Subtract1 is %v\n", subnet1Subtract2, subnet2Subtract1) + subnet1Subtract2Str := subnet1Subtract2.EnhancedString(true) + fmt.Printf("subnet1Subtract2:\n%v\n", subnet1Subtract2Str) + newLines := strings.Count(subnet1Subtract2Str, "\n") + // there should be 4 lines in subnet1Subtract2Str + require.Equal(t, 4, newLines) + require.Contains(t, subnet1Subtract2Str, "-- subnet3 => subnet1 : missing destination") + require.Contains(t, subnet1Subtract2Str, "-- subnet2 => subnet3 : missing connection") + require.Contains(t, subnet1Subtract2Str, "-- subnet0 => subnet1 : missing source and destination") + require.Contains(t, subnet1Subtract2Str, "-- subnet1 => subnet2 : missing source") + subnet2Subtract1 := subnetConfigConn2.SubnetConnectivitySubtract(subnetConfigConn1) + subnet2Subtract1Str := subnet2Subtract1.EnhancedString(false) + fmt.Printf("subnet2Subtract1:\n%v\n", subnet2Subtract1Str) + require.Equal(t, "++ subnet3 => subnet4 : missing destination\n", subnet2Subtract1Str) } From ce31ddae359c0dcd23478b106a92497f72f1e3c7 Mon Sep 17 00:00:00 2001 From: shirim Date: Thu, 19 Oct 2023 11:48:24 +0300 Subject: [PATCH 13/42] lint comments --- pkg/vpcmodel/diffSubnets_test.go | 23 +++++++------- pkg/vpcmodel/grouping_test.go | 6 ++-- pkg/vpcmodel/semanticDiffSubnets.go | 47 ++++++++++++++++++----------- pkg/vpcmodel/subnetsConnectivity.go | 8 ++--- 4 files changed, 48 insertions(+), 36 deletions(-) diff --git a/pkg/vpcmodel/diffSubnets_test.go b/pkg/vpcmodel/diffSubnets_test.go index 23f6b5d6c..f25d0f544 100644 --- a/pkg/vpcmodel/diffSubnets_test.go +++ b/pkg/vpcmodel/diffSubnets_test.go @@ -2,10 +2,11 @@ package vpcmodel import ( "fmt" - "github.com/stretchr/testify/require" "strings" "testing" + "github.com/stretchr/testify/require" + "github.com/np-guard/vpc-network-config-analyzer/pkg/common" ) @@ -31,26 +32,26 @@ import ( // subnet1 subtract subnet2: // subnet3 -> subnet4 missing dst -func configSimpleSubnetSubtract() (*SubnetConfigConnectivity, *SubnetConfigConnectivity) { +func configSimpleSubnetSubtract() (subnetConfigConn1, subnetConfigConn2 *SubnetConfigConnectivity) { cfg1 := &CloudConfig{Nodes: []Node{}, NodeSets: []NodeSet{}} cfg1.Nodes = append(cfg1.Nodes, &mockNetIntf{cidr: "10.0.20.5/32", name: "vsi1-1"}, &mockNetIntf{cidr: "10.3.20.6/32", name: "vsi1-2"}, &mockNetIntf{cidr: "10.7.20.7/32", name: "vsi1-3"}) - cfg1.NodeSets = append(cfg1.NodeSets, &mockSubnet{"10.0.20.0/22", "subnet0", []Node{cfg1.Nodes[0]}}) - cfg1.NodeSets = append(cfg1.NodeSets, &mockSubnet{"10.1.20.0/22", "subnet1", []Node{cfg1.Nodes[0]}}) - cfg1.NodeSets = append(cfg1.NodeSets, &mockSubnet{"10.2.20.0/22", "subnet2", []Node{cfg1.Nodes[1]}}) + cfg1.NodeSets = append(cfg1.NodeSets, &mockSubnet{"10.0.20.0/22", "subnet0", []Node{cfg1.Nodes[0]}}, + &mockSubnet{"10.1.20.0/22", "subnet1", []Node{cfg1.Nodes[0]}}, + &mockSubnet{"10.2.20.0/22", "subnet2", []Node{cfg1.Nodes[1]}}) cfg1.NodeSets = append(cfg1.NodeSets, &mockSubnet{"10.3.20.0/22", "subnet3", []Node{cfg1.Nodes[2]}}) cfg2 := &CloudConfig{Nodes: []Node{}, NodeSets: []NodeSet{}} - cfg2.Nodes = append(cfg1.Nodes, + cfg2.Nodes = append(cfg2.Nodes, &mockNetIntf{cidr: "10.3.20.5/32", name: "vsi2-1"}, &mockNetIntf{cidr: "10.7.20.6/32", name: "vsi2-2"}, &mockNetIntf{cidr: "10.9.20.7/32", name: "vsi2-3"}) - cfg2.NodeSets = append(cfg2.NodeSets, &mockSubnet{"10.2.20.0/22", "subnet2", []Node{cfg2.Nodes[0]}}) - cfg2.NodeSets = append(cfg2.NodeSets, &mockSubnet{"10.3.20.0/22", "subnet3", []Node{cfg2.Nodes[1]}}) - cfg2.NodeSets = append(cfg2.NodeSets, &mockSubnet{"10.4.20.0/22", "subnet4", []Node{cfg2.Nodes[2]}}) + cfg2.NodeSets = append(cfg2.NodeSets, &mockSubnet{"10.2.20.0/22", "subnet2", []Node{cfg2.Nodes[0]}}, + &mockSubnet{"10.3.20.0/22", "subnet3", []Node{cfg2.Nodes[1]}}, + &mockSubnet{"10.4.20.0/22", "subnet4", []Node{cfg2.Nodes[2]}}) subnetConnMap1 := &VPCsubnetConnectivity{AllowedConnsCombined: NewSubnetConnectivityMap()} subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[0], cfg1.NodeSets[1], common.NewConnectionSet(true)) @@ -63,8 +64,8 @@ func configSimpleSubnetSubtract() (*SubnetConfigConnectivity, *SubnetConfigConne subnetConnMap2.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg2.NodeSets[1], cfg2.NodeSets[0], common.NewConnectionSet(true)) subnetConnMap2.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg2.NodeSets[1], cfg2.NodeSets[2], common.NewConnectionSet(true)) - subnetConfigConn1 := &SubnetConfigConnectivity{cfg1, subnetConnMap1.AllowedConnsCombined} - subnetConfigConn2 := &SubnetConfigConnectivity{cfg2, subnetConnMap2.AllowedConnsCombined} + subnetConfigConn1 = &SubnetConfigConnectivity{cfg1, subnetConnMap1.AllowedConnsCombined} + subnetConfigConn2 = &SubnetConfigConnectivity{cfg2, subnetConnMap2.AllowedConnsCombined} return subnetConfigConn1, subnetConfigConn2 } diff --git a/pkg/vpcmodel/grouping_test.go b/pkg/vpcmodel/grouping_test.go index 6c3e3c067..20aa4f699 100644 --- a/pkg/vpcmodel/grouping_test.go +++ b/pkg/vpcmodel/grouping_test.go @@ -378,9 +378,9 @@ func configSubnetSelfLoop() (*CloudConfig, *VPCsubnetConnectivity) { &mockNetIntf{cidr: "10.3.20.6/32", name: "vsi2"}, &mockNetIntf{cidr: "10.7.20.7/32", name: "vsi3"}) - res.NodeSets = append(res.NodeSets, &mockSubnet{"10.0.20.0/22", "subnet1", []Node{res.Nodes[0]}}) - res.NodeSets = append(res.NodeSets, &mockSubnet{"10.3.20.0/22", "subnet2", []Node{res.Nodes[1]}}) - res.NodeSets = append(res.NodeSets, &mockSubnet{"10.7.20.0/22", "subnet3", []Node{res.Nodes[2]}}) + res.NodeSets = append(res.NodeSets, &mockSubnet{"10.0.20.0/22", "subnet1", []Node{res.Nodes[0]}}, + &mockSubnet{"10.3.20.0/22", "subnet2", []Node{res.Nodes[1]}}, + &mockSubnet{"10.7.20.0/22", "subnet3", []Node{res.Nodes[2]}}) res1 := &VPCsubnetConnectivity{AllowedConnsCombined: NewSubnetConnectivityMap()} res1.AllowedConnsCombined.updateAllowedSubnetConnsMap(res.NodeSets[0], res.NodeSets[1], common.NewConnectionSet(true)) diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 7756ad583..dfa4f84a2 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -2,6 +2,7 @@ package vpcmodel import ( "fmt" + "github.com/np-guard/vpc-network-config-analyzer/pkg/common" ) @@ -55,7 +56,7 @@ func (configs ConfigsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets } // 2. Computes delta in both directions - subnet1Aligned, subnet2Aligned := subnetsConn1.AllowedConnsCombined.getConnectivesWithSameIpBlocks(subnetsConn2.AllowedConnsCombined) + subnet1Aligned, subnet2Aligned := subnetsConn1.AllowedConnsCombined.getConnectivesWithSameIPBlocks(subnetsConn2.AllowedConnsCombined) subnetConfigConnectivity1 := SubnetConfigConnectivity{configs.config1, subnet1Aligned} subnetConfigConnectivity2 := SubnetConfigConnectivity{configs.config2, subnet2Aligned} subnet1Subtract2 := subnetConfigConnectivity1.SubnetConnectivitySubtract(&subnetConfigConnectivity2) @@ -73,19 +74,19 @@ func (configs ConfigsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets // subnet/external address in otherConfig or nil if the subnet does not exist in the other config. // ToDo: this is done based on names only at the moment. Perhaps take into account other factors such as cidr? // ToDo: instead of performing this search each time, use a map created once -func (config *CloudConfig) getEndpointElemInOtherConfig(other *CloudConfig, ep EndpointElem) *EndpointElem { +func (c *CloudConfig) getEndpointElemInOtherConfig(other *CloudConfig, ep EndpointElem) EndpointElem { if ep.IsExternal() { for _, node := range other.Nodes { if node.Name() == ep.Name() { res := EndpointElem(node) - return &res + return res } } } else { for _, nodeSet := range other.NodeSets { if nodeSet.Name() == ep.Name() { res := EndpointElem(nodeSet) - return &res + return res } } } @@ -100,12 +101,15 @@ func (config *CloudConfig) getEndpointElemInOtherConfig(other *CloudConfig, ep E // What is done here is repartitioning the ipBlocks so that the above will hold // // todo: verify that the returns objects indeed have exactly the same ipBlocks -func (connectivity SubnetConnectivityMap) getConnectivesWithSameIpBlocks(other SubnetConnectivityMap) (SubnetConnectivityMap, SubnetConnectivityMap) { +func (connectivity SubnetConnectivityMap) getConnectivesWithSameIPBlocks(other SubnetConnectivityMap) ( + alignedConnectivity SubnetConnectivityMap, alignedOther SubnetConnectivityMap) { // todo: use DisjointIPBlocks(set1, set2 []*IPBlock) []*IPBlock of ipBlock.go - return connectivity, other + alignedConnectivity = connectivity + alignedOther = other + return } -// Subtract one SubnetConnectivityMap from the other +// SubnetConnectivitySubtract Subtract one SubnetConnectivityMap from the other // assumption: any connection from connectivity and "other" have src (dst) which are either disjoint or equal func (subnetConfConnectivity *SubnetConfigConnectivity) SubnetConnectivitySubtract(other *SubnetConfigConnectivity) SubnetsDiff { connectivitySubtract := map[EndpointElem]map[EndpointElem]*connectionDiff{} @@ -121,8 +125,8 @@ func (subnetConfConnectivity *SubnetConfigConnectivity) SubnetConnectivitySubtra srcInOther := subnetConfConnectivity.config.getEndpointElemInOtherConfig(other.config, src) dstInOther := subnetConfConnectivity.config.getEndpointElemInOtherConfig(other.config, dst) if srcInOther != nil && dstInOther != nil { - if otherSrc, ok := other.subnetConnectivity[*srcInOther]; ok { - if otherSrcDst, ok := otherSrc[*dstInOther]; ok { + if otherSrc, ok := other.subnetConnectivity[srcInOther]; ok { + if otherSrcDst, ok := otherSrc[dstInOther]; ok { // ToDo: current missing stateful: // todo 1. is the delta connection stateful // todo 2. connectionProperties is identical but conn stateful while other is not @@ -141,13 +145,7 @@ func (subnetConfConnectivity *SubnetConfigConnectivity) SubnetConnectivitySubtra diffConnectionWithType.diff = MissingConnection } } else { // srcInOther == nil || dstInOther == nil - if srcInOther == nil && dstInOther == nil { - diffConnectionWithType.diff = MissingSrcDstEP - } else if srcInOther == nil { - diffConnectionWithType.diff = MissingSrcEP - } else { - diffConnectionWithType.diff = MissingDstEP - } + diffConnectionWithType.diff = getDiffType(srcInOther, dstInOther) } connectivitySubtract[src][dst] = diffConnectionWithType } @@ -155,7 +153,19 @@ func (subnetConfConnectivity *SubnetConfigConnectivity) SubnetConnectivitySubtra return connectivitySubtract } -// ToDo: likely the current printing functionality will no longer be needed once the grouping is added +func getDiffType(srcInOther, dstInOther EndpointElem) DiffType { + switch { + case srcInOther == nil && dstInOther == nil: + return MissingSrcDstEP + case srcInOther == nil && dstInOther != nil: + return MissingSrcEP + case srcInOther != nil && dstInOther == nil: + return MissingDstEP + } + return NoDiff +} + +// EnhancedString ToDo: likely the current printing functionality will no longer be needed once the grouping is added // anyways the diff print will be worked on before the final merge func (subnetDiff *SubnetsDiff) EnhancedString(thisMinusOther bool) string { var diffDirection, printDiff string @@ -198,4 +208,5 @@ func diffDecription(diff DiffType) string { // and then decode in the printing // the idea is to use instead of *common.ConnectionSet in the grouped entity a string which will encode the connection // and also the diff where relevant -// this will requires some rewriting in the existing grouping functionality and the way it provides service to subnetsConnectivity and nodesConnectivity +// this will requires some rewriting in the existing grouping functionality and the way it provides +// service to subnetsConnectivity and nodesConnectivity diff --git a/pkg/vpcmodel/subnetsConnectivity.go b/pkg/vpcmodel/subnetsConnectivity.go index 1007cfbf1..a1bb35e74 100644 --- a/pkg/vpcmodel/subnetsConnectivity.go +++ b/pkg/vpcmodel/subnetsConnectivity.go @@ -315,9 +315,9 @@ func (c *CloudConfig) GetConnectivityOutputPerEachSubnetSeparately() string { return "" } -func (subnetConnMap SubnetConnectivityMap) updateAllowedSubnetConnsMap(src, dst NodeSet, conn *common.ConnectionSet) { - if _, ok := subnetConnMap[src]; !ok { - subnetConnMap[src] = map[EndpointElem]*common.ConnectionSet{} +func (connectivity SubnetConnectivityMap) updateAllowedSubnetConnsMap(src, dst NodeSet, conn *common.ConnectionSet) { + if _, ok := connectivity[src]; !ok { + connectivity[src] = map[EndpointElem]*common.ConnectionSet{} } - subnetConnMap[src][dst] = conn + connectivity[src][dst] = conn } From e90af2a0c20f5eb6035ae913021c4b8573be6309 Mon Sep 17 00:00:00 2001 From: shirim Date: Mon, 23 Oct 2023 13:22:12 +0300 Subject: [PATCH 14/42] identifying connections with external ip that intersects but are not equal, plus a unit test --- pkg/vpcmodel/diffSubnets_test.go | 50 ++++++++++++ pkg/vpcmodel/grouping_test.go | 2 +- pkg/vpcmodel/semanticDiffSubnets.go | 122 ++++++++++++++++++++++++++-- pkg/vpcmodel/subnetsConnectivity.go | 2 +- 4 files changed, 169 insertions(+), 7 deletions(-) diff --git a/pkg/vpcmodel/diffSubnets_test.go b/pkg/vpcmodel/diffSubnets_test.go index e78b06fbe..6ea84a307 100644 --- a/pkg/vpcmodel/diffSubnets_test.go +++ b/pkg/vpcmodel/diffSubnets_test.go @@ -95,3 +95,53 @@ func TestSimpleSubnetSubtract(t *testing.T) { "protocol: TCP src-ports: 1-9,101-65535; protocol: TCP src-ports: "+ "10-100 dst-ports: 1-442,444-65535; protocol: UDP,ICMP\n", subnet2Subtract1Str) } + +func configSimpleIPAndSubnetSubtract() (subnetConfigConn1, subnetConfigConn2 *SubnetConfigConnectivity) { + cfg1 := &CloudConfig{Nodes: []Node{}, NodeSets: []NodeSet{}} + cfg1.NodeSets = append(cfg1.NodeSets, &mockSubnet{"10.1.20.0/22", "subnet1", nil}, + &mockSubnet{"10.2.20.0/22", "subnet2", nil}) + cfg1.Nodes = append(cfg1.Nodes, + &mockNetIntf{cidr: "1.2.3.0/30", name: "public1-1", isPublic: true}, + &mockNetIntf{cidr: "250.2.4.0/24", name: "public1-2", isPublic: true}) + + cfg2 := &CloudConfig{Nodes: []Node{}, NodeSets: []NodeSet{}} + cfg2.NodeSets = append(cfg2.NodeSets, &mockSubnet{"10.1.20.0/22", "subnet1", nil}, + &mockSubnet{"10.2.20.0/22", "subnet2", nil}) + cfg2.Nodes = append(cfg2.Nodes, + &mockNetIntf{cidr: "1.2.3.0/26", name: "public2-1", isPublic: true}, + &mockNetIntf{cidr: "250.2.4.0/30", name: "public2-2", isPublic: true}) + + // cfg1 cfg2 + // two comparable connections: and + // and + // other are non-comparable connections e.g. and + // and + subnetConnMap1 := &VPCsubnetConnectivity{AllowedConnsCombined: NewSubnetConnectivityMap()} + subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.Nodes[0], cfg1.NodeSets[0], common.NewConnectionSet(true)) + subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[1], cfg1.Nodes[0], common.NewConnectionSet(true)) + subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.Nodes[0], cfg1.NodeSets[1], common.NewConnectionSet(true)) + subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.Nodes[1], cfg1.NodeSets[1], common.NewConnectionSet(true)) + + subnetConnMap2 := &VPCsubnetConnectivity{AllowedConnsCombined: NewSubnetConnectivityMap()} + subnetConnMap2.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg2.Nodes[0], cfg2.NodeSets[0], common.NewConnectionSet(true)) + subnetConnMap2.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg2.NodeSets[1], cfg2.Nodes[0], common.NewConnectionSet(true)) + subnetConnMap2.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg2.Nodes[0], cfg2.NodeSets[1], common.NewConnectionSet(true)) + subnetConnMap2.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg2.Nodes[1], cfg2.NodeSets[1], common.NewConnectionSet(true)) + + subnetConfigConn1 = &SubnetConfigConnectivity{cfg1, subnetConnMap1.AllowedConnsCombined} + subnetConfigConn2 = &SubnetConfigConnectivity{cfg2, subnetConnMap2.AllowedConnsCombined} + + return subnetConfigConn1, subnetConfigConn2 +} + +func TestSimpleIPAndSubnetSubtract(t *testing.T) { + subnetConfigConn1, subnetConfigConn2 := configSimpleIPAndSubnetSubtract() + subnetConfigConn1.subnetConnectivity.getIntersectingConnections((subnetConfigConn2.subnetConnectivity)) + //subnet1Subtract2 := subnetConfigConn1.SubnetConnectivitySubtract(subnetConfigConn2) + //subnet1Subtract2Str := subnet1Subtract2.EnhancedString(true) + //fmt.Printf("subnet1Subtract2:\n%v\n", subnet1Subtract2Str) + // + //subnet2Subtract1 := subnetConfigConn2.SubnetConnectivitySubtract(subnetConfigConn1) + //subnet2Subtract1Str := subnet2Subtract1.EnhancedString(false) + //fmt.Printf("subnet2Subtract1:\n%v\n", subnet2Subtract1Str) +} diff --git a/pkg/vpcmodel/grouping_test.go b/pkg/vpcmodel/grouping_test.go index 20aa4f699..2af333147 100644 --- a/pkg/vpcmodel/grouping_test.go +++ b/pkg/vpcmodel/grouping_test.go @@ -40,7 +40,7 @@ func (m *mockNetIntf) ZoneName() string { func (m *mockNetIntf) GenerateDrawioTreeNode(gen *DrawioGenerator) drawio.TreeNodeInterface { return nil } -func (m *mockNetIntf) IsExternal() bool { return false } +func (m *mockNetIntf) IsExternal() bool { return m.cidr != "" } type mockSubnet struct { cidr string diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 68e9f7b06..5f3cab224 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -6,9 +6,6 @@ import ( "github.com/np-guard/vpc-network-config-analyzer/pkg/common" ) -// ToDo: getConnectivesWithSameIPBlocks not yet implemented - namely, diff between connections that include external addresses -// is not yet supported - type DiffType = int const ( @@ -45,6 +42,11 @@ type diffBetweenSubnets struct { GroupedSubnet1Minus1 *GroupConnLines } +type ConnectionEnd struct { + src EndpointElem + dst EndpointElem +} + func (configs ConfigsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets, error) { // 1. compute connectivity for each of the subnets subnetsConn1, err := configs.config1.GetSubnetsConnectivity(true, false) @@ -57,7 +59,11 @@ func (configs ConfigsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets } // 2. Computes delta in both directions - subnet1Aligned, subnet2Aligned := subnetsConn1.AllowedConnsCombined.getConnectivesWithSameIPBlocks(subnetsConn2.AllowedConnsCombined) + subnet1Aligned, subnet2Aligned, err := + subnetsConn1.AllowedConnsCombined.getConnectivesWithSameIPBlocks(subnetsConn2.AllowedConnsCombined) + if err != nil { + return nil, err + } subnetConfigConnectivity1 := SubnetConfigConnectivity{configs.config1, subnet1Aligned} subnetConfigConnectivity2 := SubnetConfigConnectivity{configs.config2, subnet2Aligned} subnet1Subtract2 := subnetConfigConnectivity1.SubnetConnectivitySubtract(&subnetConfigConnectivity2) @@ -103,19 +109,125 @@ func (c *CloudConfig) getEndpointElemInOtherConfig(other *CloudConfig, ep Endpoi // // todo: verify that the returns objects indeed have exactly the same ipBlocks func (connectivity SubnetConnectivityMap) getConnectivesWithSameIPBlocks(other SubnetConnectivityMap) ( - alignedConnectivity SubnetConnectivityMap, alignedOther SubnetConnectivityMap) { + alignedConnectivity SubnetConnectivityMap, alignedOther SubnetConnectivityMap, err error) { + // Until the above 1 and 2 are reached, repeat the following: + // For each connection in connectivity find all comparable connections in other (see below) + // 1. Get intersecting connections + connectivity.getIntersectingConnections(other) + + // 2. Resize connectivity and other s.t. all detected connections are resized to meet 1 and 2 above; do not resize same connection twice // todo: use DisjointIPBlocks(set1, set2 []*IPBlock) []*IPBlock of ipBlock.go alignedConnectivity = connectivity alignedOther = other return } +// For each connection in connectivity finds all connections in other in which src (dst) intersect +// src' (dst') but is not equal, and s.t. each connection is other is mapped to at most one connection +// in connectivity (the first one). +func (connectivity SubnetConnectivityMap) getIntersectingConnections(other SubnetConnectivityMap) (intersectingIndexes map[int][]int, err error) { + intersectingIndexes = make(map[int][]int) + err = nil + for src, endpointConns := range connectivity { + for dst, conns := range endpointConns { + if (!src.IsExternal() && !dst.IsExternal()) || conns.IsEmpty() { + continue // nothing to do here + } + for otherSrc, otherEndpointConns := range other { + for otherDst, otherConns := range otherEndpointConns { + if otherConns.IsEmpty() { + continue + } + bothSrcExt := src.IsExternal() && otherSrc.IsExternal() + bothDstExt := dst.IsExternal() && otherDst.IsExternal() + if (!bothSrcExt && !bothDstExt) || + otherConns.IsEmpty() { + continue // nothing to compare to here + } + myEp := &ConnectionEnd{src, dst} + otherEp := &ConnectionEnd{otherSrc, otherDst} + intersecting, err := myEp.connectionsIntersecting(otherEp) + if err != nil { + return nil, err + } + if intersecting { + fmt.Printf("<%v, %v> and <%v, %v> are comparable\n", src.Name(), dst.Name(), otherSrc.Name(), otherDst.Name()) + // ToDo: add to the database + } + } + } + } + } + return intersectingIndexes, err +} + +// two connections s.t. each contains at least one external end are comparable if either: +// both src and dst in both connections are external and they both intersect +// one end (src/dst) are external in both and intersects and the other (dst/src) are the same subnet +// two connections s.t. each contains at least one external end are comparable if either: +// both src and dst in both connections are external and they both intersect but not equal +// or end (src/dst) are external in both and intersects and the other (dst/src) are the same subnet +func (myConnEnd *ConnectionEnd) connectionsIntersecting(otherConnEnd *ConnectionEnd) (bool, error) { + srcComparable, err := pairEpsComparable(myConnEnd.src, otherConnEnd.src) + if err != nil { + return false, err + } + if !srcComparable { + return false, nil + } + dstComparable, err := pairEpsComparable(myConnEnd.dst, otherConnEnd.dst) + if err != nil { + return false, err + } + if !dstComparable { + return false, err + } + return true, nil +} + +// checks if two eps refers to the same subnet or +// refers to intersecting external addresses +func pairEpsComparable(myEp, otherEp EndpointElem) (bool, error) { + mySubnet, isMySubnet := myEp.(NodeSet) + otherSubnet, isOtherSubnet := otherEp.(NodeSet) + myExternal, isMyExternal := myEp.(Node) + otherExternal, isOtherExternal := otherEp.(Node) + if (isMySubnet != isOtherSubnet) || (isMyExternal != isOtherExternal) { + return false, nil + } + if isMySubnet { // implies that isOtherSubnet as well + if mySubnet.Name() == otherSubnet.Name() { + return true, nil + } else { + return false, nil + } + } + // if we got here then both eps refer to external IP + myIpBlock, err := common.NewIPBlock(myExternal.Cidr(), []string{}) + if err != nil { + return false, err + } + otherIpBlock, err := common.NewIPBlock(otherExternal.Cidr(), []string{}) + if err != nil { + return false, err + } + // we need to resize if the IpBlocks are intersecting but not equal + //fmt.Printf("\t<%v, %v>: cidrs: %v, %v\n", myEp.Name(), otherEp.Name(), myExternal.Cidr(), otherExternal.Cidr()) + if !myIpBlock.Equal(otherIpBlock) && !myIpBlock.Intersection(otherIpBlock).Empty() { + return true, nil + } + return false, nil +} + // SubnetConnectivitySubtract Subtract one SubnetConnectivityMap from the other // assumption: any connection from connectivity and "other" have src (dst) which are either disjoint or equal func (subnetConfConnectivity *SubnetConfigConnectivity) SubnetConnectivitySubtract(other *SubnetConfigConnectivity) SubnetsDiff { connectivitySubtract := map[EndpointElem]map[EndpointElem]*connectionDiff{} for src, endpointConns := range subnetConfConnectivity.subnetConnectivity { for dst, conns := range endpointConns { + if src.IsExternal() || dst.IsExternal() { // todo: tmp + continue + } if conns.IsEmpty() { continue } diff --git a/pkg/vpcmodel/subnetsConnectivity.go b/pkg/vpcmodel/subnetsConnectivity.go index a1bb35e74..7237706fc 100644 --- a/pkg/vpcmodel/subnetsConnectivity.go +++ b/pkg/vpcmodel/subnetsConnectivity.go @@ -315,7 +315,7 @@ func (c *CloudConfig) GetConnectivityOutputPerEachSubnetSeparately() string { return "" } -func (connectivity SubnetConnectivityMap) updateAllowedSubnetConnsMap(src, dst NodeSet, conn *common.ConnectionSet) { +func (connectivity SubnetConnectivityMap) updateAllowedSubnetConnsMap(src, dst EndpointElem, conn *common.ConnectionSet) { if _, ok := connectivity[src]; !ok { connectivity[src] = map[EndpointElem]*common.ConnectionSet{} } From 35a0338dbdb3a33e6ff49e594df5a2a5b2c6444a Mon Sep 17 00:00:00 2001 From: shirim Date: Tue, 24 Oct 2023 14:27:00 +0300 Subject: [PATCH 15/42] getIntersectingConnections() anf related functionality seems redundant. Finalized it and its unit test in case we decide to use it in the future --- pkg/vpcmodel/diffSubnets_test.go | 21 ++- pkg/vpcmodel/semanticDiffSubnets.go | 204 ++++++++++++++-------------- 2 files changed, 118 insertions(+), 107 deletions(-) diff --git a/pkg/vpcmodel/diffSubnets_test.go b/pkg/vpcmodel/diffSubnets_test.go index 6ea84a307..e04684709 100644 --- a/pkg/vpcmodel/diffSubnets_test.go +++ b/pkg/vpcmodel/diffSubnets_test.go @@ -111,11 +111,11 @@ func configSimpleIPAndSubnetSubtract() (subnetConfigConn1, subnetConfigConn2 *Su &mockNetIntf{cidr: "1.2.3.0/26", name: "public2-1", isPublic: true}, &mockNetIntf{cidr: "250.2.4.0/30", name: "public2-2", isPublic: true}) - // cfg1 cfg2 - // two comparable connections: and - // and - // other are non-comparable connections e.g. and - // and + // cfg1 cfg2 + // and are comparable + // and are comparable + // and are comparable + // and are comparable subnetConnMap1 := &VPCsubnetConnectivity{AllowedConnsCombined: NewSubnetConnectivityMap()} subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.Nodes[0], cfg1.NodeSets[0], common.NewConnectionSet(true)) subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[1], cfg1.Nodes[0], common.NewConnectionSet(true)) @@ -136,7 +136,16 @@ func configSimpleIPAndSubnetSubtract() (subnetConfigConn1, subnetConfigConn2 *Su func TestSimpleIPAndSubnetSubtract(t *testing.T) { subnetConfigConn1, subnetConfigConn2 := configSimpleIPAndSubnetSubtract() - subnetConfigConn1.subnetConnectivity.getIntersectingConnections((subnetConfigConn2.subnetConnectivity)) + res, _ := subnetConfigConn1.subnetConnectivity.getIntersectingConnections(subnetConfigConn2.subnetConnectivity) + fmt.Printf(res) + newLines := strings.Count(res, "\n") + // there should be 4 lines in subnet1Subtract2Str + require.Equal(t, 4, newLines) + require.Contains(t, res, " and intersects") + require.Contains(t, res, " and intersects") + require.Contains(t, res, " and intersects") + require.Contains(t, res, " and intersects") + //subnet1Subtract2 := subnetConfigConn1.SubnetConnectivitySubtract(subnetConfigConn2) //subnet1Subtract2Str := subnet1Subtract2.EnhancedString(true) //fmt.Printf("subnet1Subtract2:\n%v\n", subnet1Subtract2Str) diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 5f3cab224..1712fd433 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -110,114 +110,18 @@ func (c *CloudConfig) getEndpointElemInOtherConfig(other *CloudConfig, ep Endpoi // todo: verify that the returns objects indeed have exactly the same ipBlocks func (connectivity SubnetConnectivityMap) getConnectivesWithSameIPBlocks(other SubnetConnectivityMap) ( alignedConnectivity SubnetConnectivityMap, alignedOther SubnetConnectivityMap, err error) { - // Until the above 1 and 2 are reached, repeat the following: - // For each connection in connectivity find all comparable connections in other (see below) - // 1. Get intersecting connections - connectivity.getIntersectingConnections(other) - // 2. Resize connectivity and other s.t. all detected connections are resized to meet 1 and 2 above; do not resize same connection twice + // Resize connectivity and other s.t. all detected connections are resized to meet 1 and 2 above + // To that end we get a list of all disjoint src IPBlocks and dst IPBlocks + // and resize by the disjoint IPBlocks // todo: use DisjointIPBlocks(set1, set2 []*IPBlock) []*IPBlock of ipBlock.go alignedConnectivity = connectivity alignedOther = other return } -// For each connection in connectivity finds all connections in other in which src (dst) intersect -// src' (dst') but is not equal, and s.t. each connection is other is mapped to at most one connection -// in connectivity (the first one). -func (connectivity SubnetConnectivityMap) getIntersectingConnections(other SubnetConnectivityMap) (intersectingIndexes map[int][]int, err error) { - intersectingIndexes = make(map[int][]int) - err = nil - for src, endpointConns := range connectivity { - for dst, conns := range endpointConns { - if (!src.IsExternal() && !dst.IsExternal()) || conns.IsEmpty() { - continue // nothing to do here - } - for otherSrc, otherEndpointConns := range other { - for otherDst, otherConns := range otherEndpointConns { - if otherConns.IsEmpty() { - continue - } - bothSrcExt := src.IsExternal() && otherSrc.IsExternal() - bothDstExt := dst.IsExternal() && otherDst.IsExternal() - if (!bothSrcExt && !bothDstExt) || - otherConns.IsEmpty() { - continue // nothing to compare to here - } - myEp := &ConnectionEnd{src, dst} - otherEp := &ConnectionEnd{otherSrc, otherDst} - intersecting, err := myEp.connectionsIntersecting(otherEp) - if err != nil { - return nil, err - } - if intersecting { - fmt.Printf("<%v, %v> and <%v, %v> are comparable\n", src.Name(), dst.Name(), otherSrc.Name(), otherDst.Name()) - // ToDo: add to the database - } - } - } - } - } - return intersectingIndexes, err -} - -// two connections s.t. each contains at least one external end are comparable if either: -// both src and dst in both connections are external and they both intersect -// one end (src/dst) are external in both and intersects and the other (dst/src) are the same subnet -// two connections s.t. each contains at least one external end are comparable if either: -// both src and dst in both connections are external and they both intersect but not equal -// or end (src/dst) are external in both and intersects and the other (dst/src) are the same subnet -func (myConnEnd *ConnectionEnd) connectionsIntersecting(otherConnEnd *ConnectionEnd) (bool, error) { - srcComparable, err := pairEpsComparable(myConnEnd.src, otherConnEnd.src) - if err != nil { - return false, err - } - if !srcComparable { - return false, nil - } - dstComparable, err := pairEpsComparable(myConnEnd.dst, otherConnEnd.dst) - if err != nil { - return false, err - } - if !dstComparable { - return false, err - } - return true, nil -} - -// checks if two eps refers to the same subnet or -// refers to intersecting external addresses -func pairEpsComparable(myEp, otherEp EndpointElem) (bool, error) { - mySubnet, isMySubnet := myEp.(NodeSet) - otherSubnet, isOtherSubnet := otherEp.(NodeSet) - myExternal, isMyExternal := myEp.(Node) - otherExternal, isOtherExternal := otherEp.(Node) - if (isMySubnet != isOtherSubnet) || (isMyExternal != isOtherExternal) { - return false, nil - } - if isMySubnet { // implies that isOtherSubnet as well - if mySubnet.Name() == otherSubnet.Name() { - return true, nil - } else { - return false, nil - } - } - // if we got here then both eps refer to external IP - myIpBlock, err := common.NewIPBlock(myExternal.Cidr(), []string{}) - if err != nil { - return false, err - } - otherIpBlock, err := common.NewIPBlock(otherExternal.Cidr(), []string{}) - if err != nil { - return false, err - } - // we need to resize if the IpBlocks are intersecting but not equal - //fmt.Printf("\t<%v, %v>: cidrs: %v, %v\n", myEp.Name(), otherEp.Name(), myExternal.Cidr(), otherExternal.Cidr()) - if !myIpBlock.Equal(otherIpBlock) && !myIpBlock.Intersection(otherIpBlock).Empty() { - return true, nil - } - return false, nil -} +// todo: write a function that for SubnetConnectivityMap and a src/dst returns a list of *ipBlocks +//func () // SubnetConnectivitySubtract Subtract one SubnetConnectivityMap from the other // assumption: any connection from connectivity and "other" have src (dst) which are either disjoint or equal @@ -325,6 +229,104 @@ func diffDescription(diff DiffType) string { return "" } +// todo: the following code finds all couples of connections that should be resized (it IPBlock) +// +// it seems that the code is redundant; yet we keep it with its unit test in case we'll decide +// to use it in the future +// it return a string describing the intersecting connections for the unit test +func (connectivity SubnetConnectivityMap) getIntersectingConnections(other SubnetConnectivityMap) (areIntersecting string, + err error) { + err = nil + for src, endpointConns := range connectivity { + for dst, conns := range endpointConns { + if (!src.IsExternal() && !dst.IsExternal()) || conns.IsEmpty() { + continue // nothing to do here + } + for otherSrc, otherEndpointConns := range other { + for otherDst, otherConns := range otherEndpointConns { + if otherConns.IsEmpty() { + continue + } + bothSrcExt := src.IsExternal() && otherSrc.IsExternal() + bothDstExt := dst.IsExternal() && otherDst.IsExternal() + if (!bothSrcExt && !bothDstExt) || + otherConns.IsEmpty() { + continue // nothing to compare to here + } + myEp := &ConnectionEnd{src, dst} + otherEp := &ConnectionEnd{otherSrc, otherDst} + intersecting, err := myEp.connectionsIntersecting(otherEp) + if err != nil { + return areIntersecting, err + } + if intersecting { + areIntersecting += fmt.Sprintf("<%v, %v> and <%v, %v> intersects\n", src.Name(), dst.Name(), otherSrc.Name(), otherDst.Name()) + } + } + } + } + } + return areIntersecting, err +} + +// two connections s.t. each contains at least one external end are comparable if either: +// both src and dst in both connections are external and they both intersect +// one end (src/dst) are external in both and intersects and the other (dst/src) are the same subnet +// two connections s.t. each contains at least one external end are comparable if either: +// both src and dst in both connections are external and they both intersect but not equal +// or end (src/dst) are external in both and intersects and the other (dst/src) are the same subnet +func (myConnEnd *ConnectionEnd) connectionsIntersecting(otherConnEnd *ConnectionEnd) (bool, error) { + srcComparable, err := pairEpsComparable(myConnEnd.src, otherConnEnd.src) + if err != nil { + return false, err + } + if !srcComparable { + return false, nil + } + dstComparable, err := pairEpsComparable(myConnEnd.dst, otherConnEnd.dst) + if err != nil { + return false, err + } + if !dstComparable { + return false, err + } + return true, nil +} + +// checks if two eps refers to the same subnet or +// refers to intersecting external addresses +func pairEpsComparable(myEp, otherEp EndpointElem) (bool, error) { + mySubnet, isMySubnet := myEp.(NodeSet) + otherSubnet, isOtherSubnet := otherEp.(NodeSet) + myExternal, isMyExternal := myEp.(Node) + otherExternal, isOtherExternal := otherEp.(Node) + if (isMySubnet != isOtherSubnet) || (isMyExternal != isOtherExternal) { + return false, nil + } + if isMySubnet { // implies that isOtherSubnet as well + if mySubnet.Name() == otherSubnet.Name() { + return true, nil + } else { + return false, nil + } + } + // if we got here then both eps refer to external IP + myIpBlock, err := common.NewIPBlock(myExternal.Cidr(), []string{}) + if err != nil { + return false, err + } + otherIpBlock, err := common.NewIPBlock(otherExternal.Cidr(), []string{}) + if err != nil { + return false, err + } + // we need to resize if the IpBlocks are intersecting but not equal + //fmt.Printf("\t<%v, %v>: cidrs: %v, %v\n", myEp.Name(), otherEp.Name(), myExternal.Cidr(), otherExternal.Cidr()) + if !myIpBlock.Equal(otherIpBlock) && !myIpBlock.Intersection(otherIpBlock).Empty() { + return true, nil + } + return false, nil +} + // todo: instead of adding functionality to grouping, I plan to have more generic connectivity items that will be grouped // encode the SubnetsDiff into this generic item as well as the other entities we are grouping // and then decode in the printing From 096f1be8a9fef4a1d13c3c3db4f6ba895150c9dd Mon Sep 17 00:00:00 2001 From: shirim Date: Wed, 25 Oct 2023 09:06:53 +0300 Subject: [PATCH 16/42] code for resizing written --- pkg/vpcmodel/externalNetwork.go | 6 +- pkg/vpcmodel/semanticDiffSubnets.go | 164 ++++++++++++++++++++++++---- 2 files changed, 144 insertions(+), 26 deletions(-) diff --git a/pkg/vpcmodel/externalNetwork.go b/pkg/vpcmodel/externalNetwork.go index b25f98ccc..d86e69aeb 100644 --- a/pkg/vpcmodel/externalNetwork.go +++ b/pkg/vpcmodel/externalNetwork.go @@ -87,10 +87,10 @@ func getPublicInternetIPblocksList() (internetIPblocksList []*common.IPBlock, al return ipStringsToIPblocks(publicInternetAddressList) } -func newExternalNode(isPublicInternet bool, ipb *common.IPBlock) (Node, error) { +func NewExternalNode(isPublicInternet bool, ipb *common.IPBlock) (Node, error) { cidrsList := ipb.ToCidrList() if len(cidrsList) > 1 { - return nil, errors.New("newExternalNode: input ip-block should be of a single cidr") + return nil, errors.New("NewExternalNode: input ip-block should be of a single cidr") } cidr := ipb.ToCidrList()[0] return &ExternalNetwork{ @@ -114,7 +114,7 @@ func GetExternalNetworkNodes(disjointRefExternalIPBlocks []*common.IPBlock) ([]N } cidrs := ipb.ToCidrList() for _, cidr := range cidrs { - newNode, err := newExternalNode(isPublicInternet, common.NewIPBlockFromCidr(cidr)) + newNode, err := NewExternalNode(isPublicInternet, common.NewIPBlockFromCidr(cidr)) if err != nil { return nil, err } diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 1712fd433..e31e6a2a9 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -59,13 +59,13 @@ func (configs ConfigsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets } // 2. Computes delta in both directions - subnet1Aligned, subnet2Aligned, err := + connectivity1Aligned, connectivity2Aligned, err := subnetsConn1.AllowedConnsCombined.getConnectivesWithSameIPBlocks(subnetsConn2.AllowedConnsCombined) if err != nil { return nil, err } - subnetConfigConnectivity1 := SubnetConfigConnectivity{configs.config1, subnet1Aligned} - subnetConfigConnectivity2 := SubnetConfigConnectivity{configs.config2, subnet2Aligned} + subnetConfigConnectivity1 := SubnetConfigConnectivity{configs.config1, connectivity1Aligned} + subnetConfigConnectivity2 := SubnetConfigConnectivity{configs.config2, connectivity2Aligned} subnet1Subtract2 := subnetConfigConnectivity1.SubnetConnectivitySubtract(&subnetConfigConnectivity2) subnet2Subtract1 := subnetConfigConnectivity2.SubnetConnectivitySubtract(&subnetConfigConnectivity1) @@ -100,26 +100,6 @@ func (c *CloudConfig) getEndpointElemInOtherConfig(other *CloudConfig, ep Endpoi return nil } -// generates from subnet1Connectivity.AllowedConnsCombined and subnet2Connectivity.AllowedConnsCombined -// Two equivalent SubnetConnectivityMap objects s.t. any (src1, dst1) of subnet1Connectivity and -// (src2, dst2) of subnet2Connectivity are either: -// 1. src1 disjoint src2 or dst1 disjoint dst2 -// 2. src1 = src2 and dst1 = dst2 -// What is done here is repartitioning the ipBlocks so that the above will hold -// -// todo: verify that the returns objects indeed have exactly the same ipBlocks -func (connectivity SubnetConnectivityMap) getConnectivesWithSameIPBlocks(other SubnetConnectivityMap) ( - alignedConnectivity SubnetConnectivityMap, alignedOther SubnetConnectivityMap, err error) { - - // Resize connectivity and other s.t. all detected connections are resized to meet 1 and 2 above - // To that end we get a list of all disjoint src IPBlocks and dst IPBlocks - // and resize by the disjoint IPBlocks - // todo: use DisjointIPBlocks(set1, set2 []*IPBlock) []*IPBlock of ipBlock.go - alignedConnectivity = connectivity - alignedOther = other - return -} - // todo: write a function that for SubnetConnectivityMap and a src/dst returns a list of *ipBlocks //func () @@ -229,6 +209,144 @@ func diffDescription(diff DiffType) string { return "" } +// generates from subnet1Connectivity.AllowedConnsCombined and subnet2Connectivity.AllowedConnsCombined +// Two equivalent SubnetConnectivityMap objects s.t. any (src1, dst1) of subnet1Connectivity and +// (src2, dst2) of subnet2Connectivity s.t. if src1 and src2 (dst1 and dst2) are both external then +// they are either equal or disjoint +func (connectivity SubnetConnectivityMap) getConnectivesWithSameIPBlocks(other SubnetConnectivityMap) ( + alignedConnectivity SubnetConnectivityMap, alignedOther SubnetConnectivityMap, err error) { + // Get a list of all disjoint src IPBlocks and dst IPBlocks and resize by the disjoint IPBlocks + srcAlignedConnectivity, srcAlignedOther, err := connectivity.alignSrcOrDstIPBlocks(other, true) // resize src + alignedConnectivity, alignedOther, err = srcAlignedConnectivity.alignSrcOrDstIPBlocks(srcAlignedOther, false) // resize dst + return +} + +// aligns src or dst in both connectivity and other +func (connectivity SubnetConnectivityMap) alignSrcOrDstIPBlocks(other SubnetConnectivityMap, resizeSrc bool) ( + alignedConnectivity, alignedOther SubnetConnectivityMap, err error) { + err = nil + connectivitySrcOrDstIPBlist, err := connectivity.getIPBlocksList(resizeSrc) + if err != nil { + return nil, nil, err + } + otherSrcOrDstIPBlist, err := other.getIPBlocksList(resizeSrc) + if err != nil { + return nil, nil, err + } + disjointIPblocks := common.DisjointIPBlocks(connectivitySrcOrDstIPBlist, otherSrcOrDstIPBlist) + + alignedConnectivity, err = connectivity.actualAlignGivenIPBlists(disjointIPblocks, resizeSrc) + if err != nil { + return nil, nil, err + } + alignedOther, err = other.actualAlignGivenIPBlists(disjointIPblocks, resizeSrc) + if err != nil { + return nil, nil, err + } + + return alignedConnectivity, alignedOther, err +} + +func (connectivity SubnetConnectivityMap) actualAlignGivenIPBlists(disjointIPblocks []*common.IPBlock, resizeSrc bool) ( + alignedConnectivity SubnetConnectivityMap, err error) { + // goes over all sources of connections in connectivity + // if src is external then for each IPBlock in disjointIPblocks copies dsts and connection type + // otherwise just copies as is + err = nil + alignedConnectivity = map[EndpointElem]map[EndpointElem]*common.ConnectionSet{} + for src, endpointConns := range connectivity { + for dst, conns := range endpointConns { + if conns.IsEmpty() { + continue + } + // the resizing element is not external - copy as is + if (resizeSrc && !src.IsExternal()) || (!resizeSrc || dst.IsExternal()) { + alignedConnectivity[src][dst] = conns + continue + } + // the resizing element is external - go over all ipBlock and allocates the connection + // if the ipBlock is contained in the original src/dst + for _, ipBlock := range disjointIPblocks { + // 1. get ipBlock of resized index (src/dst) + var origIpBlock *common.IPBlock + var err error + if resizeSrc { + origIpBlock, err = externalNodeToIpBlock(src.(Node)) + } else { + origIpBlock, err = externalNodeToIpBlock(dst.(Node)) + } + if err != nil { + return nil, err + } + if !ipBlock.ContainedIn(origIpBlock) { // ipBlock not relevant here + continue + } + cidrList := ipBlock.ToCidrList() + var nodeOfCidr Node + for _, cidr := range cidrList { + newIpBlock, err := common.NewIPBlock(cidr, nil) + if err != nil { + return nil, err + } + nodeOfCidr, err = NewExternalNode(true, newIpBlock) + if err != nil { + return nil, err + } + } + if resizeSrc { + if _, ok := alignedConnectivity[nodeOfCidr]; !ok { + alignedConnectivity[nodeOfCidr] = map[EndpointElem]*common.ConnectionSet{} + } + alignedConnectivity[nodeOfCidr][dst] = conns + } else { + if _, ok := alignedConnectivity[src]; !ok { + alignedConnectivity[src] = map[EndpointElem]*common.ConnectionSet{} + } + alignedConnectivity[src][nodeOfCidr] = conns + } + } + } + } + alignedConnectivity = connectivity // todo tmp, to remove + return alignedConnectivity, err +} + +// get a list of IPBlocks of the src/dst of the connections +func (connectivity SubnetConnectivityMap) getIPBlocksList(getSrcList bool) (ipbList []*common.IPBlock, + err error) { + ipbList = []*common.IPBlock{} + err = nil + for src, endpointConns := range connectivity { + for dst, conns := range endpointConns { + if conns.IsEmpty() { + continue + } + if getSrcList && src.IsExternal() { + ipBlock, err := externalNodeToIpBlock(src.(Node)) + if err != nil { + return nil, err + } + ipbList = append(ipbList, ipBlock) + } else if !getSrcList && dst.IsExternal() { + ipBlock, err := externalNodeToIpBlock(dst.(Node)) + if err != nil { + return nil, err + } + ipbList = append(ipbList, ipBlock) + } + } + } + return ipbList, err +} + +func externalNodeToIpBlock(external Node) (ipBlock *common.IPBlock, err error) { + ipBlock, err = common.NewIPBlock(external.Cidr(), []string{}) + if err != nil { + return nil, err + } + return ipBlock, err +} + // todo: the following code finds all couples of connections that should be resized (it IPBlock) // // it seems that the code is redundant; yet we keep it with its unit test in case we'll decide From b544b11953bed8c4699607821927bf4183d33c63 Mon Sep 17 00:00:00 2001 From: shirim Date: Wed, 25 Oct 2023 14:56:39 +0300 Subject: [PATCH 17/42] handle error from GetSubnetsDiff properly --- pkg/vpcmodel/semanticDiffSubnets.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index e31e6a2a9..7c78a90cf 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -51,11 +51,11 @@ func (configs ConfigsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets // 1. compute connectivity for each of the subnets subnetsConn1, err := configs.config1.GetSubnetsConnectivity(true, false) if err != nil { - return nil, nil + return nil, err } subnetsConn2, err := configs.config2.GetSubnetsConnectivity(true, false) if err != nil { - return nil, nil + return nil, err } // 2. Computes delta in both directions From cbbca1d3ff2fe2e6b447a337882727827c2e835a Mon Sep 17 00:00:00 2001 From: shirim Date: Wed, 25 Oct 2023 17:25:55 +0300 Subject: [PATCH 18/42] fix typos --- pkg/vpcmodel/semanticDiffSubnets.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 7c78a90cf..6cda6f4bb 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -259,8 +259,11 @@ func (connectivity SubnetConnectivityMap) actualAlignGivenIPBlists(disjointIPblo if conns.IsEmpty() { continue } + if _, ok := alignedConnectivity[src]; !ok { + alignedConnectivity[src] = map[EndpointElem]*common.ConnectionSet{} + } // the resizing element is not external - copy as is - if (resizeSrc && !src.IsExternal()) || (!resizeSrc || dst.IsExternal()) { + if (resizeSrc && !src.IsExternal()) || (!resizeSrc && !dst.IsExternal()) { alignedConnectivity[src][dst] = conns continue } @@ -299,9 +302,6 @@ func (connectivity SubnetConnectivityMap) actualAlignGivenIPBlists(disjointIPblo } alignedConnectivity[nodeOfCidr][dst] = conns } else { - if _, ok := alignedConnectivity[src]; !ok { - alignedConnectivity[src] = map[EndpointElem]*common.ConnectionSet{} - } alignedConnectivity[src][nodeOfCidr] = conns } } From 4e034c03f38e6ef61c2dbbbe6b69349051aa5321 Mon Sep 17 00:00:00 2001 From: shirim Date: Wed, 25 Oct 2023 17:30:14 +0300 Subject: [PATCH 19/42] removed temp to be removed line --- pkg/vpcmodel/semanticDiffSubnets.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 6cda6f4bb..8385cc3c4 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -60,7 +60,7 @@ func (configs ConfigsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets // 2. Computes delta in both directions connectivity1Aligned, connectivity2Aligned, err := - subnetsConn1.AllowedConnsCombined.getConnectivesWithSameIPBlocks(subnetsConn2.AllowedConnsCombined) + subnetsConn1.AllowedConnsCombined.GetConnectivesWithSameIPBlocks(subnetsConn2.AllowedConnsCombined) if err != nil { return nil, err } @@ -209,11 +209,11 @@ func diffDescription(diff DiffType) string { return "" } -// generates from subnet1Connectivity.AllowedConnsCombined and subnet2Connectivity.AllowedConnsCombined +// GetConnectivesWithSameIPBlocks generates from subnet1Connectivity.AllowedConnsCombined and subnet2Connectivity.AllowedConnsCombined // Two equivalent SubnetConnectivityMap objects s.t. any (src1, dst1) of subnet1Connectivity and // (src2, dst2) of subnet2Connectivity s.t. if src1 and src2 (dst1 and dst2) are both external then // they are either equal or disjoint -func (connectivity SubnetConnectivityMap) getConnectivesWithSameIPBlocks(other SubnetConnectivityMap) ( +func (connectivity SubnetConnectivityMap) GetConnectivesWithSameIPBlocks(other SubnetConnectivityMap) ( alignedConnectivity SubnetConnectivityMap, alignedOther SubnetConnectivityMap, err error) { // Get a list of all disjoint src IPBlocks and dst IPBlocks and resize by the disjoint IPBlocks srcAlignedConnectivity, srcAlignedOther, err := connectivity.alignSrcOrDstIPBlocks(other, true) // resize src @@ -243,7 +243,6 @@ func (connectivity SubnetConnectivityMap) alignSrcOrDstIPBlocks(other SubnetConn if err != nil { return nil, nil, err } - return alignedConnectivity, alignedOther, err } @@ -307,7 +306,6 @@ func (connectivity SubnetConnectivityMap) actualAlignGivenIPBlists(disjointIPblo } } } - alignedConnectivity = connectivity // todo tmp, to remove return alignedConnectivity, err } From 51de1426182106f622bbdb1c115795c3f75bd263 Mon Sep 17 00:00:00 2001 From: shirim Date: Wed, 25 Oct 2023 17:35:20 +0300 Subject: [PATCH 20/42] added unit test for GetConnectivesWithSameIPBlocks --- pkg/vpcmodel/diffSubnets_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/vpcmodel/diffSubnets_test.go b/pkg/vpcmodel/diffSubnets_test.go index e04684709..72b9f9cad 100644 --- a/pkg/vpcmodel/diffSubnets_test.go +++ b/pkg/vpcmodel/diffSubnets_test.go @@ -146,6 +146,7 @@ func TestSimpleIPAndSubnetSubtract(t *testing.T) { require.Contains(t, res, " and intersects") require.Contains(t, res, " and intersects") + subnetConfigConn1.subnetConnectivity.GetConnectivesWithSameIPBlocks(subnetConfigConn2.subnetConnectivity) //subnet1Subtract2 := subnetConfigConn1.SubnetConnectivitySubtract(subnetConfigConn2) //subnet1Subtract2Str := subnet1Subtract2.EnhancedString(true) //fmt.Printf("subnet1Subtract2:\n%v\n", subnet1Subtract2Str) From d19471b617152a715eb4a5ff696fc90e524b8a05 Mon Sep 17 00:00:00 2001 From: shirim Date: Thu, 26 Oct 2023 08:33:55 +0300 Subject: [PATCH 21/42] expanded unit test to include subtract --- pkg/vpcmodel/diffSubnets_test.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pkg/vpcmodel/diffSubnets_test.go b/pkg/vpcmodel/diffSubnets_test.go index 72b9f9cad..cd5c0eb61 100644 --- a/pkg/vpcmodel/diffSubnets_test.go +++ b/pkg/vpcmodel/diffSubnets_test.go @@ -146,10 +146,16 @@ func TestSimpleIPAndSubnetSubtract(t *testing.T) { require.Contains(t, res, " and intersects") require.Contains(t, res, " and intersects") - subnetConfigConn1.subnetConnectivity.GetConnectivesWithSameIPBlocks(subnetConfigConn2.subnetConnectivity) - //subnet1Subtract2 := subnetConfigConn1.SubnetConnectivitySubtract(subnetConfigConn2) - //subnet1Subtract2Str := subnet1Subtract2.EnhancedString(true) - //fmt.Printf("subnet1Subtract2:\n%v\n", subnet1Subtract2Str) + alignedSubnet1Conn, alignedSubnet2Conn, err := subnetConfigConn1.subnetConnectivity.GetConnectivesWithSameIPBlocks(subnetConfigConn2.subnetConnectivity) + if err != nil { + fmt.Printf("err: %v\n", err.Error()) + return + } + subnetConfigConn1.subnetConnectivity = alignedSubnet1Conn + subnetConfigConn2.subnetConnectivity = alignedSubnet2Conn + subnet1Subtract2 := subnetConfigConn1.SubnetConnectivitySubtract(subnetConfigConn2) + subnet1Subtract2Str := subnet1Subtract2.EnhancedString(true) + fmt.Printf("subnet1Subtract2:\n%v\n", subnet1Subtract2Str) // //subnet2Subtract1 := subnetConfigConn2.SubnetConnectivitySubtract(subnetConfigConn1) //subnet2Subtract1Str := subnet2Subtract1.EnhancedString(false) From 6de6aefc3c58fec9c886fd95990a073bb6050867 Mon Sep 17 00:00:00 2001 From: shirim Date: Thu, 26 Oct 2023 10:30:21 +0300 Subject: [PATCH 22/42] computing diff properly for external nodes; among others, GetConnectivesWithSameIPBlocks and downwards should also define config, specifically modify Nodes[] --- pkg/vpcmodel/diffSubnets_test.go | 10 ++--- pkg/vpcmodel/semanticDiffSubnets.go | 64 ++++++++++++++++++----------- 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/pkg/vpcmodel/diffSubnets_test.go b/pkg/vpcmodel/diffSubnets_test.go index cd5c0eb61..9dd723756 100644 --- a/pkg/vpcmodel/diffSubnets_test.go +++ b/pkg/vpcmodel/diffSubnets_test.go @@ -118,15 +118,15 @@ func configSimpleIPAndSubnetSubtract() (subnetConfigConn1, subnetConfigConn2 *Su // and are comparable subnetConnMap1 := &VPCsubnetConnectivity{AllowedConnsCombined: NewSubnetConnectivityMap()} subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.Nodes[0], cfg1.NodeSets[0], common.NewConnectionSet(true)) - subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[1], cfg1.Nodes[0], common.NewConnectionSet(true)) subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.Nodes[0], cfg1.NodeSets[1], common.NewConnectionSet(true)) subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.Nodes[1], cfg1.NodeSets[1], common.NewConnectionSet(true)) + subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[1], cfg1.Nodes[0], common.NewConnectionSet(true)) subnetConnMap2 := &VPCsubnetConnectivity{AllowedConnsCombined: NewSubnetConnectivityMap()} subnetConnMap2.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg2.Nodes[0], cfg2.NodeSets[0], common.NewConnectionSet(true)) - subnetConnMap2.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg2.NodeSets[1], cfg2.Nodes[0], common.NewConnectionSet(true)) subnetConnMap2.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg2.Nodes[0], cfg2.NodeSets[1], common.NewConnectionSet(true)) subnetConnMap2.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg2.Nodes[1], cfg2.NodeSets[1], common.NewConnectionSet(true)) + subnetConnMap2.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg2.NodeSets[1], cfg2.Nodes[0], common.NewConnectionSet(true)) subnetConfigConn1 = &SubnetConfigConnectivity{cfg1, subnetConnMap1.AllowedConnsCombined} subnetConfigConn2 = &SubnetConfigConnectivity{cfg2, subnetConnMap2.AllowedConnsCombined} @@ -146,14 +146,12 @@ func TestSimpleIPAndSubnetSubtract(t *testing.T) { require.Contains(t, res, " and intersects") require.Contains(t, res, " and intersects") - alignedSubnet1Conn, alignedSubnet2Conn, err := subnetConfigConn1.subnetConnectivity.GetConnectivesWithSameIPBlocks(subnetConfigConn2.subnetConnectivity) + alignedSubnet1Conn, alignedSubnet1Conn2, err := subnetConfigConn1.GetConnectivesWithSameIPBlocks(subnetConfigConn2) if err != nil { fmt.Printf("err: %v\n", err.Error()) return } - subnetConfigConn1.subnetConnectivity = alignedSubnet1Conn - subnetConfigConn2.subnetConnectivity = alignedSubnet2Conn - subnet1Subtract2 := subnetConfigConn1.SubnetConnectivitySubtract(subnetConfigConn2) + subnet1Subtract2 := alignedSubnet1Conn.SubnetConnectivitySubtract(alignedSubnet1Conn2) subnet1Subtract2Str := subnet1Subtract2.EnhancedString(true) fmt.Printf("subnet1Subtract2:\n%v\n", subnet1Subtract2Str) // diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 8385cc3c4..1467c4479 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -58,16 +58,18 @@ func (configs ConfigsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets return nil, err } - // 2. Computes delta in both directions - connectivity1Aligned, connectivity2Aligned, err := - subnetsConn1.AllowedConnsCombined.GetConnectivesWithSameIPBlocks(subnetsConn2.AllowedConnsCombined) + //2. Computes delta in both directions + subnetConfigConn1 := &SubnetConfigConnectivity{configs.config1, + subnetsConn1.AllowedConnsCombined} + subnetConfigConn2 := &SubnetConfigConnectivity{configs.config2, + subnetsConn2.AllowedConnsCombined} + alignedConfigConnectivity1, alignedConfigConnectivity2, err := + subnetConfigConn1.GetConnectivesWithSameIPBlocks(subnetConfigConn2) if err != nil { return nil, err } - subnetConfigConnectivity1 := SubnetConfigConnectivity{configs.config1, connectivity1Aligned} - subnetConfigConnectivity2 := SubnetConfigConnectivity{configs.config2, connectivity2Aligned} - subnet1Subtract2 := subnetConfigConnectivity1.SubnetConnectivitySubtract(&subnetConfigConnectivity2) - subnet2Subtract1 := subnetConfigConnectivity2.SubnetConnectivitySubtract(&subnetConfigConnectivity1) + subnet1Subtract2 := alignedConfigConnectivity1.SubnetConnectivitySubtract(alignedConfigConnectivity2) + subnet2Subtract1 := alignedConfigConnectivity2.SubnetConnectivitySubtract(alignedConfigConnectivity1) // 3. ToDo: grouping, see comment at the end of this file @@ -109,9 +111,6 @@ func (subnetConfConnectivity *SubnetConfigConnectivity) SubnetConnectivitySubtra connectivitySubtract := map[EndpointElem]map[EndpointElem]*connectionDiff{} for src, endpointConns := range subnetConfConnectivity.subnetConnectivity { for dst, conns := range endpointConns { - if src.IsExternal() || dst.IsExternal() { // todo: tmp - continue - } if conns.IsEmpty() { continue } @@ -213,37 +212,51 @@ func diffDescription(diff DiffType) string { // Two equivalent SubnetConnectivityMap objects s.t. any (src1, dst1) of subnet1Connectivity and // (src2, dst2) of subnet2Connectivity s.t. if src1 and src2 (dst1 and dst2) are both external then // they are either equal or disjoint -func (connectivity SubnetConnectivityMap) GetConnectivesWithSameIPBlocks(other SubnetConnectivityMap) ( - alignedConnectivity SubnetConnectivityMap, alignedOther SubnetConnectivityMap, err error) { +// todo: instead of connectivity -> SubnetConfigConnectivity +func (connectivityConfig *SubnetConfigConnectivity) GetConnectivesWithSameIPBlocks(other *SubnetConfigConnectivity) ( + alignedConnectivity, alignedOther *SubnetConfigConnectivity, err error) { // Get a list of all disjoint src IPBlocks and dst IPBlocks and resize by the disjoint IPBlocks - srcAlignedConnectivity, srcAlignedOther, err := connectivity.alignSrcOrDstIPBlocks(other, true) // resize src + srcAlignedConnectivity, srcAlignedOther, err := connectivityConfig.alignSrcOrDstIPBlocks(other, true) // resize src alignedConnectivity, alignedOther, err = srcAlignedConnectivity.alignSrcOrDstIPBlocks(srcAlignedOther, false) // resize dst return } +// todo: instead of connectivity -> SubnetConfigConnectivity both in receiver and return value // aligns src or dst in both connectivity and other -func (connectivity SubnetConnectivityMap) alignSrcOrDstIPBlocks(other SubnetConnectivityMap, resizeSrc bool) ( - alignedConnectivity, alignedOther SubnetConnectivityMap, err error) { +func (connectivityConfig *SubnetConfigConnectivity) alignSrcOrDstIPBlocks(other *SubnetConfigConnectivity, resizeSrc bool) ( + alignedConfigConnectivity, alignedConfigOther *SubnetConfigConnectivity, err error) { err = nil - connectivitySrcOrDstIPBlist, err := connectivity.getIPBlocksList(resizeSrc) + // copy config, excluding nodes + alignedConfig := connectivityConfig.config + alignedConfig.Nodes = nil // todo nodes + alignedOtherConfig := other.config + alignedOtherConfig.Nodes = nil // todo nodes + connectivitySrcOrDstIPBlist, err := connectivityConfig.subnetConnectivity.getIPBlocksList(resizeSrc) if err != nil { return nil, nil, err } - otherSrcOrDstIPBlist, err := other.getIPBlocksList(resizeSrc) + otherSrcOrDstIPBlist, err := other.subnetConnectivity.getIPBlocksList(resizeSrc) if err != nil { return nil, nil, err } disjointIPblocks := common.DisjointIPBlocks(connectivitySrcOrDstIPBlist, otherSrcOrDstIPBlist) + // todo: 2. nodes: range over old nodes and inside range over disjoint blocks + // if a disjoint block is contained in an old node - create external node and add it + // if no disjoint block is contained in an old node - add the old node as is - alignedConnectivity, err = connectivity.actualAlignGivenIPBlists(disjointIPblocks, resizeSrc) + fmt.Printf("\nresizeSrc %v\n--------------\n", resizeSrc) + fmt.Println("resizing connectivity") + alignedConnectivity, err := connectivityConfig.subnetConnectivity.actualAlignGivenIPBlists(disjointIPblocks, resizeSrc) if err != nil { return nil, nil, err } - alignedOther, err = other.actualAlignGivenIPBlists(disjointIPblocks, resizeSrc) + fmt.Println("resizing other") + alignedOtherConnectivity, err := other.subnetConnectivity.actualAlignGivenIPBlists(disjointIPblocks, resizeSrc) if err != nil { return nil, nil, err } - return alignedConnectivity, alignedOther, err + return &SubnetConfigConnectivity{alignedConfig, alignedConnectivity}, + &SubnetConfigConnectivity{alignedOtherConfig, alignedOtherConnectivity}, err } func (connectivity SubnetConnectivityMap) actualAlignGivenIPBlists(disjointIPblocks []*common.IPBlock, resizeSrc bool) ( @@ -264,6 +277,7 @@ func (connectivity SubnetConnectivityMap) actualAlignGivenIPBlists(disjointIPblo // the resizing element is not external - copy as is if (resizeSrc && !src.IsExternal()) || (!resizeSrc && !dst.IsExternal()) { alignedConnectivity[src][dst] = conns + fmt.Printf("\t\tnew (old): %v %v %v\n", src.Name(), dst.Name(), conns.EnhancedString()) continue } // the resizing element is external - go over all ipBlock and allocates the connection @@ -290,6 +304,7 @@ func (connectivity SubnetConnectivityMap) actualAlignGivenIPBlists(disjointIPblo if err != nil { return nil, err } + // todo: take node from config nodeOfCidr, err = NewExternalNode(true, newIpBlock) if err != nil { return nil, err @@ -300,8 +315,10 @@ func (connectivity SubnetConnectivityMap) actualAlignGivenIPBlists(disjointIPblo alignedConnectivity[nodeOfCidr] = map[EndpointElem]*common.ConnectionSet{} } alignedConnectivity[nodeOfCidr][dst] = conns + fmt.Printf("\t\tnew:%v %v %v\n", nodeOfCidr.Cidr(), dst.Name(), conns.EnhancedString()) } else { alignedConnectivity[src][nodeOfCidr] = conns + fmt.Printf("\t\tnew:%v %v %v\n", src.Name(), nodeOfCidr.Cidr(), conns.EnhancedString()) } } } @@ -346,10 +363,9 @@ func externalNodeToIpBlock(external Node) (ipBlock *common.IPBlock, err error) { } // todo: the following code finds all couples of connections that should be resized (it IPBlock) -// -// it seems that the code is redundant; yet we keep it with its unit test in case we'll decide -// to use it in the future -// it return a string describing the intersecting connections for the unit test +// todo: it seems that the code is redundant; yet we keep it with its unit test in case we'll decide +// todo: to use it in the future +// todo: it return a string describing the intersecting connections for the unit test func (connectivity SubnetConnectivityMap) getIntersectingConnections(other SubnetConnectivityMap) (areIntersecting string, err error) { err = nil From 04cd4becfd8b32f2c4bf9e938feb77ff110a6900 Mon Sep 17 00:00:00 2001 From: shirim Date: Thu, 26 Oct 2023 13:55:37 +0300 Subject: [PATCH 23/42] Nodes of config computed and used --- pkg/vpcmodel/semanticDiffSubnets.go | 125 ++++++++++++++++++++-------- pkg/vpcmodel/subnetsConnectivity.go | 8 +- 2 files changed, 92 insertions(+), 41 deletions(-) diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 1467c4479..7e7107f74 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -81,8 +81,6 @@ func (configs ConfigsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets // for a given EndpointElem (representing a subnet or an external ip) in config return the EndpointElem representing the // subnet/external address in otherConfig or nil if the subnet does not exist in the other config. -// ToDo: this is done based on names only at the moment. Perhaps take into account other factors such as cidr? -// ToDo: instead of performing this search each time, use a map created once func (c *CloudConfig) getEndpointElemInOtherConfig(other *CloudConfig, ep EndpointElem) EndpointElem { if ep.IsExternal() { for _, node := range other.Nodes { @@ -102,9 +100,6 @@ func (c *CloudConfig) getEndpointElemInOtherConfig(other *CloudConfig, ep Endpoi return nil } -// todo: write a function that for SubnetConnectivityMap and a src/dst returns a list of *ipBlocks -//func () - // SubnetConnectivitySubtract Subtract one SubnetConnectivityMap from the other // assumption: any connection from connectivity and "other" have src (dst) which are either disjoint or equal func (subnetConfConnectivity *SubnetConfigConnectivity) SubnetConnectivitySubtract(other *SubnetConfigConnectivity) SubnetsDiff { @@ -212,72 +207,122 @@ func diffDescription(diff DiffType) string { // Two equivalent SubnetConnectivityMap objects s.t. any (src1, dst1) of subnet1Connectivity and // (src2, dst2) of subnet2Connectivity s.t. if src1 and src2 (dst1 and dst2) are both external then // they are either equal or disjoint -// todo: instead of connectivity -> SubnetConfigConnectivity -func (connectivityConfig *SubnetConfigConnectivity) GetConnectivesWithSameIPBlocks(other *SubnetConfigConnectivity) ( +func (subnetConfConnectivity *SubnetConfigConnectivity) GetConnectivesWithSameIPBlocks(othersubnetConfConnectivity *SubnetConfigConnectivity) ( alignedConnectivity, alignedOther *SubnetConfigConnectivity, err error) { // Get a list of all disjoint src IPBlocks and dst IPBlocks and resize by the disjoint IPBlocks - srcAlignedConnectivity, srcAlignedOther, err := connectivityConfig.alignSrcOrDstIPBlocks(other, true) // resize src + srcAlignedConnectivity, srcAlignedOther, err := subnetConfConnectivity.alignSrcOrDstIPBlocks(othersubnetConfConnectivity, true) // resize src + if err != nil { + return + } alignedConnectivity, alignedOther, err = srcAlignedConnectivity.alignSrcOrDstIPBlocks(srcAlignedOther, false) // resize dst return } -// todo: instead of connectivity -> SubnetConfigConnectivity both in receiver and return value // aligns src or dst in both connectivity and other -func (connectivityConfig *SubnetConfigConnectivity) alignSrcOrDstIPBlocks(other *SubnetConfigConnectivity, resizeSrc bool) ( +func (subnetConfConnectivity *SubnetConfigConnectivity) alignSrcOrDstIPBlocks(otherConfConnectivity *SubnetConfigConnectivity, resizeSrc bool) ( alignedConfigConnectivity, alignedConfigOther *SubnetConfigConnectivity, err error) { err = nil - // copy config, excluding nodes - alignedConfig := connectivityConfig.config - alignedConfig.Nodes = nil // todo nodes - alignedOtherConfig := other.config - alignedOtherConfig.Nodes = nil // todo nodes - connectivitySrcOrDstIPBlist, err := connectivityConfig.subnetConnectivity.getIPBlocksList(resizeSrc) + // computes disjoint IPBlocks + connectivitySrcOrDstIPBlist, err := subnetConfConnectivity.subnetConnectivity.getIPBlocksList(resizeSrc) if err != nil { return nil, nil, err } - otherSrcOrDstIPBlist, err := other.subnetConnectivity.getIPBlocksList(resizeSrc) + otherSrcOrDstIPBlist, err := otherConfConnectivity.subnetConnectivity.getIPBlocksList(resizeSrc) if err != nil { return nil, nil, err } disjointIPblocks := common.DisjointIPBlocks(connectivitySrcOrDstIPBlist, otherSrcOrDstIPBlist) - // todo: 2. nodes: range over old nodes and inside range over disjoint blocks - // if a disjoint block is contained in an old node - create external node and add it - // if no disjoint block is contained in an old node - add the old node as is + + // copy configs + alignedConfig, err := subnetConfConnectivity.config.copyConfig(disjointIPblocks) + if err != nil { + return nil, nil, err + } + otherAlignedConfig, err := otherConfConnectivity.config.copyConfig(disjointIPblocks) + if err != nil { + return nil, nil, err + } fmt.Printf("\nresizeSrc %v\n--------------\n", resizeSrc) fmt.Println("resizing connectivity") - alignedConnectivity, err := connectivityConfig.subnetConnectivity.actualAlignGivenIPBlists(disjointIPblocks, resizeSrc) + alignedConnectivity, err := subnetConfConnectivity.subnetConnectivity.actualAlignGivenIPBlists( + alignedConfig, disjointIPblocks, resizeSrc) if err != nil { return nil, nil, err } - fmt.Println("resizing other") - alignedOtherConnectivity, err := other.subnetConnectivity.actualAlignGivenIPBlists(disjointIPblocks, resizeSrc) + fmt.Println("resizing otherConfConnectivity") + alignedOtherConnectivity, err := otherConfConnectivity.subnetConnectivity.actualAlignGivenIPBlists( + otherAlignedConfig, disjointIPblocks, resizeSrc) if err != nil { return nil, nil, err } return &SubnetConfigConnectivity{alignedConfig, alignedConnectivity}, - &SubnetConfigConnectivity{alignedOtherConfig, alignedOtherConnectivity}, err + &SubnetConfigConnectivity{otherAlignedConfig, alignedOtherConnectivity}, err +} + +// aligned config: copies from old config everything but nodes, +// nodes are resized by disjointIPblocks +func (oldConfig *CloudConfig) copyConfig(disjointIPblocks []*common.IPBlock) (alignedConfig *CloudConfig, err error) { + // copy config + alignedConfig = oldConfig + // nodes - external addresses - are resized + alignedConfig.Nodes, err = resizeNodes(oldConfig.Nodes, disjointIPblocks) + return alignedConfig, err +} + +func resizeNodes(oldNodes []Node, disjointIPblocks []*common.IPBlock) (newNodes []Node, err error) { + newNodes = []Node{} + // range over old nodes and inside range over disjoint blocks + // if a disjoint block is contained in an old oldNode - create external oldNode and add it + // if no disjoint block is contained in an old oldNode - add the old oldNode as is + for _, oldNode := range oldNodes { + nodeIpBlock, err := common.NewIPBlock(oldNode.Cidr(), nil) + if err != nil { + return nil, err + } + disjointContained := false + fmt.Printf("oldNode %v %v\n", oldNode.Name(), oldNode.Cidr()) + for _, disjointIpBlock := range disjointIPblocks { + if disjointIpBlock.ContainedIn(nodeIpBlock) { + disjointContained = true + newNode, err := NewExternalNode(true, disjointIpBlock) + if err != nil { + return nil, err + } + fmt.Printf("\tnewNode %v %v\n", newNode.Cidr(), newNode.Name()) + newNodes = append(newNodes, newNode) + } + } + if !disjointContained { + fmt.Printf("!disjointContained adding %v %v\n", oldNode.Name(), oldNode.Cidr()) + newNodes = append(newNodes, oldNode) + } + } + + return newNodes, nil } -func (connectivity SubnetConnectivityMap) actualAlignGivenIPBlists(disjointIPblocks []*common.IPBlock, resizeSrc bool) ( +func (subnetConnectivity *SubnetConnectivityMap) actualAlignGivenIPBlists(config *CloudConfig, disjointIPblocks []*common.IPBlock, resizeSrc bool) ( alignedConnectivity SubnetConnectivityMap, err error) { // goes over all sources of connections in connectivity // if src is external then for each IPBlock in disjointIPblocks copies dsts and connection type // otherwise just copies as is err = nil alignedConnectivity = map[EndpointElem]map[EndpointElem]*common.ConnectionSet{} - for src, endpointConns := range connectivity { + for src, endpointConns := range *subnetConnectivity { for dst, conns := range endpointConns { if conns.IsEmpty() { continue } + fmt.Printf("actualAlignGivenIPBlists %v => %v %v\n", src.Name(), dst.Name(), conns.EnhancedString()) if _, ok := alignedConnectivity[src]; !ok { alignedConnectivity[src] = map[EndpointElem]*common.ConnectionSet{} } + fmt.Printf("\told: %v %v %v\n", src.Name(), dst.Name(), conns.EnhancedString()) // the resizing element is not external - copy as is if (resizeSrc && !src.IsExternal()) || (!resizeSrc && !dst.IsExternal()) { alignedConnectivity[src][dst] = conns - fmt.Printf("\t\tnew (old): %v %v %v\n", src.Name(), dst.Name(), conns.EnhancedString()) + fmt.Printf("\t\tcopy old: %v %v %v\n", src.Name(), dst.Name(), conns.EnhancedString()) continue } // the resizing element is external - go over all ipBlock and allocates the connection @@ -300,12 +345,7 @@ func (connectivity SubnetConnectivityMap) actualAlignGivenIPBlists(disjointIPblo cidrList := ipBlock.ToCidrList() var nodeOfCidr Node for _, cidr := range cidrList { - newIpBlock, err := common.NewIPBlock(cidr, nil) - if err != nil { - return nil, err - } - // todo: take node from config - nodeOfCidr, err = NewExternalNode(true, newIpBlock) + nodeOfCidr, err = findNodeWithCidr(config.Nodes, cidr) if err != nil { return nil, err } @@ -326,12 +366,23 @@ func (connectivity SubnetConnectivityMap) actualAlignGivenIPBlists(disjointIPblo return alignedConnectivity, err } +// gets node with given cidr +func findNodeWithCidr(configNodes []Node, cidr string) (Node, error) { + for _, node := range configNodes { + if node.Cidr() == cidr { + fmt.Printf("findNodeWithCidr cidr %v %v %v\n", cidr, node.Cidr(), cidr) + return node, nil + } + } + return nil, fmt.Errorf(fmt.Sprintf("A node with cidr %v not found in conf", cidr)) // should not get here +} + // get a list of IPBlocks of the src/dst of the connections -func (connectivity SubnetConnectivityMap) getIPBlocksList(getSrcList bool) (ipbList []*common.IPBlock, +func (subnetConnectivity SubnetConnectivityMap) getIPBlocksList(getSrcList bool) (ipbList []*common.IPBlock, err error) { ipbList = []*common.IPBlock{} err = nil - for src, endpointConns := range connectivity { + for src, endpointConns := range subnetConnectivity { for dst, conns := range endpointConns { if conns.IsEmpty() { continue @@ -366,10 +417,10 @@ func externalNodeToIpBlock(external Node) (ipBlock *common.IPBlock, err error) { // todo: it seems that the code is redundant; yet we keep it with its unit test in case we'll decide // todo: to use it in the future // todo: it return a string describing the intersecting connections for the unit test -func (connectivity SubnetConnectivityMap) getIntersectingConnections(other SubnetConnectivityMap) (areIntersecting string, +func (subnetConnectivity SubnetConnectivityMap) getIntersectingConnections(other SubnetConnectivityMap) (areIntersecting string, err error) { err = nil - for src, endpointConns := range connectivity { + for src, endpointConns := range subnetConnectivity { for dst, conns := range endpointConns { if (!src.IsExternal() && !dst.IsExternal()) || conns.IsEmpty() { continue // nothing to do here diff --git a/pkg/vpcmodel/subnetsConnectivity.go b/pkg/vpcmodel/subnetsConnectivity.go index 7237706fc..e1f097b24 100644 --- a/pkg/vpcmodel/subnetsConnectivity.go +++ b/pkg/vpcmodel/subnetsConnectivity.go @@ -315,9 +315,9 @@ func (c *CloudConfig) GetConnectivityOutputPerEachSubnetSeparately() string { return "" } -func (connectivity SubnetConnectivityMap) updateAllowedSubnetConnsMap(src, dst EndpointElem, conn *common.ConnectionSet) { - if _, ok := connectivity[src]; !ok { - connectivity[src] = map[EndpointElem]*common.ConnectionSet{} +func (subnetConnectivity SubnetConnectivityMap) updateAllowedSubnetConnsMap(src, dst EndpointElem, conn *common.ConnectionSet) { + if _, ok := subnetConnectivity[src]; !ok { + subnetConnectivity[src] = map[EndpointElem]*common.ConnectionSet{} } - connectivity[src][dst] = conn + subnetConnectivity[src][dst] = conn } From 21d90022141c10bacf9579d762e7636bbff77db5 Mon Sep 17 00:00:00 2001 From: shirim Date: Thu, 26 Oct 2023 15:59:36 +0300 Subject: [PATCH 24/42] bug fixed: a single disjoint ipBlock may contain more than one cidr --- pkg/vpcmodel/externalNetwork.go | 7 +++++++ pkg/vpcmodel/semanticDiffSubnets.go | 32 +++++++++++++---------------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/pkg/vpcmodel/externalNetwork.go b/pkg/vpcmodel/externalNetwork.go index d86e69aeb..08ebfb719 100644 --- a/pkg/vpcmodel/externalNetwork.go +++ b/pkg/vpcmodel/externalNetwork.go @@ -99,6 +99,13 @@ func NewExternalNode(isPublicInternet bool, ipb *common.IPBlock) (Node, error) { isPublicInternet: isPublicInternet}, nil } +func NewExternalNodeForCidr(cidr string) Node { + return &ExternalNetwork{ + ResourceType: publicInternetNodeName, + CidrStr: cidr, + isPublicInternet: true} +} + func GetExternalNetworkNodes(disjointRefExternalIPBlocks []*common.IPBlock) ([]Node, error) { res := []Node{} internetIPblocks, allInternetRagnes, err := getPublicInternetIPblocksList() diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 7e7107f74..38d992d52 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -243,14 +243,11 @@ func (subnetConfConnectivity *SubnetConfigConnectivity) alignSrcOrDstIPBlocks(ot return nil, nil, err } - fmt.Printf("\nresizeSrc %v\n--------------\n", resizeSrc) - fmt.Println("resizing connectivity") alignedConnectivity, err := subnetConfConnectivity.subnetConnectivity.actualAlignGivenIPBlists( alignedConfig, disjointIPblocks, resizeSrc) if err != nil { return nil, nil, err } - fmt.Println("resizing otherConfConnectivity") alignedOtherConnectivity, err := otherConfConnectivity.subnetConnectivity.actualAlignGivenIPBlists( otherAlignedConfig, disjointIPblocks, resizeSrc) if err != nil { @@ -281,20 +278,16 @@ func resizeNodes(oldNodes []Node, disjointIPblocks []*common.IPBlock) (newNodes return nil, err } disjointContained := false - fmt.Printf("oldNode %v %v\n", oldNode.Name(), oldNode.Cidr()) for _, disjointIpBlock := range disjointIPblocks { if disjointIpBlock.ContainedIn(nodeIpBlock) { disjointContained = true - newNode, err := NewExternalNode(true, disjointIpBlock) - if err != nil { - return nil, err + for _, thisCidr := range disjointIpBlock.ToCidrList() { + newNode := NewExternalNodeForCidr(thisCidr) + newNodes = append(newNodes, newNode) } - fmt.Printf("\tnewNode %v %v\n", newNode.Cidr(), newNode.Name()) - newNodes = append(newNodes, newNode) } } if !disjointContained { - fmt.Printf("!disjointContained adding %v %v\n", oldNode.Name(), oldNode.Cidr()) newNodes = append(newNodes, oldNode) } } @@ -314,15 +307,12 @@ func (subnetConnectivity *SubnetConnectivityMap) actualAlignGivenIPBlists(config if conns.IsEmpty() { continue } - fmt.Printf("actualAlignGivenIPBlists %v => %v %v\n", src.Name(), dst.Name(), conns.EnhancedString()) if _, ok := alignedConnectivity[src]; !ok { alignedConnectivity[src] = map[EndpointElem]*common.ConnectionSet{} } - fmt.Printf("\told: %v %v %v\n", src.Name(), dst.Name(), conns.EnhancedString()) // the resizing element is not external - copy as is if (resizeSrc && !src.IsExternal()) || (!resizeSrc && !dst.IsExternal()) { alignedConnectivity[src][dst] = conns - fmt.Printf("\t\tcopy old: %v %v %v\n", src.Name(), dst.Name(), conns.EnhancedString()) continue } // the resizing element is external - go over all ipBlock and allocates the connection @@ -355,10 +345,8 @@ func (subnetConnectivity *SubnetConnectivityMap) actualAlignGivenIPBlists(config alignedConnectivity[nodeOfCidr] = map[EndpointElem]*common.ConnectionSet{} } alignedConnectivity[nodeOfCidr][dst] = conns - fmt.Printf("\t\tnew:%v %v %v\n", nodeOfCidr.Cidr(), dst.Name(), conns.EnhancedString()) } else { alignedConnectivity[src][nodeOfCidr] = conns - fmt.Printf("\t\tnew:%v %v %v\n", src.Name(), nodeOfCidr.Cidr(), conns.EnhancedString()) } } } @@ -370,7 +358,6 @@ func (subnetConnectivity *SubnetConnectivityMap) actualAlignGivenIPBlists(config func findNodeWithCidr(configNodes []Node, cidr string) (Node, error) { for _, node := range configNodes { if node.Cidr() == cidr { - fmt.Printf("findNodeWithCidr cidr %v %v %v\n", cidr, node.Cidr(), cidr) return node, nil } } @@ -502,8 +489,6 @@ func pairEpsComparable(myEp, otherEp EndpointElem) (bool, error) { if err != nil { return false, err } - // we need to resize if the IpBlocks are intersecting but not equal - //fmt.Printf("\t<%v, %v>: cidrs: %v, %v\n", myEp.Name(), otherEp.Name(), myExternal.Cidr(), otherExternal.Cidr()) if !myIpBlock.Equal(otherIpBlock) && !myIpBlock.Intersection(otherIpBlock).Empty() { return true, nil } @@ -517,3 +502,14 @@ func pairEpsComparable(myEp, otherEp EndpointElem) (bool, error) { // and also the diff where relevant // this will requires some rewriting in the existing grouping functionality and the way it provides // service to subnetsConnectivity and nodesConnectivity + +func (subnetConnectivity *SubnetConnectivityMap) printConnectivity() { + for src, endpointConns := range *subnetConnectivity { + for dst, conns := range endpointConns { + if conns.IsEmpty() { + continue + } + fmt.Printf("\t%v => %v %v\n", src.Name(), dst.Name(), conns.EnhancedString()) + } + } +} From 994cba0eb59912d86489722934377f48c4fa272a Mon Sep 17 00:00:00 2001 From: shirim Date: Thu, 26 Oct 2023 16:31:51 +0300 Subject: [PATCH 25/42] another bug fixed; still another bug to fix --- pkg/vpcmodel/diffSubnets_test.go | 2 ++ pkg/vpcmodel/semanticDiffSubnets.go | 41 ++++++++++++++++------------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/pkg/vpcmodel/diffSubnets_test.go b/pkg/vpcmodel/diffSubnets_test.go index 9dd723756..2f57972db 100644 --- a/pkg/vpcmodel/diffSubnets_test.go +++ b/pkg/vpcmodel/diffSubnets_test.go @@ -154,6 +154,8 @@ func TestSimpleIPAndSubnetSubtract(t *testing.T) { subnet1Subtract2 := alignedSubnet1Conn.SubnetConnectivitySubtract(alignedSubnet1Conn2) subnet1Subtract2Str := subnet1Subtract2.EnhancedString(true) fmt.Printf("subnet1Subtract2:\n%v\n", subnet1Subtract2Str) + // todo should be equal to -- Public Internet [250.2.4.128/25] => subnet2 : missing connection + // not clear why [1.2.3.0/30] lines are there // //subnet2Subtract1 := subnetConfigConn2.SubnetConnectivitySubtract(subnetConfigConn1) //subnet2Subtract1Str := subnet2Subtract1.EnhancedString(false) diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 38d992d52..7592b825a 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -317,21 +317,23 @@ func (subnetConnectivity *SubnetConnectivityMap) actualAlignGivenIPBlists(config } // the resizing element is external - go over all ipBlock and allocates the connection // if the ipBlock is contained in the original src/dst + var origIpBlock *common.IPBlock + var err error + if resizeSrc { + origIpBlock, err = externalNodeToIpBlock(src.(Node)) + } else { + origIpBlock, err = externalNodeToIpBlock(dst.(Node)) + } + if err != nil { + return nil, err + } + origBlockContainsIPBlocks := false for _, ipBlock := range disjointIPblocks { // 1. get ipBlock of resized index (src/dst) - var origIpBlock *common.IPBlock - var err error - if resizeSrc { - origIpBlock, err = externalNodeToIpBlock(src.(Node)) - } else { - origIpBlock, err = externalNodeToIpBlock(dst.(Node)) - } - if err != nil { - return nil, err - } if !ipBlock.ContainedIn(origIpBlock) { // ipBlock not relevant here continue } + origBlockContainsIPBlocks = true cidrList := ipBlock.ToCidrList() var nodeOfCidr Node for _, cidr := range cidrList { @@ -339,16 +341,19 @@ func (subnetConnectivity *SubnetConnectivityMap) actualAlignGivenIPBlists(config if err != nil { return nil, err } - } - if resizeSrc { - if _, ok := alignedConnectivity[nodeOfCidr]; !ok { - alignedConnectivity[nodeOfCidr] = map[EndpointElem]*common.ConnectionSet{} + if resizeSrc { + if _, ok := alignedConnectivity[nodeOfCidr]; !ok { + alignedConnectivity[nodeOfCidr] = map[EndpointElem]*common.ConnectionSet{} + } + alignedConnectivity[nodeOfCidr][dst] = conns + } else { + alignedConnectivity[src][nodeOfCidr] = conns } - alignedConnectivity[nodeOfCidr][dst] = conns - } else { - alignedConnectivity[src][nodeOfCidr] = conns } } + if !origBlockContainsIPBlocks { // keep as origin + alignedConnectivity[src][dst] = conns + } } } return alignedConnectivity, err @@ -503,7 +508,7 @@ func pairEpsComparable(myEp, otherEp EndpointElem) (bool, error) { // this will requires some rewriting in the existing grouping functionality and the way it provides // service to subnetsConnectivity and nodesConnectivity -func (subnetConnectivity *SubnetConnectivityMap) printConnectivity() { +func (subnetConnectivity *SubnetConnectivityMap) PrintConnectivity() { for src, endpointConns := range *subnetConnectivity { for dst, conns := range endpointConns { if conns.IsEmpty() { From e90bc41dc968bda0a0fbf3857be429f8200362a1 Mon Sep 17 00:00:00 2001 From: shirim Date: Mon, 30 Oct 2023 08:48:15 +0200 Subject: [PATCH 26/42] some renaming to avoid confusion --- pkg/vpcmodel/diffSubnets_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/vpcmodel/diffSubnets_test.go b/pkg/vpcmodel/diffSubnets_test.go index 2f57972db..10a7fe3b6 100644 --- a/pkg/vpcmodel/diffSubnets_test.go +++ b/pkg/vpcmodel/diffSubnets_test.go @@ -88,9 +88,9 @@ func TestSimpleSubnetSubtract(t *testing.T) { require.Contains(t, subnet1Subtract2Str, "-- subnet0 => subnet1 : missing source and destination") require.Contains(t, subnet1Subtract2Str, "-- subnet1 => subnet2 : missing source") - subnet2Subtract1 := subnetConfigConn2.SubnetConnectivitySubtract(subnetConfigConn1) - subnet2Subtract1Str := subnet2Subtract1.EnhancedString(false) - fmt.Printf("subnet2Subtract1:\n%v\n", subnet2Subtract1Str) + cfg2Subtract1 := subnetConfigConn2.SubnetConnectivitySubtract(subnetConfigConn1) + subnet2Subtract1Str := cfg2Subtract1.EnhancedString(false) + fmt.Printf("cfg2Subtract1:\n%v\n", subnet2Subtract1Str) require.Equal(t, "++ subnet3 => subnet4 : changed connection "+ "protocol: TCP src-ports: 1-9,101-65535; protocol: TCP src-ports: "+ "10-100 dst-ports: 1-442,444-65535; protocol: UDP,ICMP\n", subnet2Subtract1Str) @@ -139,21 +139,21 @@ func TestSimpleIPAndSubnetSubtract(t *testing.T) { res, _ := subnetConfigConn1.subnetConnectivity.getIntersectingConnections(subnetConfigConn2.subnetConnectivity) fmt.Printf(res) newLines := strings.Count(res, "\n") - // there should be 4 lines in subnet1Subtract2Str + // there should be 4 lines in cfg1SubtractCfg2Str require.Equal(t, 4, newLines) require.Contains(t, res, " and intersects") require.Contains(t, res, " and intersects") require.Contains(t, res, " and intersects") require.Contains(t, res, " and intersects") - alignedSubnet1Conn, alignedSubnet1Conn2, err := subnetConfigConn1.GetConnectivesWithSameIPBlocks(subnetConfigConn2) + alignedCfg1Conn, alignedCfg1Conn2, err := subnetConfigConn1.GetConnectivesWithSameIPBlocks(subnetConfigConn2) if err != nil { fmt.Printf("err: %v\n", err.Error()) return } - subnet1Subtract2 := alignedSubnet1Conn.SubnetConnectivitySubtract(alignedSubnet1Conn2) - subnet1Subtract2Str := subnet1Subtract2.EnhancedString(true) - fmt.Printf("subnet1Subtract2:\n%v\n", subnet1Subtract2Str) + cfg1SubCfg2 := alignedCfg1Conn.SubnetConnectivitySubtract(alignedCfg1Conn2) + cfg1SubtractCfg2Str := cfg1SubCfg2.EnhancedString(true) + fmt.Printf("cfg1SubCfg2:\n%v\n", cfg1SubtractCfg2Str) // todo should be equal to -- Public Internet [250.2.4.128/25] => subnet2 : missing connection // not clear why [1.2.3.0/30] lines are there // From c07bc9bd7523929b2e471f210914b197e2f5c2d0 Mon Sep 17 00:00:00 2001 From: shirim Date: Mon, 30 Oct 2023 12:42:37 +0200 Subject: [PATCH 27/42] find nodes by CIDR and not by name --- pkg/vpcmodel/semanticDiffSubnets.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 7592b825a..7ba9697a9 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -83,12 +83,8 @@ func (configs ConfigsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets // subnet/external address in otherConfig or nil if the subnet does not exist in the other config. func (c *CloudConfig) getEndpointElemInOtherConfig(other *CloudConfig, ep EndpointElem) EndpointElem { if ep.IsExternal() { - for _, node := range other.Nodes { - if node.Name() == ep.Name() { - res := EndpointElem(node) - return res - } - } + nodeSameCidr, _ := findNodeWithCidr(other.Nodes, ep.(Node).Cidr()) + return nodeSameCidr } else { for _, nodeSet := range other.NodeSets { if nodeSet.Name() == ep.Name() { From 15f6cc8430542e7bcc282b524a9339d7ac4a4b40 Mon Sep 17 00:00:00 2001 From: shirim Date: Mon, 30 Oct 2023 14:07:42 +0200 Subject: [PATCH 28/42] fixed bug: Nodes and disjointBlocks computed now for both src and dst together --- pkg/vpcmodel/semanticDiffSubnets.go | 62 ++++++++++++++--------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 7ba9697a9..f4b220ccd 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -203,33 +203,21 @@ func diffDescription(diff DiffType) string { // Two equivalent SubnetConnectivityMap objects s.t. any (src1, dst1) of subnet1Connectivity and // (src2, dst2) of subnet2Connectivity s.t. if src1 and src2 (dst1 and dst2) are both external then // they are either equal or disjoint -func (subnetConfConnectivity *SubnetConfigConnectivity) GetConnectivesWithSameIPBlocks(othersubnetConfConnectivity *SubnetConfigConnectivity) ( - alignedConnectivity, alignedOther *SubnetConfigConnectivity, err error) { - // Get a list of all disjoint src IPBlocks and dst IPBlocks and resize by the disjoint IPBlocks - srcAlignedConnectivity, srcAlignedOther, err := subnetConfConnectivity.alignSrcOrDstIPBlocks(othersubnetConfConnectivity, true) // resize src - if err != nil { - return - } - alignedConnectivity, alignedOther, err = srcAlignedConnectivity.alignSrcOrDstIPBlocks(srcAlignedOther, false) // resize dst - return -} - -// aligns src or dst in both connectivity and other -func (subnetConfConnectivity *SubnetConfigConnectivity) alignSrcOrDstIPBlocks(otherConfConnectivity *SubnetConfigConnectivity, resizeSrc bool) ( - alignedConfigConnectivity, alignedConfigOther *SubnetConfigConnectivity, err error) { - err = nil - // computes disjoint IPBlocks - connectivitySrcOrDstIPBlist, err := subnetConfConnectivity.subnetConnectivity.getIPBlocksList(resizeSrc) +func (subnetConfConnectivity *SubnetConfigConnectivity) GetConnectivesWithSameIPBlocks(otherConfConnectivity *SubnetConfigConnectivity) ( + alignedConnectivityConfig, alignedOtherConnectivityConfig *SubnetConfigConnectivity, err error) { + // 1. computes new set of external nodes (only type of nodes here) in cfg1 and cfg2 + // does so by computing disjoint block between src+dst ipBlocks in cfg1 and in cfg2 + // the new set of external nodes is determined based on them + connectivityIPBlist, err := subnetConfConnectivity.subnetConnectivity.getIPBlocksList() if err != nil { return nil, nil, err } - otherSrcOrDstIPBlist, err := otherConfConnectivity.subnetConnectivity.getIPBlocksList(resizeSrc) + otherIPBlist, err := otherConfConnectivity.subnetConnectivity.getIPBlocksList() if err != nil { return nil, nil, err } - disjointIPblocks := common.DisjointIPBlocks(connectivitySrcOrDstIPBlist, otherSrcOrDstIPBlist) - - // copy configs + disjointIPblocks := common.DisjointIPBlocks(connectivityIPBlist, otherIPBlist) + // 2. copy configs and generates Nodes[] as per disjointIPblocks alignedConfig, err := subnetConfConnectivity.config.copyConfig(disjointIPblocks) if err != nil { return nil, nil, err @@ -238,14 +226,14 @@ func (subnetConfConnectivity *SubnetConfigConnectivity) alignSrcOrDstIPBlocks(ot if err != nil { return nil, nil, err } - - alignedConnectivity, err := subnetConfConnectivity.subnetConnectivity.actualAlignGivenIPBlists( - alignedConfig, disjointIPblocks, resizeSrc) + // 3. resize connections as per the new Nodes[] + alignedConnectivity, err := subnetConfConnectivity.subnetConnectivity.alignConnectionsGivenIPBlists( + alignedConfig, disjointIPblocks) if err != nil { return nil, nil, err } - alignedOtherConnectivity, err := otherConfConnectivity.subnetConnectivity.actualAlignGivenIPBlists( - otherAlignedConfig, disjointIPblocks, resizeSrc) + alignedOtherConnectivity, err := otherConfConnectivity.subnetConnectivity.alignConnectionsGivenIPBlists( + otherAlignedConfig, disjointIPblocks) if err != nil { return nil, nil, err } @@ -253,6 +241,16 @@ func (subnetConfConnectivity *SubnetConfigConnectivity) alignSrcOrDstIPBlocks(ot &SubnetConfigConnectivity{otherAlignedConfig, alignedOtherConnectivity}, err } +func (subnetConnectivity *SubnetConnectivityMap) alignConnectionsGivenIPBlists(config *CloudConfig, disjointIPblocks []*common.IPBlock) ( + alignedConnectivity SubnetConnectivityMap, err error) { + alignedConnectivitySrc, err := subnetConnectivity.actualAlignSrcOrDstGivenIPBlists(config, disjointIPblocks, true) + if err != nil { + return nil, err + } + alignedConnectivity, err = alignedConnectivitySrc.actualAlignSrcOrDstGivenIPBlists(config, disjointIPblocks, false) + return alignedConnectivity, nil +} + // aligned config: copies from old config everything but nodes, // nodes are resized by disjointIPblocks func (oldConfig *CloudConfig) copyConfig(disjointIPblocks []*common.IPBlock) (alignedConfig *CloudConfig, err error) { @@ -287,11 +285,10 @@ func resizeNodes(oldNodes []Node, disjointIPblocks []*common.IPBlock) (newNodes newNodes = append(newNodes, oldNode) } } - return newNodes, nil } -func (subnetConnectivity *SubnetConnectivityMap) actualAlignGivenIPBlists(config *CloudConfig, disjointIPblocks []*common.IPBlock, resizeSrc bool) ( +func (subnetConnectivity *SubnetConnectivityMap) actualAlignSrcOrDstGivenIPBlists(config *CloudConfig, disjointIPblocks []*common.IPBlock, resizeSrc bool) ( alignedConnectivity SubnetConnectivityMap, err error) { // goes over all sources of connections in connectivity // if src is external then for each IPBlock in disjointIPblocks copies dsts and connection type @@ -365,8 +362,8 @@ func findNodeWithCidr(configNodes []Node, cidr string) (Node, error) { return nil, fmt.Errorf(fmt.Sprintf("A node with cidr %v not found in conf", cidr)) // should not get here } -// get a list of IPBlocks of the src/dst of the connections -func (subnetConnectivity SubnetConnectivityMap) getIPBlocksList(getSrcList bool) (ipbList []*common.IPBlock, +// get a list of IPBlocks of the src and dst of the connections +func (subnetConnectivity SubnetConnectivityMap) getIPBlocksList() (ipbList []*common.IPBlock, err error) { ipbList = []*common.IPBlock{} err = nil @@ -375,13 +372,14 @@ func (subnetConnectivity SubnetConnectivityMap) getIPBlocksList(getSrcList bool) if conns.IsEmpty() { continue } - if getSrcList && src.IsExternal() { + if src.IsExternal() { ipBlock, err := externalNodeToIpBlock(src.(Node)) if err != nil { return nil, err } ipbList = append(ipbList, ipBlock) - } else if !getSrcList && dst.IsExternal() { + } + if dst.IsExternal() { ipBlock, err := externalNodeToIpBlock(dst.(Node)) if err != nil { return nil, err From 52647b219f50f772ab52ec19f9363b6aab6cbd0a Mon Sep 17 00:00:00 2001 From: shirim Date: Mon, 30 Oct 2023 14:38:21 +0200 Subject: [PATCH 29/42] temp debug prints --- pkg/vpcmodel/diffSubnets_test.go | 54 +++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/pkg/vpcmodel/diffSubnets_test.go b/pkg/vpcmodel/diffSubnets_test.go index 10a7fe3b6..1ae89bb1d 100644 --- a/pkg/vpcmodel/diffSubnets_test.go +++ b/pkg/vpcmodel/diffSubnets_test.go @@ -135,29 +135,59 @@ func configSimpleIPAndSubnetSubtract() (subnetConfigConn1, subnetConfigConn2 *Su } func TestSimpleIPAndSubnetSubtract(t *testing.T) { - subnetConfigConn1, subnetConfigConn2 := configSimpleIPAndSubnetSubtract() - res, _ := subnetConfigConn1.subnetConnectivity.getIntersectingConnections(subnetConfigConn2.subnetConnectivity) + cfgConn1, cfgConn2 := configSimpleIPAndSubnetSubtract() + res, _ := cfgConn1.subnetConnectivity.getIntersectingConnections(cfgConn2.subnetConnectivity) fmt.Printf(res) - newLines := strings.Count(res, "\n") + //newLines := strings.Count(res, "\n") // there should be 4 lines in cfg1SubtractCfg2Str - require.Equal(t, 4, newLines) - require.Contains(t, res, " and intersects") - require.Contains(t, res, " and intersects") - require.Contains(t, res, " and intersects") - require.Contains(t, res, " and intersects") - - alignedCfg1Conn, alignedCfg1Conn2, err := subnetConfigConn1.GetConnectivesWithSameIPBlocks(subnetConfigConn2) + //require.Equal(t, 4, newLines) + //require.Contains(t, res, " and intersects") + //require.Contains(t, res, " and intersects") + //require.Contains(t, res, " and intersects") + //require.Contains(t, res, " and intersects") + fmt.Printf("origin connectivites, before alignment\ncfg1:\n------\n") + cfgConn1.subnetConnectivity.PrintConnectivity() + fmt.Println("\ncfg2\n------") + cfgConn2.subnetConnectivity.PrintConnectivity() + alignedCfgConn1, alignedCfgConn2, err := cfgConn1.GetConnectivesWithSameIPBlocks(cfgConn2) if err != nil { fmt.Printf("err: %v\n", err.Error()) return } - cfg1SubCfg2 := alignedCfg1Conn.SubnetConnectivitySubtract(alignedCfg1Conn2) + fmt.Printf("connectivites after alignment\ncfg1:\n------\n") + alignedCfgConn1.subnetConnectivity.PrintConnectivity() + + fmt.Println("nodes in cfg1:") + for _, node := range alignedCfgConn1.config.Nodes { + fmt.Printf("\tnode: %v %v \n", node.Name(), node.Cidr()) + fmt.Println("\tconnections:") + if alignedConnection, ok := alignedCfgConn1.subnetConnectivity[node]; ok { + for dst, conn := range alignedConnection { + fmt.Printf("%v => %v %v\n", node.Name(), dst.Name(), conn) + } + } + } + + fmt.Println("nodes in cfg2:") + for _, node := range alignedCfgConn2.config.Nodes { + fmt.Printf("\tnode: %v %v \n", node.Name(), node.Cidr()) + fmt.Println("\tconnections:") + if alignedConnection, ok := alignedCfgConn2.subnetConnectivity[node]; ok { + for dst, conn := range alignedConnection { + fmt.Printf("\t\t%v => %v %v\n", node.Name(), dst.Name(), conn) + } + } + } + fmt.Println("\nconnection in cfg2\n------") + alignedCfgConn2.subnetConnectivity.PrintConnectivity() + + cfg1SubCfg2 := alignedCfgConn1.SubnetConnectivitySubtract(alignedCfgConn2) cfg1SubtractCfg2Str := cfg1SubCfg2.EnhancedString(true) fmt.Printf("cfg1SubCfg2:\n%v\n", cfg1SubtractCfg2Str) // todo should be equal to -- Public Internet [250.2.4.128/25] => subnet2 : missing connection // not clear why [1.2.3.0/30] lines are there // - //subnet2Subtract1 := subnetConfigConn2.SubnetConnectivitySubtract(subnetConfigConn1) + //subnet2Subtract1 := cfgConn2.SubnetConnectivitySubtract(cfgConn1) //subnet2Subtract1Str := subnet2Subtract1.EnhancedString(false) //fmt.Printf("subnet2Subtract1:\n%v\n", subnet2Subtract1Str) } From 840d9a62979336bad044fe8319b02dea1e6ff5b1 Mon Sep 17 00:00:00 2001 From: shirim Date: Mon, 30 Oct 2023 16:19:27 +0200 Subject: [PATCH 30/42] finalize unit test --- pkg/vpcmodel/diffSubnets_test.go | 76 +++++++++++++------------------- 1 file changed, 31 insertions(+), 45 deletions(-) diff --git a/pkg/vpcmodel/diffSubnets_test.go b/pkg/vpcmodel/diffSubnets_test.go index 1ae89bb1d..f06afd75c 100644 --- a/pkg/vpcmodel/diffSubnets_test.go +++ b/pkg/vpcmodel/diffSubnets_test.go @@ -102,14 +102,16 @@ func configSimpleIPAndSubnetSubtract() (subnetConfigConn1, subnetConfigConn2 *Su &mockSubnet{"10.2.20.0/22", "subnet2", nil}) cfg1.Nodes = append(cfg1.Nodes, &mockNetIntf{cidr: "1.2.3.0/30", name: "public1-1", isPublic: true}, - &mockNetIntf{cidr: "250.2.4.0/24", name: "public1-2", isPublic: true}) + &mockNetIntf{cidr: "250.2.4.0/24", name: "public1-2", isPublic: true}, + &mockNetIntf{cidr: "200.2.4.0/24", name: "public1-3", isPublic: true}) cfg2 := &CloudConfig{Nodes: []Node{}, NodeSets: []NodeSet{}} cfg2.NodeSets = append(cfg2.NodeSets, &mockSubnet{"10.1.20.0/22", "subnet1", nil}, &mockSubnet{"10.2.20.0/22", "subnet2", nil}) cfg2.Nodes = append(cfg2.Nodes, &mockNetIntf{cidr: "1.2.3.0/26", name: "public2-1", isPublic: true}, - &mockNetIntf{cidr: "250.2.4.0/30", name: "public2-2", isPublic: true}) + &mockNetIntf{cidr: "250.2.4.0/30", name: "public2-2", isPublic: true}, + &mockNetIntf{cidr: "200.2.4.0/24", name: "public1-3", isPublic: true}) // cfg1 cfg2 // and are comparable @@ -121,12 +123,16 @@ func configSimpleIPAndSubnetSubtract() (subnetConfigConn1, subnetConfigConn2 *Su subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.Nodes[0], cfg1.NodeSets[1], common.NewConnectionSet(true)) subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.Nodes[1], cfg1.NodeSets[1], common.NewConnectionSet(true)) subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[1], cfg1.Nodes[0], common.NewConnectionSet(true)) + subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.NodeSets[1], cfg1.Nodes[2], common.NewConnectionSet(true)) subnetConnMap2 := &VPCsubnetConnectivity{AllowedConnsCombined: NewSubnetConnectivityMap()} subnetConnMap2.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg2.Nodes[0], cfg2.NodeSets[0], common.NewConnectionSet(true)) subnetConnMap2.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg2.Nodes[0], cfg2.NodeSets[1], common.NewConnectionSet(true)) subnetConnMap2.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg2.Nodes[1], cfg2.NodeSets[1], common.NewConnectionSet(true)) subnetConnMap2.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg2.NodeSets[1], cfg2.Nodes[0], common.NewConnectionSet(true)) + connectionTCP := common.NewConnectionSet(false) + connectionTCP.AddTCPorUDPConn(common.ProtocolTCP, 0, 1000, 0, 443) + subnetConnMap2.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg2.NodeSets[1], cfg2.Nodes[2], connectionTCP) subnetConfigConn1 = &SubnetConfigConnectivity{cfg1, subnetConnMap1.AllowedConnsCombined} subnetConfigConn2 = &SubnetConfigConnectivity{cfg2, subnetConnMap2.AllowedConnsCombined} @@ -136,58 +142,38 @@ func configSimpleIPAndSubnetSubtract() (subnetConfigConn1, subnetConfigConn2 *Su func TestSimpleIPAndSubnetSubtract(t *testing.T) { cfgConn1, cfgConn2 := configSimpleIPAndSubnetSubtract() + + // functionality not used res, _ := cfgConn1.subnetConnectivity.getIntersectingConnections(cfgConn2.subnetConnectivity) fmt.Printf(res) - //newLines := strings.Count(res, "\n") - // there should be 4 lines in cfg1SubtractCfg2Str - //require.Equal(t, 4, newLines) - //require.Contains(t, res, " and intersects") - //require.Contains(t, res, " and intersects") - //require.Contains(t, res, " and intersects") - //require.Contains(t, res, " and intersects") - fmt.Printf("origin connectivites, before alignment\ncfg1:\n------\n") - cfgConn1.subnetConnectivity.PrintConnectivity() - fmt.Println("\ncfg2\n------") - cfgConn2.subnetConnectivity.PrintConnectivity() + newLines := strings.Count(res, "\n") + //there should be 4 lines in cfg1SubtractCfg2Str + require.Equal(t, 4, newLines) + require.Contains(t, res, " and intersects") + require.Contains(t, res, " and intersects") + require.Contains(t, res, " and intersects") + require.Contains(t, res, " and intersects") + alignedCfgConn1, alignedCfgConn2, err := cfgConn1.GetConnectivesWithSameIPBlocks(cfgConn2) if err != nil { fmt.Printf("err: %v\n", err.Error()) + require.Equal(t, err, nil) return } - fmt.Printf("connectivites after alignment\ncfg1:\n------\n") - alignedCfgConn1.subnetConnectivity.PrintConnectivity() - - fmt.Println("nodes in cfg1:") - for _, node := range alignedCfgConn1.config.Nodes { - fmt.Printf("\tnode: %v %v \n", node.Name(), node.Cidr()) - fmt.Println("\tconnections:") - if alignedConnection, ok := alignedCfgConn1.subnetConnectivity[node]; ok { - for dst, conn := range alignedConnection { - fmt.Printf("%v => %v %v\n", node.Name(), dst.Name(), conn) - } - } - } - - fmt.Println("nodes in cfg2:") - for _, node := range alignedCfgConn2.config.Nodes { - fmt.Printf("\tnode: %v %v \n", node.Name(), node.Cidr()) - fmt.Println("\tconnections:") - if alignedConnection, ok := alignedCfgConn2.subnetConnectivity[node]; ok { - for dst, conn := range alignedConnection { - fmt.Printf("\t\t%v => %v %v\n", node.Name(), dst.Name(), conn) - } - } - } - fmt.Println("\nconnection in cfg2\n------") - alignedCfgConn2.subnetConnectivity.PrintConnectivity() + // verified bit by bit :-) cfg1SubCfg2 := alignedCfgConn1.SubnetConnectivitySubtract(alignedCfgConn2) cfg1SubtractCfg2Str := cfg1SubCfg2.EnhancedString(true) fmt.Printf("cfg1SubCfg2:\n%v\n", cfg1SubtractCfg2Str) - // todo should be equal to -- Public Internet [250.2.4.128/25] => subnet2 : missing connection - // not clear why [1.2.3.0/30] lines are there - // - //subnet2Subtract1 := cfgConn2.SubnetConnectivitySubtract(cfgConn1) - //subnet2Subtract1Str := subnet2Subtract1.EnhancedString(false) - //fmt.Printf("subnet2Subtract1:\n%v\n", subnet2Subtract1Str) + newLines = strings.Count(cfg1SubtractCfg2Str, "\n") + // there should be 6 lines in subnet1Subtract2Str + require.Equal(t, 7, newLines) + require.Contains(t, cfg1SubtractCfg2Str, "-- Public Internet [250.2.4.4/30] => subnet2 : missing connection") + require.Contains(t, cfg1SubtractCfg2Str, "-- Public Internet [250.2.4.4/30] => subnet2 : missing connection") + require.Contains(t, cfg1SubtractCfg2Str, "-- Public Internet [250.2.4.64/26] => subnet2 : missing connection") + require.Contains(t, cfg1SubtractCfg2Str, "-- Public Internet [250.2.4.128/25] => subnet2 : missing connection") + require.Contains(t, cfg1SubtractCfg2Str, "-- Public Internet [250.2.4.8/29] => subnet2 : missing connection") + require.Contains(t, cfg1SubtractCfg2Str, "-- Public Internet [250.2.4.32/27] => subnet2 : missing connection") + require.Contains(t, cfg1SubtractCfg2Str, "-- subnet2 => Public Internet [200.2.4.0/24] : changed connection "+ + "protocol: TCP src-ports: 1-1000 dst-ports: 444-65535; protocol: TCP src-ports: 1001-65535; protocol: UDP,ICMP") } From 7b8da690f18bdd5c1537436abcf3f8ea666b554a Mon Sep 17 00:00:00 2001 From: shirim Date: Mon, 30 Oct 2023 17:00:11 +0200 Subject: [PATCH 31/42] lint comments --- pkg/vpcmodel/diffSubnets_test.go | 12 ++--- pkg/vpcmodel/semanticDiffSubnets.go | 78 +++++++++++++---------------- 2 files changed, 41 insertions(+), 49 deletions(-) diff --git a/pkg/vpcmodel/diffSubnets_test.go b/pkg/vpcmodel/diffSubnets_test.go index 5007261c6..74763815e 100644 --- a/pkg/vpcmodel/diffSubnets_test.go +++ b/pkg/vpcmodel/diffSubnets_test.go @@ -114,10 +114,10 @@ func configSimpleIPAndSubnetSubtract() (subnetConfigConn1, subnetConfigConn2 *Su &mockNetIntf{cidr: "200.2.4.0/24", name: "public1-3", isPublic: true}) // cfg1 cfg2 - // and are comparable - // and are comparable - // and are comparable - // and are comparable + // and are comparable + // and are comparable + // and are comparable + // and are comparable subnetConnMap1 := &VPCsubnetConnectivity{AllowedConnsCombined: NewSubnetConnectivityMap()} subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.Nodes[0], cfg1.NodeSets[0], common.NewConnectionSet(true)) subnetConnMap1.AllowedConnsCombined.updateAllowedSubnetConnsMap(cfg1.Nodes[0], cfg1.NodeSets[1], common.NewConnectionSet(true)) @@ -145,9 +145,9 @@ func TestSimpleIPAndSubnetSubtract(t *testing.T) { // functionality not used res, _ := cfgConn1.subnetConnectivity.getIntersectingConnections(cfgConn2.subnetConnectivity) - fmt.Printf(res) + fmt.Printf("%v", res) newLines := strings.Count(res, "\n") - //there should be 4 lines in cfg1SubtractCfg2Str + // there should be 4 lines in cfg1SubtractCfg2Str require.Equal(t, 4, newLines) require.Contains(t, res, " and intersects") require.Contains(t, res, " and intersects") diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index d830afc16..28bc96eff 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -58,7 +58,7 @@ func (configs ConfigsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets return nil, err } - //2. Computes delta in both directions + // 2. Computes delta in both directions subnetConfigConn1 := &SubnetConfigConnectivity{configs.config1, subnetsConn1.AllowedConnsCombined} subnetConfigConn2 := &SubnetConfigConnectivity{configs.config2, @@ -85,12 +85,11 @@ func (c *VPCConfig) getEndpointElemInOtherConfig(other *VPCConfig, ep EndpointEl if ep.IsExternal() { nodeSameCidr, _ := findNodeWithCidr(other.Nodes, ep.(Node).Cidr()) return nodeSameCidr - } else { - for _, nodeSet := range other.NodeSets { - if nodeSet.Name() == ep.Name() { - res := EndpointElem(nodeSet) - return res - } + } + for _, nodeSet := range other.NodeSets { + if nodeSet.Name() == ep.Name() { + res := EndpointElem(nodeSet) + return res } } return nil @@ -114,12 +113,7 @@ func (subnetConfConnectivity *SubnetConfigConnectivity) SubnetConnectivitySubtra if srcInOther != nil && dstInOther != nil { if otherSrc, ok := other.subnetConnectivity[srcInOther]; ok { if otherSrcDst, ok := otherSrc[dstInOther]; ok { - // ToDo: current missing stateful: - // todo 1. is the delta connection stateful - // todo 2. connectionProperties is identical but conn stateful while other is not - // the 2nd item can be computed by conns.Subtract, with enhancement to relevant structure - // the 1st can not since we do not know where exactly the statefullness came from - // we might need to repeat the statefullness computation for the delta connection + // ToDo: https://github.com/np-guard/vpc-network-config-analyzer/issues/199 subtractConn := conns.Subtract(otherSrcDst) if subtractConn.IsEmpty() { continue // no diff @@ -204,7 +198,7 @@ func diffDescription(diff DiffType) string { // (src2, dst2) of subnet2Connectivity s.t. if src1 and src2 (dst1 and dst2) are both external then // they are either equal or disjoint func (subnetConfConnectivity *SubnetConfigConnectivity) GetConnectivesWithSameIPBlocks(otherConfConnectivity *SubnetConfigConnectivity) ( - alignedConnectivityConfig, alignedOtherConnectivityConfig *SubnetConfigConnectivity, err error) { + alignedConnectivityConfig, alignedOtherConnectivityConfig *SubnetConfigConnectivity, myErr error) { // 1. computes new set of external nodes (only type of nodes here) in cfg1 and cfg2 // does so by computing disjoint block between src+dst ipBlocks in cfg1 and in cfg2 // the new set of external nodes is determined based on them @@ -238,7 +232,7 @@ func (subnetConfConnectivity *SubnetConfigConnectivity) GetConnectivesWithSameIP return nil, nil, err } return &SubnetConfigConnectivity{alignedConfig, alignedConnectivity}, - &SubnetConfigConnectivity{otherAlignedConfig, alignedOtherConnectivity}, err + &SubnetConfigConnectivity{otherAlignedConfig, alignedOtherConnectivity}, myErr } func (subnetConnectivity *SubnetConnectivityMap) alignConnectionsGivenIPBlists(config *VPCConfig, disjointIPblocks []*common.IPBlock) ( @@ -248,16 +242,16 @@ func (subnetConnectivity *SubnetConnectivityMap) alignConnectionsGivenIPBlists(c return nil, err } alignedConnectivity, err = alignedConnectivitySrc.actualAlignSrcOrDstGivenIPBlists(config, disjointIPblocks, false) - return alignedConnectivity, nil + return alignedConnectivity, err } // aligned config: copies from old config everything but nodes, // nodes are resized by disjointIPblocks -func (oldConfig *VPCConfig) copyConfig(disjointIPblocks []*common.IPBlock) (alignedConfig *VPCConfig, err error) { +func (c *VPCConfig) copyConfig(disjointIPblocks []*common.IPBlock) (alignedConfig *VPCConfig, err error) { // copy config - alignedConfig = oldConfig + alignedConfig = c // nodes - external addresses - are resized - alignedConfig.Nodes, err = resizeNodes(oldConfig.Nodes, disjointIPblocks) + alignedConfig.Nodes, err = resizeNodes(c.Nodes, disjointIPblocks) return alignedConfig, err } @@ -267,15 +261,15 @@ func resizeNodes(oldNodes []Node, disjointIPblocks []*common.IPBlock) (newNodes // if a disjoint block is contained in an old oldNode - create external oldNode and add it // if no disjoint block is contained in an old oldNode - add the old oldNode as is for _, oldNode := range oldNodes { - nodeIpBlock, err := common.NewIPBlock(oldNode.Cidr(), nil) + nodeIPBlock, err := common.NewIPBlock(oldNode.Cidr(), nil) if err != nil { return nil, err } disjointContained := false - for _, disjointIpBlock := range disjointIPblocks { - if disjointIpBlock.ContainedIn(nodeIpBlock) { + for _, disjointIPBlock := range disjointIPblocks { + if disjointIPBlock.ContainedIn(nodeIPBlock) { disjointContained = true - for _, thisCidr := range disjointIpBlock.ToCidrList() { + for _, thisCidr := range disjointIPBlock.ToCidrList() { newNode := NewExternalNodeForCidr(thisCidr) newNodes = append(newNodes, newNode) } @@ -310,12 +304,11 @@ func (subnetConnectivity *SubnetConnectivityMap) actualAlignSrcOrDstGivenIPBlist } // the resizing element is external - go over all ipBlock and allocates the connection // if the ipBlock is contained in the original src/dst - var origIpBlock *common.IPBlock - var err error + var origIPBlock *common.IPBlock if resizeSrc { - origIpBlock, err = externalNodeToIpBlock(src.(Node)) + origIPBlock, err = externalNodeToIPBlock(src.(Node)) } else { - origIpBlock, err = externalNodeToIpBlock(dst.(Node)) + origIPBlock, err = externalNodeToIPBlock(dst.(Node)) } if err != nil { return nil, err @@ -323,7 +316,7 @@ func (subnetConnectivity *SubnetConnectivityMap) actualAlignSrcOrDstGivenIPBlist origBlockContainsIPBlocks := false for _, ipBlock := range disjointIPblocks { // 1. get ipBlock of resized index (src/dst) - if !ipBlock.ContainedIn(origIpBlock) { // ipBlock not relevant here + if !ipBlock.ContainedIn(origIPBlock) { // ipBlock not relevant here continue } origBlockContainsIPBlocks = true @@ -359,28 +352,28 @@ func findNodeWithCidr(configNodes []Node, cidr string) (Node, error) { return node, nil } } - return nil, fmt.Errorf(fmt.Sprintf("A node with cidr %v not found in conf", cidr)) // should not get here + return nil, fmt.Errorf("%s", fmt.Sprintf("A node with cidr %v not found in conf", cidr)) // should not get here } // get a list of IPBlocks of the src and dst of the connections func (subnetConnectivity SubnetConnectivityMap) getIPBlocksList() (ipbList []*common.IPBlock, - err error) { + myErr error) { ipbList = []*common.IPBlock{} - err = nil + myErr = nil for src, endpointConns := range subnetConnectivity { for dst, conns := range endpointConns { if conns.IsEmpty() { continue } if src.IsExternal() { - ipBlock, err := externalNodeToIpBlock(src.(Node)) + ipBlock, err := externalNodeToIPBlock(src.(Node)) if err != nil { return nil, err } ipbList = append(ipbList, ipBlock) } if dst.IsExternal() { - ipBlock, err := externalNodeToIpBlock(dst.(Node)) + ipBlock, err := externalNodeToIPBlock(dst.(Node)) if err != nil { return nil, err } @@ -388,10 +381,10 @@ func (subnetConnectivity SubnetConnectivityMap) getIPBlocksList() (ipbList []*co } } } - return ipbList, err + return ipbList, myErr } -func externalNodeToIpBlock(external Node) (ipBlock *common.IPBlock, err error) { +func externalNodeToIPBlock(external Node) (ipBlock *common.IPBlock, err error) { ipBlock, err = common.NewIPBlock(external.Cidr(), []string{}) if err != nil { return nil, err @@ -424,9 +417,9 @@ func (subnetConnectivity SubnetConnectivityMap) getIntersectingConnections(other } myEp := &ConnectionEnd{src, dst} otherEp := &ConnectionEnd{otherSrc, otherDst} - intersecting, err := myEp.connectionsIntersecting(otherEp) - if err != nil { - return areIntersecting, err + intersecting, err1 := myEp.connectionsIntersecting(otherEp) + if err1 != nil { + return areIntersecting, err1 } if intersecting { areIntersecting += fmt.Sprintf("<%v, %v> and <%v, %v> intersects\n", src.Name(), dst.Name(), otherSrc.Name(), otherDst.Name()) @@ -475,20 +468,19 @@ func pairEpsComparable(myEp, otherEp EndpointElem) (bool, error) { if isMySubnet { // implies that isOtherSubnet as well if mySubnet.Name() == otherSubnet.Name() { return true, nil - } else { - return false, nil } + return false, nil } // if we got here then both eps refer to external IP - myIpBlock, err := common.NewIPBlock(myExternal.Cidr(), []string{}) + myIPBlock, err := common.NewIPBlock(myExternal.Cidr(), []string{}) if err != nil { return false, err } - otherIpBlock, err := common.NewIPBlock(otherExternal.Cidr(), []string{}) + otherIPBlock, err := common.NewIPBlock(otherExternal.Cidr(), []string{}) if err != nil { return false, err } - if !myIpBlock.Equal(otherIpBlock) && !myIpBlock.Intersection(otherIpBlock).Empty() { + if !myIPBlock.Equal(otherIPBlock) && !myIPBlock.Intersection(otherIPBlock).Empty() { return true, nil } return false, nil From 562efa473adbcef98236790d8dac468c7d1d1b46 Mon Sep 17 00:00:00 2001 From: shirim Date: Mon, 30 Oct 2023 17:51:22 +0200 Subject: [PATCH 32/42] commented non used code --- pkg/vpcmodel/diffSubnets_test.go | 22 +-- pkg/vpcmodel/semanticDiffSubnets.go | 216 ++++++++++++++-------------- 2 files changed, 119 insertions(+), 119 deletions(-) diff --git a/pkg/vpcmodel/diffSubnets_test.go b/pkg/vpcmodel/diffSubnets_test.go index 74763815e..df602e97d 100644 --- a/pkg/vpcmodel/diffSubnets_test.go +++ b/pkg/vpcmodel/diffSubnets_test.go @@ -143,16 +143,16 @@ func configSimpleIPAndSubnetSubtract() (subnetConfigConn1, subnetConfigConn2 *Su func TestSimpleIPAndSubnetSubtract(t *testing.T) { cfgConn1, cfgConn2 := configSimpleIPAndSubnetSubtract() - // functionality not used - res, _ := cfgConn1.subnetConnectivity.getIntersectingConnections(cfgConn2.subnetConnectivity) - fmt.Printf("%v", res) - newLines := strings.Count(res, "\n") - // there should be 4 lines in cfg1SubtractCfg2Str - require.Equal(t, 4, newLines) - require.Contains(t, res, " and intersects") - require.Contains(t, res, " and intersects") - require.Contains(t, res, " and intersects") - require.Contains(t, res, " and intersects") + //// functionality not used + //res, _ := cfgConn1.subnetConnectivity.getIntersectingConnections(cfgConn2.subnetConnectivity) + //fmt.Printf("%v", res) + //newLines := strings.Count(res, "\n") + //// there should be 4 lines in cfg1SubtractCfg2Str + //require.Equal(t, 4, newLines) + //require.Contains(t, res, " and intersects") + //require.Contains(t, res, " and intersects") + //require.Contains(t, res, " and intersects") + //require.Contains(t, res, " and intersects") alignedCfgConn1, alignedCfgConn2, err := cfgConn1.GetConnectivesWithSameIPBlocks(cfgConn2) if err != nil { @@ -165,7 +165,7 @@ func TestSimpleIPAndSubnetSubtract(t *testing.T) { cfg1SubCfg2 := alignedCfgConn1.SubnetConnectivitySubtract(alignedCfgConn2) cfg1SubtractCfg2Str := cfg1SubCfg2.EnhancedString(true) fmt.Printf("cfg1SubCfg2:\n%v\n", cfg1SubtractCfg2Str) - newLines = strings.Count(cfg1SubtractCfg2Str, "\n") + newLines := strings.Count(cfg1SubtractCfg2Str, "\n") // there should be 6 lines in subnet1Subtract2Str require.Equal(t, 7, newLines) require.Contains(t, cfg1SubtractCfg2Str, "-- Public Internet [250.2.4.4/30] => subnet2 : missing connection") diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 28bc96eff..6de3e765b 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -396,111 +396,111 @@ func externalNodeToIPBlock(external Node) (ipBlock *common.IPBlock, err error) { // todo: it seems that the code is redundant; yet we keep it with its unit test in case we'll decide // todo: to use it in the future // todo: it return a string describing the intersecting connections for the unit test -func (subnetConnectivity SubnetConnectivityMap) getIntersectingConnections(other SubnetConnectivityMap) (areIntersecting string, - err error) { - err = nil - for src, endpointConns := range subnetConnectivity { - for dst, conns := range endpointConns { - if (!src.IsExternal() && !dst.IsExternal()) || conns.IsEmpty() { - continue // nothing to do here - } - for otherSrc, otherEndpointConns := range other { - for otherDst, otherConns := range otherEndpointConns { - if otherConns.IsEmpty() { - continue - } - bothSrcExt := src.IsExternal() && otherSrc.IsExternal() - bothDstExt := dst.IsExternal() && otherDst.IsExternal() - if (!bothSrcExt && !bothDstExt) || - otherConns.IsEmpty() { - continue // nothing to compare to here - } - myEp := &ConnectionEnd{src, dst} - otherEp := &ConnectionEnd{otherSrc, otherDst} - intersecting, err1 := myEp.connectionsIntersecting(otherEp) - if err1 != nil { - return areIntersecting, err1 - } - if intersecting { - areIntersecting += fmt.Sprintf("<%v, %v> and <%v, %v> intersects\n", src.Name(), dst.Name(), otherSrc.Name(), otherDst.Name()) - } - } - } - } - } - return areIntersecting, err -} - -// two connections s.t. each contains at least one external end are comparable if either: -// both src and dst in both connections are external and they both intersect -// one end (src/dst) are external in both and intersects and the other (dst/src) are the same subnet -// two connections s.t. each contains at least one external end are comparable if either: -// both src and dst in both connections are external and they both intersect but not equal -// or end (src/dst) are external in both and intersects and the other (dst/src) are the same subnet -func (myConnEnd *ConnectionEnd) connectionsIntersecting(otherConnEnd *ConnectionEnd) (bool, error) { - srcComparable, err := pairEpsComparable(myConnEnd.src, otherConnEnd.src) - if err != nil { - return false, err - } - if !srcComparable { - return false, nil - } - dstComparable, err := pairEpsComparable(myConnEnd.dst, otherConnEnd.dst) - if err != nil { - return false, err - } - if !dstComparable { - return false, err - } - return true, nil -} - -// checks if two eps refers to the same subnet or -// refers to intersecting external addresses -func pairEpsComparable(myEp, otherEp EndpointElem) (bool, error) { - mySubnet, isMySubnet := myEp.(NodeSet) - otherSubnet, isOtherSubnet := otherEp.(NodeSet) - myExternal, isMyExternal := myEp.(Node) - otherExternal, isOtherExternal := otherEp.(Node) - if (isMySubnet != isOtherSubnet) || (isMyExternal != isOtherExternal) { - return false, nil - } - if isMySubnet { // implies that isOtherSubnet as well - if mySubnet.Name() == otherSubnet.Name() { - return true, nil - } - return false, nil - } - // if we got here then both eps refer to external IP - myIPBlock, err := common.NewIPBlock(myExternal.Cidr(), []string{}) - if err != nil { - return false, err - } - otherIPBlock, err := common.NewIPBlock(otherExternal.Cidr(), []string{}) - if err != nil { - return false, err - } - if !myIPBlock.Equal(otherIPBlock) && !myIPBlock.Intersection(otherIPBlock).Empty() { - return true, nil - } - return false, nil -} - -// todo: instead of adding functionality to grouping, I plan to have more generic connectivity items that will be grouped -// encode the SubnetsDiff into this generic item as well as the other entities we are grouping -// and then decode in the printing -// the idea is to use instead of *common.ConnectionSet in the grouped entity a string which will encode the connection -// and also the diff where relevant -// this will requires some rewriting in the existing grouping functionality and the way it provides -// service to subnetsConnectivity and nodesConnectivity - -func (subnetConnectivity *SubnetConnectivityMap) PrintConnectivity() { - for src, endpointConns := range *subnetConnectivity { - for dst, conns := range endpointConns { - if conns.IsEmpty() { - continue - } - fmt.Printf("\t%v => %v %v\n", src.Name(), dst.Name(), conns.EnhancedString()) - } - } -} +//func (subnetConnectivity SubnetConnectivityMap) getIntersectingConnections(other SubnetConnectivityMap) (areIntersecting string, +// err error) { +// err = nil +// for src, endpointConns := range subnetConnectivity { +// for dst, conns := range endpointConns { +// if (!src.IsExternal() && !dst.IsExternal()) || conns.IsEmpty() { +// continue // nothing to do here +// } +// for otherSrc, otherEndpointConns := range other { +// for otherDst, otherConns := range otherEndpointConns { +// if otherConns.IsEmpty() { +// continue +// } +// bothSrcExt := src.IsExternal() && otherSrc.IsExternal() +// bothDstExt := dst.IsExternal() && otherDst.IsExternal() +// if (!bothSrcExt && !bothDstExt) || +// otherConns.IsEmpty() { +// continue // nothing to compare to here +// } +// myEp := &ConnectionEnd{src, dst} +// otherEp := &ConnectionEnd{otherSrc, otherDst} +// intersecting, err1 := myEp.connectionsIntersecting(otherEp) +// if err1 != nil { +// return areIntersecting, err1 +// } +// if intersecting { +// areIntersecting += fmt.Sprintf("<%v, %v> and <%v, %v> intersects\n", src.Name(), dst.Name(), otherSrc.Name(), otherDst.Name()) +// } +// } +// } +// } +// } +// return areIntersecting, err +//} +// +//// two connections s.t. each contains at least one external end are comparable if either: +//// both src and dst in both connections are external and they both intersect +//// one end (src/dst) are external in both and intersects and the other (dst/src) are the same subnet +//// two connections s.t. each contains at least one external end are comparable if either: +//// both src and dst in both connections are external and they both intersect but not equal +//// or end (src/dst) are external in both and intersects and the other (dst/src) are the same subnet +//func (myConnEnd *ConnectionEnd) connectionsIntersecting(otherConnEnd *ConnectionEnd) (bool, error) { +// srcComparable, err := pairEpsComparable(myConnEnd.src, otherConnEnd.src) +// if err != nil { +// return false, err +// } +// if !srcComparable { +// return false, nil +// } +// dstComparable, err := pairEpsComparable(myConnEnd.dst, otherConnEnd.dst) +// if err != nil { +// return false, err +// } +// if !dstComparable { +// return false, err +// } +// return true, nil +//} +// +//// checks if two eps refers to the same subnet or +//// refers to intersecting external addresses +//func pairEpsComparable(myEp, otherEp EndpointElem) (bool, error) { +// mySubnet, isMySubnet := myEp.(NodeSet) +// otherSubnet, isOtherSubnet := otherEp.(NodeSet) +// myExternal, isMyExternal := myEp.(Node) +// otherExternal, isOtherExternal := otherEp.(Node) +// if (isMySubnet != isOtherSubnet) || (isMyExternal != isOtherExternal) { +// return false, nil +// } +// if isMySubnet { // implies that isOtherSubnet as well +// if mySubnet.Name() == otherSubnet.Name() { +// return true, nil +// } +// return false, nil +// } +// // if we got here then both eps refer to external IP +// myIPBlock, err := common.NewIPBlock(myExternal.Cidr(), []string{}) +// if err != nil { +// return false, err +// } +// otherIPBlock, err := common.NewIPBlock(otherExternal.Cidr(), []string{}) +// if err != nil { +// return false, err +// } +// if !myIPBlock.Equal(otherIPBlock) && !myIPBlock.Intersection(otherIPBlock).Empty() { +// return true, nil +// } +// return false, nil +//} +// +//// todo: instead of adding functionality to grouping, I plan to have more generic connectivity items that will be grouped +//// encode the SubnetsDiff into this generic item as well as the other entities we are grouping +//// and then decode in the printing +//// the idea is to use instead of *common.ConnectionSet in the grouped entity a string which will encode the connection +//// and also the diff where relevant +//// this will requires some rewriting in the existing grouping functionality and the way it provides +//// service to subnetsConnectivity and nodesConnectivity +// +//func (subnetConnectivity *SubnetConnectivityMap) PrintConnectivity() { +// for src, endpointConns := range *subnetConnectivity { +// for dst, conns := range endpointConns { +// if conns.IsEmpty() { +// continue +// } +// fmt.Printf("\t%v => %v %v\n", src.Name(), dst.Name(), conns.EnhancedString()) +// } +// } +//} From ee8bc55036d9c5d72ca951c70a2c4fecfc2b5843 Mon Sep 17 00:00:00 2001 From: shirim Date: Mon, 30 Oct 2023 18:10:48 +0200 Subject: [PATCH 33/42] lint comments --- pkg/vpcmodel/diffSubnets_test.go | 12 ----- pkg/vpcmodel/semanticDiffSubnets.go | 82 ++++++++++++++++------------- 2 files changed, 44 insertions(+), 50 deletions(-) diff --git a/pkg/vpcmodel/diffSubnets_test.go b/pkg/vpcmodel/diffSubnets_test.go index df602e97d..8159c376c 100644 --- a/pkg/vpcmodel/diffSubnets_test.go +++ b/pkg/vpcmodel/diffSubnets_test.go @@ -142,18 +142,6 @@ func configSimpleIPAndSubnetSubtract() (subnetConfigConn1, subnetConfigConn2 *Su func TestSimpleIPAndSubnetSubtract(t *testing.T) { cfgConn1, cfgConn2 := configSimpleIPAndSubnetSubtract() - - //// functionality not used - //res, _ := cfgConn1.subnetConnectivity.getIntersectingConnections(cfgConn2.subnetConnectivity) - //fmt.Printf("%v", res) - //newLines := strings.Count(res, "\n") - //// there should be 4 lines in cfg1SubtractCfg2Str - //require.Equal(t, 4, newLines) - //require.Contains(t, res, " and intersects") - //require.Contains(t, res, " and intersects") - //require.Contains(t, res, " and intersects") - //require.Contains(t, res, " and intersects") - alignedCfgConn1, alignedCfgConn2, err := cfgConn1.GetConnectivesWithSameIPBlocks(cfgConn2) if err != nil { fmt.Printf("err: %v\n", err.Error()) diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 6de3e765b..76f930687 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -42,11 +42,6 @@ type diffBetweenSubnets struct { GroupedSubnet1Minus1 *GroupConnLines } -type ConnectionEnd struct { - src EndpointElem - dst EndpointElem -} - func (configs ConfigsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets, error) { // 1. compute connectivity for each of the subnets subnetsConn1, err := configs.config1.GetSubnetsConnectivity(true, false) @@ -282,7 +277,8 @@ func resizeNodes(oldNodes []Node, disjointIPblocks []*common.IPBlock) (newNodes return newNodes, nil } -func (subnetConnectivity *SubnetConnectivityMap) actualAlignSrcOrDstGivenIPBlists(config *VPCConfig, disjointIPblocks []*common.IPBlock, resizeSrc bool) ( +func (subnetConnectivity *SubnetConnectivityMap) actualAlignSrcOrDstGivenIPBlists(config *VPCConfig, + disjointIPblocks []*common.IPBlock, resizeSrc bool) ( alignedConnectivity SubnetConnectivityMap, err error) { // goes over all sources of connections in connectivity // if src is external then for each IPBlock in disjointIPblocks copies dsts and connection type @@ -313,36 +309,42 @@ func (subnetConnectivity *SubnetConnectivityMap) actualAlignSrcOrDstGivenIPBlist if err != nil { return nil, err } - origBlockContainsIPBlocks := false - for _, ipBlock := range disjointIPblocks { - // 1. get ipBlock of resized index (src/dst) - if !ipBlock.ContainedIn(origIPBlock) { // ipBlock not relevant here - continue - } - origBlockContainsIPBlocks = true - cidrList := ipBlock.ToCidrList() - var nodeOfCidr Node - for _, cidr := range cidrList { - nodeOfCidr, err = findNodeWithCidr(config.Nodes, cidr) - if err != nil { - return nil, err - } - if resizeSrc { - if _, ok := alignedConnectivity[nodeOfCidr]; !ok { - alignedConnectivity[nodeOfCidr] = map[EndpointElem]*common.ConnectionSet{} - } - alignedConnectivity[nodeOfCidr][dst] = conns - } else { - alignedConnectivity[src][nodeOfCidr] = conns - } - } + err = config.addIPBlockToConnectivityMap(disjointIPblocks, origIPBlock, alignedConnectivity, src, dst, conns, resizeSrc) + } + } + return alignedConnectivity, err +} + +func (c *VPCConfig) addIPBlockToConnectivityMap(disjointIPblocks []*common.IPBlock, + origIPBlock *common.IPBlock, alignedConnectivity map[EndpointElem]map[EndpointElem]*common.ConnectionSet, + src, dst EndpointElem, conns *common.ConnectionSet, resizeSrc bool) error { + origBlockContainsIPBlocks := false + for _, ipBlock := range disjointIPblocks { + // 1. get ipBlock of resized index (src/dst) + if !ipBlock.ContainedIn(origIPBlock) { // ipBlock not relevant here + continue + } + origBlockContainsIPBlocks = true + cidrList := ipBlock.ToCidrList() + for _, cidr := range cidrList { + nodeOfCidr, err := findNodeWithCidr(c.Nodes, cidr) + if err != nil { + return err } - if !origBlockContainsIPBlocks { // keep as origin - alignedConnectivity[src][dst] = conns + if resizeSrc { + if _, ok := alignedConnectivity[nodeOfCidr]; !ok { + alignedConnectivity[nodeOfCidr] = map[EndpointElem]*common.ConnectionSet{} + } + alignedConnectivity[nodeOfCidr][dst] = conns + } else { + alignedConnectivity[src][nodeOfCidr] = conns } } } - return alignedConnectivity, err + if !origBlockContainsIPBlocks { // keep as origin + alignedConnectivity[src][dst] = conns + } + return nil } // gets node with given cidr @@ -396,7 +398,11 @@ func externalNodeToIPBlock(external Node) (ipBlock *common.IPBlock, err error) { // todo: it seems that the code is redundant; yet we keep it with its unit test in case we'll decide // todo: to use it in the future // todo: it return a string describing the intersecting connections for the unit test -//func (subnetConnectivity SubnetConnectivityMap) getIntersectingConnections(other SubnetConnectivityMap) (areIntersecting string, +// type ConnectionEnd struct { +// src EndpointElem +// dst EndpointElem +// } +// func (subnetConnectivity SubnetConnectivityMap) getIntersectingConnections(other SubnetConnectivityMap) (areIntersecting string, // err error) { // err = nil // for src, endpointConns := range subnetConnectivity { @@ -437,7 +443,7 @@ func externalNodeToIPBlock(external Node) (ipBlock *common.IPBlock, err error) { //// two connections s.t. each contains at least one external end are comparable if either: //// both src and dst in both connections are external and they both intersect but not equal //// or end (src/dst) are external in both and intersects and the other (dst/src) are the same subnet -//func (myConnEnd *ConnectionEnd) connectionsIntersecting(otherConnEnd *ConnectionEnd) (bool, error) { +// func (myConnEnd *ConnectionEnd) connectionsIntersecting(otherConnEnd *ConnectionEnd) (bool, error) { // srcComparable, err := pairEpsComparable(myConnEnd.src, otherConnEnd.src) // if err != nil { // return false, err @@ -453,11 +459,11 @@ func externalNodeToIPBlock(external Node) (ipBlock *common.IPBlock, err error) { // return false, err // } // return true, nil -//} +// } // //// checks if two eps refers to the same subnet or //// refers to intersecting external addresses -//func pairEpsComparable(myEp, otherEp EndpointElem) (bool, error) { +// func pairEpsComparable(myEp, otherEp EndpointElem) (bool, error) { // mySubnet, isMySubnet := myEp.(NodeSet) // otherSubnet, isOtherSubnet := otherEp.(NodeSet) // myExternal, isMyExternal := myEp.(Node) @@ -494,7 +500,7 @@ func externalNodeToIPBlock(external Node) (ipBlock *common.IPBlock, err error) { //// this will requires some rewriting in the existing grouping functionality and the way it provides //// service to subnetsConnectivity and nodesConnectivity // -//func (subnetConnectivity *SubnetConnectivityMap) PrintConnectivity() { +// func (subnetConnectivity *SubnetConnectivityMap) PrintConnectivity() { // for src, endpointConns := range *subnetConnectivity { // for dst, conns := range endpointConns { // if conns.IsEmpty() { @@ -503,4 +509,4 @@ func externalNodeToIPBlock(external Node) (ipBlock *common.IPBlock, err error) { // fmt.Printf("\t%v => %v %v\n", src.Name(), dst.Name(), conns.EnhancedString()) // } // } -//} +// } From 0223c9c6378ba1d23d3d770d075fc5d55fb131e1 Mon Sep 17 00:00:00 2001 From: shirim Date: Wed, 1 Nov 2023 11:58:38 +0200 Subject: [PATCH 34/42] begun adding required parms to command line --- cmd/analyzer/main.go | 4 ++++ cmd/analyzer/parse_args.go | 18 +++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/cmd/analyzer/main.go b/cmd/analyzer/main.go index 703feb818..12e8625cb 100644 --- a/cmd/analyzer/main.go +++ b/cmd/analyzer/main.go @@ -84,6 +84,10 @@ func _main(cmdlineArgs []string) error { if err != nil { return fmt.Errorf("error generating cloud config from input vpc resources file: %w", err) } + // todo: perform the required analysis + if *inArgs.InputSecondConfigFile != "" { + fmt.Println("2nd config file is", *inArgs.InputSecondConfigFile) + } for _, vpcConfig := range vpcConfigs { err = analysisPerVPCConfig(vpcConfig, inArgs) if err != nil { diff --git a/cmd/analyzer/parse_args.go b/cmd/analyzer/parse_args.go index 663dcaff8..792bb3905 100644 --- a/cmd/analyzer/parse_args.go +++ b/cmd/analyzer/parse_args.go @@ -8,13 +8,14 @@ import ( // InArgs contains the input arguments for the analyzer type InArgs struct { - InputConfigFile *string - OutputFile *string - OutputFormat *string - AnalysisType *string - Grouping *bool - VPC *string - Debug *bool + InputConfigFile *string + InputSecondConfigFile *string + OutputFile *string + OutputFormat *string + AnalysisType *string + Grouping *bool + VPC *string + Debug *bool } const ( @@ -60,6 +61,7 @@ func ParseInArgs(cmdlineArgs []string) (*InArgs, error) { args := InArgs{} flagset := flag.NewFlagSet("vpc-network-config-analyzer", flag.ContinueOnError) args.InputConfigFile = flagset.String("vpc-config", "", "file path to input config") + args.InputSecondConfigFile = flagset.String("vpc-config-second", "", "file path to second input config for semantic diff") args.OutputFile = flagset.String("output-file", "", "file path to store results") args.OutputFormat = flagset.String("format", TEXTFormat, "output format; must be one of "+getSupportedValuesString(supportedOutputFormats)) args.AnalysisType = flagset.String("analysis-type", allEndpoints, @@ -77,6 +79,8 @@ func ParseInArgs(cmdlineArgs []string) (*InArgs, error) { flagset.PrintDefaults() return nil, fmt.Errorf("missing parameter: vpc-config") } + // todo: add semantic diffs to analysis_type + // todo: if 2nd config file is given then analysis must be semantic_diff if !supportedOutputFormats[*args.OutputFormat] { flagset.PrintDefaults() From b546133f65756f5e4b9bde6aa8cd897e66a5f86b Mon Sep 17 00:00:00 2001 From: shirim Date: Wed, 1 Nov 2023 18:51:03 +0200 Subject: [PATCH 35/42] merge with main --- cmd/analyzer/main.go | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/cmd/analyzer/main.go b/cmd/analyzer/main.go index 12e8625cb..f2071c814 100644 --- a/cmd/analyzer/main.go +++ b/cmd/analyzer/main.go @@ -40,28 +40,27 @@ func analysisTypeToUseCase(inArgs *InArgs) vpcmodel.OutputUseCase { return vpcmodel.AllEndpoints } -func analysisPerVPCConfig(c *vpcmodel.VPCConfig, inArgs *InArgs) error { +func analysisPerVPCConfig(c *vpcmodel.VPCConfig, inArgs *InArgs, outFile string) (*vpcmodel.VPCAnalysisOutput, error) { og, err := vpcmodel.NewOutputGenerator(c, *inArgs.Grouping, analysisTypeToUseCase(inArgs), *inArgs.OutputFormat == ARCHDRAWIOFormat) if err != nil { - return err + return nil, err } - outFile := "" - if inArgs.OutputFile != nil { - outFile = *inArgs.OutputFile + + var genOutFile string + // currently for drawio output only one vpc level is supported, and not as aggregated output of multiple vpcs + if *inArgs.OutputFormat == ARCHDRAWIOFormat || *inArgs.OutputFormat == DRAWIOFormat { + genOutFile = outFile } outFormat := getOutputFormat(inArgs) - output, err := og.Generate(outFormat, outFile) + output, err := og.Generate(outFormat, genOutFile) if err != nil { - return fmt.Errorf("output generation error: %w", err) + return nil, fmt.Errorf("output generation error: %w", err) } - // print to stdout as well - fmt.Println(output) - - return nil + return output, nil } // The actual main function @@ -84,16 +83,31 @@ func _main(cmdlineArgs []string) error { if err != nil { return fmt.Errorf("error generating cloud config from input vpc resources file: %w", err) } + outFile := "" + if inArgs.OutputFile != nil { + outFile = *inArgs.OutputFile + } + + outputPerVPC := make([]*vpcmodel.VPCAnalysisOutput, len(vpcConfigs)) + i := 0 // todo: perform the required analysis if *inArgs.InputSecondConfigFile != "" { fmt.Println("2nd config file is", *inArgs.InputSecondConfigFile) } for _, vpcConfig := range vpcConfigs { - err = analysisPerVPCConfig(vpcConfig, inArgs) - if err != nil { - return err + vpcAnalysisOutput, err2 := analysisPerVPCConfig(vpcConfig, inArgs, outFile) + if err2 != nil { + return err2 } + outputPerVPC[i] = vpcAnalysisOutput + i++ + } + + out, err := vpcmodel.AggregateVPCsOutput(outputPerVPC, getOutputFormat(inArgs), outFile) + if err != nil { + return err } + fmt.Println(out) return nil } From f69ab209b354fe2c76980a699fbea2898529a4ee Mon Sep 17 00:00:00 2001 From: shirim Date: Wed, 1 Nov 2023 18:51:22 +0200 Subject: [PATCH 36/42] parsing arguments for diff analysis --- cmd/analyzer/main.go | 8 ++++---- cmd/analyzer/parse_args.go | 38 ++++++++++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/cmd/analyzer/main.go b/cmd/analyzer/main.go index f2071c814..6dbb8b5a9 100644 --- a/cmd/analyzer/main.go +++ b/cmd/analyzer/main.go @@ -74,6 +74,10 @@ func _main(cmdlineArgs []string) error { return fmt.Errorf("error parsing arguments: %w", err) } + if *inArgs.AnalysisType == allSubnetsDiff || *inArgs.AnalysisType == allEndpointsDiff { + return nil // todo: tmp + } + rc, err := ibmvpc.ParseResourcesFromFile(*inArgs.InputConfigFile) if err != nil { return fmt.Errorf("error parsing input vpc resources file: %w", err) @@ -90,10 +94,6 @@ func _main(cmdlineArgs []string) error { outputPerVPC := make([]*vpcmodel.VPCAnalysisOutput, len(vpcConfigs)) i := 0 - // todo: perform the required analysis - if *inArgs.InputSecondConfigFile != "" { - fmt.Println("2nd config file is", *inArgs.InputSecondConfigFile) - } for _, vpcConfig := range vpcConfigs { vpcAnalysisOutput, err2 := analysisPerVPCConfig(vpcConfig, inArgs, outFile) if err2 != nil { diff --git a/cmd/analyzer/parse_args.go b/cmd/analyzer/parse_args.go index 792bb3905..05e93a813 100644 --- a/cmd/analyzer/parse_args.go +++ b/cmd/analyzer/parse_args.go @@ -28,9 +28,11 @@ const ( DEBUGFormat = "debug" // connectivity analysis types supported - allEndpoints = "all_endpoints" // vsi to vsi connectivity analysis - allSubnets = "all_subnets" // subnet to subnet connectivity analysis - singleSubnet = "single_subnet" // single subnet connectivity analysis + allEndpoints = "all_endpoints" // vsi to vsi connectivity analysis + allSubnets = "all_subnets" // subnet to subnet connectivity analysis + singleSubnet = "single_subnet" // single subnet connectivity analysis + allEndpointsDiff = "diff_all_endpoints" // semantic diff of allEndpoints analysis between two configurations + allSubnetsDiff = "diff_all_subnets" // semantic diff of allSubnets analysis between two configurations ) var supportedOutputFormats = map[string]bool{ @@ -42,9 +44,11 @@ var supportedOutputFormats = map[string]bool{ DEBUGFormat: true, } var supportedAnalysisTypes = map[string]bool{ - allEndpoints: true, - allSubnets: true, - singleSubnet: true, + allEndpoints: true, + allSubnets: true, + singleSubnet: true, + allSubnetsDiff: true, + allEndpointsDiff: false, } func getSupportedValuesString(supportedValues map[string]bool) string { @@ -79,14 +83,32 @@ func ParseInArgs(cmdlineArgs []string) (*InArgs, error) { flagset.PrintDefaults() return nil, fmt.Errorf("missing parameter: vpc-config") } - // todo: add semantic diffs to analysis_type - // todo: if 2nd config file is given then analysis must be semantic_diff + + fileForDiffSpecified := args.InputSecondConfigFile != nil && *args.InputSecondConfigFile != "" + if fileForDiffSpecified && *args.AnalysisType != allEndpointsDiff && + *args.AnalysisType != allSubnetsDiff { + return nil, fmt.Errorf("wrong analysis type %s for 2nd file (%v) specified for diff; "+ + "must be diff_all_endpoints or diff_all_subnets", *args.AnalysisType, *args.InputSecondConfigFile) + } + + if !fileForDiffSpecified && (*args.AnalysisType == allEndpointsDiff || + *args.AnalysisType == allSubnetsDiff) { + return nil, fmt.Errorf("missing parameter vpc-config-second for diff analysis %s", *args.AnalysisType) + } if !supportedOutputFormats[*args.OutputFormat] { flagset.PrintDefaults() return nil, fmt.Errorf("wrong output format %s; must be one of %s", *args.OutputFormat, getSupportedValuesString(supportedOutputFormats)) } + if *args.AnalysisType == allEndpointsDiff { + return nil, fmt.Errorf("diff_all_endpoints not supported yet") + } + + if *args.AnalysisType == allSubnetsDiff && *args.OutputFormat != TEXTFormat { + return nil, fmt.Errorf("currently only txt output format supported with diff_all_subnets") + } + if !supportedAnalysisTypes[*args.AnalysisType] { flagset.PrintDefaults() return nil, fmt.Errorf("wrong analysis type %s; must be one of: %s", *args.AnalysisType, getSupportedValuesString(supportedAnalysisTypes)) From bce07cb48e5298dc48cca11a8dd6ab89320de00f Mon Sep 17 00:00:00 2001 From: shirim Date: Thu, 2 Nov 2023 14:29:51 +0200 Subject: [PATCH 37/42] Added end-to-end support for semantic diff of subnets --- cmd/analyzer/main.go | 78 +++++++++++++++++++++++------ pkg/ibmvpc/analysis_output_test.go | 2 +- pkg/vpcmodel/debugOutput.go | 1 + pkg/vpcmodel/drawioOutput.go | 6 ++- pkg/vpcmodel/jsonOutput.go | 1 + pkg/vpcmodel/mdOutput.go | 1 + pkg/vpcmodel/output.go | 27 +++++++--- pkg/vpcmodel/semanticDiffSubnets.go | 12 +++-- pkg/vpcmodel/textOutput.go | 3 ++ 9 files changed, 101 insertions(+), 30 deletions(-) diff --git a/cmd/analyzer/main.go b/cmd/analyzer/main.go index 6dbb8b5a9..ca79443b8 100644 --- a/cmd/analyzer/main.go +++ b/cmd/analyzer/main.go @@ -36,12 +36,14 @@ func analysisTypeToUseCase(inArgs *InArgs) vpcmodel.OutputUseCase { return vpcmodel.SingleSubnet case allSubnets: return vpcmodel.AllSubnets + case allSubnetsDiff: + return vpcmodel.AllSubnetsDiff } return vpcmodel.AllEndpoints } func analysisPerVPCConfig(c *vpcmodel.VPCConfig, inArgs *InArgs, outFile string) (*vpcmodel.VPCAnalysisOutput, error) { - og, err := vpcmodel.NewOutputGenerator(c, + og, err := vpcmodel.NewOutputGenerator(c, nil, *inArgs.Grouping, analysisTypeToUseCase(inArgs), *inArgs.OutputFormat == ARCHDRAWIOFormat) @@ -63,6 +65,24 @@ func analysisPerVPCConfig(c *vpcmodel.VPCConfig, inArgs *InArgs, outFile string) return output, nil } +func analysisDiffVPCConfig(c1, c2 *vpcmodel.VPCConfig, inArgs *InArgs, outFile string) (*vpcmodel.VPCAnalysisOutput, error) { + og, err := vpcmodel.NewOutputGenerator(c1, c2, + *inArgs.Grouping, + analysisTypeToUseCase(inArgs), + false) + if err != nil { + return nil, err + } + + outFormat := getOutputFormat(inArgs) + output, err := og.Generate(outFormat, outFile) + if err != nil { + return nil, fmt.Errorf("output generation error: %w", err) + } + + return output, nil +} + // The actual main function // Takes command-line flags and returns an error rather than exiting, so it can be more easily used in testing func _main(cmdlineArgs []string) error { @@ -74,10 +94,6 @@ func _main(cmdlineArgs []string) error { return fmt.Errorf("error parsing arguments: %w", err) } - if *inArgs.AnalysisType == allSubnetsDiff || *inArgs.AnalysisType == allEndpointsDiff { - return nil // todo: tmp - } - rc, err := ibmvpc.ParseResourcesFromFile(*inArgs.InputConfigFile) if err != nil { return fmt.Errorf("error parsing input vpc resources file: %w", err) @@ -87,28 +103,58 @@ func _main(cmdlineArgs []string) error { if err != nil { return fmt.Errorf("error generating cloud config from input vpc resources file: %w", err) } + outFile := "" if inArgs.OutputFile != nil { outFile = *inArgs.OutputFile } - outputPerVPC := make([]*vpcmodel.VPCAnalysisOutput, len(vpcConfigs)) - i := 0 - for _, vpcConfig := range vpcConfigs { - vpcAnalysisOutput, err2 := analysisPerVPCConfig(vpcConfig, inArgs, outFile) + diffAnalysis := *inArgs.AnalysisType == allEndpointsDiff || *inArgs.AnalysisType == allSubnetsDiff + if !diffAnalysis { + outputPerVPC := make([]*vpcmodel.VPCAnalysisOutput, len(vpcConfigs)) + i := 0 + for _, vpcConfig := range vpcConfigs { + vpcAnalysisOutput, err2 := analysisPerVPCConfig(vpcConfig, inArgs, outFile) + if err2 != nil { + return err2 + } + outputPerVPC[i] = vpcAnalysisOutput + i++ + } + + out, err := vpcmodel.AggregateVPCsOutput(outputPerVPC, getOutputFormat(inArgs), outFile) + if err != nil { + return err + } + fmt.Println(out) + } else { + // Diff analysis + // ToDo SM: for diff analysis assume 2 configs only, the 2nd given through vpc-config-second + var rc2ndForDiff *ibmvpc.ResourcesContainer + rc2ndForDiff, err = ibmvpc.ParseResourcesFromFile(*inArgs.InputSecondConfigFile) + if err != nil { + return fmt.Errorf("error parsing arguments: %w", err) + } + vpc2ndConfigs, err := ibmvpc.VPCConfigsFromResources(rc2ndForDiff, *inArgs.VPC, *inArgs.Debug) + if err != nil { + return fmt.Errorf("error generating cloud config from input vpc resources file: %w", err) + } + // For diff analysis each vpcConfigs have a single element + c1 := getFirstCfg(vpcConfigs) + c2 := getFirstCfg(vpc2ndConfigs) + out, err2 := analysisDiffVPCConfig(c1, c2, inArgs, outFile) if err2 != nil { return err2 } - outputPerVPC[i] = vpcAnalysisOutput - i++ + fmt.Println(out) } + return nil +} - out, err := vpcmodel.AggregateVPCsOutput(outputPerVPC, getOutputFormat(inArgs), outFile) - if err != nil { - return err +func getFirstCfg(vpcConfigs map[string]*vpcmodel.VPCConfig) *vpcmodel.VPCConfig { + for _, vpcConfig := range vpcConfigs { + return vpcConfig } - fmt.Println(out) - return nil } diff --git a/pkg/ibmvpc/analysis_output_test.go b/pkg/ibmvpc/analysis_output_test.go index fefe6eb19..9d5d98e0f 100644 --- a/pkg/ibmvpc/analysis_output_test.go +++ b/pkg/ibmvpc/analysis_output_test.go @@ -472,7 +472,7 @@ func runTestPerUseCase(t *testing.T, return err } - og, err := vpcmodel.NewOutputGenerator(vpcConfig, tt.grouping, uc, tt.format == vpcmodel.ARCHDRAWIO) + og, err := vpcmodel.NewOutputGenerator(vpcConfig, nil, tt.grouping, uc, tt.format == vpcmodel.ARCHDRAWIO) if err != nil { return err } diff --git a/pkg/vpcmodel/debugOutput.go b/pkg/vpcmodel/debugOutput.go index bdc46d47a..69622c825 100644 --- a/pkg/vpcmodel/debugOutput.go +++ b/pkg/vpcmodel/debugOutput.go @@ -6,6 +6,7 @@ type DebugOutputFormatter struct { func (t *DebugOutputFormatter) WriteOutput(c *VPCConfig, conn *VPCConnectivity, subnetsConn *VPCsubnetConnectivity, + subnetsDiff *DiffBetweenSubnets, outFile string, grouping bool, uc OutputUseCase) (*VPCAnalysisOutput, error) { diff --git a/pkg/vpcmodel/drawioOutput.go b/pkg/vpcmodel/drawioOutput.go index 78b093526..8084b8b33 100644 --- a/pkg/vpcmodel/drawioOutput.go +++ b/pkg/vpcmodel/drawioOutput.go @@ -112,6 +112,7 @@ func (d *DrawioOutputFormatter) createEdges() { func (d *DrawioOutputFormatter) WriteOutput(c *VPCConfig, conn *VPCConnectivity, subnetsConn *VPCsubnetConnectivity, + subnetsDiff *DiffBetweenSubnets, outFile string, grouping bool, uc OutputUseCase) (*VPCAnalysisOutput, error) { @@ -139,14 +140,15 @@ type ArchDrawioOutputFormatter struct { func (d *ArchDrawioOutputFormatter) WriteOutput(c *VPCConfig, conn *VPCConnectivity, subnetsConn *VPCsubnetConnectivity, + subnetsDiff *DiffBetweenSubnets, outFile string, grouping bool, uc OutputUseCase) (*VPCAnalysisOutput, error) { switch uc { case AllEndpoints: - return d.DrawioOutputFormatter.WriteOutput(c, nil, nil, outFile, grouping, uc) + return d.DrawioOutputFormatter.WriteOutput(c, nil, nil, nil, outFile, grouping, uc) case AllSubnets, SingleSubnet: - return d.DrawioOutputFormatter.WriteOutput(nil, nil, nil, outFile, grouping, uc) + return d.DrawioOutputFormatter.WriteOutput(nil, nil, nil, nil, outFile, grouping, uc) } return &VPCAnalysisOutput{}, nil } diff --git a/pkg/vpcmodel/jsonOutput.go b/pkg/vpcmodel/jsonOutput.go index 32ab69cd0..bc6328849 100644 --- a/pkg/vpcmodel/jsonOutput.go +++ b/pkg/vpcmodel/jsonOutput.go @@ -14,6 +14,7 @@ type JSONoutputFormatter struct { func (j *JSONoutputFormatter) WriteOutput(c *VPCConfig, conn *VPCConnectivity, subnetsConn *VPCsubnetConnectivity, + subnetsDiff *DiffBetweenSubnets, outFile string, grouping bool, uc OutputUseCase) (*VPCAnalysisOutput, error) { diff --git a/pkg/vpcmodel/mdOutput.go b/pkg/vpcmodel/mdOutput.go index 6c3c8971d..c1c5dcb05 100644 --- a/pkg/vpcmodel/mdOutput.go +++ b/pkg/vpcmodel/mdOutput.go @@ -18,6 +18,7 @@ const ( func (m *MDoutputFormatter) WriteOutput(c *VPCConfig, conn *VPCConnectivity, subnetsConn *VPCsubnetConnectivity, + subnetsDiff *DiffBetweenSubnets, outFile string, grouping bool, uc OutputUseCase) (*VPCAnalysisOutput, error) { diff --git a/pkg/vpcmodel/output.go b/pkg/vpcmodel/output.go index b3161604e..1e0a1e26d 100644 --- a/pkg/vpcmodel/output.go +++ b/pkg/vpcmodel/output.go @@ -24,10 +24,12 @@ const ( type OutputUseCase int +// ToDo SM: subnets connectivity "only nacl" relevant to diff? const ( AllEndpoints OutputUseCase = iota // connectivity between network interfaces and external ip-blocks SingleSubnet // connectivity per single subnet with nacl AllSubnets // connectivity between subnets (consider nacl + pgw) + AllSubnetsDiff // diff between two subnets connectivity (consider nacl + pgw) AllSubnetsNoPGW // connectivity between subnets (consider nacl only) ) @@ -35,35 +37,44 @@ const ( // the functionality to generate the analysis output in various formats, for that vpc type OutputGenerator struct { config *VPCConfig + config2 *VPCConfig // specified only when analysis is diff outputGrouping bool useCase OutputUseCase nodesConn *VPCConnectivity subnetsConn *VPCsubnetConnectivity + subnetsDiff *DiffBetweenSubnets } -func NewOutputGenerator(c *VPCConfig, grouping bool, uc OutputUseCase, archOnly bool) (*OutputGenerator, error) { +func NewOutputGenerator(c1, c2 *VPCConfig, grouping bool, uc OutputUseCase, archOnly bool) (*OutputGenerator, error) { res := &OutputGenerator{ - config: c, + config: c1, outputGrouping: grouping, useCase: uc, } if !archOnly { if uc == AllEndpoints { - nodesConn, err := c.GetVPCNetworkConnectivity(grouping) + nodesConn, err := c1.GetVPCNetworkConnectivity(grouping) if err != nil { return nil, err } res.nodesConn = nodesConn } if uc == AllSubnets { - subnetsConn, err := c.GetSubnetsConnectivity(true, grouping) + subnetsConn, err := c1.GetSubnetsConnectivity(true, grouping) if err != nil { return nil, err } res.subnetsConn = subnetsConn } + if uc == AllSubnetsDiff { + configsForDiff := &ConfigsForDiff{c1, c2} + subnetsDiff, err := configsForDiff.GetSubnetsDiff(grouping) + if err != nil { + return nil, err + } + res.subnetsDiff = subnetsDiff + } } - // todo: add for diff return res, nil } @@ -95,12 +106,12 @@ func (o *OutputGenerator) Generate(f OutFormat, outFile string) (*VPCAnalysisOut return nil, errors.New("unsupported output format") } - return formatter.WriteOutput(o.config, o.nodesConn, o.subnetsConn, outFile, o.outputGrouping, o.useCase) + return formatter.WriteOutput(o.config, o.nodesConn, o.subnetsConn, o.subnetsDiff, outFile, o.outputGrouping, o.useCase) } type OutputFormatter interface { - WriteOutput(c *VPCConfig, conn *VPCConnectivity, subnetsConn *VPCsubnetConnectivity, outFile string, - grouping bool, uc OutputUseCase) (*VPCAnalysisOutput, error) + WriteOutput(c *VPCConfig, conn *VPCConnectivity, subnetsConn *VPCsubnetConnectivity, subnetsDiff *DiffBetweenSubnets, + outFile string, grouping bool, uc OutputUseCase) (*VPCAnalysisOutput, error) } func WriteToFile(content, fileName string) (string, error) { diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 76f930687..cd556d663 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -34,7 +34,7 @@ type SubnetConfigConnectivity struct { subnetConnectivity SubnetConnectivityMap } -type diffBetweenSubnets struct { +type DiffBetweenSubnets struct { subnet1Subtract2 SubnetsDiff subnet2Subtract1 SubnetsDiff @@ -42,7 +42,7 @@ type diffBetweenSubnets struct { GroupedSubnet1Minus1 *GroupConnLines } -func (configs ConfigsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets, error) { +func (configs ConfigsForDiff) GetSubnetsDiff(grouping bool) (*DiffBetweenSubnets, error) { // 1. compute connectivity for each of the subnets subnetsConn1, err := configs.config1.GetSubnetsConnectivity(true, false) if err != nil { @@ -68,7 +68,7 @@ func (configs ConfigsForDiff) GetSubnetsDiff(grouping bool) (*diffBetweenSubnets // 3. ToDo: grouping, see comment at the end of this file - res := &diffBetweenSubnets{ + res := &DiffBetweenSubnets{ subnet1Subtract2: subnet1Subtract2, subnet2Subtract1: subnet2Subtract1} return res, nil @@ -152,6 +152,12 @@ func getDiffType(src, srcInOther, dst, dstInOther EndpointElem) DiffType { // EnhancedString ToDo: likely the current printing functionality will no longer be needed once the grouping is added // anyways the diff print will be worked on before the final merge + +func (diff *DiffBetweenSubnets) String() string { + return diff.subnet1Subtract2.EnhancedString(true) + "\n" + + diff.subnet2Subtract1.EnhancedString(false) +} + func (subnetDiff *SubnetsDiff) EnhancedString(thisMinusOther bool) string { var diffDirection, printDiff string if thisMinusOther { diff --git a/pkg/vpcmodel/textOutput.go b/pkg/vpcmodel/textOutput.go index 520d26192..f97f32957 100644 --- a/pkg/vpcmodel/textOutput.go +++ b/pkg/vpcmodel/textOutput.go @@ -14,6 +14,7 @@ func headerOfAnalyzedVPC(vpcName string) string { func (t *TextOutputFormatter) WriteOutput(c *VPCConfig, conn *VPCConnectivity, subnetsConn *VPCsubnetConnectivity, + subnetsDiff *DiffBetweenSubnets, outFile string, grouping bool, uc OutputUseCase) (*VPCAnalysisOutput, error) { @@ -27,6 +28,8 @@ func (t *TextOutputFormatter) WriteOutput(c *VPCConfig, out += subnetsConn.String() case SingleSubnet: out += c.GetConnectivityOutputPerEachSubnetSeparately() + case AllSubnetsDiff: + out += subnetsDiff.String() } // write output to file and return the output string _, err := WriteToFile(out, outFile) From 8c29515e1e4a373bad4b46eb00abc2383bc9266e Mon Sep 17 00:00:00 2001 From: shirim Date: Thu, 2 Nov 2023 15:00:15 +0200 Subject: [PATCH 38/42] fix typo --- cmd/analyzer/main.go | 4 ++-- pkg/vpcmodel/semanticDiffSubnets.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/analyzer/main.go b/cmd/analyzer/main.go index ca79443b8..7b230fb2b 100644 --- a/cmd/analyzer/main.go +++ b/cmd/analyzer/main.go @@ -142,11 +142,11 @@ func _main(cmdlineArgs []string) error { // For diff analysis each vpcConfigs have a single element c1 := getFirstCfg(vpcConfigs) c2 := getFirstCfg(vpc2ndConfigs) - out, err2 := analysisDiffVPCConfig(c1, c2, inArgs, outFile) + analysisOutput, err2 := analysisDiffVPCConfig(c1, c2, inArgs, outFile) if err2 != nil { return err2 } - fmt.Println(out) + fmt.Println(analysisOutput.Output) } return nil } diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index cd556d663..5cd7e531c 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -44,11 +44,11 @@ type DiffBetweenSubnets struct { func (configs ConfigsForDiff) GetSubnetsDiff(grouping bool) (*DiffBetweenSubnets, error) { // 1. compute connectivity for each of the subnets - subnetsConn1, err := configs.config1.GetSubnetsConnectivity(true, false) + subnetsConn1, err := configs.config1.GetSubnetsConnectivity(true, grouping) if err != nil { return nil, err } - subnetsConn2, err := configs.config2.GetSubnetsConnectivity(true, false) + subnetsConn2, err := configs.config2.GetSubnetsConnectivity(true, grouping) if err != nil { return nil, err } @@ -499,7 +499,7 @@ func externalNodeToIPBlock(external Node) (ipBlock *common.IPBlock, err error) { //} // //// todo: instead of adding functionality to grouping, I plan to have more generic connectivity items that will be grouped -//// encode the SubnetsDiff into this generic item as well as the other entities we are grouping +//// encode the subnetsDiff into this generic item as well as the other entities we are grouping //// and then decode in the printing //// the idea is to use instead of *common.ConnectionSet in the grouped entity a string which will encode the connection //// and also the diff where relevant From 2c2dfa017b4b26a3f24a9d32b01c8ac038e5124c Mon Sep 17 00:00:00 2001 From: shirim Date: Sun, 5 Nov 2023 08:53:19 +0200 Subject: [PATCH 39/42] headerOfAnalyzedVPC to reflect diff --- pkg/vpcmodel/debugOutput.go | 8 ++++---- pkg/vpcmodel/drawioOutput.go | 10 +++++----- pkg/vpcmodel/jsonOutput.go | 4 ++-- pkg/vpcmodel/mdOutput.go | 6 +++--- pkg/vpcmodel/nodesConnectivity.go | 2 +- pkg/vpcmodel/output.go | 11 ++++++----- pkg/vpcmodel/semanticDiffSubnets.go | 8 ++++---- pkg/vpcmodel/textOutput.go | 20 ++++++++++++++------ pkg/vpcmodel/vpcConnectivity.go | 4 ++-- 9 files changed, 41 insertions(+), 32 deletions(-) diff --git a/pkg/vpcmodel/debugOutput.go b/pkg/vpcmodel/debugOutput.go index 69622c825..bccd04daf 100644 --- a/pkg/vpcmodel/debugOutput.go +++ b/pkg/vpcmodel/debugOutput.go @@ -3,14 +3,14 @@ package vpcmodel type DebugOutputFormatter struct { } -func (t *DebugOutputFormatter) WriteOutput(c *VPCConfig, +func (t *DebugOutputFormatter) WriteOutput(c1, c2 *VPCConfig, conn *VPCConnectivity, subnetsConn *VPCsubnetConnectivity, subnetsDiff *DiffBetweenSubnets, outFile string, grouping bool, uc OutputUseCase) (*VPCAnalysisOutput, error) { - out := headerOfAnalyzedVPC(c.VPC.Name()) + out := headerOfAnalyzedVPC(c1.VPC.Name(), "") switch uc { case AllEndpoints: // TODO: add a flag of whether to include grouped output or not @@ -19,8 +19,8 @@ func (t *DebugOutputFormatter) WriteOutput(c *VPCConfig, case AllSubnets: out = subnetsConn.String() case SingleSubnet: - out = c.GetConnectivityOutputPerEachSubnetSeparately() + out = c1.GetConnectivityOutputPerEachSubnetSeparately() } _, err := WriteToFile(out, outFile) - return &VPCAnalysisOutput{Output: out, VPCName: c.VPC.Name(), format: Debug}, err + return &VPCAnalysisOutput{Output: out, VPCName: c1.VPC.Name(), format: Debug}, err } diff --git a/pkg/vpcmodel/drawioOutput.go b/pkg/vpcmodel/drawioOutput.go index 8084b8b33..493088604 100644 --- a/pkg/vpcmodel/drawioOutput.go +++ b/pkg/vpcmodel/drawioOutput.go @@ -109,7 +109,7 @@ func (d *DrawioOutputFormatter) createEdges() { } } -func (d *DrawioOutputFormatter) WriteOutput(c *VPCConfig, +func (d *DrawioOutputFormatter) WriteOutput(c1, c2 *VPCConfig, conn *VPCConnectivity, subnetsConn *VPCsubnetConnectivity, subnetsDiff *DiffBetweenSubnets, @@ -119,7 +119,7 @@ func (d *DrawioOutputFormatter) WriteOutput(c *VPCConfig, var err error switch uc { case AllEndpoints: - d.init(c, conn) + d.init(c1, conn) d.createDrawioTree() err = drawio.CreateDrawioConnectivityMapFile(d.gen.Network(), outFile) case AllSubnets, SingleSubnet: @@ -137,7 +137,7 @@ type ArchDrawioOutputFormatter struct { DrawioOutputFormatter } -func (d *ArchDrawioOutputFormatter) WriteOutput(c *VPCConfig, +func (d *ArchDrawioOutputFormatter) WriteOutput(c1, c2 *VPCConfig, conn *VPCConnectivity, subnetsConn *VPCsubnetConnectivity, subnetsDiff *DiffBetweenSubnets, @@ -146,9 +146,9 @@ func (d *ArchDrawioOutputFormatter) WriteOutput(c *VPCConfig, uc OutputUseCase) (*VPCAnalysisOutput, error) { switch uc { case AllEndpoints: - return d.DrawioOutputFormatter.WriteOutput(c, nil, nil, nil, outFile, grouping, uc) + return d.DrawioOutputFormatter.WriteOutput(c1, c2, nil, nil, nil, outFile, grouping, uc) case AllSubnets, SingleSubnet: - return d.DrawioOutputFormatter.WriteOutput(nil, nil, nil, nil, outFile, grouping, uc) + return d.DrawioOutputFormatter.WriteOutput(nil, c2, nil, nil, nil, outFile, grouping, uc) } return &VPCAnalysisOutput{}, nil } diff --git a/pkg/vpcmodel/jsonOutput.go b/pkg/vpcmodel/jsonOutput.go index bc6328849..0c88219af 100644 --- a/pkg/vpcmodel/jsonOutput.go +++ b/pkg/vpcmodel/jsonOutput.go @@ -11,7 +11,7 @@ import ( type JSONoutputFormatter struct { } -func (j *JSONoutputFormatter) WriteOutput(c *VPCConfig, +func (j *JSONoutputFormatter) WriteOutput(c1, c2 *VPCConfig, conn *VPCConnectivity, subnetsConn *VPCsubnetConnectivity, subnetsDiff *DiffBetweenSubnets, @@ -28,7 +28,7 @@ func (j *JSONoutputFormatter) WriteOutput(c *VPCConfig, return nil, errors.New("DebugSubnet use case not supported for JSON format currently ") } outStr, err := writeJSON(all, outFile) - return &VPCAnalysisOutput{Output: outStr, VPCName: c.VPC.Name(), format: JSON, jsonStruct: all}, err + return &VPCAnalysisOutput{Output: outStr, VPCName: c1.VPC.Name(), format: JSON, jsonStruct: all}, err } type connLine struct { diff --git a/pkg/vpcmodel/mdOutput.go b/pkg/vpcmodel/mdOutput.go index c1c5dcb05..82afca332 100644 --- a/pkg/vpcmodel/mdOutput.go +++ b/pkg/vpcmodel/mdOutput.go @@ -15,7 +15,7 @@ const ( mdHeader = "| src | dst | conn |\n|-----|-----|------|" ) -func (m *MDoutputFormatter) WriteOutput(c *VPCConfig, +func (m *MDoutputFormatter) WriteOutput(c1, c2 *VPCConfig, conn *VPCConnectivity, subnetsConn *VPCsubnetConnectivity, subnetsDiff *DiffBetweenSubnets, @@ -23,7 +23,7 @@ func (m *MDoutputFormatter) WriteOutput(c *VPCConfig, grouping bool, uc OutputUseCase) (*VPCAnalysisOutput, error) { // get output by analysis type - out := "# " + headerOfAnalyzedVPC(c.VPC.Name()) + out := "# " + headerOfAnalyzedVPC(c1.VPC.Name(), "") switch uc { case AllEndpoints: lines := []string{mdTitle, mdHeader} @@ -39,7 +39,7 @@ func (m *MDoutputFormatter) WriteOutput(c *VPCConfig, } _, err := WriteToFile(out, outFile) - return &VPCAnalysisOutput{Output: out, VPCName: c.VPC.Name(), format: MD}, err + return &VPCAnalysisOutput{Output: out, VPCName: c1.VPC.Name(), format: MD}, err } func (m *MDoutputFormatter) getGroupedOutput(conn *VPCConnectivity) []string { diff --git a/pkg/vpcmodel/nodesConnectivity.go b/pkg/vpcmodel/nodesConnectivity.go index b28fc268c..0741216c2 100644 --- a/pkg/vpcmodel/nodesConnectivity.go +++ b/pkg/vpcmodel/nodesConnectivity.go @@ -131,7 +131,7 @@ func (c *VPCConfig) getAllowedConnsPerDirection(isIngress bool, capturedNode Nod allLayersRes[peerNode] = NoConns() continue } - // ibm-config: appliedFilters are either both nacl and sg (for pgw) or only sg (for fip) + // ibm-config1: appliedFilters are either both nacl and sg (for pgw) or only sg (for fip) // TODO: consider moving to pkg ibm-vpc appliedFilters := appliedRouter.AppliedFiltersKinds() for layer := range appliedFilters { diff --git a/pkg/vpcmodel/output.go b/pkg/vpcmodel/output.go index 1e0a1e26d..12e5ca41f 100644 --- a/pkg/vpcmodel/output.go +++ b/pkg/vpcmodel/output.go @@ -33,10 +33,10 @@ const ( AllSubnetsNoPGW // connectivity between subnets (consider nacl only) ) -// OutputGenerator captures one vpc config with its connectivity analysis results, and implements +// OutputGenerator captures one vpc config1 with its connectivity analysis results, and implements // the functionality to generate the analysis output in various formats, for that vpc type OutputGenerator struct { - config *VPCConfig + config1 *VPCConfig config2 *VPCConfig // specified only when analysis is diff outputGrouping bool useCase OutputUseCase @@ -47,7 +47,8 @@ type OutputGenerator struct { func NewOutputGenerator(c1, c2 *VPCConfig, grouping bool, uc OutputUseCase, archOnly bool) (*OutputGenerator, error) { res := &OutputGenerator{ - config: c1, + config1: c1, + config2: c2, outputGrouping: grouping, useCase: uc, } @@ -106,11 +107,11 @@ func (o *OutputGenerator) Generate(f OutFormat, outFile string) (*VPCAnalysisOut return nil, errors.New("unsupported output format") } - return formatter.WriteOutput(o.config, o.nodesConn, o.subnetsConn, o.subnetsDiff, outFile, o.outputGrouping, o.useCase) + return formatter.WriteOutput(o.config1, o.config2, o.nodesConn, o.subnetsConn, o.subnetsDiff, outFile, o.outputGrouping, o.useCase) } type OutputFormatter interface { - WriteOutput(c *VPCConfig, conn *VPCConnectivity, subnetsConn *VPCsubnetConnectivity, subnetsDiff *DiffBetweenSubnets, + WriteOutput(c1, c2 *VPCConfig, conn *VPCConnectivity, subnetsConn *VPCsubnetConnectivity, subnetsDiff *DiffBetweenSubnets, outFile string, grouping bool, uc OutputUseCase) (*VPCAnalysisOutput, error) } diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index 5cd7e531c..be6de2315 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -74,8 +74,8 @@ func (configs ConfigsForDiff) GetSubnetsDiff(grouping bool) (*DiffBetweenSubnets return res, nil } -// for a given EndpointElem (representing a subnet or an external ip) in config return the EndpointElem representing the -// subnet/external address in otherConfig or nil if the subnet does not exist in the other config. +// for a given EndpointElem (representing a subnet or an external ip) in config1 return the EndpointElem representing the +// subnet/external address in otherConfig or nil if the subnet does not exist in the other config1. func (c *VPCConfig) getEndpointElemInOtherConfig(other *VPCConfig, ep EndpointElem) EndpointElem { if ep.IsExternal() { nodeSameCidr, _ := findNodeWithCidr(other.Nodes, ep.(Node).Cidr()) @@ -246,10 +246,10 @@ func (subnetConnectivity *SubnetConnectivityMap) alignConnectionsGivenIPBlists(c return alignedConnectivity, err } -// aligned config: copies from old config everything but nodes, +// aligned config1: copies from old config1 everything but nodes, // nodes are resized by disjointIPblocks func (c *VPCConfig) copyConfig(disjointIPblocks []*common.IPBlock) (alignedConfig *VPCConfig, err error) { - // copy config + // copy config1 alignedConfig = c // nodes - external addresses - are resized alignedConfig.Nodes, err = resizeNodes(c.Nodes, disjointIPblocks) diff --git a/pkg/vpcmodel/textOutput.go b/pkg/vpcmodel/textOutput.go index f97f32957..75527e083 100644 --- a/pkg/vpcmodel/textOutput.go +++ b/pkg/vpcmodel/textOutput.go @@ -7,19 +7,27 @@ type TextOutputFormatter struct { const asteriskDetails = "\n\nconnections are stateful unless marked with *\n" -func headerOfAnalyzedVPC(vpcName string) string { - return fmt.Sprintf("Analysis for VPC %s\n", vpcName) +func headerOfAnalyzedVPC(vpcName, vpc2Name string) string { + if vpc2Name == "" { + return fmt.Sprintf("Analysis for VPC %s\n", vpcName) + } + // 2nd cfg given - the analysis is a semantic diff and concerns a single cfg + return fmt.Sprintf("Analysis for diff between VPC %s and VPC %s\n", vpcName, vpc2Name) } -func (t *TextOutputFormatter) WriteOutput(c *VPCConfig, +func (t *TextOutputFormatter) WriteOutput(c1, c2 *VPCConfig, conn *VPCConnectivity, subnetsConn *VPCsubnetConnectivity, subnetsDiff *DiffBetweenSubnets, outFile string, grouping bool, uc OutputUseCase) (*VPCAnalysisOutput, error) { + vpc2Name := "" + if c2 != nil { + vpc2Name = c2.VPC.Name() + } // header line - specify the VPC analyzed - out := headerOfAnalyzedVPC(c.VPC.Name()) + out := headerOfAnalyzedVPC(c1.VPC.Name(), vpc2Name) // get output by analysis type switch uc { case AllEndpoints: @@ -27,11 +35,11 @@ func (t *TextOutputFormatter) WriteOutput(c *VPCConfig, case AllSubnets: out += subnetsConn.String() case SingleSubnet: - out += c.GetConnectivityOutputPerEachSubnetSeparately() + out += c1.GetConnectivityOutputPerEachSubnetSeparately() case AllSubnetsDiff: out += subnetsDiff.String() } // write output to file and return the output string _, err := WriteToFile(out, outFile) - return &VPCAnalysisOutput{Output: out, VPCName: c.VPC.Name(), format: Text}, err + return &VPCAnalysisOutput{Output: out, VPCName: c1.VPC.Name(), format: Text}, err } diff --git a/pkg/vpcmodel/vpcConnectivity.go b/pkg/vpcmodel/vpcConnectivity.go index f585b84f3..4e32a7deb 100644 --- a/pkg/vpcmodel/vpcConnectivity.go +++ b/pkg/vpcmodel/vpcConnectivity.go @@ -4,7 +4,7 @@ import ( "github.com/np-guard/vpc-network-config-analyzer/pkg/common" ) -// VPCConnectivity holds detailed representation of allowed connectivity considering all resources in a vpc config instance +// VPCConnectivity holds detailed representation of allowed connectivity considering all resources in a vpc config1 instance type VPCConnectivity struct { // computed for each layer separately its allowed connections (ingress and egress separately) AllowedConnsPerLayer map[Node]map[string]*ConnectivityResult @@ -66,7 +66,7 @@ func NewIPbasedConnectivityResult() *IPbasedConnectivityResult { } } -// ConfigBasedConnectivityResults is used to capture allowed connectivity to/from elements in the vpc config (subnets / external ip-blocks) +// ConfigBasedConnectivityResults is used to capture allowed connectivity to/from elements in the vpc config1 (subnets / external ip-blocks) // It is associated with a subnet when analyzing connectivity of subnets based on NACL resources type ConfigBasedConnectivityResults struct { IngressAllowedConns map[EndpointElem]*common.ConnectionSet From 7076e251dd52253b99742bb9adbcf05773e5e0a0 Mon Sep 17 00:00:00 2001 From: shirim Date: Sun, 5 Nov 2023 10:13:57 +0200 Subject: [PATCH 40/42] added end-to-end test flow --- .../acl_testing5subnetsBased_withPGW.txt | 2 +- pkg/ibmvpc/examples/input_acl_testing5.json | 2 +- .../input_acl_testing5_2nd_for_diff.json | 1716 +++++++++++++++++ 3 files changed, 1718 insertions(+), 2 deletions(-) create mode 100644 pkg/ibmvpc/examples/input_acl_testing5_2nd_for_diff.json diff --git a/pkg/ibmvpc/examples/acl_testing5subnetsBased_withPGW.txt b/pkg/ibmvpc/examples/acl_testing5subnetsBased_withPGW.txt index 07a1043ad..e88fecd49 100644 --- a/pkg/ibmvpc/examples/acl_testing5subnetsBased_withPGW.txt +++ b/pkg/ibmvpc/examples/acl_testing5subnetsBased_withPGW.txt @@ -1,4 +1,4 @@ -Analysis for VPC test-vpc-ky +Analysis for VPC test-vpc-ky1 combined connections between subnets: sub1-1-ky => Public Internet 8.8.8.8/32 : protocol: UDP dst-ports: 53 sub1-1-ky => sub1-2-ky : protocol: TCP diff --git a/pkg/ibmvpc/examples/input_acl_testing5.json b/pkg/ibmvpc/examples/input_acl_testing5.json index 3bc7f8096..31ef2b356 100644 --- a/pkg/ibmvpc/examples/input_acl_testing5.json +++ b/pkg/ibmvpc/examples/input_acl_testing5.json @@ -53,7 +53,7 @@ }, "href": "href:2", "id": "id:3", - "name": "test-vpc-ky", + "name": "test-vpc-ky1", "resource_group": { "href": "href:15", "id": "id:16", diff --git a/pkg/ibmvpc/examples/input_acl_testing5_2nd_for_diff.json b/pkg/ibmvpc/examples/input_acl_testing5_2nd_for_diff.json new file mode 100644 index 000000000..ae534b4ac --- /dev/null +++ b/pkg/ibmvpc/examples/input_acl_testing5_2nd_for_diff.json @@ -0,0 +1,1716 @@ +{ + "vpcs": [ + { + "classic_access": false, + "created_at": "2023-07-26T08:00:25.000Z", + "crn": "crn:1", + "cse_source_ips": [ + { + "ip": { + "address": "10.16.239.23" + }, + "zone": { + "href": "href:4", + "name": "us-south-1" + } + }, + { + "ip": { + "address": "10.16.246.249" + }, + "zone": { + "href": "href:5", + "name": "us-south-2" + } + }, + { + "ip": { + "address": "10.22.232.160" + }, + "zone": { + "href": "href:6", + "name": "us-south-3" + } + } + ], + "default_network_acl": { + "crn": "crn:7", + "href": "href:8", + "id": "id:9", + "name": "jawed-amicably-prepay-harmonize" + }, + "default_routing_table": { + "href": "href:10", + "id": "id:11", + "name": "emblem-spleen-valise-semisweet", + "resource_type": "routing_table" + }, + "default_security_group": { + "crn": "crn:12", + "href": "href:13", + "id": "id:14", + "name": "matching-treadmill-lunchbox-gigahertz" + }, + "href": "href:2", + "id": "id:3", + "name": "test-vpc-ky2", + "resource_group": { + "href": "href:15", + "id": "id:16", + "name": "anonymous" + }, + "resource_type": "vpc", + "status": "available", + "tags": [] + } + ], + "subnets": [ + { + "available_ipv4_address_count": 251, + "created_at": "2023-07-26T08:01:20.000Z", + "crn": "crn:17", + "href": "href:18", + "id": "id:19", + "ip_version": "ipv4", + "ipv4_cidr_block": "10.240.1.0/24", + "name": "sub1-1-ky", + "network_acl": { + "crn": "crn:20", + "href": "href:21", + "id": "id:22", + "name": "acl1-1-ky" + }, + "public_gateway": { + "crn": "crn:23", + "href": "href:24", + "id": "id:25", + "name": "public-gw1-ky", + "resource_type": "public_gateway" + }, + "resource_group": { + "href": "href:15", + "id": "id:16", + "name": "anonymous" + }, + "resource_type": "subnet", + "routing_table": { + "href": "href:10", + "id": "id:11", + "name": "emblem-spleen-valise-semisweet", + "resource_type": "routing_table" + }, + "status": "available", + "total_ipv4_address_count": 256, + "vpc": { + "crn": "crn:1", + "href": "href:2", + "id": "id:3", + "name": "test-vpc-ky", + "resource_type": "vpc" + }, + "zone": { + "href": "href:4", + "name": "us-south-1" + }, + "reserved_ips": [ + { + "address": "10.240.1.0", + "auto_delete": false, + "created_at": "2023-07-26T08:01:20.000Z", + "href": "href:26", + "id": "id:27", + "lifecycle_state": "stable", + "name": "ibm-network-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.1.1", + "auto_delete": false, + "created_at": "2023-07-26T08:01:20.000Z", + "href": "href:28", + "id": "id:29", + "lifecycle_state": "stable", + "name": "ibm-default-gateway", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.1.2", + "auto_delete": false, + "created_at": "2023-07-26T08:01:20.000Z", + "href": "href:30", + "id": "id:31", + "lifecycle_state": "stable", + "name": "ibm-dns-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.1.3", + "auto_delete": false, + "created_at": "2023-07-26T08:01:20.000Z", + "href": "href:32", + "id": "id:33", + "lifecycle_state": "stable", + "name": "ibm-reserved-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.1.255", + "auto_delete": false, + "created_at": "2023-07-26T08:01:20.000Z", + "href": "href:34", + "id": "id:35", + "lifecycle_state": "stable", + "name": "ibm-broadcast-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + } + ], + "tags": [] + }, + { + "available_ipv4_address_count": 251, + "created_at": "2023-07-26T08:01:08.000Z", + "crn": "crn:36", + "href": "href:37", + "id": "id:38", + "ip_version": "ipv4", + "ipv4_cidr_block": "10.240.2.0/24", + "name": "sub1-2-ky", + "network_acl": { + "crn": "crn:39", + "href": "href:40", + "id": "id:41", + "name": "acl1-2-ky" + }, + "resource_group": { + "href": "href:15", + "id": "id:16", + "name": "anonymous" + }, + "resource_type": "subnet", + "routing_table": { + "href": "href:10", + "id": "id:11", + "name": "emblem-spleen-valise-semisweet", + "resource_type": "routing_table" + }, + "status": "available", + "total_ipv4_address_count": 256, + "vpc": { + "crn": "crn:1", + "href": "href:2", + "id": "id:3", + "name": "test-vpc-ky", + "resource_type": "vpc" + }, + "zone": { + "href": "href:4", + "name": "us-south-1" + }, + "reserved_ips": [ + { + "address": "10.240.2.0", + "auto_delete": false, + "created_at": "2023-07-26T08:01:08.000Z", + "href": "href:42", + "id": "id:43", + "lifecycle_state": "stable", + "name": "ibm-network-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.2.1", + "auto_delete": false, + "created_at": "2023-07-26T08:01:08.000Z", + "href": "href:44", + "id": "id:45", + "lifecycle_state": "stable", + "name": "ibm-default-gateway", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.2.2", + "auto_delete": false, + "created_at": "2023-07-26T08:01:08.000Z", + "href": "href:46", + "id": "id:47", + "lifecycle_state": "stable", + "name": "ibm-dns-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.2.3", + "auto_delete": false, + "created_at": "2023-07-26T08:01:08.000Z", + "href": "href:48", + "id": "id:49", + "lifecycle_state": "stable", + "name": "ibm-reserved-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.2.255", + "auto_delete": false, + "created_at": "2023-07-26T08:01:08.000Z", + "href": "href:50", + "id": "id:51", + "lifecycle_state": "stable", + "name": "ibm-broadcast-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + } + ], + "tags": [] + }, + { + "available_ipv4_address_count": 251, + "created_at": "2023-07-26T08:01:08.000Z", + "crn": "crn:52", + "href": "href:53", + "id": "id:54", + "ip_version": "ipv4", + "ipv4_cidr_block": "10.240.64.0/24", + "name": "sub2-1-ky", + "network_acl": { + "crn": "crn:55", + "href": "href:56", + "id": "id:57", + "name": "acl2-1-ky" + }, + "public_gateway": { + "crn": "crn:58", + "href": "href:59", + "id": "id:60", + "name": "public-gw2-ky", + "resource_type": "public_gateway" + }, + "resource_group": { + "href": "href:15", + "id": "id:16", + "name": "anonymous" + }, + "resource_type": "subnet", + "routing_table": { + "href": "href:10", + "id": "id:11", + "name": "emblem-spleen-valise-semisweet", + "resource_type": "routing_table" + }, + "status": "available", + "total_ipv4_address_count": 256, + "vpc": { + "crn": "crn:1", + "href": "href:2", + "id": "id:3", + "name": "test-vpc-ky", + "resource_type": "vpc" + }, + "zone": { + "href": "href:5", + "name": "us-south-2" + }, + "reserved_ips": [ + { + "address": "10.240.64.0", + "auto_delete": false, + "created_at": "2023-07-26T08:01:08.000Z", + "href": "href:61", + "id": "id:62", + "lifecycle_state": "stable", + "name": "ibm-network-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.64.1", + "auto_delete": false, + "created_at": "2023-07-26T08:01:08.000Z", + "href": "href:63", + "id": "id:64", + "lifecycle_state": "stable", + "name": "ibm-default-gateway", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.64.2", + "auto_delete": false, + "created_at": "2023-07-26T08:01:08.000Z", + "href": "href:65", + "id": "id:66", + "lifecycle_state": "stable", + "name": "ibm-dns-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.64.3", + "auto_delete": false, + "created_at": "2023-07-26T08:01:08.000Z", + "href": "href:67", + "id": "id:68", + "lifecycle_state": "stable", + "name": "ibm-reserved-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.64.255", + "auto_delete": false, + "created_at": "2023-07-26T08:01:08.000Z", + "href": "href:69", + "id": "id:70", + "lifecycle_state": "stable", + "name": "ibm-broadcast-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + } + ], + "tags": [] + }, + { + "available_ipv4_address_count": 251, + "created_at": "2023-07-26T08:00:55.000Z", + "crn": "crn:71", + "href": "href:72", + "id": "id:73", + "ip_version": "ipv4", + "ipv4_cidr_block": "10.240.3.0/24", + "name": "sub1-3-ky", + "network_acl": { + "crn": "crn:39", + "href": "href:40", + "id": "id:41", + "name": "acl1-2-ky" + }, + "resource_group": { + "href": "href:15", + "id": "id:16", + "name": "anonymous" + }, + "resource_type": "subnet", + "routing_table": { + "href": "href:10", + "id": "id:11", + "name": "emblem-spleen-valise-semisweet", + "resource_type": "routing_table" + }, + "status": "available", + "total_ipv4_address_count": 256, + "vpc": { + "crn": "crn:1", + "href": "href:2", + "id": "id:3", + "name": "test-vpc-ky", + "resource_type": "vpc" + }, + "zone": { + "href": "href:4", + "name": "us-south-1" + }, + "reserved_ips": [ + { + "address": "10.240.3.0", + "auto_delete": false, + "created_at": "2023-07-26T08:00:55.000Z", + "href": "href:74", + "id": "id:75", + "lifecycle_state": "stable", + "name": "ibm-network-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.3.1", + "auto_delete": false, + "created_at": "2023-07-26T08:00:55.000Z", + "href": "href:76", + "id": "id:77", + "lifecycle_state": "stable", + "name": "ibm-default-gateway", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.3.2", + "auto_delete": false, + "created_at": "2023-07-26T08:00:55.000Z", + "href": "href:78", + "id": "id:79", + "lifecycle_state": "stable", + "name": "ibm-dns-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.3.3", + "auto_delete": false, + "created_at": "2023-07-26T08:00:55.000Z", + "href": "href:80", + "id": "id:81", + "lifecycle_state": "stable", + "name": "ibm-reserved-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.3.255", + "auto_delete": false, + "created_at": "2023-07-26T08:00:55.000Z", + "href": "href:82", + "id": "id:83", + "lifecycle_state": "stable", + "name": "ibm-broadcast-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + } + ], + "tags": [] + }, + { + "available_ipv4_address_count": 251, + "created_at": "2023-07-26T08:00:55.000Z", + "crn": "crn:84", + "href": "href:85", + "id": "id:86", + "ip_version": "ipv4", + "ipv4_cidr_block": "10.240.65.0/24", + "name": "sub2-2-ky", + "network_acl": { + "crn": "crn:87", + "href": "href:88", + "id": "id:89", + "name": "acl2-2-ky" + }, + "public_gateway": { + "crn": "crn:58", + "href": "href:59", + "id": "id:60", + "name": "public-gw2-ky", + "resource_type": "public_gateway" + }, + "resource_group": { + "href": "href:15", + "id": "id:16", + "name": "anonymous" + }, + "resource_type": "subnet", + "routing_table": { + "href": "href:10", + "id": "id:11", + "name": "emblem-spleen-valise-semisweet", + "resource_type": "routing_table" + }, + "status": "available", + "total_ipv4_address_count": 256, + "vpc": { + "crn": "crn:1", + "href": "href:2", + "id": "id:3", + "name": "test-vpc-ky", + "resource_type": "vpc" + }, + "zone": { + "href": "href:5", + "name": "us-south-2" + }, + "reserved_ips": [ + { + "address": "10.240.65.0", + "auto_delete": false, + "created_at": "2023-07-26T08:00:55.000Z", + "href": "href:90", + "id": "id:91", + "lifecycle_state": "stable", + "name": "ibm-network-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.65.1", + "auto_delete": false, + "created_at": "2023-07-26T08:00:55.000Z", + "href": "href:92", + "id": "id:93", + "lifecycle_state": "stable", + "name": "ibm-default-gateway", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.65.2", + "auto_delete": false, + "created_at": "2023-07-26T08:00:55.000Z", + "href": "href:94", + "id": "id:95", + "lifecycle_state": "stable", + "name": "ibm-dns-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.65.3", + "auto_delete": false, + "created_at": "2023-07-26T08:00:55.000Z", + "href": "href:96", + "id": "id:97", + "lifecycle_state": "stable", + "name": "ibm-reserved-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.65.255", + "auto_delete": false, + "created_at": "2023-07-26T08:00:55.000Z", + "href": "href:98", + "id": "id:99", + "lifecycle_state": "stable", + "name": "ibm-broadcast-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + } + ], + "tags": [] + }, + { + "available_ipv4_address_count": 251, + "created_at": "2023-07-26T08:00:49.000Z", + "crn": "crn:100", + "href": "href:101", + "id": "id:102", + "ip_version": "ipv4", + "ipv4_cidr_block": "10.240.128.0/24", + "name": "sub3-1-ky", + "network_acl": { + "crn": "crn:103", + "href": "href:104", + "id": "id:105", + "name": "acl3-1-ky" + }, + "resource_group": { + "href": "href:15", + "id": "id:16", + "name": "anonymous" + }, + "resource_type": "subnet", + "routing_table": { + "href": "href:10", + "id": "id:11", + "name": "emblem-spleen-valise-semisweet", + "resource_type": "routing_table" + }, + "status": "available", + "total_ipv4_address_count": 256, + "vpc": { + "crn": "crn:1", + "href": "href:2", + "id": "id:3", + "name": "test-vpc-ky", + "resource_type": "vpc" + }, + "zone": { + "href": "href:6", + "name": "us-south-3" + }, + "reserved_ips": [ + { + "address": "10.240.128.0", + "auto_delete": false, + "created_at": "2023-07-26T08:00:49.000Z", + "href": "href:106", + "id": "id:107", + "lifecycle_state": "stable", + "name": "ibm-network-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.128.1", + "auto_delete": false, + "created_at": "2023-07-26T08:00:49.000Z", + "href": "href:108", + "id": "id:109", + "lifecycle_state": "stable", + "name": "ibm-default-gateway", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.128.2", + "auto_delete": false, + "created_at": "2023-07-26T08:00:49.000Z", + "href": "href:110", + "id": "id:111", + "lifecycle_state": "stable", + "name": "ibm-dns-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.128.3", + "auto_delete": false, + "created_at": "2023-07-26T08:00:49.000Z", + "href": "href:112", + "id": "id:113", + "lifecycle_state": "stable", + "name": "ibm-reserved-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + }, + { + "address": "10.240.128.255", + "auto_delete": false, + "created_at": "2023-07-26T08:00:49.000Z", + "href": "href:114", + "id": "id:115", + "lifecycle_state": "stable", + "name": "ibm-broadcast-address", + "owner": "provider", + "resource_type": "subnet_reserved_ip" + } + ], + "tags": [] + } + ], + "public_gateways": [ + { + "created_at": "2023-07-26T08:00:43.000Z", + "crn": "crn:23", + "floating_ip": { + "address": "52.116.140.187", + "crn": "crn:116", + "href": "href:117", + "id": "id:118", + "name": "public-gw1-ky" + }, + "href": "href:24", + "id": "id:25", + "name": "public-gw1-ky", + "resource_group": { + "href": "href:15", + "id": "id:16", + "name": "anonymous" + }, + "resource_type": "public_gateway", + "status": "available", + "vpc": { + "crn": "crn:1", + "href": "href:2", + "id": "id:3", + "name": "test-vpc-ky", + "resource_type": "vpc" + }, + "zone": { + "href": "href:4", + "name": "us-south-1" + }, + "tags": [] + }, + { + "created_at": "2023-07-26T08:00:42.000Z", + "crn": "crn:58", + "floating_ip": { + "address": "52.118.211.246", + "crn": "crn:119", + "href": "href:120", + "id": "id:121", + "name": "public-gw2-ky" + }, + "href": "href:59", + "id": "id:60", + "name": "public-gw2-ky", + "resource_group": { + "href": "href:15", + "id": "id:16", + "name": "anonymous" + }, + "resource_type": "public_gateway", + "status": "available", + "vpc": { + "crn": "crn:1", + "href": "href:2", + "id": "id:3", + "name": "test-vpc-ky", + "resource_type": "vpc" + }, + "zone": { + "href": "href:5", + "name": "us-south-2" + }, + "tags": [] + } + ], + "floating_ips": [ + { + "address": "52.118.211.246", + "created_at": "2023-07-26T08:00:42.000Z", + "crn": "crn:119", + "href": "href:120", + "id": "id:121", + "name": "public-gw2-ky", + "resource_group": { + "href": "href:15", + "id": "id:16", + "name": "anonymous" + }, + "status": "available", + "target": { + "href": "href:59", + "id": "id:60", + "name": "public-gw2-ky", + "resource_type": "public_gateway", + "crn": "crn:58" + }, + "zone": { + "href": "href:5", + "name": "us-south-2" + }, + "tags": [] + }, + { + "address": "52.116.140.187", + "created_at": "2023-07-26T08:00:42.000Z", + "crn": "crn:116", + "href": "href:117", + "id": "id:118", + "name": "public-gw1-ky", + "resource_group": { + "href": "href:15", + "id": "id:16", + "name": "anonymous" + }, + "status": "available", + "target": { + "href": "href:24", + "id": "id:25", + "name": "public-gw1-ky", + "resource_type": "public_gateway", + "crn": "crn:23" + }, + "zone": { + "href": "href:4", + "name": "us-south-1" + }, + "tags": [] + } + ], + "network_acls": [ + { + "created_at": "2023-07-26T08:00:42.000Z", + "crn": "crn:39", + "href": "href:40", + "id": "id:41", + "name": "acl1-2-ky", + "resource_group": { + "href": "href:15", + "id": "id:16", + "name": "anonymous" + }, + "rules": [ + { + "action": "allow", + "before": { + "href": "href:126", + "id": "id:127", + "name": "i1" + }, + "created_at": "2023-07-26T08:00:43.000Z", + "destination": "10.240.2.0/23", + "direction": "outbound", + "href": "href:124", + "id": "id:125", + "ip_version": "ipv4", + "name": "o2", + "source": "10.240.2.0/23", + "destination_port_max": 65535, + "destination_port_min": 1, + "protocol": "tcp", + "source_port_max": 65535, + "source_port_min": 1 + }, + { + "action": "allow", + "before": { + "href": "href:128", + "id": "id:129", + "name": "i2" + }, + "created_at": "2023-07-26T08:00:44.000Z", + "destination": "10.240.2.0/23", + "direction": "inbound", + "href": "href:126", + "id": "id:127", + "ip_version": "ipv4", + "name": "i1", + "source": "10.240.1.0/24", + "destination_port_max": 65535, + "destination_port_min": 1, + "protocol": "tcp", + "source_port_max": 65535, + "source_port_min": 1 + }, + { + "action": "allow", + "created_at": "2023-07-26T08:00:44.000Z", + "destination": "10.240.2.0/23", + "direction": "inbound", + "href": "href:128", + "id": "id:129", + "ip_version": "ipv4", + "name": "i2", + "source": "10.240.2.0/23", + "destination_port_max": 65535, + "destination_port_min": 1, + "protocol": "tcp", + "source_port_max": 65535, + "source_port_min": 1 + } + ], + "subnets": [ + { + "crn": "crn:36", + "href": "href:37", + "id": "id:38", + "name": "sub1-2-ky", + "resource_type": "subnet" + }, + { + "crn": "crn:71", + "href": "href:72", + "id": "id:73", + "name": "sub1-3-ky", + "resource_type": "subnet" + } + ], + "vpc": { + "crn": "crn:1", + "href": "href:2", + "id": "id:3", + "name": "test-vpc-ky", + "resource_type": "vpc" + }, + "tags": [] + }, + { + "created_at": "2023-07-26T08:00:42.000Z", + "crn": "crn:55", + "href": "href:56", + "id": "id:57", + "name": "acl2-1-ky", + "resource_group": { + "href": "href:15", + "id": "id:16", + "name": "anonymous" + }, + "rules": [ + { + "action": "allow", + "before": { + "href": "href:132", + "id": "id:133", + "name": "o2" + }, + "created_at": "2023-07-26T08:00:43.000Z", + "destination": "8.8.8.8/28", + "direction": "outbound", + "href": "href:130", + "id": "id:131", + "ip_version": "ipv4", + "name": "o1", + "source": "10.240.64.0/24", + "destination_port_max": 53, + "destination_port_min": 53, + "protocol": "udp", + "source_port_max": 65535, + "source_port_min": 1 + }, + { + "action": "allow", + "before": { + "href": "href:132", + "id": "id:133", + "name": "o2" + }, + "created_at": "2023-07-26T08:00:43.000Z", + "destination": "8.8.8.8/32", + "direction": "outbound", + "href": "href:130", + "id": "id:131", + "ip_version": "ipv4", + "name": "o1", + "source": "10.240.64.0/24", + "destination_port_max": 43, + "destination_port_min": 43, + "protocol": "udp", + "source_port_max": 65535, + "source_port_min": 1 + }, + { + "action": "allow", + "before": { + "href": "href:134", + "id": "id:135", + "name": "o3" + }, + "created_at": "2023-07-26T08:00:44.000Z", + "destination": "10.240.65.0/24", + "direction": "outbound", + "href": "href:132", + "id": "id:133", + "ip_version": "ipv4", + "name": "o2", + "source": "10.240.64.0/24", + "protocol": "all" + }, + { + "action": "allow", + "before": { + "href": "href:136", + "id": "id:137", + "name": "o4" + }, + "created_at": "2023-07-26T08:00:44.000Z", + "destination": "10.240.128.0/24", + "direction": "outbound", + "href": "href:134", + "id": "id:135", + "ip_version": "ipv4", + "name": "o3", + "source": "10.240.64.0/24", + "destination_port_max": 65535, + "destination_port_min": 1, + "protocol": "tcp", + "source_port_max": 443, + "source_port_min": 443 + }, + { + "action": "allow", + "before": { + "href": "href:138", + "id": "id:139", + "name": "i1" + }, + "created_at": "2023-07-26T08:00:45.000Z", + "destination": "10.240.128.0/24", + "direction": "outbound", + "href": "href:136", + "id": "id:137", + "ip_version": "ipv4", + "name": "o4", + "source": "10.240.64.0/24", + "code": 0, + "protocol": "icmp", + "type": 0 + }, + { + "action": "allow", + "before": { + "href": "href:140", + "id": "id:141", + "name": "i2" + }, + "created_at": "2023-07-26T08:00:45.000Z", + "destination": "10.240.64.0/24", + "direction": "inbound", + "href": "href:138", + "id": "id:139", + "ip_version": "ipv4", + "name": "i1", + "source": "8.8.8.8/32", + "destination_port_max": 65535, + "destination_port_min": 1, + "protocol": "udp", + "source_port_max": 53, + "source_port_min": 53 + }, + { + "action": "allow", + "before": { + "href": "href:142", + "id": "id:143", + "name": "i3" + }, + "created_at": "2023-07-26T08:00:45.000Z", + "destination": "10.240.64.0/24", + "direction": "inbound", + "href": "href:140", + "id": "id:141", + "ip_version": "ipv4", + "name": "i2", + "source": "10.240.65.0/24", + "protocol": "all" + }, + { + "action": "allow", + "before": { + "href": "href:144", + "id": "id:145", + "name": "i4" + }, + "created_at": "2023-07-26T08:00:46.000Z", + "destination": "10.240.64.0/24", + "direction": "inbound", + "href": "href:142", + "id": "id:143", + "ip_version": "ipv4", + "name": "i3", + "source": "10.240.128.0/24", + "destination_port_max": 443, + "destination_port_min": 443, + "protocol": "tcp", + "source_port_max": 65535, + "source_port_min": 1 + }, + { + "action": "allow", + "created_at": "2023-07-26T08:00:46.000Z", + "destination": "10.240.64.0/24", + "direction": "inbound", + "href": "href:144", + "id": "id:145", + "ip_version": "ipv4", + "name": "i4", + "source": "10.240.128.0/24", + "code": 0, + "protocol": "icmp", + "type": 0 + } + ], + "subnets": [ + { + "crn": "crn:52", + "href": "href:53", + "id": "id:54", + "name": "sub2-1-ky", + "resource_type": "subnet" + } + ], + "vpc": { + "crn": "crn:1", + "href": "href:2", + "id": "id:3", + "name": "test-vpc-ky", + "resource_type": "vpc" + }, + "tags": [] + }, + { + "created_at": "2023-07-26T08:00:42.000Z", + "crn": "crn:20", + "href": "href:21", + "id": "id:22", + "name": "acl1-1-ky", + "resource_group": { + "href": "href:15", + "id": "id:16", + "name": "anonymous" + }, + "rules": [ + { + "action": "allow", + "before": { + "href": "href:148", + "id": "id:149", + "name": "o2" + }, + "created_at": "2023-07-26T08:00:42.000Z", + "destination": "8.8.8.8/32", + "direction": "outbound", + "href": "href:146", + "id": "id:147", + "ip_version": "ipv4", + "name": "o1", + "source": "10.240.1.0/24", + "destination_port_max": 53, + "destination_port_min": 53, + "protocol": "udp", + "source_port_max": 65535, + "source_port_min": 1 + }, + { + "action": "allow", + "before": { + "href": "href:150", + "id": "id:151", + "name": "o3" + }, + "created_at": "2023-07-26T08:00:43.000Z", + "destination": "10.240.2.0/23", + "direction": "outbound", + "href": "href:148", + "id": "id:149", + "ip_version": "ipv4", + "name": "o2", + "source": "10.240.1.0/24", + "destination_port_max": 65535, + "destination_port_min": 1, + "protocol": "tcp", + "source_port_max": 65535, + "source_port_min": 1 + }, + { + "action": "allow", + "before": { + "href": "href:152", + "id": "id:153", + "name": "i1" + }, + "created_at": "2023-07-26T08:00:43.000Z", + "destination": "10.240.128.0/24", + "direction": "outbound", + "href": "href:150", + "id": "id:151", + "ip_version": "ipv4", + "name": "o3", + "source": "10.240.1.0/24", + "code": 0, + "protocol": "icmp", + "type": 0 + }, + { + "action": "allow", + "before": { + "href": "href:154", + "id": "id:155", + "name": "i2" + }, + "created_at": "2023-07-26T08:00:44.000Z", + "destination": "10.240.1.0/24", + "direction": "inbound", + "href": "href:152", + "id": "id:153", + "ip_version": "ipv4", + "name": "i1", + "source": "8.8.8.8/32", + "destination_port_max": 65535, + "destination_port_min": 1, + "protocol": "udp", + "source_port_max": 53, + "source_port_min": 53 + }, + { + "action": "allow", + "before": { + "href": "href:156", + "id": "id:157", + "name": "i3" + }, + "created_at": "2023-07-26T08:00:45.000Z", + "destination": "10.240.1.0/24", + "direction": "inbound", + "href": "href:154", + "id": "id:155", + "ip_version": "ipv4", + "name": "i2", + "source": "10.240.2.0/23", + "destination_port_max": 65535, + "destination_port_min": 1, + "protocol": "tcp", + "source_port_max": 65535, + "source_port_min": 1 + }, + { + "action": "allow", + "created_at": "2023-07-26T08:00:45.000Z", + "destination": "10.240.1.0/24", + "direction": "inbound", + "href": "href:156", + "id": "id:157", + "ip_version": "ipv4", + "name": "i3", + "source": "10.240.128.0/24", + "code": 0, + "protocol": "icmp", + "type": 0 + } + ], + "subnets": [ + { + "crn": "crn:17", + "href": "href:18", + "id": "id:19", + "name": "sub1-1-ky", + "resource_type": "subnet" + } + ], + "vpc": { + "crn": "crn:1", + "href": "href:2", + "id": "id:3", + "name": "test-vpc-ky", + "resource_type": "vpc" + }, + "tags": [] + }, + { + "created_at": "2023-07-26T08:00:42.000Z", + "crn": "crn:87", + "href": "href:88", + "id": "id:89", + "name": "acl2-2-ky", + "resource_group": { + "href": "href:15", + "id": "id:16", + "name": "anonymous" + }, + "rules": [ + { + "action": "allow", + "before": { + "href": "href:160", + "id": "id:161", + "name": "i1" + }, + "created_at": "2023-07-26T08:00:43.000Z", + "destination": "10.240.64.0/24", + "direction": "outbound", + "href": "href:158", + "id": "id:159", + "ip_version": "ipv4", + "name": "o1", + "source": "10.240.65.0/24", + "protocol": "all" + }, + { + "action": "allow", + "created_at": "2023-07-26T08:00:43.000Z", + "destination": "10.240.65.0/24", + "direction": "inbound", + "href": "href:160", + "id": "id:161", + "ip_version": "ipv4", + "name": "i1", + "source": "10.240.64.0/24", + "protocol": "all" + } + ], + "subnets": [ + { + "crn": "crn:84", + "href": "href:85", + "id": "id:86", + "name": "sub2-2-ky", + "resource_type": "subnet" + } + ], + "vpc": { + "crn": "crn:1", + "href": "href:2", + "id": "id:3", + "name": "test-vpc-ky", + "resource_type": "vpc" + }, + "tags": [] + }, + { + "created_at": "2023-07-26T08:00:42.000Z", + "crn": "crn:103", + "href": "href:104", + "id": "id:105", + "name": "acl3-1-ky", + "resource_group": { + "href": "href:15", + "id": "id:16", + "name": "anonymous" + }, + "rules": [ + { + "action": "allow", + "before": { + "href": "href:164", + "id": "id:165", + "name": "o2" + }, + "created_at": "2023-07-26T08:00:43.000Z", + "destination": "10.240.64.0/24", + "direction": "outbound", + "href": "href:162", + "id": "id:163", + "ip_version": "ipv4", + "name": "o1", + "source": "10.240.128.0/24", + "destination_port_max": 443, + "destination_port_min": 443, + "protocol": "tcp", + "source_port_max": 65535, + "source_port_min": 1 + }, + { + "action": "allow", + "before": { + "href": "href:166", + "id": "id:167", + "name": "o3" + }, + "created_at": "2023-07-26T08:00:44.000Z", + "destination": "10.240.64.0/24", + "direction": "outbound", + "href": "href:164", + "id": "id:165", + "ip_version": "ipv4", + "name": "o2", + "source": "10.240.128.0/24", + "code": 0, + "protocol": "icmp", + "type": 0 + }, + { + "action": "allow", + "before": { + "href": "href:168", + "id": "id:169", + "name": "o4" + }, + "created_at": "2023-07-26T08:00:44.000Z", + "destination": "10.240.1.0/24", + "direction": "outbound", + "href": "href:166", + "id": "id:167", + "ip_version": "ipv4", + "name": "o3", + "source": "10.240.128.0/24", + "code": 0, + "protocol": "icmp", + "type": 0 + }, + { + "action": "allow", + "before": { + "href": "href:170", + "id": "id:171", + "name": "i1" + }, + "created_at": "2023-07-26T08:00:45.000Z", + "destination": "10.240.64.0/24", + "direction": "outbound", + "href": "href:168", + "id": "id:169", + "ip_version": "ipv4", + "name": "o4", + "source": "10.240.128.0/24", + "destination_port_max": 443, + "destination_port_min": 443, + "protocol": "tcp", + "source_port_max": 65535, + "source_port_min": 1 + }, + { + "action": "allow", + "before": { + "href": "href:172", + "id": "id:173", + "name": "i2" + }, + "created_at": "2023-07-26T08:00:45.000Z", + "destination": "10.240.128.0/24", + "direction": "inbound", + "href": "href:170", + "id": "id:171", + "ip_version": "ipv4", + "name": "i1", + "source": "10.240.64.0/24", + "destination_port_max": 65535, + "destination_port_min": 1, + "protocol": "tcp", + "source_port_max": 443, + "source_port_min": 443 + }, + { + "action": "allow", + "before": { + "href": "href:174", + "id": "id:175", + "name": "i3" + }, + "created_at": "2023-07-26T08:00:46.000Z", + "destination": "10.240.128.0/24", + "direction": "inbound", + "href": "href:172", + "id": "id:173", + "ip_version": "ipv4", + "name": "i2", + "source": "10.240.64.0/24", + "code": 0, + "protocol": "icmp", + "type": 0 + }, + { + "action": "allow", + "before": { + "href": "href:176", + "id": "id:177", + "name": "i4" + }, + "created_at": "2023-07-26T08:00:46.000Z", + "destination": "10.240.128.0/24", + "direction": "inbound", + "href": "href:174", + "id": "id:175", + "ip_version": "ipv4", + "name": "i3", + "source": "10.240.1.0/24", + "code": 0, + "protocol": "icmp", + "type": 0 + }, + { + "action": "allow", + "created_at": "2023-07-26T08:00:47.000Z", + "destination": "10.240.128.0/24", + "direction": "inbound", + "href": "href:176", + "id": "id:177", + "ip_version": "ipv4", + "name": "i4", + "source": "10.240.64.0/24", + "destination_port_max": 65535, + "destination_port_min": 1, + "protocol": "tcp", + "source_port_max": 443, + "source_port_min": 443 + } + ], + "subnets": [ + { + "crn": "crn:100", + "href": "href:101", + "id": "id:102", + "name": "sub3-1-ky", + "resource_type": "subnet" + } + ], + "vpc": { + "crn": "crn:1", + "href": "href:2", + "id": "id:3", + "name": "test-vpc-ky", + "resource_type": "vpc" + }, + "tags": [] + }, + { + "created_at": "2023-07-26T08:00:25.000Z", + "crn": "crn:7", + "href": "href:8", + "id": "id:9", + "name": "jawed-amicably-prepay-harmonize", + "resource_group": { + "href": "href:15", + "id": "id:16", + "name": "anonymous" + }, + "rules": [ + { + "action": "allow", + "before": { + "href": "href:180", + "id": "id:181", + "name": "allow-outbound" + }, + "created_at": "2023-07-26T08:00:25.000Z", + "destination": "0.0.0.0/0", + "direction": "inbound", + "href": "href:178", + "id": "id:179", + "ip_version": "ipv4", + "name": "allow-inbound", + "source": "0.0.0.0/0", + "protocol": "all" + }, + { + "action": "allow", + "created_at": "2023-07-26T08:00:25.000Z", + "destination": "0.0.0.0/0", + "direction": "outbound", + "href": "href:180", + "id": "id:181", + "ip_version": "ipv4", + "name": "allow-outbound", + "source": "0.0.0.0/0", + "protocol": "all" + } + ], + "subnets": [], + "vpc": { + "crn": "crn:1", + "href": "href:2", + "id": "id:3", + "name": "test-vpc-ky", + "resource_type": "vpc" + }, + "tags": [] + } + ], + "security_groups": [ + { + "created_at": "2023-07-26T08:00:42.000Z", + "crn": "crn:182", + "href": "href:183", + "id": "id:184", + "name": "sg1-ky", + "resource_group": { + "href": "href:15", + "id": "id:16", + "name": "anonymous" + }, + "rules": [ + { + "direction": "outbound", + "href": "href:185", + "id": "id:186", + "ip_version": "ipv4", + "remote": { + "cidr_block": "0.0.0.0/0" + }, + "protocol": "all" + }, + { + "direction": "inbound", + "href": "href:187", + "id": "id:188", + "ip_version": "ipv4", + "remote": { + "cidr_block": "0.0.0.0/0" + }, + "protocol": "all" + } + ], + "targets": [], + "vpc": { + "crn": "crn:1", + "href": "href:2", + "id": "id:3", + "name": "test-vpc-ky", + "resource_type": "vpc" + }, + "tags": [] + }, + { + "created_at": "2023-07-26T08:00:25.000Z", + "crn": "crn:12", + "href": "href:13", + "id": "id:14", + "name": "matching-treadmill-lunchbox-gigahertz", + "resource_group": { + "href": "href:15", + "id": "id:16", + "name": "anonymous" + }, + "rules": [ + { + "direction": "outbound", + "href": "href:189", + "id": "id:190", + "ip_version": "ipv4", + "remote": { + "cidr_block": "0.0.0.0/0" + }, + "protocol": "all" + }, + { + "direction": "inbound", + "href": "href:191", + "id": "id:192", + "ip_version": "ipv4", + "remote": { + "crn": "crn:12", + "href": "href:13", + "id": "id:14", + "name": "matching-treadmill-lunchbox-gigahertz" + }, + "protocol": "all" + } + ], + "targets": [], + "vpc": { + "crn": "crn:1", + "href": "href:2", + "id": "id:3", + "name": "test-vpc-ky", + "resource_type": "vpc" + }, + "tags": [] + } + ], + "endpoint_gateways": [], + "instances": [], + "routing_tables": [ + { + "accept_routes_from": [ + { + "resource_type": "vpn_gateway" + }, + { + "resource_type": "vpn_server" + } + ], + "created_at": "2023-07-26T08:00:25.000Z", + "href": "href:10", + "id": "id:11", + "is_default": true, + "lifecycle_state": "stable", + "name": "emblem-spleen-valise-semisweet", + "resource_type": "routing_table", + "route_direct_link_ingress": false, + "route_internet_ingress": false, + "route_transit_gateway_ingress": false, + "route_vpc_zone_ingress": false, + "subnets": [ + { + "crn": "crn:17", + "href": "href:18", + "id": "id:19", + "name": "sub1-1-ky", + "resource_type": "subnet" + }, + { + "crn": "crn:36", + "href": "href:37", + "id": "id:38", + "name": "sub1-2-ky", + "resource_type": "subnet" + }, + { + "crn": "crn:52", + "href": "href:53", + "id": "id:54", + "name": "sub2-1-ky", + "resource_type": "subnet" + }, + { + "crn": "crn:71", + "href": "href:72", + "id": "id:73", + "name": "sub1-3-ky", + "resource_type": "subnet" + }, + { + "crn": "crn:84", + "href": "href:85", + "id": "id:86", + "name": "sub2-2-ky", + "resource_type": "subnet" + }, + { + "crn": "crn:100", + "href": "href:101", + "id": "id:102", + "name": "sub3-1-ky", + "resource_type": "subnet" + } + ], + "routes": [] + } + ], + "load_balancers": [], + "iks_worker_nodes": [] +} From 4e29c3b5c04756033da92245c59223000ba06cad Mon Sep 17 00:00:00 2001 From: shirim Date: Sun, 5 Nov 2023 14:54:10 +0200 Subject: [PATCH 41/42] diff test added --- pkg/ibmvpc/analysis_output_test.go | 50 +++++++++++++++---- .../examples/acl_testing5subnetsDiff.txt | 9 ++++ ..._diff.json => input_acl_testing5_2nd.json} | 0 pkg/vpcmodel/semanticDiffSubnets.go | 12 +++-- 4 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 pkg/ibmvpc/examples/acl_testing5subnetsDiff.txt rename pkg/ibmvpc/examples/{input_acl_testing5_2nd_for_diff.json => input_acl_testing5_2nd.json} (100%) diff --git a/pkg/ibmvpc/analysis_output_test.go b/pkg/ibmvpc/analysis_output_test.go index 9d5d98e0f..6e2ba8103 100644 --- a/pkg/ibmvpc/analysis_output_test.go +++ b/pkg/ibmvpc/analysis_output_test.go @@ -33,6 +33,7 @@ const ( type vpcGeneralTest struct { name string // test name inputConfig string // name (relative path) of input config file (json) + inputConfig2nd string // 2nd input file for diff expectedOutput map[vpcmodel.OutputUseCase]string // expected output file path actualOutput map[vpcmodel.OutputUseCase]string // actual output file path useCases []vpcmodel.OutputUseCase // the list of output use cases to test @@ -50,6 +51,7 @@ const ( suffixOutFileDebugSubnet = "_analysisPerSubnetSeparately" suffixOutFileSubnetsLevel = "subnetsBased_withPGW" suffixOutFileSubnetsLevelNoPGW = "subnetsBased_withoutPGW" + suffixOutFileDiffSubnets = "subnetsDiff" txtOutSuffix = ".txt" debugOutSuffix = "_debug.txt" mdOutSuffix = ".md" @@ -93,6 +95,8 @@ func getTestFileName(testName string, res = baseName + suffixOutFileSubnetsLevel case vpcmodel.AllSubnetsNoPGW: res = baseName + suffixOutFileSubnetsLevelNoPGW + case vpcmodel.AllSubnetsDiff: + res = baseName + suffixOutFileDiffSubnets } switch format { case vpcmodel.Text: @@ -120,9 +124,9 @@ func getTestFileName(testName string, // files names (actual and expected), per use case func (tt *vpcGeneralTest) initTest() { tt.inputConfig = inputFilePrefix + tt.name + ".json" + tt.inputConfig2nd = inputFilePrefix + tt.name + "_2nd.json" tt.expectedOutput = map[vpcmodel.OutputUseCase]string{} tt.actualOutput = map[vpcmodel.OutputUseCase]string{} - // init field of expected errs if tt.errPerUseCase == nil { tt.errPerUseCase = map[vpcmodel.OutputUseCase]error{} @@ -326,6 +330,11 @@ var tests = []*vpcGeneralTest{ useCases: []vpcmodel.OutputUseCase{vpcmodel.AllSubnets}, format: vpcmodel.Text, }, + { + name: "acl_testing5", + useCases: []vpcmodel.OutputUseCase{vpcmodel.AllSubnetsDiff}, + format: vpcmodel.Text, + }, } var formatsAvoidComparison = map[vpcmodel.OutFormat]bool{vpcmodel.ARCHDRAWIO: true, vpcmodel.DRAWIO: true} @@ -387,11 +396,23 @@ func (tt *vpcGeneralTest) runTest(t *testing.T) { tt.initTest() // get vpcConfigs obj from parsing + analyzing input config file - vpcConfigs := getVPCConfigs(t, tt) + vpcConfigs := getVPCConfigs(t, tt, true) + var vpcConfigs2nd map[string]*vpcmodel.VPCConfig + diffUseCase := false + for _, useCase := range tt.useCases { + if useCase == vpcmodel.AllSubnetsDiff { + diffUseCase = true + } + } + if diffUseCase { + vpcConfigs2nd = getVPCConfigs(t, tt, false) + } else { // inputConfig2nd should be ignored if not diffUseCase + tt.inputConfig2nd = "" + } // generate actual output for all use cases specified for this test for _, uc := range tt.useCases { - err := runTestPerUseCase(t, tt, vpcConfigs, uc, tt.mode) + err := runTestPerUseCase(t, tt, vpcConfigs, vpcConfigs2nd, uc, tt.mode) require.Equal(t, tt.errPerUseCase[uc], err, "comparing actual err to expected err") } for uc, outFile := range tt.actualOutput { @@ -400,8 +421,14 @@ func (tt *vpcGeneralTest) runTest(t *testing.T) { } // getVPCConfigs returns map[string]*vpcmodel.VPCConfig obj for the input test (config json file) -func getVPCConfigs(t *testing.T, tt *vpcGeneralTest) map[string]*vpcmodel.VPCConfig { - inputConfigFile := filepath.Join(getTestsDir(), tt.inputConfig) +func getVPCConfigs(t *testing.T, tt *vpcGeneralTest, firstCfg bool) map[string]*vpcmodel.VPCConfig { + var inputConfig string + if firstCfg { + inputConfig = tt.inputConfig + } else { + inputConfig = tt.inputConfig2nd + } + inputConfigFile := filepath.Join(getTestsDir(), inputConfig) inputConfigContent, err := os.ReadFile(inputConfigFile) if err != nil { t.Fatalf("err: %s", err) @@ -461,18 +488,23 @@ func initTestFileNames(tt *vpcGeneralTest, // runTestPerUseCase runs the connectivity analysis for the required use case and compares/generates the output func runTestPerUseCase(t *testing.T, tt *vpcGeneralTest, - c map[string]*vpcmodel.VPCConfig, + c1, c2 map[string]*vpcmodel.VPCConfig, uc vpcmodel.OutputUseCase, mode testMode) error { - numConfigs := len(c) + numConfigs := len(c1) allVPCsOutput := make([]*vpcmodel.VPCAnalysisOutput, numConfigs) i := 0 - for _, vpcConfig := range c { + var vpcConfig2nd *vpcmodel.VPCConfig + // note that for diff analysis mode a single vpcConfig in c1 is provided; c2 is assumed to have a single cfg + for _, vpcConfig := range c2 { + vpcConfig2nd = vpcConfig + } + for _, vpcConfig := range c1 { if err := initTestFileNames(tt, uc, numConfigs, vpcConfig.VPC.Name(), false); err != nil { return err } - og, err := vpcmodel.NewOutputGenerator(vpcConfig, nil, tt.grouping, uc, tt.format == vpcmodel.ARCHDRAWIO) + og, err := vpcmodel.NewOutputGenerator(vpcConfig, vpcConfig2nd, tt.grouping, uc, tt.format == vpcmodel.ARCHDRAWIO) if err != nil { return err } diff --git a/pkg/ibmvpc/examples/acl_testing5subnetsDiff.txt b/pkg/ibmvpc/examples/acl_testing5subnetsDiff.txt new file mode 100644 index 000000000..5a3d7510e --- /dev/null +++ b/pkg/ibmvpc/examples/acl_testing5subnetsDiff.txt @@ -0,0 +1,9 @@ +Analysis for diff between VPC test-vpc-ky1 and VPC test-vpc-ky2 +-- sub1-2-ky => sub1-1-ky : missing connection +-- sub1-3-ky => sub1-1-ky : missing connection + +++ sub2-1-ky => Public Internet [8.8.8.0/29] : missing connection +++ sub2-1-ky => Public Internet [8.8.8.10/31] : missing connection +++ sub2-1-ky => Public Internet [8.8.8.12/30] : missing connection +++ sub2-1-ky => Public Internet [8.8.8.8/32] : changed connection protocol: UDP dst-ports: 43 +++ sub2-1-ky => Public Internet [8.8.8.9/32] : missing connection diff --git a/pkg/ibmvpc/examples/input_acl_testing5_2nd_for_diff.json b/pkg/ibmvpc/examples/input_acl_testing5_2nd.json similarity index 100% rename from pkg/ibmvpc/examples/input_acl_testing5_2nd_for_diff.json rename to pkg/ibmvpc/examples/input_acl_testing5_2nd.json diff --git a/pkg/vpcmodel/semanticDiffSubnets.go b/pkg/vpcmodel/semanticDiffSubnets.go index be6de2315..7fac4eaa5 100644 --- a/pkg/vpcmodel/semanticDiffSubnets.go +++ b/pkg/vpcmodel/semanticDiffSubnets.go @@ -2,6 +2,8 @@ package vpcmodel import ( "fmt" + "sort" + "strings" "github.com/np-guard/vpc-network-config-analyzer/pkg/common" ) @@ -159,7 +161,8 @@ func (diff *DiffBetweenSubnets) String() string { } func (subnetDiff *SubnetsDiff) EnhancedString(thisMinusOther bool) string { - var diffDirection, printDiff string + var diffDirection string + strList := []string{} if thisMinusOther { diffDirection = "--" } else { @@ -171,11 +174,14 @@ func (subnetDiff *SubnetsDiff) EnhancedString(thisMinusOther bool) string { if connDiff.ConnectionSet != nil { connectionSetDiff = connDiff.ConnectionSet.EnhancedString() } - printDiff += fmt.Sprintf("%s %s => %s : %s %s\n", diffDirection, src.Name(), dst.Name(), + printDiff := fmt.Sprintf("%s %s => %s : %s %s\n", diffDirection, src.Name(), dst.Name(), diffDescription(connDiff.diff), connectionSetDiff) + strList = append(strList, printDiff) } } - return printDiff + sort.Strings(strList) + res := strings.Join(strList, "") + return res } func diffDescription(diff DiffType) string { From fbe2077afb22be27632a83db21a1d2958e0f5775 Mon Sep 17 00:00:00 2001 From: shirim Date: Sun, 5 Nov 2023 16:02:25 +0200 Subject: [PATCH 42/42] lint comments --- cmd/analyzer/main.go | 27 ++++++++++------ cmd/analyzer/parse_args.go | 65 ++++++++++++++++++++------------------ 2 files changed, 52 insertions(+), 40 deletions(-) diff --git a/cmd/analyzer/main.go b/cmd/analyzer/main.go index 7b230fb2b..d84e93c6a 100644 --- a/cmd/analyzer/main.go +++ b/cmd/analyzer/main.go @@ -10,6 +10,13 @@ import ( "github.com/np-guard/vpc-network-config-analyzer/pkg/vpcmodel" ) +const ( + ParsingErr = "error parsing arguments:" + OutGenerationErr = "output generation error:" + InGenerationErr = "error generating cloud config from input vpc resources file:" + ErrorFormat = "%s %w" +) + func getOutputFormat(inArgs *InArgs) vpcmodel.OutFormat { switch *inArgs.OutputFormat { case TEXTFormat: @@ -59,7 +66,7 @@ func analysisPerVPCConfig(c *vpcmodel.VPCConfig, inArgs *InArgs, outFile string) outFormat := getOutputFormat(inArgs) output, err := og.Generate(outFormat, genOutFile) if err != nil { - return nil, fmt.Errorf("output generation error: %w", err) + return nil, fmt.Errorf(ErrorFormat, OutGenerationErr, err) } return output, nil @@ -74,13 +81,14 @@ func analysisDiffVPCConfig(c1, c2 *vpcmodel.VPCConfig, inArgs *InArgs, outFile s return nil, err } + var analysisOut *vpcmodel.VPCAnalysisOutput outFormat := getOutputFormat(inArgs) - output, err := og.Generate(outFormat, outFile) + analysisOut, err = og.Generate(outFormat, outFile) if err != nil { - return nil, fmt.Errorf("output generation error: %w", err) + return nil, fmt.Errorf(ErrorFormat, OutGenerationErr, err) } - return output, nil + return analysisOut, nil } // The actual main function @@ -91,7 +99,7 @@ func _main(cmdlineArgs []string) error { return nil } if err != nil { - return fmt.Errorf("error parsing arguments: %w", err) + return fmt.Errorf(ErrorFormat, ParsingErr, err) } rc, err := ibmvpc.ParseResourcesFromFile(*inArgs.InputConfigFile) @@ -101,7 +109,7 @@ func _main(cmdlineArgs []string) error { vpcConfigs, err := ibmvpc.VPCConfigsFromResources(rc, *inArgs.VPC, *inArgs.Debug) if err != nil { - return fmt.Errorf("error generating cloud config from input vpc resources file: %w", err) + return fmt.Errorf(ErrorFormat, InGenerationErr, err) } outFile := "" @@ -122,7 +130,8 @@ func _main(cmdlineArgs []string) error { i++ } - out, err := vpcmodel.AggregateVPCsOutput(outputPerVPC, getOutputFormat(inArgs), outFile) + var out string + out, err = vpcmodel.AggregateVPCsOutput(outputPerVPC, getOutputFormat(inArgs), outFile) if err != nil { return err } @@ -133,11 +142,11 @@ func _main(cmdlineArgs []string) error { var rc2ndForDiff *ibmvpc.ResourcesContainer rc2ndForDiff, err = ibmvpc.ParseResourcesFromFile(*inArgs.InputSecondConfigFile) if err != nil { - return fmt.Errorf("error parsing arguments: %w", err) + return fmt.Errorf(ErrorFormat, ParsingErr, err) } vpc2ndConfigs, err := ibmvpc.VPCConfigsFromResources(rc2ndForDiff, *inArgs.VPC, *inArgs.Debug) if err != nil { - return fmt.Errorf("error generating cloud config from input vpc resources file: %w", err) + return fmt.Errorf(ErrorFormat, InGenerationErr, err) } // For diff analysis each vpcConfigs have a single element c1 := getFirstCfg(vpcConfigs) diff --git a/cmd/analyzer/parse_args.go b/cmd/analyzer/parse_args.go index 05e93a813..e2ce0dad4 100644 --- a/cmd/analyzer/parse_args.go +++ b/cmd/analyzer/parse_args.go @@ -78,52 +78,55 @@ func ParseInArgs(cmdlineArgs []string) (*InArgs, error) { if err != nil { return nil, err } + err = errorInErgs(&args, flagset) + if err != nil { + return nil, err + } + err = notSupportedYetArgs(&args) + if err != nil { + return nil, err + } + return &args, nil +} + +func errorInErgs(args *InArgs, flagset *flag.FlagSet) error { if args.InputConfigFile == nil || *args.InputConfigFile == "" { flagset.PrintDefaults() - return nil, fmt.Errorf("missing parameter: vpc-config") - } - - fileForDiffSpecified := args.InputSecondConfigFile != nil && *args.InputSecondConfigFile != "" - if fileForDiffSpecified && *args.AnalysisType != allEndpointsDiff && - *args.AnalysisType != allSubnetsDiff { - return nil, fmt.Errorf("wrong analysis type %s for 2nd file (%v) specified for diff; "+ - "must be diff_all_endpoints or diff_all_subnets", *args.AnalysisType, *args.InputSecondConfigFile) + return fmt.Errorf("missing parameter: vpc-config") } - - if !fileForDiffSpecified && (*args.AnalysisType == allEndpointsDiff || - *args.AnalysisType == allSubnetsDiff) { - return nil, fmt.Errorf("missing parameter vpc-config-second for diff analysis %s", *args.AnalysisType) + if !supportedAnalysisTypes[*args.AnalysisType] { + flagset.PrintDefaults() + return fmt.Errorf("wrong analysis type %s; must be one of: %s", *args.AnalysisType, getSupportedValuesString(supportedAnalysisTypes)) } - if !supportedOutputFormats[*args.OutputFormat] { flagset.PrintDefaults() - return nil, fmt.Errorf("wrong output format %s; must be one of %s", *args.OutputFormat, getSupportedValuesString(supportedOutputFormats)) + return fmt.Errorf("wrong output format %s; must be one of %s", *args.OutputFormat, getSupportedValuesString(supportedOutputFormats)) } - - if *args.AnalysisType == allEndpointsDiff { - return nil, fmt.Errorf("diff_all_endpoints not supported yet") + diffAnalysis := *args.AnalysisType == allEndpointsDiff || *args.AnalysisType == allSubnetsDiff + fileForDiffSpecified := args.InputSecondConfigFile != nil && *args.InputSecondConfigFile != "" + if fileForDiffSpecified && !diffAnalysis { + return fmt.Errorf("wrong analysis type %s for 2nd file (%v) specified for diff", + *args.AnalysisType, *args.InputSecondConfigFile) } - - if *args.AnalysisType == allSubnetsDiff && *args.OutputFormat != TEXTFormat { - return nil, fmt.Errorf("currently only txt output format supported with diff_all_subnets") - } - - if !supportedAnalysisTypes[*args.AnalysisType] { - flagset.PrintDefaults() - return nil, fmt.Errorf("wrong analysis type %s; must be one of: %s", *args.AnalysisType, getSupportedValuesString(supportedAnalysisTypes)) + if !fileForDiffSpecified && diffAnalysis { + return fmt.Errorf("missing parameter vpc-config-second for diff analysis %s", *args.AnalysisType) } + return nil +} +func notSupportedYetArgs(args *InArgs) error { if *args.AnalysisType != allEndpoints && *args.OutputFormat != TEXTFormat && *args.OutputFormat != JSONFormat { - return nil, fmt.Errorf("currently only txt/json output format supported with %s analysis type", *args.AnalysisType) + return fmt.Errorf("currently only txt/json output format supported with %s analysis type", *args.AnalysisType) } - if *args.AnalysisType == singleSubnet && *args.Grouping { - return nil, fmt.Errorf("currently singleSubnet analysis type does not support grouping") + return fmt.Errorf("currently singleSubnet analysis type does not support grouping") } - if *args.OutputFormat == JSONFormat && *args.Grouping { - return nil, fmt.Errorf("json output format is not supported with grouping") + return fmt.Errorf("json output format is not supported with grouping") } - return &args, nil + if *args.AnalysisType == allSubnetsDiff && *args.OutputFormat != TEXTFormat { + return fmt.Errorf("currently only txt output format supported with diff_all_subnets") + } + return nil }