Skip to content

Commit

Permalink
Merge pull request #104 from Kuadrant/103-topology-scaffolding-persis…
Browse files Browse the repository at this point in the history
…ting-in-local-disk-the-existing-topology

topology command: persist in local disk
  • Loading branch information
eguzki authored Dec 23, 2024
2 parents 2bc1faf + 0018dfc commit f79f616
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 69 deletions.
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ kuadrantctl [command] [subcommand] [flags]
| ------------ | ---------------------------------------------------------- |
| `completion` | Generate autocompletion scripts for the specified shell |
| `generate` | Commands related to Kubernetes Gateway API and Kuadrant resource generation from OpenAPI 3.x specifications |
| `topology` | Command related to Kuadrant topology |
| `help` | Help about any command |
| `version` | Print the version number of `kuadrantctl` |
| `version` | Print the version number of `kuadrantctl` |

### Flags

Expand Down Expand Up @@ -81,6 +82,29 @@ Generate Gateway API resources from an OpenAPI 3.x specification
| ---------- | ------------------------------------------------ | --------------------------------- |
| `httproute`| Generate Gateway API HTTPRoute from OpenAPI 3.0.X| `--oas string` Path to OpenAPI spec file (in JSON or YAML format), URL, or '-' to read from standard input (required). `-o` Output format: 'yaml' or 'json'. (default "yaml") |

#### `topology`

Export and visualize kuadrant topology

### Usage

```shell
$ kuadrantctl topology -h
Export and visualize kuadrant topology

Usage:
kuadrantctl topology [flags]

Flags:
-d, --dot string Graphviz DOT output file
-h, --help help for topology
-n, --namespace string Topology's namespace (default "kuadrant-system")
-o, --output string SVG image output file
Global Flags:
-v, --verbose verbose output
```
##### `generate kuadrant`
Generate Kuadrant resources from an OpenAPI 3.x specification
Expand Down
11 changes: 8 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ limitations under the License.
package cmd

import (
"context"
"os"

"github.com/spf13/cobra"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
Expand All @@ -32,6 +35,10 @@ func GetRootCmd(args []string) *cobra.Command {
Use: "kuadrantctl",
Short: "Kuadrant configuration command line utility",
Long: "Kuadrant configuration command line utility",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
logf.SetLogger(zap.New(zap.UseDevMode(verbose), zap.WriteTo(os.Stdout)))
cmd.SetContext(context.Background())
},
}

rootCmd.SetArgs(args)
Expand All @@ -42,9 +49,7 @@ func GetRootCmd(args []string) *cobra.Command {

rootCmd.AddCommand(versionCommand())
rootCmd.AddCommand(generateCommand())

loggerOpts := zap.Options{Development: verbose}
logf.SetLogger(zap.New(zap.UseFlagOptions(&loggerOpts)))
rootCmd.AddCommand(topologyCommand())

return rootCmd
}
135 changes: 135 additions & 0 deletions cmd/topology.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package cmd

import (
"bytes"
"errors"
"os"
"os/exec"
"strings"

"github.com/goccy/go-graphviz"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/config"
logf "sigs.k8s.io/controller-runtime/pkg/log"
)

var (
topologyNS string
topologySVGOutputFile string
topologyDOTOutputFile string
)

func topologyCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "topology",
Short: "Export and visualize kuadrant topology",
Long: "Export and visualize kuadrant topology",
RunE: runTopology,
}

cmd.Flags().StringVarP(&topologyNS, "namespace", "n", "kuadrant-system", "Topology's namespace")
cmd.Flags().StringVarP(&topologySVGOutputFile, "output", "o", "", "SVG image output file")
cmd.Flags().StringVarP(&topologyDOTOutputFile, "dot", "d", "", "Graphviz DOT output file")
err := cmd.MarkFlagRequired("output")
if err != nil {
panic(err)
}
return cmd
}

