-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add cli tests and update readme (#64)
* add cli tests and update readme Signed-off-by: adisos <[email protected]> * Update README.md Co-authored-by: Ziv Nevo <[email protected]> * Update README.md Co-authored-by: Ziv Nevo <[email protected]> * Update README.md Co-authored-by: Ziv Nevo <[email protected]> * Update README.md Signed-off-by: adisos <[email protected]> Co-authored-by: Ziv Nevo <[email protected]>
- Loading branch information
Showing
6 changed files
with
324 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,124 @@ | ||
# netpol-analyzer | ||
A Golang library for analyzing connectivity-configuration resources (a.k.a. network policies) | ||
|
||
## About netpol-analyzer | ||
This repo contains a Golang library and CLI for analyzing k8s connectivity-configuration resources (a.k.a. network policies). | ||
|
||
|
||
## CLI usage | ||
|
||
### Evaluate command | ||
``` | ||
Evaluate if a specific connection allowed | ||
Usage: | ||
k8snetpolicy evaluate [flags] | ||
Aliases: | ||
evaluate, eval, check, allow | ||
Examples: | ||
# Evaluate if a specific connection is allowed on given resources from dir path | ||
k8snetpolicy eval --dirpath ./resources_dir/ -s pod-1 -d pod-2 -p 80 | ||
# Evaluate if a specific connection is allowed on a live k8s cluster | ||
k8snetpolicy eval -k ./kube/config -s pod-1 -d pod-2 -p 80 | ||
Flags: | ||
--destination-ip string Destination (external) IP address | ||
--destination-namespace string Destination pod namespace (default "default") | ||
-d, --destination-pod string Destination pod name | ||
-p, --destination-port string Destination port (name or number) | ||
-h, --help help for evaluate | ||
--protocol string Protocol in use (tcp, udp, sctp) (default "tcp") | ||
--source-ip string Source (external) IP address | ||
-n, --source-namespace string Source pod namespace (default "default") | ||
-s, --source-pod string Source pod name, required | ||
Global Flags: | ||
-c, --context string Kubernetes context to use when evaluating connections in a live cluster | ||
--dirpath string Resources dir path when evaluating connections from a dir | ||
-k, --kubeconfig string Path and file to use for kubeconfig when evaluating connections in a live cluster (default "/home/adisos/.kube/config") | ||
``` | ||
|
||
### List command | ||
``` | ||
Lists all allowed connections based on the workloads and network policies | ||
defined | ||
Usage: | ||
k8snetpolicy list [flags] | ||
Examples: | ||
# Get list of allowed connections from resources dir path | ||
k8snetpolicy list --dirpath ./resources_dir/ | ||
# Get list of allowed connections from live k8s cluster | ||
k8snetpolicy list -k ./kube/config | ||
Flags: | ||
-h, --help help for list | ||
Global Flags: | ||
-c, --context string Kubernetes context to use when evaluating connections in a live cluster | ||
--dirpath string Resources dir path when evaluating connections from a dir | ||
-k, --kubeconfig string Path and file to use for kubeconfig when evaluating connections in a live cluster (default "/home/adisos/.kube/config") | ||
``` | ||
|
||
|
||
|
||
### Example outputs: | ||
``` | ||
$ k8snetpolicy eval --dirpath tests/onlineboutique -s adservice-77d5cd745d-t8mx4 -d emailservice-54c7c5d9d-vp27n -p 80 | ||
default/adservice-77d5cd745d-t8mx4 => default/emailservice-54c7c5d9d-vp27n over tcp/80: false | ||
$ k8snetpolicy list --dirpath tests/onlineboutique_workloads | ||
0.0.0.0-255.255.255.255 => default/redis-cart[Deployment] : All Connections | ||
default/adservice[Deployment] => default/adservice[Deployment] : All Connections | ||
default/cartservice[Deployment] => default/cartservice[Deployment] : All Connections | ||
default/checkoutservice[Deployment] => default/cartservice[Deployment] : TCP 7070 | ||
default/checkoutservice[Deployment] => default/checkoutservice[Deployment] : All Connections | ||
default/checkoutservice[Deployment] => default/currencyservice[Deployment] : TCP 7000 | ||
default/checkoutservice[Deployment] => default/emailservice[Deployment] : TCP 8080 | ||
default/checkoutservice[Deployment] => default/paymentservice[Deployment] : TCP 50051 | ||
default/checkoutservice[Deployment] => default/productcatalogservice[Deployment] : TCP 3550 | ||
default/checkoutservice[Deployment] => default/shippingservice[Deployment] : TCP 50051 | ||
default/currencyservice[Deployment] => default/currencyservice[Deployment] : All Connections | ||
default/emailservice[Deployment] => default/emailservice[Deployment] : All Connections | ||
default/frontend[Deployment] => default/adservice[Deployment] : TCP 9555 | ||
default/frontend[Deployment] => default/cartservice[Deployment] : TCP 7070 | ||
default/frontend[Deployment] => default/checkoutservice[Deployment] : TCP 5050 | ||
default/frontend[Deployment] => default/currencyservice[Deployment] : TCP 7000 | ||
default/frontend[Deployment] => default/frontend[Deployment] : All Connections | ||
default/frontend[Deployment] => default/productcatalogservice[Deployment] : TCP 3550 | ||
default/frontend[Deployment] => default/recommendationservice[Deployment] : TCP 8080 | ||
default/frontend[Deployment] => default/shippingservice[Deployment] : TCP 50051 | ||
default/loadgenerator[Deployment] => default/frontend[Deployment] : TCP 8080 | ||
default/paymentservice[Deployment] => default/paymentservice[Deployment] : All Connections | ||
default/productcatalogservice[Deployment] => default/productcatalogservice[Deployment] : All Connections | ||
default/recommendationservice[Deployment] => default/productcatalogservice[Deployment] : TCP 3550 | ||
default/recommendationservice[Deployment] => default/recommendationservice[Deployment] : All Connections | ||
default/redis-cart[Deployment] => 0.0.0.0-255.255.255.255 : All Connections | ||
default/redis-cart[Deployment] => default/redis-cart[Deployment] : All Connections | ||
default/shippingservice[Deployment] => default/shippingservice[Deployment] : All Connections | ||
``` | ||
|
||
## Build the project | ||
|
||
Make sure you have golang 1.18+ on your platform | ||
|
||
```commandline | ||
git clone [email protected]:np-guard/netpol-analyzer.git | ||
cd netpol-analyzer | ||
make mod | ||
make build | ||
``` | ||
|
||
Test your build by running `./bin/k8snetpolicy -h`. | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
package cmd | ||
|
||
import ( | ||
_ "embed" | ||
"errors" | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
var ( | ||
stdoutFile *os.File | ||
stderrFile *os.File | ||
testOutR *os.File | ||
testOutW *os.File | ||
testErrR *os.File | ||
testErrW *os.File | ||
|
||
//go:embed tests_outputs/test_legal_list.txt | ||
testLegalListOutput string | ||
) | ||
|
||
func preTestRun() { | ||
stdoutFile = os.Stdout | ||
stderrFile = os.Stderr | ||
testOutR, testOutW, _ = os.Pipe() | ||
os.Stdout = testOutW | ||
testErrR, testErrW, _ = os.Pipe() | ||
os.Stderr = testErrW | ||
} | ||
|
||
// finalize test and get its output | ||
func postTestRun(isErr bool) string { | ||
testOutW.Close() | ||
testErrW.Close() | ||
out, _ := io.ReadAll(testOutR) | ||
errOut, _ := io.ReadAll(testErrR) | ||
os.Stdout = stdoutFile | ||
os.Stderr = stderrFile | ||
actualOutput := string(out) | ||
actualErr := string(errOut) | ||
if isErr { | ||
return actualErr | ||
} | ||
return actualOutput | ||
} | ||
|
||
func runTest(test cmdTest, t *testing.T) { | ||
// run the test and get its output | ||
preTestRun() | ||
rootCmd.SetArgs(test.args) | ||
err := rootCmd.Execute() | ||
if !test.isErr { | ||
require.Nilf(t, err, "expected no errors, but got %v", err) | ||
} else { | ||
require.NotNil(t, err, "expected error, but got no error") | ||
} | ||
actual := postTestRun(test.isErr) | ||
|
||
// compare actual to test.expectedOutput | ||
if test.exact { | ||
assert.Equal(t, test.expectedOutput, actual, "error - unexpected output") | ||
} else if test.containment { | ||
isContained := strings.Contains(actual, test.expectedOutput) | ||
assert.True(t, isContained, "test %s error: %s not contained in %s", test.name, test.expectedOutput, actual) | ||
} else { | ||
assert.Error(t, errors.New(""), "test %s: missing containment or equality flag for test") | ||
} | ||
} | ||
|
||
type cmdTest struct { | ||
name string | ||
args []string | ||
expectedOutput string | ||
exact bool | ||
containment bool | ||
isErr bool | ||
} | ||
|
||
func TestCommannds(t *testing.T) { | ||
tests := []cmdTest{ | ||
{ | ||
name: "test_illegal_command", | ||
args: []string{"A"}, | ||
expectedOutput: "Error: unknown command \"A\" for \"k8snetpolicy\"", | ||
containment: true, | ||
isErr: true, | ||
}, | ||
|
||
{ | ||
name: "test_illegal_eval_no_args", | ||
args: []string{"eval"}, | ||
expectedOutput: "no source defined", | ||
containment: true, | ||
isErr: true, | ||
}, | ||
|
||
{ | ||
name: "test_illegal_eval_peer_not_found", | ||
args: []string{ | ||
"eval", | ||
"--dirpath", | ||
filepath.Join(getTestsDir(), "onlineboutique"), | ||
"-s", | ||
"default/adservice-77d5cd745d-t8mx4", | ||
"-d", | ||
"default/emailservice-54c7c5d9d-vp27n", | ||
"-p", | ||
"80"}, | ||
expectedOutput: "could not find peer default/default/adservice-77d5cd745d", | ||
containment: true, | ||
isErr: true, | ||
}, | ||
|
||
{ | ||
name: "test_legal_eval", | ||
args: []string{ | ||
"eval", | ||
"--dirpath", | ||
filepath.Join(getTestsDir(), "onlineboutique"), | ||
"-s", | ||
"adservice-77d5cd745d-t8mx4", | ||
"-d", | ||
"emailservice-54c7c5d9d-vp27n", | ||
"-p", | ||
"80"}, | ||
expectedOutput: "default/adservice-77d5cd745d-t8mx4 => default/emailservice-54c7c5d9d-vp27n over tcp/80: false\n", | ||
exact: true, | ||
isErr: false, | ||
}, | ||
|
||
{ | ||
name: "test_legal_list", | ||
args: []string{ | ||
"list", | ||
"--dirpath", | ||
filepath.Join(getTestsDir(), "onlineboutique"), | ||
}, | ||
expectedOutput: testLegalListOutput, | ||
exact: true, | ||
isErr: false, | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
runTest(test, t) | ||
} | ||
} | ||
|
||
func getTestsDir() string { | ||
currentDir, _ := os.Getwd() | ||
res := filepath.Join(currentDir, "..", "..", "..", "tests") | ||
return res | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
0.0.0.0-255.255.255.255 => default/redis-cart-78746d49dc[ReplicaSet] : All Connections | ||
default/adservice-77d5cd745d[ReplicaSet] => default/adservice-77d5cd745d[ReplicaSet] : All Connections | ||
default/cartservice-74f56fd4b[ReplicaSet] => default/cartservice-74f56fd4b[ReplicaSet] : All Connections | ||
default/checkoutservice-69c8ff664b[ReplicaSet] => default/cartservice-74f56fd4b[ReplicaSet] : TCP 7070 | ||
default/checkoutservice-69c8ff664b[ReplicaSet] => default/checkoutservice-69c8ff664b[ReplicaSet] : All Connections | ||
default/checkoutservice-69c8ff664b[ReplicaSet] => default/currencyservice-77654bbbdd[ReplicaSet] : TCP 7000 | ||
default/checkoutservice-69c8ff664b[ReplicaSet] => default/emailservice-54c7c5d9d[ReplicaSet] : TCP 8080 | ||
default/checkoutservice-69c8ff664b[ReplicaSet] => default/paymentservice-bbcbdc6b6[ReplicaSet] : TCP 50051 | ||
default/checkoutservice-69c8ff664b[ReplicaSet] => default/productcatalogservice-68765d49b6[ReplicaSet] : TCP 3550 | ||
default/checkoutservice-69c8ff664b[ReplicaSet] => default/shippingservice-5bd985c46d[ReplicaSet] : TCP 50051 | ||
default/currencyservice-77654bbbdd[ReplicaSet] => default/currencyservice-77654bbbdd[ReplicaSet] : All Connections | ||
default/emailservice-54c7c5d9d[ReplicaSet] => default/emailservice-54c7c5d9d[ReplicaSet] : All Connections | ||
default/frontend-99684f7f8[ReplicaSet] => default/adservice-77d5cd745d[ReplicaSet] : TCP 9555 | ||
default/frontend-99684f7f8[ReplicaSet] => default/cartservice-74f56fd4b[ReplicaSet] : TCP 7070 | ||
default/frontend-99684f7f8[ReplicaSet] => default/checkoutservice-69c8ff664b[ReplicaSet] : TCP 5050 | ||
default/frontend-99684f7f8[ReplicaSet] => default/currencyservice-77654bbbdd[ReplicaSet] : TCP 7000 | ||
default/frontend-99684f7f8[ReplicaSet] => default/frontend-99684f7f8[ReplicaSet] : All Connections | ||
default/frontend-99684f7f8[ReplicaSet] => default/productcatalogservice-68765d49b6[ReplicaSet] : TCP 3550 | ||
default/frontend-99684f7f8[ReplicaSet] => default/recommendationservice-5f8c456796[ReplicaSet] : TCP 8080 | ||
default/frontend-99684f7f8[ReplicaSet] => default/shippingservice-5bd985c46d[ReplicaSet] : TCP 50051 | ||
default/loadgenerator-555fbdc87d[ReplicaSet] => default/frontend-99684f7f8[ReplicaSet] : TCP 8080 | ||
default/loadgenerator-555fbdc87d[ReplicaSet] => default/loadgenerator-555fbdc87d[ReplicaSet] : All Connections | ||
default/paymentservice-bbcbdc6b6[ReplicaSet] => default/paymentservice-bbcbdc6b6[ReplicaSet] : All Connections | ||
default/productcatalogservice-68765d49b6[ReplicaSet] => default/productcatalogservice-68765d49b6[ReplicaSet] : All Connections | ||
default/recommendationservice-5f8c456796[ReplicaSet] => default/productcatalogservice-68765d49b6[ReplicaSet] : TCP 3550 | ||
default/recommendationservice-5f8c456796[ReplicaSet] => default/recommendationservice-5f8c456796[ReplicaSet] : All Connections | ||
default/redis-cart-78746d49dc[ReplicaSet] => 0.0.0.0-255.255.255.255 : All Connections | ||
default/redis-cart-78746d49dc[ReplicaSet] => default/redis-cart-78746d49dc[ReplicaSet] : All Connections | ||
default/shippingservice-5bd985c46d[ReplicaSet] => default/shippingservice-5bd985c46d[ReplicaSet] : All Connections |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters