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 new #220

Merged
merged 31 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 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
cdeff24
Merge remote-tracking branch 'origin/main'
ShiriMoran Oct 31, 2023
b9220e6
Merge remote-tracking branch 'origin/main'
ShiriMoran Nov 6, 2023
027d643
Merge remote-tracking branch 'origin/main'
ShiriMoran Nov 7, 2023
a6c60ee
parsing arguments for diff analysis
ShiriMoran Nov 1, 2023
30184db
Added end-to-end support for semantic diff of subnets
ShiriMoran Nov 2, 2023
8e1cedb
fix typo
ShiriMoran Nov 2, 2023
44c11d3
headerOfAnalyzedVPC to reflect diff
ShiriMoran Nov 5, 2023
146f8d6
added end-to-end test flow
ShiriMoran Nov 5, 2023
3658742
diff test added
ShiriMoran Nov 5, 2023
d9b1414
lint comments
ShiriMoran Nov 5, 2023
8f43305
somehow cherrypick missed it
ShiriMoran Nov 7, 2023
5b1fa71
Merge branch 'main' into 163_semantic_diff_endToEnd_new
ShiriMoran Nov 8, 2023
be9d669
Update pkg/ibmvpc/analysis_output_test.go
ShiriMoran Nov 8, 2023
0ce54a7
CR comments
ShiriMoran Nov 8, 2023
f01fb2c
Merge remote-tracking branch 'origin/163_semantic_diff_endToEnd_new' …
ShiriMoran Nov 8, 2023
adde85b
lint comments
ShiriMoran Nov 8, 2023
61f6343
Update cmd/analyzer/main.go
ShiriMoran Nov 8, 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
101 changes: 85 additions & 16 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,33 +109,74 @@ 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)
if err2 != nil {
return err2
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
}
outputPerVPC[i] = vpcAnalysisOutput
i++
fmt.Println(out)
} else {
return diffAnalysisMain(inArgs, vpcConfigs, outFile)
}
return nil
}

out, err := vpcmodel.AggregateVPCsOutput(outputPerVPC, getOutputFormat(inArgs), outFile)
func diffAnalysisMain(inArgs *InArgs, vpcConfigs map[string]*vpcmodel.VPCConfig, outFile string) error {
// ToDo SM: for diff analysis assume 2 configs only, the 2nd given through vpc-config-second
rc2ndForDiff, err := ibmvpc.ParseResourcesFromFile(*inArgs.InputSecondConfigFile)
if err != nil {
return err
return fmt.Errorf(ErrorFormat, ParsingErr, err)
}
fmt.Println(out)

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, single1 := getSingleCfg(vpcConfigs)
c2, single2 := getSingleCfg(vpc2ndConfigs)
if !single1 || !single2 {
return fmt.Errorf("for diff mode %v a single configuration should be provided "+
"for both -vpc-config and -vpc-config-second", *inArgs.AnalysisType)
}
analysisOutput, err2 := analysisDiffVPCConfig(c1, c2, inArgs, outFile)
if err2 != nil {
return err2
}
fmt.Println(analysisOutput.Output)
return nil
}

func getSingleCfg(vpcConfigs map[string]*vpcmodel.VPCConfig) (*vpcmodel.VPCConfig, bool) {
if len(vpcConfigs) > 1 {
return nil, false
}
for _, vpcConfig := range vpcConfigs {
return vpcConfig, true
}
return nil, false
}

func main() {
err := _main(os.Args[1:])
if err != 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
}
Loading
Loading