func runTopology(cmd *cobra.Command, args []string) error {
if !strings.HasSuffix(topologySVGOutputFile, ".svg") {
return errors.New("output file must have .svg extension")
}
ctx := cmd.Context()
configuration, err := config.GetConfig()
if err != nil {
return err
}

k8sClient, err := client.New(configuration, client.Options{Scheme: scheme.Scheme})
if err != nil {
return err
}

topologyKey := client.ObjectKey{Name: "topology", Namespace: topologyNS}
topologyConfigMap := &corev1.ConfigMap{}
err = k8sClient.Get(ctx, topologyKey, topologyConfigMap)
logf.Log.V(1).Info("reading topology", "object", client.ObjectKeyFromObject(topologyConfigMap), "error", err)
if err != nil {
return err
}

if topologyDOTOutputFile != "" {
fDot, err := os.Create(topologyDOTOutputFile)
if err != nil {
return err
}
defer fDot.Close()

_, err = fDot.WriteString(topologyConfigMap.Data["topology"])
logf.Log.V(1).Info("write topology in DOT format to file", "file", topologyDOTOutputFile, "error", err)
if err != nil {
return err
}
}

g, err := graphviz.New(ctx)
if err != nil {
return err
}

graph, err := graphviz.ParseBytes([]byte(topologyConfigMap.Data["topology"]))
logf.Log.V(1).Info("parse DOT graph", "graph empty", graph == nil, "error", err)
if err != nil {
return err
}

nodeNum, err := graph.NodeNum()
logf.Log.V(1).Info("info graph", "graph nodenum", nodeNum, "error", err)
if err != nil {
return err
}

// 1. write encoded PNG data to buffer
var buf bytes.Buffer
err = g.Render(ctx, graph, graphviz.SVG, &buf)
logf.Log.V(1).Info("render graph to SVG", "buf len", buf.Len(), "error", err)
if err != nil {
return err
}

// write to file
fSvg, err := os.Create(topologySVGOutputFile)
if err != nil {
return err
}
defer fSvg.Close()

_, err = fSvg.Write(buf.Bytes())
logf.Log.V(1).Info("write topology in SVG format to file", "file", topologySVGOutputFile, "error", err)
if err != nil {
return err
}

externalCommand := "xdg-open"
if _, err := exec.LookPath("open"); err == nil {
externalCommand = "open"
}

openCmd := exec.Command(externalCommand, topologySVGOutputFile)
// pipe the commands output to the applications
// standard output
openCmd.Stdout = os.Stdout

// Run still runs the command and waits for completion
// but the output is instantly piped to Stdout
if err := openCmd.Run(); err != nil {
return err
}

return nil
}
6 changes: 0 additions & 6 deletions cmd/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (

"github.com/spf13/cobra"

"github.com/kuadrant/kuadrantctl/pkg/utils"
"github.com/kuadrant/kuadrantctl/version"
)

Expand All @@ -15,11 +14,6 @@ func versionCommand() *cobra.Command {
Short: "Print the version number of kuadrantctl",
Long: "Print the version number of kuadrantctl",
RunE: func(cmd *cobra.Command, args []string) error {
err := utils.SetupScheme()
if err != nil {
return err
}

fmt.Printf("kuadrantctl %s (%s)\n", version.Version, version.GitHash)
return nil
},
Expand Down
31 changes: 18 additions & 13 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,32 +1,37 @@
module github.com/kuadrant/kuadrantctl

go 1.21
go 1.22.0

toolchain go1.22.5

require (
github.com/getkin/kin-openapi v0.120.0
github.com/ghodss/yaml v1.0.0
github.com/goccy/go-graphviz v0.2.9
github.com/kuadrant/authorino v0.15.0
github.com/kuadrant/kuadrant-operator v0.7.1
github.com/onsi/ginkgo/v2 v2.13.2
github.com/onsi/gomega v1.30.0
github.com/operator-framework/api v0.19.0
github.com/spf13/cobra v1.8.0
k8s.io/apiextensions-apiserver v0.28.4
k8s.io/api v0.28.4
k8s.io/apimachinery v0.28.4
k8s.io/client-go v0.28.4
k8s.io/utils v0.0.0-20231127182322-b307cd553661
sigs.k8s.io/controller-runtime v0.16.3
sigs.k8s.io/gateway-api v1.0.1-0.20231204134048-c7da42e6eafc
sigs.k8s.io/yaml v1.4.0
)

require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/disintegration/imaging v1.6.2 // indirect
github.com/elliotchance/orderedmap/v2 v2.2.0 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch/v5 v5.7.0 // indirect
github.com/flopp/go-findfont v0.1.0 // indirect
github.com/fogleman/gg v1.3.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-logr/zapr v1.2.4 // indirect
Expand All @@ -35,6 +40,7 @@ require (
github.com/go-openapi/swag v0.22.4 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
Expand All @@ -47,7 +53,6 @@ require (
github.com/invopop/yaml v0.2.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kuadrant/limitador-operator v0.7.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
Expand All @@ -62,34 +67,34 @@ require (
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/tetratelabs/wazero v1.8.1 // indirect
github.com/tidwall/gjson v1.14.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/image v0.21.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/oauth2 v0.15.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/term v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/term v0.20.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.16.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.28.4 // indirect
k8s.io/apiextensions-apiserver v0.28.4 // indirect
k8s.io/component-base v0.28.4 // indirect
k8s.io/klog/v2 v2.110.1 // indirect
k8s.io/kube-openapi v0.0.0-20231129212854-f0671cc7e66a // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)

replace github.com/imdario/mergo => dario.cat/mergo v0.3.5
Expand Down
Loading

0 comments on commit f79f616

Please sign in to comment.