Skip to content

Commit

Permalink
163 semantic diff end to end new (#220)
Browse files Browse the repository at this point in the history
end-to-end support it diff_all_subnets; includes adding cli parms a test and code for executing with the cli parms
  • Loading branch information
ShiriMoran authored Nov 8, 2023
1 parent ca7ae6e commit 11b650f
Show file tree
Hide file tree
Showing 16 changed files with 1,992 additions and 95 deletions.
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

0 comments on commit 11b650f

Please sign in to comment.