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

Refactor implementation #10

Merged
merged 7 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
38 changes: 30 additions & 8 deletions cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import (
"encoding/json"
"fmt"
"io"
"sort"
"strconv"
"strings"
"text/tabwriter"

"github.com/cilium/cilium/api/v1/client/endpoint"
"github.com/spf13/cobra"
"golang.org/x/exp/maps"
)

func init() {
Expand Down Expand Up @@ -40,6 +42,20 @@ type derivedFromEntry struct {
Name string `json:"name"`
}

func lessDerivedFromEntry(x, y *derivedFromEntry) bool {
ret := strings.Compare(x.Direction, y.Direction)
if ret == 0 {
ret = strings.Compare(x.Kind, y.Kind)
}
if ret == 0 {
ret = strings.Compare(x.Namespace, y.Namespace)
}
if ret == 0 {
ret = strings.Compare(x.Name, y.Name)
}
return ret < 0
Comment on lines +46 to +56
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if the first ret is negative, it will be overwritten by other string.Compare()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chez-shanpu
It should be blocked by if ret == 0 ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yokaze I was mistaken. thank you :) lgtm

}

func parseDerivedFromEntry(input []string, direction string) derivedFromEntry {
val := derivedFromEntry{
Direction: direction,
Expand Down Expand Up @@ -77,22 +93,28 @@ func runList(ctx context.Context, w io.Writer, name string) error {
return err
}

policyList := make([]derivedFromEntry, 0)
// The same rule appears multiple times in the response, so we need to dedup it
policySet := make(map[derivedFromEntry]struct{})
chez-shanpu marked this conversation as resolved.
Show resolved Hide resolved

ingressRules := response.Payload.Status.Policy.Realized.L4.Ingress
for _, rule := range ingressRules {
for _, r := range rule.DerivedFromRules {
policyList = append(policyList, parseDerivedFromEntry(r, directionIngress))
entry := parseDerivedFromEntry(r, directionIngress)
policySet[entry] = struct{}{}
}
}

egressRules := response.Payload.Status.Policy.Realized.L4.Egress
for _, rule := range egressRules {
for _, r := range rule.DerivedFromRules {
policyList = append(policyList, parseDerivedFromEntry(r, directionEgress))
entry := parseDerivedFromEntry(r, directionEgress)
policySet[entry] = struct{}{}
}
}

policyList := maps.Keys(policySet)
sort.Slice(policyList, func(i, j int) bool { return lessDerivedFromEntry(&policyList[i], &policyList[j]) })

switch rootOptions.output {
case OutputJson:
text, err := json.MarshalIndent(policyList, "", " ")
Expand All @@ -103,13 +125,13 @@ func runList(ctx context.Context, w io.Writer, name string) error {
return err
case OutputSimple:
tw := tabwriter.NewWriter(w, 0, 1, 1, ' ', 0)
_, err := tw.Write([]byte("DIRECTION\tKIND\tNAMESPACE\tNAME\n"))
if err != nil {
return err
if !rootOptions.noHeaders {
if _, err := tw.Write([]byte("DIRECTION\tKIND\tNAMESPACE\tNAME\n")); err != nil {
return err
}
}
for _, p := range policyList {
_, err := tw.Write([]byte(fmt.Sprintf("%v\t%v\t%v\t%v\n", p.Direction, p.Kind, p.Namespace, p.Name)))
if err != nil {
if _, err := tw.Write([]byte(fmt.Sprintf("%v\t%v\t%v\t%v\n", p.Direction, p.Kind, p.Namespace, p.Name))); err != nil {
return err
}
}
Expand Down
2 changes: 2 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ var rootOptions struct {
proxySelector string
proxyPort uint16
output string
noHeaders bool
}

func init() {
rootCmd.PersistentFlags().StringVarP(&rootOptions.namespace, "namespace", "n", "default", "namespace of a pod")
rootCmd.PersistentFlags().StringVar(&rootOptions.proxySelector, "proxy-selector", "app.kubernetes.io/name=cilium-agent-proxy", "label selector to find the proxy pods")
rootCmd.PersistentFlags().Uint16Var(&rootOptions.proxyPort, "proxy-port", 8080, "port number of the proxy endpoints")
rootCmd.PersistentFlags().StringVarP(&rootOptions.output, "output", "o", OutputSimple, "output format")
rootCmd.PersistentFlags().BoolVar(&rootOptions.noHeaders, "no-headers", false, "stop printing header")
}

var rootCmd = &cobra.Command{}
Expand Down
15 changes: 13 additions & 2 deletions e2e/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,33 @@ start:
$(MAKE) --no-print-directory wait-for-workloads

run-test-pod-%:
@# https://github.com/orgs/aquaproj/discussions/2964
@echo Hello | yq > /dev/null
cat testdata/template/ubuntu.yaml | \
yq '.metadata.name = "$*"' | \
yq '.spec.selector.matchLabels = {"test": "$*"}' | \
yq '.spec.template.metadata.labels = {"test": "$*"}' | \
yq '.spec.template.metadata.labels = {"test": "$*", "group": "test"}' | \
kubectl apply -f -

.PHONY: install-test-pod
install-test-pod:
$(MAKE) --no-print-directory run-test-pod-self
$(MAKE) --no-print-directory run-test-pod-l3-ingress-explicit-allow
$(MAKE) --no-print-directory run-test-pod-l3-ingress-no-rule
$(MAKE) --no-print-directory run-test-pod-l3-ingress-implicit-deny
$(MAKE) --no-print-directory run-test-pod-l3-ingress-explicit-deny
$(MAKE) --no-print-directory run-test-pod-l3-egress-implicit-deny
$(MAKE) --no-print-directory run-test-pod-l3-egress-explicit-deny

$(MAKE) --no-print-directory run-test-pod-l4-ingress-explicit-allow-any
$(MAKE) --no-print-directory run-test-pod-l4-ingress-explicit-allow-tcp
$(MAKE) --no-print-directory run-test-pod-l4-ingress-explicit-deny-any
$(MAKE) --no-print-directory run-test-pod-l4-ingress-explicit-deny-udp
$(MAKE) --no-print-directory run-test-pod-l4-egress-explicit-deny-any
$(MAKE) --no-print-directory run-test-pod-l4-egress-explicit-deny-tcp
$(MAKE) --no-print-directory wait-for-workloads

kubectl apply -f testdata/policy/l3.yaml
kubectl apply -f testdata/policy/l4.yaml

.PHONY: install-policy-viewer
install-policy-viewer:
Expand Down
101 changes: 62 additions & 39 deletions e2e/list_test.go
Original file line number Diff line number Diff line change
@@ -1,71 +1,94 @@
package e2e

import (
"encoding/json"
"fmt"
"reflect"
"strings"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func testJson(g Gomega, target []byte, expected string) {
var t, e interface{}
err := json.Unmarshal(target, &t)
g.Expect(err).NotTo(HaveOccurred(), "actual: %s", target)

err = json.Unmarshal([]byte(expected), &e)
g.Expect(err).NotTo(HaveOccurred(), "expected: %s", expected)

if !reflect.DeepEqual(t, e) {
err := fmt.Errorf("compare failed. actual: %s, expected: %s", target, expected)
g.Expect(err).NotTo(HaveOccurred())
}
}

func testList() {
cases := []struct {
Selector string
Expected string
}{
{
Selector: "test=self",
Expected: `[{
"direction": "EGRESS",
"kind": "CiliumNetworkPolicy",
"namespace": "default",
"name": "l3-egress"
}]`,
Expected: `EGRESS,CiliumNetworkPolicy,default,l3-baseline
EGRESS,CiliumNetworkPolicy,default,l3-egress
EGRESS,CiliumNetworkPolicy,default,l4-egress
INGRESS,CiliumNetworkPolicy,default,l3-baseline`,
},
{
Selector: "test=l3-ingress-explicit-allow",
Expected: `[{
"direction": "INGRESS",
"kind": "CiliumNetworkPolicy",
"namespace": "default",
"name": "l3-ingress-explicit-allow"
}]`,
Expected: `EGRESS,CiliumNetworkPolicy,default,l3-baseline
INGRESS,CiliumNetworkPolicy,default,l3-baseline
INGRESS,CiliumNetworkPolicy,default,l3-ingress-explicit-allow`,
},
{
Selector: "test=l3-ingress-no-rule",
Expected: `[]`,
Selector: "test=l3-ingress-implicit-deny",
Expected: `EGRESS,CiliumNetworkPolicy,default,l3-baseline
INGRESS,CiliumNetworkPolicy,default,l3-baseline`,
},
{
Selector: "test=l3-ingress-explicit-deny",
Expected: `[{
"direction": "INGRESS",
"kind": "CiliumNetworkPolicy",
"namespace": "default",
"name": "l3-ingress-explicit-deny"
}]`,
Expected: `EGRESS,CiliumNetworkPolicy,default,l3-baseline
INGRESS,CiliumNetworkPolicy,default,l3-baseline
INGRESS,CiliumNetworkPolicy,default,l3-ingress-explicit-deny`,
},
{
Selector: "test=l3-egress-implicit-deny",
Expected: `EGRESS,CiliumNetworkPolicy,default,l3-baseline
INGRESS,CiliumNetworkPolicy,default,l3-baseline`,
},
{
Selector: "test=l3-egress-explicit-deny",
Expected: `EGRESS,CiliumNetworkPolicy,default,l3-baseline
INGRESS,CiliumNetworkPolicy,default,l3-baseline`,
},
{
Selector: "test=l4-ingress-explicit-allow-any",
Expected: `EGRESS,CiliumNetworkPolicy,default,l3-baseline
INGRESS,CiliumNetworkPolicy,default,l3-baseline
INGRESS,CiliumNetworkPolicy,default,l4-ingress-explicit-allow-any`,
},
{
Selector: "test=l4-ingress-explicit-allow-tcp",
Expected: `EGRESS,CiliumNetworkPolicy,default,l3-baseline
INGRESS,CiliumNetworkPolicy,default,l3-baseline
INGRESS,CiliumNetworkPolicy,default,l4-ingress-explicit-allow-tcp`,
},
{
Selector: "test=l4-ingress-explicit-deny-any",
Expected: `EGRESS,CiliumNetworkPolicy,default,l3-baseline
INGRESS,CiliumNetworkPolicy,default,l3-baseline
INGRESS,CiliumNetworkPolicy,default,l4-ingress-explicit-deny-any`,
},
{
Selector: "test=l4-ingress-explicit-deny-udp",
Expected: `EGRESS,CiliumNetworkPolicy,default,l3-baseline
INGRESS,CiliumNetworkPolicy,default,l3-baseline
INGRESS,CiliumNetworkPolicy,default,l4-ingress-explicit-deny-udp`,
},
{
Selector: "test=l4-egress-explicit-deny-any",
Expected: `EGRESS,CiliumNetworkPolicy,default,l3-baseline
INGRESS,CiliumNetworkPolicy,default,l3-baseline`,
},
{
Selector: "test=l4-egress-explicit-deny-tcp",
Expected: `EGRESS,CiliumNetworkPolicy,default,l3-baseline
INGRESS,CiliumNetworkPolicy,default,l3-baseline`,
},
}

It("should list applied policies", func() {
for _, c := range cases {
podName := onePodByLabelSelector(Default, "default", c.Selector)
result := runViewerSafe(Default, nil, "list", "-o=json", podName)
testJson(Default, result, c.Expected)
result := runViewerSafe(Default, nil, "list", "-o=json", "--no-headers", podName)
result = jqSafe(Default, result, "-r", ".[] | [.direction, .kind, .namespace, .name] | @csv")
resultString := strings.Replace(string(result), `"`, "", -1)
Expect(resultString).To(Equal(c.Expected), "compare failed. selector: %s, actual: %s, expected: %s", c.Selector, resultString, c.Expected)
}
})
}
15 changes: 15 additions & 0 deletions e2e/testdata/policy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# NetworkPolicy Configuration for Test Pods

| Target | From self (Egress) | To pod (Ingress) |
|-|-|-|
| l3-ingress-explicit-allow | allow | allow |
| l3-ingress-implicit-deny | allow | - |
| l3-ingress-explicit-deny | allow | deny |
| l3-egress-implicit-deny | - | - |
| l3-egress-explicit-deny | deny | - |
| l4-ingress-explicit-allow-any | allow (L4) | allow (L4) |
| l4-ingress-explicit-allow-tcp | allow (L4) | allow (L4) |
| l4-ingress-explicit-deny-any | allow (L4) | deny (L4) |
| l4-ingress-explicit-deny-udp | allow (L4) | deny (L4) |
| l4-egress-explicit-deny-any | deny (L4) | - |
| l4-egress-explicit-deny-tcp | deny (L4) | - |
20 changes: 20 additions & 0 deletions e2e/testdata/policy/l3.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: l3-baseline
spec:
endpointSelector:
matchLabels:
k8s:group: test
ingressDeny:
- fromEndpoints:
- matchLabels:
k8s:test: scapegoat
egressDeny:
- toEndpoints:
- matchLabels:
k8s:test: scapegoat
---
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: l3-egress
spec:
Expand All @@ -10,6 +27,9 @@ spec:
- toEndpoints:
- matchLabels:
k8s:test: l3-ingress-explicit-allow
- toEndpoints:
- matchLabels:
k8s:test: l3-ingress-no-rule
- toEndpoints:
- matchLabels:
k8s:test: l3-ingress-implicit-deny
Expand Down
Loading
Loading