diff --git a/README.md b/README.md index 50ccb63..cd72a31 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ Usage of ./bin/net-top: input directory path (required) -outputfile string file path to store results + -format string + output format; must be either "json" or "yaml" (default "json") -netpols whether to synthesize NetworkPolicies to allow only the discovered connections -q runs quietly, reports only severe errors and results diff --git a/cmd/nettop/main_test.go b/cmd/nettop/main_test.go index 6f7e665..e88545e 100644 --- a/cmd/nettop/main_test.go +++ b/cmd/nettop/main_test.go @@ -8,6 +8,8 @@ import ( "path/filepath" "strings" "testing" + + "github.com/stretchr/testify/require" ) func TestConnectionsOutput(t *testing.T) { @@ -18,18 +20,11 @@ func TestConnectionsOutput(t *testing.T) { args := getTestArgs(dirPath, outFile, JSONFormat, false, false, false) err := detectTopology(args) - if err != nil { - t.Fatalf("expected err to be nil, but got %v", err) - } + require.Nil(t, err) res, err := compareFiles(expectedOutput, outFile) - - if err != nil { - t.Fatalf("expected err to be nil, but got %v", err) - } - if !res { - t.Fatalf("expected res to be true, but got false") - } + require.Nil(t, err) + require.True(t, res) os.Remove(outFile) } @@ -42,18 +37,11 @@ func TestConnectionsYamlOutput(t *testing.T) { args := getTestArgs(dirPath, outFile, YamlFormat, false, false, false) err := detectTopology(args) - if err != nil { - t.Fatalf("expected err to be nil, but got %v", err) - } + require.Nil(t, err) res, err := compareFiles(expectedOutput, outFile) - - if err != nil { - t.Fatalf("expected err to be nil, but got %v", err) - } - if !res { - t.Fatalf("expected res to be true, but got false") - } + require.Nil(t, err) + require.True(t, res) os.Remove(outFile) } @@ -66,18 +54,11 @@ func TestDirScan(t *testing.T) { args := getTestArgs(dirPath, outFile, JSONFormat, false, true, false) err := detectTopology(args) - if err != nil { - t.Fatalf("expected err to be nil, but got %v", err) - } + require.Nil(t, err) res, err := compareFiles(expectedOutput, outFile) - - if err != nil { - t.Fatalf("expected err to be nil, but got %v", err) - } - if !res { - t.Fatalf("expected res to be true, but got false") - } + require.Nil(t, err) + require.True(t, res) os.Remove(outFile) } @@ -110,16 +91,11 @@ func TestNetpolsJsonOutput(t *testing.T) { for testName, testDetails := range tests { args := getTestArgs(testDetails.dirPath, testDetails.outFile, JSONFormat, true, false, true) err := detectTopology(args) - if err != nil { - t.Fatalf("Test %v: expected Start to return no error, but got %v", testName, err) - } + require.Nilf(t, err, "on test %s", testName) + res, err := compareFiles(testDetails.expectedOutput, testDetails.outFile) - if err != nil { - t.Fatalf("Test %v: expected err to be nil, but got %v", testName, err) - } - if !res { - t.Fatalf("Test %v: expected res to be true, but got false", testName) - } + require.Nilf(t, err, "on test %s", testName) + require.Truef(t, res, "on test %s", testName) os.Remove(testDetails.outFile) } } @@ -132,18 +108,11 @@ func TestNetpolsYamlOutput(t *testing.T) { args := getTestArgs(dirPath, outFile, YamlFormat, true, false, false) err := detectTopology(args) - if err != nil { - t.Fatalf("expected err to be nil, but got %v", err) - } + require.Nil(t, err) res, err := compareFiles(expectedOutput, outFile) - - if err != nil { - t.Fatalf("expected err to be nil, but got %v", err) - } - if !res { - t.Fatalf("expected res to be true, but got false") - } + require.Nil(t, err) + require.True(t, res) os.Remove(outFile) } diff --git a/cmd/nettop/parse_args.go b/cmd/nettop/parse_args.go index 906fd3c..7eafabf 100644 --- a/cmd/nettop/parse_args.go +++ b/cmd/nettop/parse_args.go @@ -22,7 +22,7 @@ type InArgs struct { func ParseInArgs(args *InArgs) error { args.DirPath = flag.String("dirpath", "", "input directory path") args.OutputFile = flag.String("outputfile", "", "file path to store results") - args.OutputFormat = flag.String("format", JSONFormat, "output format (must be either json or yaml)") + args.OutputFormat = flag.String("format", JSONFormat, "output format; must be either \"json\" or \"yaml\"") args.SynthNetpols = flag.Bool("netpols", false, "whether to synthesize NetworkPolicies to allow only the discovered connections") args.Quiet = flag.Bool("q", false, "runs quietly, reports only severe errors and results") args.Verbose = flag.Bool("v", false, "runs with more informative messages printed to log") diff --git a/go.mod b/go.mod index a15856f..b25f5c9 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/np-guard/cluster-topology-analyzer go 1.17 require ( + github.com/stretchr/testify v1.8.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.23.8 k8s.io/apimachinery v0.23.8 @@ -10,6 +11,7 @@ require ( ) require ( + github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logr/logr v1.2.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/go-cmp v0.5.5 // indirect @@ -17,6 +19,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect golang.org/x/text v0.3.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index 3f00e34..9dccfc7 100644 --- a/go.sum +++ b/go.sum @@ -213,12 +213,15 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/pkg/controller/controller_test.go b/pkg/controller/controller_test.go index 360424b..3b4ff08 100644 --- a/pkg/controller/controller_test.go +++ b/pkg/controller/controller_test.go @@ -9,6 +9,8 @@ import ( "path/filepath" "strings" "testing" + + "github.com/stretchr/testify/require" ) // TestOutput calls controller.Start() with an example repo dir tests/onlineboutique/ , @@ -23,107 +25,64 @@ func TestPoliciesSynthesizerAPI(t *testing.T) { logger := NewDefaultLogger() synthesizer := NewPoliciesSynthesizer(WithLogger(logger)) netpols, err := synthesizer.PoliciesFromFolderPath(dirPath) - if err != nil { - t.Fatalf("expected no fatal errors, but got %v", err) - } + require.Nilf(t, err, "expected no fatal errors, but got %v", err) + fileScanningErrors := synthesizer.Errors() - if len(fileScanningErrors) > 0 { - t.Fatalf("expected no file-scanning errors, but got %v", fileScanningErrors) - } - if len(netpols) == 0 { - t.Fatalf("expected policies to be non-empty, but got empty") - } + require.Empty(t, fileScanningErrors) + require.NotEmpty(t, netpols) buf, _ := json.MarshalIndent(netpols, "", " ") fp, err := os.Create(outFile) - if err != nil { - t.Fatalf("failed opening output file: %v", err) - } + require.Nil(t, err, "failed opening output file") _, err = fp.Write(buf) - if err != nil { - t.Fatalf("failed writing to output file: %v", err) - } + require.Nil(t, err, "failed writing to output file") fp.Close() + res, err := compareFiles(expectedOutput, outFile) - if err != nil { - t.Fatalf("expected err to be nil, but got %v", err) - } - if !res { - t.Fatalf("expected res to be true, but got false") - } + require.Nil(t, err, "error comparing files") + require.True(t, res, "files not equal") os.Remove(outFile) } func TestPoliciesSynthesizerAPIFatalError(t *testing.T) { dirPath := filepath.Join(getTestsDir(), "badPath") - logger := NewDefaultLogger() synthesizer := NewPoliciesSynthesizer(WithLogger(logger)) netpols, err := synthesizer.PoliciesFromFolderPath(dirPath) - if err == nil { - t.Fatal("expected a fatal error, but got none") - } - fileScanningErrors := synthesizer.Errors() - if len(fileScanningErrors) != 1 { - t.Fatalf("expected 1 file-scanning error, but got %d", len(fileScanningErrors)) - } - if len(netpols) != 0 { - t.Fatalf("expected no policies, but got %d policies", len(netpols)) - } + require.NotNil(t, err) + require.Len(t, synthesizer.Errors(), 1) + require.Empty(t, netpols) } func TestPoliciesSynthesizerAPIFailFast(t *testing.T) { dirPath := filepath.Join(getTestsDir(), "bad_yamls") - synthesizer := NewPoliciesSynthesizer(WithStopOnError()) netpols, err := synthesizer.PoliciesFromFolderPath(dirPath) - if err != nil { - t.Fatalf("expected no fatal errors, but got %v", err) - } - fileScanningErrors := synthesizer.Errors() - if len(fileScanningErrors) != 1 { - t.Fatalf("expected 1 file-scanning error, but got %d", len(fileScanningErrors)) - } - if len(netpols) != 0 { - t.Fatalf("expected no policies, but got %d policies", len(netpols)) - } + require.Nil(t, err) + require.Len(t, synthesizer.Errors(), 1) + require.Empty(t, netpols) } func TestExtractConnectionsNoK8sResources(t *testing.T) { - testsDir := getTestsDir() - dirPath := filepath.Join(testsDir, "bad_yamls", "irrelevant_k8s_resources.yaml") + dirPath := filepath.Join(getTestsDir(), "bad_yamls", "irrelevant_k8s_resources.yaml") conns, errs := extractConnections(dirPath, false) - if len(errs) != 1 { - t.Fatalf("expected one error but got %d", len(errs)) - } - if len(conns) > 0 { - t.Fatalf("expected no conns but got %d", len(conns)) - } + require.Len(t, errs, 1) + require.Empty(t, conns) } func TestExtractConnectionsNoK8sResourcesFailFast(t *testing.T) { - testsDir := getTestsDir() - dirPath := filepath.Join(testsDir, "bad_yamls") + dirPath := filepath.Join(getTestsDir(), "bad_yamls") conns, errs := extractConnections(dirPath, true) - if len(errs) != 1 { - t.Fatalf("expected one error but got %d", len(errs)) - } - if len(conns) > 0 { - t.Fatalf("expected no conns but got %d", len(conns)) - } + require.Len(t, errs, 1) + require.Empty(t, conns) } func TestExtractConnectionsBadConfigMapRefs(t *testing.T) { - testsDir := getTestsDir() - dirPath := filepath.Join(testsDir, "bad_yamls", "bad_configmap_refs.yaml") + dirPath := filepath.Join(getTestsDir(), "bad_yamls", "bad_configmap_refs.yaml") conns, errs := extractConnections(dirPath, false) - if len(errs) != 3 { - t.Fatalf("expected 3 errors but got %d", len(errs)) - } - if len(conns) > 0 { - t.Fatalf("expected no conns but got %d", len(conns)) - } + require.Len(t, errs, 3) + require.Empty(t, conns) } func readLines(path string) ([]string, error) { diff --git a/pkg/controller/utils_test.go b/pkg/controller/utils_test.go index e1d9d12..2d4a28b 100644 --- a/pkg/controller/utils_test.go +++ b/pkg/controller/utils_test.go @@ -3,93 +3,67 @@ package controller import ( "path/filepath" "testing" + + "github.com/stretchr/testify/require" ) func TestGetK8sDeploymentResourcesBadYamlDocument(t *testing.T) { dirPath := filepath.Join(getTestsDir(), "bad_yamls", "document_with_syntax_error.yaml") objs, errs := getK8sDeploymentResources(dirPath, false) - if len(errs) != 1 { - t.Fatalf("expected one error but got %d", len(errs)) - } - if docID, err := errs[0].DocumentID(); docID != 6 || err != nil { - t.Fatalf("expected bad Document ID to be 6 but got %d", errs[0].docID) - } - if len(objs) != 1 { - t.Fatalf("expected 1 parsed file but got %d", len(objs)) - } - if len(objs[0].DeployObjects) != 6 { - t.Fatalf("expected 6 parsed objects but got %d", len(objs[0].DeployObjects)) - } + require.Len(t, errs, 1) + + docID, err := errs[0].DocumentID() + require.Equal(t, 6, docID) + require.Nil(t, err) + + require.Len(t, objs, 1) + require.Len(t, objs[0].DeployObjects, 6) } func TestGetK8sDeploymentResourcesBadYamlDocumentFailFast(t *testing.T) { dirPath := filepath.Join(getTestsDir(), "bad_yamls", "document_with_syntax_error.yaml") objs, errs := getK8sDeploymentResources(dirPath, true) - if len(errs) != 1 { - t.Fatalf("expected one error but got %d", len(errs)) - } - if docID, err := errs[0].DocumentID(); docID != 6 || err != nil { - t.Fatalf("expected bad Document ID to be 6 but got %d", errs[0].docID) - } - if len(objs) != 0 { - t.Fatalf("expected no parsed file but got %d", len(objs)) - } + require.Len(t, errs, 1) + + docID, err := errs[0].DocumentID() + require.Equal(t, 6, docID) + require.Nil(t, err) + + require.Empty(t, objs) } func TestGetK8sDeploymentResourcesNoK8sResource(t *testing.T) { dirPath := filepath.Join(getTestsDir(), "bad_yamls", "not_a_k8s_resource.yaml") objs, errs := getK8sDeploymentResources(dirPath, false) - if len(errs) != 1 { - t.Fatalf("expected one error but got %d", len(errs)) - } - if len(objs) != 1 { - t.Fatalf("expected 1 parsed file but got %d", len(objs)) - } - if len(objs[0].DeployObjects) != 1 { - t.Fatalf("expected 1 parsed object but got %d", len(objs[0].DeployObjects)) - } + require.Len(t, errs, 1) + require.Len(t, objs, 1) + require.Len(t, objs[0].DeployObjects, 1) } func TestGetK8sDeploymentResourcesNoYAMLs(t *testing.T) { dirPath := filepath.Join(getTestsDir(), "bad_yamls", "subdir2") objs, errs := getK8sDeploymentResources(dirPath, false) - if len(errs) != 1 { - t.Fatalf("expected one error but got %d", len(errs)) - } - if len(objs) != 0 { - t.Fatalf("expected no parsed files but got %d", len(objs)) - } + require.Len(t, errs, 1) + require.Empty(t, objs) } func TestGetK8sDeploymentResourcesBadDir(t *testing.T) { dirPath := filepath.Join(getTestsDir(), "bad_yamls", "subdir3") // doesn't exist objs, errs := getK8sDeploymentResources(dirPath, false) - if len(errs) != 1 { - t.Fatalf("expected 1 errors but got %d", len(errs)) - } - if len(objs) != 0 { - t.Fatalf("expected no parsed files but got %d", len(objs)) - } + require.Len(t, errs, 1) + require.Empty(t, objs) } func TestGetK8sDeploymentResourcesBadDirFailFast(t *testing.T) { dirPath := filepath.Join(getTestsDir(), "bad_yamls", "subdir3") // doesn't exist objs, errs := getK8sDeploymentResources(dirPath, true) - if len(errs) != 1 { - t.Fatalf("expected 1 error but got %d", len(errs)) - } - if len(objs) != 0 { - t.Fatalf("expected no parsed files but got %d", len(objs)) - } + require.Len(t, errs, 1) + require.Empty(t, objs) } func TestSearchDeploymentManifests(t *testing.T) { dirPath := filepath.Join(getTestsDir(), "bad_yamls") yamlFiles, errs := searchDeploymentManifests(dirPath, false) - if len(yamlFiles) != 5 { - t.Fatalf("expected 5 yamls but got %d", len(yamlFiles)) - } - if len(errs) > 0 { - t.Fatalf("expected no errors but got %d", len(errs)) - } + require.Empty(t, errs) + require.Len(t, yamlFiles, 5) }