diff --git a/README.md b/README.md index 8772df6..af66ee6 100644 --- a/README.md +++ b/README.md @@ -5,23 +5,31 @@ Currently, this only generates a diagram similar to https://github.com/kubernete For examples of the generated diagrams, see [Examples](#examples) below. ## Implementations -There are two implementations, bash script version and go version. Bash script version is just a wrapper to run go version inside container. + +There are two implementations, bash script version and go version. Bash script version is just a wrapper to run go version inside a Docker container. ## Prerequisites + ### Bash script version + `k8sviz.sh` requires: + - bash - getopt - docker To build a container image (optional), it requires: + - make ### Go version + `k8sviz` requires: + - dot (graphviz) command To build binary, it requires: + - make - go @@ -33,33 +41,45 @@ To build binary, it requires: | k8sviz 0.3.3 or later | No | Yes | ## Installation + ### Bash script version + Just download `k8sviz.sh` file and add execute permission. + ```shell -$ curl -LO https://raw.githubusercontent.com/mkimuram/k8sviz/master/k8sviz.sh -$ chmod u+x k8sviz.sh +curl -LO https://raw.githubusercontent.com/mkimuram/k8sviz/master/k8sviz.sh +chmod u+x k8sviz.sh ``` ### Go version + Build the binary with below commands: + ```shell -$ git clone https://github.com/mkimuram/k8sviz.git -$ cd k8sviz -$ make build +git clone https://github.com/mkimuram/k8sviz.git +cd k8sviz +make build ``` -`icons` directory needs to be in the same directory to the k8sviz binary. +By default, the `icons` directory is expected to be in the same directory as the `k8sviz` binary. So, move them to the proper directory (Replace `PATH_TO_INSTALL` as you like). + ```shell -$ PATH_TO_INSTALL=$HOME/bin -$ cp bin/k8sviz ${PATH_TO_INSTALL} -$ cp -r icons ${PATH_TO_INSTALL} +PATH_TO_INSTALL=$HOME/bin +cp bin/k8sviz ${PATH_TO_INSTALL} +cp -r icons ${PATH_TO_INSTALL} ``` +In case you wish to not put them in the same directory, you can run `k8sviz` with the command line flag `--icons` to pass the path to the directory containing the required icons. +The path can either be relative to the binary (e.g. `../share/icons`) or absolute (e.g. `/usr/share/k8sviz/icons`). + ## Usage + ### Bash script version + ```shell -$ ./k8sviz.sh --help +> ./k8sviz.sh --help + USAGE: ./k8sviz.sh [flags] args flags: -n,--namespace: The namespace to visualize. (default: 'default') @@ -72,72 +92,90 @@ flags: - ⚠️ WARNING - If you are using Mac, only short options can be used. - If you would like to use long options, you can install gnu-getopt and enable it by defining - `FLAGS_GETOPT_CMD` environment variable. - ```shell - $ brew install gnu-getopt - $ export FLAGS_GETOPT_CMD=/usr/local/opt/gnu-getopt/bin/getopt - $ ./k8sviz.sh -h - ``` + If you are using Mac, only short options can be used. + If you would like to use long options, you can install gnu-getopt and enable it by defining + `FLAGS_GETOPT_CMD` environment variable. + + ```shell + brew install gnu-getopt + export FLAGS_GETOPT_CMD=/usr/local/opt/gnu-getopt/bin/getopt + ./k8sviz.sh -h + ``` - 📝NOTE - If you can't pull the container image or need to build it by yourself, - you can do it by `make image-build`. It would be helpful if you specify - `DEVEL_IMAGE` and `DEVEL_TAG` to make the image name the same to the - default one (Below example will set image name like `mkimuram/k8sviz:0.3.4`). - ```shell - $ DEVEL_IMAGE=mkimuram/k8sviz DEVEL_TAG=$(cat version.txt) make image-build - ``` - - An example use case of creating custom image is to include AWS SDK or Google Cloud SDK. - To create a custom image that include AWS SDK, run below command: - ```shell - $ DEVEL_IMAGE=mkimuram/k8sviz DEVEL_TAG=$(cat version.txt) TARGET=aws make image-build - ``` - To create a custom image that include Google Cloud SDK, run below command: - ```shell - $ DEVEL_IMAGE=mkimuram/k8sviz DEVEL_TAG=$(cat version.txt) TARGET=gcloud make image-build - ``` + If you can't pull the container image or need to build it by yourself, + you can do it by `make image-build`. It would be helpful if you specify + `DEVEL_IMAGE` and `DEVEL_TAG` to make the image name the same to the + default one (Below example will set image name like `mkimuram/k8sviz:0.3.4`). + + ```shell + DEVEL_IMAGE=mkimuram/k8sviz DEVEL_TAG=$(cat version.txt) make image-build + ``` + + An example use case of creating custom image is to include AWS SDK or Google Cloud SDK. + To create a custom image that include AWS SDK, run below command: + + ```shell + DEVEL_IMAGE=mkimuram/k8sviz DEVEL_TAG=$(cat version.txt) TARGET=aws make image-build + ``` + + To create a custom image that include Google Cloud SDK, run below command: + + ```shell + DEVEL_IMAGE=mkimuram/k8sviz DEVEL_TAG=$(cat version.txt) TARGET=gcloud make image-build + ``` ### Go version + ```shell -$ ./k8sviz -h +> ./k8sviz -h + Usage of ./k8sviz: + -i string + path of directory containing icons (shorthand) (default "icons") + -icons string + path of directory containing icons (default "icons") -kubeconfig string - absolute path to the kubeconfig file (default "/home/user1/.kube/config") + absolute path to the kubeconfig file (default "/home/mathym/.kube/config") -n string - namespace to visualize (shorthand) (default "default") + namespace to visualize (shorthand) (default "default") -namespace string - namespace to visualize (default "default") + namespace to visualize (default "default") -o string - output filename (shorthand) (default "k8sviz.out") + output filename (shorthand) (default "k8sviz.out") -outfile string - output filename (default "k8sviz.out") + output filename (default "k8sviz.out") -t string - type of output (shorthand) (default "dot") + type of output (shorthand) (default "dot") -type string - type of output (default "dot") + type of output (default "dot") ``` ## Examples + Examples are only shown for old bash script version, but current go version should work in the same way. ### Examples for tutorial deployments in default namespace + - Generate dot file for namespace `default` - ```shell - ./k8sviz.sh -n default -o default.dot - ``` + + ```shell + ./k8sviz.sh -n default -o default.dot + ``` + - Generate png file for namespace `default` - ```shell - $ ./k8sviz.sh -n default -t png -o default.png - ``` + + ```shell + ./k8sviz.sh -n default -t png -o default.png + ``` + - Output for [an example wordpress deployment](https://kubernetes.io/docs/tutorials/stateful-application/mysql-wordpress-persistent-volume/) will be like below: - [default.dot](./examples/wordpress/default.dot) - [default.png](./examples/wordpress/default.png): + - Output for [an example cassandra deployment with statefulset](https://kubernetes.io/docs/tutorials/stateful-application/cassandra/) will be like below: - [default.dot](./examples/cassandra/default.dot) - [default.png](./examples/cassandra/default.png): @@ -145,16 +183,21 @@ Examples are only shown for old bash script version, but current go version shou ### Examples for more complex deployment ([kubeflow](https://www.kubeflow.org/docs/started/k8s/kfctl-k8s-istio/) case) + - Generate dot file for namespace `kubeflow` and `istio-system` - ```shell - $ ./k8sviz.sh -n kubeflow -o examples/kubeflow/kubeflow.dot - $ ./k8sviz.sh -n istio-system -o examples/kubeflow/istio-system.dot - ``` + + ```shell + ./k8sviz.sh -n kubeflow -o examples/kubeflow/kubeflow.dot + ./k8sviz.sh -n istio-system -o examples/kubeflow/istio-system.dot + ``` + - Generate png file for namespace `kubeflow` and `istio-system` - ```shell - $ ./k8sviz.sh -n kubeflow -t png -o examples/kubeflow/kubeflow.png - $ ./k8sviz.sh -n istio-system -t png -o examples/kubeflow/istio-system.png - ``` + + ```shell + ./k8sviz.sh -n kubeflow -t png -o examples/kubeflow/kubeflow.png + ./k8sviz.sh -n istio-system -t png -o examples/kubeflow/istio-system.png + ``` + - Output: - [kubeflow.dot](./examples/kubeflow/kubeflow.dot) - [istio-system.dot](./examples/kubeflow/istio-system.dot) @@ -167,4 +210,5 @@ Examples are only shown for old bash script version, but current go version shou ## License + This project is licensed under the Apache License - see the [LICENSE file](./LICENSE) for details diff --git a/cmd/k8sviz/main.go b/cmd/k8sviz/main.go index de62d60..ce889df 100644 --- a/cmd/k8sviz/main.go +++ b/cmd/k8sviz/main.go @@ -24,19 +24,23 @@ const ( defaultNamespace = "default" defaultOutFile = "k8sviz.out" defaultOutType = "dot" + defaultIconsDir = "icons" descNamespaceOpt = "namespace to visualize" descOutFileOpt = "output filename" descOutTypeOpt = "type of output" + descIconsDirOpt = "path of directory containing icons" descShortOptSuffix = " (shorthand)" ) var ( clientset *kubernetes.Clientset dir string + iconsPath string // Flags namespace string outFile string outType string + iconsDir string ) func init() { @@ -55,6 +59,8 @@ func init() { flag.StringVar(&outFile, "o", defaultOutFile, descOutFileOpt+descShortOptSuffix) flag.StringVar(&outType, "type", defaultOutType, descOutTypeOpt) flag.StringVar(&outType, "t", defaultOutType, descOutTypeOpt+descShortOptSuffix) + flag.StringVar(&iconsDir, "icons", defaultIconsDir, descIconsDirOpt) + flag.StringVar(&iconsDir, "i", defaultIconsDir, descIconsDirOpt+descShortOptSuffix) flag.Parse() // use the current context in kubeconfig @@ -83,6 +89,12 @@ func init() { fmt.Fprintf(os.Stderr, "Failed to find the directory of this command: %v\n", err) os.Exit(1) } + + if filepath.IsAbs(iconsDir) { + iconsPath = iconsDir + } else { + iconsPath = filepath.Join(dir, iconsDir) + } } func main() { @@ -97,7 +109,14 @@ func main() { os.Exit(1) } - g := graph.NewGraph(res, dir) + if _, err := os.Stat(iconsPath); err != nil { + if os.IsNotExist(err) { + fmt.Fprintf(os.Stderr, "Icons cannot be found at path: %v\n", err) + os.Exit(1) + } + } + + g := graph.NewGraph(res, dir, iconsPath) if outType == "dot" { if err := g.WriteDotFile(outFile); err != nil { diff --git a/k8sviz.sh b/k8sviz.sh index 014d28d..44f6877 100755 --- a/k8sviz.sh +++ b/k8sviz.sh @@ -1,4 +1,4 @@ -#! /bin/bash +#!/usr/bin/env bash #### Variables #### NAMESPACE="default" diff --git a/pkg/graph/graph.go b/pkg/graph/graph.go index 2507277..1d8aab6 100644 --- a/pkg/graph/graph.go +++ b/pkg/graph/graph.go @@ -17,14 +17,15 @@ import ( // Graph represents a graph of k8s resources type Graph struct { - dir string - res *resources.Resources - gviz *gographviz.Graph + dir string + iconsPath string + res *resources.Resources + gviz *gographviz.Graph } // NewGraph returns a Graph of k8s resources -func NewGraph(res *resources.Resources, dir string) *Graph { - g := &Graph{res: res, dir: dir, gviz: gographviz.NewGraph()} +func NewGraph(res *resources.Resources, dir, iconsPath string) *Graph { + g := &Graph{res: res, dir: dir, iconsPath: iconsPath, gviz: gographviz.NewGraph()} g.generate() return g diff --git a/pkg/graph/graph_test.go b/pkg/graph/graph_test.go index 45aad8d..288b5d1 100644 --- a/pkg/graph/graph_test.go +++ b/pkg/graph/graph_test.go @@ -24,6 +24,7 @@ import ( var ( testns = "testns" dir = "/testdir" + iconsPath = "/testdir/icons" goldenDir = "testdata" goldenSuffix = ".golden" // if -update flag is specified on test run, golden file for the test will be updated @@ -121,7 +122,7 @@ var ( OwnerReferences: []metav1.OwnerReference{{APIVersion: "batch/v1", Kind: "Job", Name: "job1"}}}}, &appsv1.DaemonSet{ObjectMeta: metav1.ObjectMeta{Namespace: testns, Name: "ds1"}}, &batchv1.Job{ObjectMeta: metav1.ObjectMeta{Namespace: testns, Name: "job1", - OwnerReferences: []metav1.OwnerReference{{APIVersion: "batch/v1beta1", Kind: "cronJob", Name: "cronjob1"}}}}, + OwnerReferences: []metav1.OwnerReference{{APIVersion: "batch/v1beta1", Kind: "CronJob", Name: "cronjob1"}}}}, &batchv1.CronJob{ObjectMeta: metav1.ObjectMeta{Namespace: testns, Name: "cronjob1"}}, } ) @@ -132,8 +133,7 @@ func prepTestGraph(t *testing.T, objs ...runtime.Object) *Graph { if err != nil { t.Fatalf("NewResources failed: %v", err) } - - return NewGraph(res, dir) + return NewGraph(res, dir, iconsPath) } func getGoldenFilePath(name string) string { diff --git a/pkg/graph/graph_utils.go b/pkg/graph/graph_utils.go index 06ebc99..e13a894 100644 --- a/pkg/graph/graph_utils.go +++ b/pkg/graph/graph_utils.go @@ -10,22 +10,24 @@ import ( ) // imagePath returns the path to the image file -// path is {dir}/icons/{resource}-128.png +// path is {dir}/{iconsDir}/{resource}-128.png // ex) /icons/pod-128.png func (g *Graph) imagePath(kind string) string { - return filepath.Join(g.dir, "icons", kind+imageSuffix) + return filepath.Join(g.iconsPath, kind+imageSuffix) } // clusterLabel returns the resource label for namespace // ex) -// <
my-namespace
> +// +// <
my-namespace
> func (g *Graph) clusterLabel() string { return g.resourceLabel("ns", g.res.Namespace) } // resourceLabel returns the resource label for a resource // ex) -// <
my-pod
> +// +// <
my-pod
> func (g *Graph) resourceLabel(kind, name string) string { return fmt.Sprintf("<
%s
>", g.imagePath(kind), name) } diff --git a/test/e2e/e2e.sh b/test/e2e/e2e.sh index 75fe862..8790ed1 100755 --- a/test/e2e/e2e.sh +++ b/test/e2e/e2e.sh @@ -1,4 +1,4 @@ -#! /bin/bash +#!/usr/bin/env bash KEEP_RESULT="${KEEP_RESULT:-false}" @@ -74,7 +74,7 @@ function create_cluster() { curl -Lo ${TEST_KIND_BIN} https://kind.sigs.k8s.io/dl/v0.10.0/kind-linux-amd64 chmod +x ${TEST_KIND_BIN} - # Change path for kubeconfig + # Change path for kubeconfig export KUBECONFIG=${TEST_KUBECONFIG_PATH} # Create cluster @@ -91,7 +91,7 @@ function prepare_all() { } function delete_cluster() { - # Change path for kubeconfig + # Change path for kubeconfig export KUBECONFIG=${TEST_KUBECONFIG_PATH} # Delete cluster