Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

163 semantic diff end to end #215

Closed
wants to merge 45 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
78401ee
structs and main functionality of https://github.com/np-guard/vpc-net…
ShiriMoran Oct 12, 2023
9a4f947
Highlevel code and structs
ShiriMoran Oct 15, 2023
15970f4
subnetConnectivitySubtract code; still needs to fill inside functiona…
ShiriMoran Oct 16, 2023
9400063
Redefined made the connection set diff and added todos
ShiriMoran Oct 17, 2023
bf18d01
Minor reorgs
ShiriMoran Oct 17, 2023
625fa41
Export SubnetConnectivityMap and enable external creation for unit test
ShiriMoran Oct 17, 2023
d2fbe4e
Added grouping subnet unittesting as preliminery stage to writing uni…
ShiriMoran Oct 18, 2023
823503e
exporting functionality for unit test; SubnetConnectivitySubtract sho…
ShiriMoran Oct 18, 2023
769eb7a
semantic diff simple unit test
ShiriMoran Oct 18, 2023
5a652cc
improved semantic diff computation
ShiriMoran Oct 18, 2023
f735908
fixed a bug/typo, added ad-hoc printing functionality
ShiriMoran Oct 18, 2023
20b4e98
unit test written for current functionality
ShiriMoran Oct 18, 2023
ce31dda
lint comments
ShiriMoran Oct 19, 2023
6b0897b
Merge remote-tracking branch 'origin/main'
ShiriMoran Oct 23, 2023
e90af2a
identifying connections with external ip that intersects but are not…
ShiriMoran Oct 23, 2023
35a0338
getIntersectingConnections() anf related functionality seems redundan…
ShiriMoran Oct 24, 2023
096f1be
code for resizing written
ShiriMoran Oct 25, 2023
b544b11
handle error from GetSubnetsDiff properly
ShiriMoran Oct 25, 2023
cbbca1d
fix typos
ShiriMoran Oct 25, 2023
4e034c0
removed temp to be removed line
ShiriMoran Oct 25, 2023
51de142
added unit test for GetConnectivesWithSameIPBlocks
ShiriMoran Oct 25, 2023
d19471b
expanded unit test to include subtract
ShiriMoran Oct 26, 2023
6de6aef
computing diff properly for external nodes; among others, GetConnecti…
ShiriMoran Oct 26, 2023
04cd4be
Nodes of config computed and used
ShiriMoran Oct 26, 2023
21d9002
bug fixed: a single disjoint ipBlock may contain more than one cidr
ShiriMoran Oct 26, 2023
994cba0
another bug fixed; still another bug to fix
ShiriMoran Oct 26, 2023
e90bc41
some renaming to avoid confusion
ShiriMoran Oct 30, 2023
c07bc9b
find nodes by CIDR and not by name
ShiriMoran Oct 30, 2023
15f6cc8
fixed bug: Nodes and disjointBlocks computed now for both src and dst…
ShiriMoran Oct 30, 2023
52647b2
temp debug prints
ShiriMoran Oct 30, 2023
840d9a6
finalize unit test
ShiriMoran Oct 30, 2023
36c72b9
merge with main
ShiriMoran Oct 30, 2023
7b8da69
lint comments
ShiriMoran Oct 30, 2023
562efa4
commented non used code
ShiriMoran Oct 30, 2023
ee8bc55
lint comments
ShiriMoran Oct 30, 2023
0223c9c
begun adding required parms to command line
ShiriMoran Nov 1, 2023
e62638e
merge with main
ShiriMoran Nov 1, 2023
b546133
merge with main
ShiriMoran Nov 1, 2023
f69ab20
parsing arguments for diff analysis
ShiriMoran Nov 1, 2023
bce07cb
Added end-to-end support for semantic diff of subnets
ShiriMoran Nov 2, 2023
8c29515
fix typo
ShiriMoran Nov 2, 2023
2c2dfa0
headerOfAnalyzedVPC to reflect diff
ShiriMoran Nov 5, 2023
7076e25
added end-to-end test flow
ShiriMoran Nov 5, 2023
4e29c3b
diff test added
ShiriMoran Nov 5, 2023
fbe2077
lint comments
ShiriMoran Nov 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 74 additions & 15 deletions cmd/analyzer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -36,12 +43,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)
Expand All @@ -57,12 +66,31 @@ 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
}

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
}

var analysisOut *vpcmodel.VPCAnalysisOutput
outFormat := getOutputFormat(inArgs)
analysisOut, err = og.Generate(outFormat, outFile)
if err != nil {
return nil, fmt.Errorf(ErrorFormat, OutGenerationErr, err)
}

return analysisOut, 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 {
Expand All @@ -71,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)
Expand All @@ -81,30 +109,61 @@ 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 := ""
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++
}

var out string
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(ErrorFormat, ParsingErr, err)
}
vpc2ndConfigs, err := ibmvpc.VPCConfigsFromResources(rc2ndForDiff, *inArgs.VPC, *inArgs.Debug)
if err != nil {
return fmt.Errorf(ErrorFormat, InGenerationErr, err)
}
// For diff analysis each vpcConfigs have a single element
c1 := getFirstCfg(vpcConfigs)
c2 := getFirstCfg(vpc2ndConfigs)
analysisOutput, err2 := analysisDiffVPCConfig(c1, c2, inArgs, outFile)
if err2 != nil {
return err2
}
outputPerVPC[i] = vpcAnalysisOutput
i++
fmt.Println(analysisOutput.Output)
}
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
}

Expand Down
81 changes: 55 additions & 26 deletions cmd/analyzer/parse_args.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -27,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{
Expand All @@ -41,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 {
Expand All @@ -60,6 +65,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,
Expand All @@ -72,32 +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")
return fmt.Errorf("missing parameter: vpc-config")
}

if !supportedOutputFormats[*args.OutputFormat] {
if !supportedAnalysisTypes[*args.AnalysisType] {
flagset.PrintDefaults()
return nil, fmt.Errorf("wrong output format %s; must be one of %s", *args.OutputFormat, getSupportedValuesString(supportedOutputFormats))
return fmt.Errorf("wrong analysis type %s; must be one of: %s", *args.AnalysisType, getSupportedValuesString(supportedAnalysisTypes))
}

if !supportedAnalysisTypes[*args.AnalysisType] {
if !supportedOutputFormats[*args.OutputFormat] {
flagset.PrintDefaults()
return nil, fmt.Errorf("wrong analysis type %s; must be one of: %s", *args.AnalysisType, getSupportedValuesString(supportedAnalysisTypes))
return fmt.Errorf("wrong output format %s; must be one of %s", *args.OutputFormat, getSupportedValuesString(supportedOutputFormats))
}
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 !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
}
50 changes: 41 additions & 9 deletions pkg/ibmvpc/analysis_output_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -50,6 +51,7 @@ const (
suffixOutFileDebugSubnet = "_analysisPerSubnetSeparately"
suffixOutFileSubnetsLevel = "subnetsBased_withPGW"
suffixOutFileSubnetsLevelNoPGW = "subnetsBased_withoutPGW"
suffixOutFileDiffSubnets = "subnetsDiff"
txtOutSuffix = ".txt"
debugOutSuffix = "_debug.txt"
mdOutSuffix = ".md"
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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{}
Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -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 {
Expand All @@ -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)
Expand Down Expand Up @@ -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, 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
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/ibmvpc/examples/acl_testing5subnetsBased_withPGW.txt
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Loading
Loading