From 0de2a988cc701cb294d917964d05f3ef0b4ff474 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Thu, 15 Jul 2021 17:54:21 +0200 Subject: [PATCH 01/20] kg: add new handler for rendering the topology graph --- Dockerfile | 2 +- cmd/kg/handlers.go | 118 +++++++++++++++++++++++++++++++++++++++++++++ cmd/kg/main.go | 5 +- 3 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 cmd/kg/handlers.go diff --git a/Dockerfile b/Dockerfile index 2b2402f5..c6889071 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ ARG GOARCH ARG ALPINE_VERSION=v3.12 LABEL maintainer="squat " RUN echo -e "https://alpine.global.ssl.fastly.net/alpine/$ALPINE_VERSION/main\nhttps://alpine.global.ssl.fastly.net/alpine/$ALPINE_VERSION/community" > /etc/apk/repositories && \ - apk add --no-cache ipset iptables ip6tables wireguard-tools + apk add --no-cache ipset iptables ip6tables wireguard-tools graphviz COPY --from=cni bridge host-local loopback portmap /opt/cni/bin/ COPY bin/linux/$GOARCH/kg /opt/bin/ ENTRYPOINT ["/opt/bin/kg"] diff --git a/cmd/kg/handlers.go b/cmd/kg/handlers.go new file mode 100644 index 00000000..00724297 --- /dev/null +++ b/cmd/kg/handlers.go @@ -0,0 +1,118 @@ +package main + +import ( + "bytes" + "fmt" + "io" + "mime" + "net/http" + "os" + "os/exec" + + "github.com/squat/kilo/pkg/mesh" +) + +type GraphHandler struct { + mesh *mesh.Mesh + granularity mesh.Granularity +} + +func (h *GraphHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + ns, err := h.mesh.Nodes().List() + if err != nil { + http.Error(w, fmt.Sprintf("failed to list nodes: %v", err), 500) + return + } + ps, err := h.mesh.Peers().List() + if err != nil { + http.Error(w, fmt.Sprintf("failed to list peers: %v", err), 500) + return + } + + var hostname string + subnet := mesh.DefaultKiloSubnet + nodes := make(map[string]*mesh.Node) + for _, n := range ns { + if n.Ready() { + nodes[n.Name] = n + hostname = n.Name + } + if n.WireGuardIP != nil { + subnet = n.WireGuardIP + } + } + subnet.IP = subnet.IP.Mask(subnet.Mask) + if len(nodes) == 0 { + http.Error(w, "did not find any valid Kilo nodes in the cluster", 500) + return + } + peers := make(map[string]*mesh.Peer) + for _, p := range ps { + if p.Ready() { + peers[p.Name] = p + } + } + topo, err := mesh.NewTopology(nodes, peers, h.granularity, hostname, 0, []byte{}, subnet, nodes[hostname].PersistentKeepalive, nil) + if err != nil { + http.Error(w, fmt.Sprintf("failed to create topology: %v", err), 500) + return + } + + dot, err := topo.Dot() + if err != nil { + http.Error(w, fmt.Sprintf("failed to generate graph: %v", err), 500) + } + + buf := bytes.NewBufferString(dot) + + format := r.URL.Query().Get("format") + if format == "" { + format = "png" + } else if format == ".dot" || format == ".gv" { + // If the raw dot data is requested, return it as string. + // This allows client-side rendering rather than server-side. + w.Write(buf.Bytes()) + return + } + + command := exec.Command("dot", "-T"+format) + command.Stderr = os.Stderr + + stdin, err := command.StdinPipe() + if err != nil { + http.Error(w, err.Error(), 500) + return + } + + _, err = io.Copy(stdin, buf) + if err != nil { + http.Error(w, err.Error(), 500) + return + } + + err = stdin.Close() + if err != nil { + http.Error(w, err.Error(), 500) + return + } + + output, err := command.Output() + if err != nil { + http.Error(w, fmt.Sprintf("unable to execute dot: %v (is graphviz package installed?)", err), 500) + return + } + + mimeType := mime.TypeByExtension("." + format) + if mimeType == "" { + mimeType = "application/octet-stream" + } + + w.Write(output) +} + +type HealthHandler struct { +} + +func (h *HealthHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) +} diff --git a/cmd/kg/main.go b/cmd/kg/main.go index fa298838..94b7e2f0 100644 --- a/cmd/kg/main.go +++ b/cmd/kg/main.go @@ -196,9 +196,8 @@ func Main() error { { // Run the HTTP server. mux := http.NewServeMux() - mux.HandleFunc("/health", func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusOK) - }) + mux.Handle("/health", &HealthHandler{}) + mux.Handle("/graph", &GraphHandler{m, gr}) mux.Handle("/metrics", promhttp.HandlerFor(r, promhttp.HandlerOpts{})) l, err := net.Listen("tcp", *listen) if err != nil { From aa93f89eae73b01f5155c7dee34f9b33e1276f42 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Thu, 15 Jul 2021 17:57:04 +0200 Subject: [PATCH 02/20] docker: add missing fonts for rasterized graphviz output formats --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c6889071..72fc4150 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ ARG GOARCH ARG ALPINE_VERSION=v3.12 LABEL maintainer="squat " RUN echo -e "https://alpine.global.ssl.fastly.net/alpine/$ALPINE_VERSION/main\nhttps://alpine.global.ssl.fastly.net/alpine/$ALPINE_VERSION/community" > /etc/apk/repositories && \ - apk add --no-cache ipset iptables ip6tables wireguard-tools graphviz + apk add --no-cache ipset iptables ip6tables wireguard-tools graphviz font-bitstream-type1 COPY --from=cni bridge host-local loopback portmap /opt/cni/bin/ COPY bin/linux/$GOARCH/kg /opt/bin/ ENTRYPOINT ["/opt/bin/kg"] From cd96616a91f14751b97e5f2a85d1059cf0f509d9 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Thu, 15 Jul 2021 18:54:08 +0200 Subject: [PATCH 03/20] add missing license header --- cmd/kg/handlers.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cmd/kg/handlers.go b/cmd/kg/handlers.go index 00724297..4e05a0a9 100644 --- a/cmd/kg/handlers.go +++ b/cmd/kg/handlers.go @@ -1,3 +1,17 @@ +// Copyright 2019 the Kilo authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( From cfb680e99a0746265fbe01f5306ef623f00c363b Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Fri, 16 Jul 2021 11:54:00 +0200 Subject: [PATCH 04/20] kg: do not export handlers --- cmd/kg/handlers.go | 8 ++++---- cmd/kg/main.go | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/kg/handlers.go b/cmd/kg/handlers.go index 4e05a0a9..f3c9fcf3 100644 --- a/cmd/kg/handlers.go +++ b/cmd/kg/handlers.go @@ -26,12 +26,12 @@ import ( "github.com/squat/kilo/pkg/mesh" ) -type GraphHandler struct { +type graphHandler struct { mesh *mesh.Mesh granularity mesh.Granularity } -func (h *GraphHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { +func (h *graphHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ns, err := h.mesh.Nodes().List() if err != nil { http.Error(w, fmt.Sprintf("failed to list nodes: %v", err), 500) @@ -124,9 +124,9 @@ func (h *GraphHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Write(output) } -type HealthHandler struct { +type healthHandler struct { } -func (h *HealthHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) { +func (h *healthHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) } diff --git a/cmd/kg/main.go b/cmd/kg/main.go index 94b7e2f0..743e7afd 100644 --- a/cmd/kg/main.go +++ b/cmd/kg/main.go @@ -196,8 +196,8 @@ func Main() error { { // Run the HTTP server. mux := http.NewServeMux() - mux.Handle("/health", &HealthHandler{}) - mux.Handle("/graph", &GraphHandler{m, gr}) + mux.Handle("/health", &healthHandler{}) + mux.Handle("/graph", &graphHandler{m, gr}) mux.Handle("/metrics", promhttp.HandlerFor(r, promhttp.HandlerOpts{})) l, err := net.Listen("tcp", *listen) if err != nil { From ae4e4173c381f96c284120e72aa6c3af3caae81c Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Fri, 16 Jul 2021 11:55:21 +0200 Subject: [PATCH 05/20] use http package for status codes --- cmd/kg/handlers.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cmd/kg/handlers.go b/cmd/kg/handlers.go index f3c9fcf3..5c84b97a 100644 --- a/cmd/kg/handlers.go +++ b/cmd/kg/handlers.go @@ -34,12 +34,12 @@ type graphHandler struct { func (h *graphHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ns, err := h.mesh.Nodes().List() if err != nil { - http.Error(w, fmt.Sprintf("failed to list nodes: %v", err), 500) + http.Error(w, fmt.Sprintf("failed to list nodes: %v", err), http.StatusInternalServerError) return } ps, err := h.mesh.Peers().List() if err != nil { - http.Error(w, fmt.Sprintf("failed to list peers: %v", err), 500) + http.Error(w, fmt.Sprintf("failed to list peers: %v", err), http.StatusInternalServerError) return } @@ -57,7 +57,7 @@ func (h *graphHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } subnet.IP = subnet.IP.Mask(subnet.Mask) if len(nodes) == 0 { - http.Error(w, "did not find any valid Kilo nodes in the cluster", 500) + http.Error(w, "did not find any valid Kilo nodes in the cluster", http.StatusInternalServerError) return } peers := make(map[string]*mesh.Peer) @@ -68,13 +68,13 @@ func (h *graphHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } topo, err := mesh.NewTopology(nodes, peers, h.granularity, hostname, 0, []byte{}, subnet, nodes[hostname].PersistentKeepalive, nil) if err != nil { - http.Error(w, fmt.Sprintf("failed to create topology: %v", err), 500) + http.Error(w, fmt.Sprintf("failed to create topology: %v", err), http.StatusInternalServerError) return } dot, err := topo.Dot() if err != nil { - http.Error(w, fmt.Sprintf("failed to generate graph: %v", err), 500) + http.Error(w, fmt.Sprintf("failed to generate graph: %v", err), http.StatusInternalServerError) } buf := bytes.NewBufferString(dot) @@ -94,25 +94,25 @@ func (h *graphHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { stdin, err := command.StdinPipe() if err != nil { - http.Error(w, err.Error(), 500) + http.Error(w, err.Error(), http.StatusInternalServerError) return } _, err = io.Copy(stdin, buf) if err != nil { - http.Error(w, err.Error(), 500) + http.Error(w, err.Error(), http.StatusInternalServerError) return } err = stdin.Close() if err != nil { - http.Error(w, err.Error(), 500) + http.Error(w, err.Error(), http.StatusInternalServerError) return } output, err := command.Output() if err != nil { - http.Error(w, fmt.Sprintf("unable to execute dot: %v (is graphviz package installed?)", err), 500) + http.Error(w, fmt.Sprintf("unable to execute dot: %v (is graphviz package installed?)", err), http.StatusInternalServerError) return } From 46542e44ec73acb67a5a3b762af52996e21c4e0c Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Fri, 16 Jul 2021 11:57:11 +0200 Subject: [PATCH 06/20] keep checks for errors in a single line --- cmd/kg/handlers.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cmd/kg/handlers.go b/cmd/kg/handlers.go index 5c84b97a..6b95ead0 100644 --- a/cmd/kg/handlers.go +++ b/cmd/kg/handlers.go @@ -98,14 +98,12 @@ func (h *graphHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - _, err = io.Copy(stdin, buf) - if err != nil { + if _, err = io.Copy(stdin, buf); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } - err = stdin.Close() - if err != nil { + if err = stdin.Close(); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } From 02b1c5223b2c0c296b63188104ca3937f39a5c2c Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Fri, 16 Jul 2021 12:00:40 +0200 Subject: [PATCH 07/20] simplify error message about failed invocation of dot --- cmd/kg/handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/kg/handlers.go b/cmd/kg/handlers.go index 6b95ead0..7f864a1b 100644 --- a/cmd/kg/handlers.go +++ b/cmd/kg/handlers.go @@ -110,7 +110,7 @@ func (h *graphHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { output, err := command.Output() if err != nil { - http.Error(w, fmt.Sprintf("unable to execute dot: %v (is graphviz package installed?)", err), http.StatusInternalServerError) + http.Error(w, "unable to render graph", http.StatusInternalServerError) return } From bf0b637311d819879f38e2b4b20564e6318830ef Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Fri, 16 Jul 2021 12:08:13 +0200 Subject: [PATCH 08/20] pass node hostname and subnet to graph handler --- cmd/kg/handlers.go | 12 ++++-------- cmd/kg/main.go | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/cmd/kg/handlers.go b/cmd/kg/handlers.go index 7f864a1b..e3392d4d 100644 --- a/cmd/kg/handlers.go +++ b/cmd/kg/handlers.go @@ -19,6 +19,7 @@ import ( "fmt" "io" "mime" + "net" "net/http" "os" "os/exec" @@ -29,6 +30,8 @@ import ( type graphHandler struct { mesh *mesh.Mesh granularity mesh.Granularity + hostname *string + subnet *net.IPNet } func (h *graphHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -43,19 +46,12 @@ func (h *graphHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - var hostname string - subnet := mesh.DefaultKiloSubnet nodes := make(map[string]*mesh.Node) for _, n := range ns { if n.Ready() { nodes[n.Name] = n - hostname = n.Name - } - if n.WireGuardIP != nil { - subnet = n.WireGuardIP } } - subnet.IP = subnet.IP.Mask(subnet.Mask) if len(nodes) == 0 { http.Error(w, "did not find any valid Kilo nodes in the cluster", http.StatusInternalServerError) return @@ -66,7 +62,7 @@ func (h *graphHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { peers[p.Name] = p } } - topo, err := mesh.NewTopology(nodes, peers, h.granularity, hostname, 0, []byte{}, subnet, nodes[hostname].PersistentKeepalive, nil) + topo, err := mesh.NewTopology(nodes, peers, h.granularity, *h.hostname, 0, []byte{}, h.subnet, nodes[*h.hostname].PersistentKeepalive, nil) if err != nil { http.Error(w, fmt.Sprintf("failed to create topology: %v", err), http.StatusInternalServerError) return diff --git a/cmd/kg/main.go b/cmd/kg/main.go index 743e7afd..3ac6ae4f 100644 --- a/cmd/kg/main.go +++ b/cmd/kg/main.go @@ -197,7 +197,7 @@ func Main() error { // Run the HTTP server. mux := http.NewServeMux() mux.Handle("/health", &healthHandler{}) - mux.Handle("/graph", &graphHandler{m, gr}) + mux.Handle("/graph", &graphHandler{m, gr, hostname, s}) mux.Handle("/metrics", promhttp.HandlerFor(r, promhttp.HandlerOpts{})) l, err := net.Listen("tcp", *listen) if err != nil { From b41654f0ce223fbb58cc0d953d8d6e7bd815e7bd Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Fri, 16 Jul 2021 12:08:28 +0200 Subject: [PATCH 09/20] use SVG as default format for graph handler --- cmd/kg/handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/kg/handlers.go b/cmd/kg/handlers.go index e3392d4d..da001ebd 100644 --- a/cmd/kg/handlers.go +++ b/cmd/kg/handlers.go @@ -77,7 +77,7 @@ func (h *graphHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { format := r.URL.Query().Get("format") if format == "" { - format = "png" + format = "svg" } else if format == ".dot" || format == ".gv" { // If the raw dot data is requested, return it as string. // This allows client-side rendering rather than server-side. From 641bd6a9e9a3a4b6fa2cd622fc8b8e315520da4c Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Fri, 16 Jul 2021 12:10:13 +0200 Subject: [PATCH 10/20] register health handler with HandleFunc --- cmd/kg/handlers.go | 5 +---- cmd/kg/main.go | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/cmd/kg/handlers.go b/cmd/kg/handlers.go index da001ebd..3296a7ac 100644 --- a/cmd/kg/handlers.go +++ b/cmd/kg/handlers.go @@ -118,9 +118,6 @@ func (h *graphHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Write(output) } -type healthHandler struct { -} - -func (h *healthHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) { +func healthHandler(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) } diff --git a/cmd/kg/main.go b/cmd/kg/main.go index 3ac6ae4f..3baeec56 100644 --- a/cmd/kg/main.go +++ b/cmd/kg/main.go @@ -196,7 +196,7 @@ func Main() error { { // Run the HTTP server. mux := http.NewServeMux() - mux.Handle("/health", &healthHandler{}) + mux.HandleFunc("/health", healthHandler) mux.Handle("/graph", &graphHandler{m, gr, hostname, s}) mux.Handle("/metrics", promhttp.HandlerFor(r, promhttp.HandlerOpts{})) l, err := net.Listen("tcp", *listen) From 5a6c878fb5b58775853b6fbd52e9f9c71d40b721 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Fri, 16 Jul 2021 12:23:00 +0200 Subject: [PATCH 11/20] add option for selecting layout to graph handler and using circo as new default --- cmd/kg/handlers.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/cmd/kg/handlers.go b/cmd/kg/handlers.go index 3296a7ac..42134878 100644 --- a/cmd/kg/handlers.go +++ b/cmd/kg/handlers.go @@ -76,16 +76,27 @@ func (h *graphHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { buf := bytes.NewBufferString(dot) format := r.URL.Query().Get("format") - if format == "" { - format = "svg" - } else if format == ".dot" || format == ".gv" { + if format == "dot" || format == "gv" { // If the raw dot data is requested, return it as string. // This allows client-side rendering rather than server-side. w.Write(buf.Bytes()) return + } else if format == "" { + format = "svg" + } else if format != "png" && format != "bmp" && format != "fig" && format != "gif" && format != "json" && format != "ps" { + http.Error(w, "unsupported format", http.StatusInternalServerError) + return + } + + layout := r.URL.Query().Get("layout") + if layout == "" { + layout = "circo" + } else if layout != "dot" && layout != "neato" && format != "twopi" && format != "fdp" { + http.Error(w, "unsupported layout", http.StatusInternalServerError) + return } - command := exec.Command("dot", "-T"+format) + command := exec.Command("dot", "-K"+layout, "-T"+format) command.Stderr = os.Stderr stdin, err := command.StdinPipe() From 08bf9b1cb4734ae96fe63d55a14a12644a3b816b Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Fri, 16 Jul 2021 12:37:41 +0200 Subject: [PATCH 12/20] e2e: add tests for HTTP handlers --- Makefile | 2 +- e2e/handlers.sh | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 e2e/handlers.sh diff --git a/Makefile b/Makefile index f7d02f2c..9ec640a9 100644 --- a/Makefile +++ b/Makefile @@ -209,7 +209,7 @@ $(BASH_UNIT): chmod +x $@ e2e: container $(KIND_BINARY) $(KUBECTL_BINARY) $(BASH_UNIT) bin/$(OS)/$(ARCH)/kgctl - KILO_IMAGE=$(IMAGE):$(ARCH)-$(VERSION) KIND_BINARY=$(KIND_BINARY) KUBECTL_BINARY=$(KUBECTL_BINARY) KGCTL_BINARY=$(shell pwd)/bin/$(OS)/$(ARCH)/kgctl $(BASH_UNIT) $(BASH_UNIT_FLAGS) ./e2e/setup.sh ./e2e/full-mesh.sh ./e2e/location-mesh.sh ./e2e/multi-cluster.sh ./e2e/teardown.sh + KILO_IMAGE=$(IMAGE):$(ARCH)-$(VERSION) KIND_BINARY=$(KIND_BINARY) KUBECTL_BINARY=$(KUBECTL_BINARY) KGCTL_BINARY=$(shell pwd)/bin/$(OS)/$(ARCH)/kgctl $(BASH_UNIT) $(BASH_UNIT_FLAGS) ./e2e/setup.sh ./e2e/full-mesh.sh ./e2e/location-mesh.sh ./e2e/multi-cluster.sh ./e2e/handlers.sh ./e2e/teardown.sh header: .header @HEADER=$$(cat .header); \ diff --git a/e2e/handlers.sh b/e2e/handlers.sh new file mode 100644 index 00000000..02525582 --- /dev/null +++ b/e2e/handlers.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091 +. lib.sh + +setup_suite() { + # shellcheck disable=SC2016 + _kubectl patch ds -n kube-system kilo -p '{"spec": {"template":{"spec":{"containers":[{"name":"kilo","args":["--hostname=$(NODE_NAME)","--create-interface=false","--kubeconfig=/etc/kubernetes/kubeconfig","--mesh-granularity=full"]}]}}}}' + block_until_ready_by_name kube-system kilo-userspace + _kubectl wait pod -l app.kubernetes.io/name=adjacency --for=condition=Ready --timeout 3m +} + +test_graph_handler() { + assert "curl_pod http://10.4.0.1:1107/graph?format=svg&layout=circo | grep -q ' Date: Fri, 16 Jul 2021 13:18:29 +0200 Subject: [PATCH 13/20] e2e: fix and simplify handler tests --- e2e/handlers.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/e2e/handlers.sh b/e2e/handlers.sh index 02525582..867868d8 100644 --- a/e2e/handlers.sh +++ b/e2e/handlers.sh @@ -4,14 +4,13 @@ setup_suite() { # shellcheck disable=SC2016 - _kubectl patch ds -n kube-system kilo -p '{"spec": {"template":{"spec":{"containers":[{"name":"kilo","args":["--hostname=$(NODE_NAME)","--create-interface=false","--kubeconfig=/etc/kubernetes/kubeconfig","--mesh-granularity=full"]}]}}}}' block_until_ready_by_name kube-system kilo-userspace _kubectl wait pod -l app.kubernetes.io/name=adjacency --for=condition=Ready --timeout 3m } test_graph_handler() { assert "curl_pod http://10.4.0.1:1107/graph?format=svg&layout=circo | grep -q ' Date: Fri, 16 Jul 2021 13:31:31 +0200 Subject: [PATCH 14/20] add should comments to assertions --- e2e/handlers.sh | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/e2e/handlers.sh b/e2e/handlers.sh index 867868d8..9f99b991 100644 --- a/e2e/handlers.sh +++ b/e2e/handlers.sh @@ -9,16 +9,18 @@ setup_suite() { } test_graph_handler() { - assert "curl_pod http://10.4.0.1:1107/graph?format=svg&layout=circo | grep -q ' 0 ))" "metrics handler should provide metric: kilo_nodes > 0" } From 0789b4f9fa92bd5430eeb2c7c9e17b1c37c4f680 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Fri, 16 Jul 2021 13:34:35 +0200 Subject: [PATCH 15/20] e2s: use assert_fail instead of assert _not --- e2e/handlers.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/handlers.sh b/e2e/handlers.sh index 9f99b991..5af8d3f3 100644 --- a/e2e/handlers.sh +++ b/e2e/handlers.sh @@ -12,8 +12,8 @@ test_graph_handler() { assert "curl_pod http://10.4.0.1:1107/graph?format=svg&layout=circo | grep -q ' Date: Sun, 18 Jul 2021 23:56:13 +0200 Subject: [PATCH 16/20] add missing mime-type header for graph handler --- cmd/kg/handlers.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/kg/handlers.go b/cmd/kg/handlers.go index 42134878..986b06ca 100644 --- a/cmd/kg/handlers.go +++ b/cmd/kg/handlers.go @@ -126,6 +126,7 @@ func (h *graphHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { mimeType = "application/octet-stream" } + w.Header().Add("content-type", mimeType) w.Write(output) } From 9cc5f05f8ca20678109e17b491c5164752b7c094 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Sun, 18 Jul 2021 23:57:26 +0200 Subject: [PATCH 17/20] use switch/case statements for validating formats / layouts --- cmd/kg/handlers.go | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/cmd/kg/handlers.go b/cmd/kg/handlers.go index 986b06ca..2c7504ff 100644 --- a/cmd/kg/handlers.go +++ b/cmd/kg/handlers.go @@ -76,22 +76,32 @@ func (h *graphHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { buf := bytes.NewBufferString(dot) format := r.URL.Query().Get("format") - if format == "dot" || format == "gv" { + switch format { + case "": + format = "svg" + case "dot", "gv": // If the raw dot data is requested, return it as string. // This allows client-side rendering rather than server-side. w.Write(buf.Bytes()) return - } else if format == "" { - format = "svg" - } else if format != "png" && format != "bmp" && format != "fig" && format != "gif" && format != "json" && format != "ps" { + + case "svg", "png", "bmp", "fig", "gif", "json", "ps": + // Accepted format + + default: http.Error(w, "unsupported format", http.StatusInternalServerError) return } layout := r.URL.Query().Get("layout") - if layout == "" { + switch layout { + case "": layout = "circo" - } else if layout != "dot" && layout != "neato" && format != "twopi" && format != "fdp" { + + case "circo", "dot", "neato", "twopi", "fdp": + // Accepted layout + + default: http.Error(w, "unsupported layout", http.StatusInternalServerError) return } From 4c0381b3aa9cc03c74428cbc265586b0e7c49916 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Thu, 22 Jul 2021 12:22:31 +0200 Subject: [PATCH 18/20] e2e: fix handlers tests Co-authored-by: leonnicolas <60091705+leonnicolas@users.noreply.github.com> --- e2e/handlers.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/handlers.sh b/e2e/handlers.sh index 5af8d3f3..03e9baf5 100644 --- a/e2e/handlers.sh +++ b/e2e/handlers.sh @@ -9,7 +9,7 @@ setup_suite() { } test_graph_handler() { - assert "curl_pod http://10.4.0.1:1107/graph?format=svg&layout=circo | grep -q ' 0 ))" "metrics handler should provide metric: kilo_nodes > 0" + assert "(( $(curl_pod http://10.4.0.1:1107/metrics | grep -E ^kilo_nodes | cut -d " " -f 2) > 0 ))" "metrics handler should provide metric: kilo_nodes > 0" } From 8f7ab0b871f35fd2cac4d73106221eb9daf364ab Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 9 Aug 2021 21:37:04 +0200 Subject: [PATCH 19/20] graph-handler: add missing font to Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 72fc4150..25974d13 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ ARG GOARCH ARG ALPINE_VERSION=v3.12 LABEL maintainer="squat " RUN echo -e "https://alpine.global.ssl.fastly.net/alpine/$ALPINE_VERSION/main\nhttps://alpine.global.ssl.fastly.net/alpine/$ALPINE_VERSION/community" > /etc/apk/repositories && \ - apk add --no-cache ipset iptables ip6tables wireguard-tools graphviz font-bitstream-type1 + apk add --no-cache ipset iptables ip6tables wireguard-tools graphviz font-bitstream-type1 font-noto COPY --from=cni bridge host-local loopback portmap /opt/cni/bin/ COPY bin/linux/$GOARCH/kg /opt/bin/ ENTRYPOINT ["/opt/bin/kg"] From b34e7f6c2491865020f5600df21b2b2ddeca3ace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Serv=C3=A9n=20Mar=C3=ADn?= Date: Wed, 18 Aug 2021 13:18:45 +0200 Subject: [PATCH 20/20] Dockerfile: remove unnecessary font MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit leaves Noto as the only font package, as one font package is sufficient for the container. Signed-off-by: Lucas Servén Marín --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 25974d13..3b1d4185 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ ARG GOARCH ARG ALPINE_VERSION=v3.12 LABEL maintainer="squat " RUN echo -e "https://alpine.global.ssl.fastly.net/alpine/$ALPINE_VERSION/main\nhttps://alpine.global.ssl.fastly.net/alpine/$ALPINE_VERSION/community" > /etc/apk/repositories && \ - apk add --no-cache ipset iptables ip6tables wireguard-tools graphviz font-bitstream-type1 font-noto + apk add --no-cache ipset iptables ip6tables wireguard-tools graphviz font-noto COPY --from=cni bridge host-local loopback portmap /opt/cni/bin/ COPY bin/linux/$GOARCH/kg /opt/bin/ ENTRYPOINT ["/opt/bin/kg"]