Skip to content

Commit

Permalink
Move client from github/korrel8r/korrel8r
Browse files Browse the repository at this point in the history
Move client code to its own repo.
  • Loading branch information
alanconway committed May 30, 2024
1 parent efbb7b2 commit d0b1c1b
Show file tree
Hide file tree
Showing 43 changed files with 4,837 additions and 0 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Client library and command for Korrel8r

[![Build](https://github.com/korrel8r/client/actions/workflows/build.yml/badge.svg)](https://github.com/korrel8r/client/actions/workflows/build.yml)
[![Go Reference](https://pkg.go.dev/badge/github.com/korrel8r/client.svg)](https://pkg.go.dev/github.com/korrel8r/client)

Client library and command for the [Korrel8r](http://github.com/korrel8r/korrel8r) REST API.

Installation:

go install github.com/korrel8r/korrel8r/client/cmd/korrel8rcli@latest

Command help:

korrel8rcli help
49 changes: 49 additions & 0 deletions cmd/korrel8rcli/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright: This file is part of korrel8r, released under https://github.com/korrel8r/korrel8r/blob/main/LICENSE

package main

import (
"time"

stdlog "log"

"github.com/spf13/cobra"
"github.com/spf13/cobra/doc"
)

func init() {
docCmd := &cobra.Command{
Use: "doc",
Short: "Generate documentation",
}
rootCmd.AddCommand(docCmd)

manCmd := &cobra.Command{
Use: "man DIR",
Short: "Generate man pages in directory DIR",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
header := &doc.GenManHeader{
Title: cmd.Name(),
Section: "1",
Date: func() *time.Time { t := time.Now(); return &t }(),
}
if err := doc.GenManTree(rootCmd, header, args[0]); err != nil {
stdlog.Fatalln(err)
}
},
}
docCmd.AddCommand(manCmd)

markdownCmd := &cobra.Command{
Use: "markdown DIR",
Short: "Generate markdown documentation in directory DIR",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
if err := doc.GenMarkdownTree(rootCmd, args[0]); err != nil {
stdlog.Fatalln(err)
}
},
}
docCmd.AddCommand(markdownCmd)
}
29 changes: 29 additions & 0 deletions cmd/korrel8rcli/enum_flag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright: This file is part of korrel8r, released under https://github.com/korrel8r/korrel8r/blob/main/LICENSE

package main

import (
"fmt"
"slices"
"strings"
)

// EnumFlagValue is a flag value that validates its value is one of a set of allowed values.
type EnumFlagValue struct {
Allowed []string
Value string
}

// EnumFlag returns an enum flag value with the allowed strings. First one is the default.
func EnumFlag(allowed ...string) *EnumFlagValue {
return &EnumFlagValue{Allowed: allowed, Value: allowed[0]}
}
func (f *EnumFlagValue) String() string { return f.Value }
func (f *EnumFlagValue) Type() string { return fmt.Sprintf("enum(%v)", strings.Join(f.Allowed, ",")) }
func (f *EnumFlagValue) Set(s string) error {
if !slices.Contains(f.Allowed, s) {
return fmt.Errorf("invalid value %q: must be one of %v", s, f.Allowed)
}
f.Value = s
return nil
}
52 changes: 52 additions & 0 deletions cmd/korrel8rcli/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright: This file is part of korrel8r, released under https://github.com/korrel8r/korrel8r/blob/main/LICENSE

package main

import (
_ "embed"
"log"
"net/url"
"path/filepath"

"os"

httptransport "github.com/go-openapi/runtime/client"
"github.com/korrel8r/korrel8r/client/pkg/swagger/client"
"github.com/spf13/cobra"
)

const (
remoteEnv = "KORREL8R_REMOTE"
)

var (
rootCmd = &cobra.Command{
Use: "korrel8rcli COMMAND",
Short: "REST client for a remote korrel8r server.",
}

// Global Flags
output = EnumFlag("yaml", "json-pretty", "json")
)

func main() {
rootCmd.PersistentFlags().VarP(output, "output", "o", "Output format")
log.SetPrefix(filepath.Base(os.Args[0]) + ": ")
log.SetFlags(0)
check(rootCmd.Execute())
}

func check(err error) {
if err != nil {
log.Fatalln(err)
}
}

func newClient(urlStr string) *client.RESTAPI {
u, err := url.Parse(urlStr)
check(err)
if u.Path == "" {
u.Path = client.DefaultBasePath
}
return client.New(httptransport.New(u.Host, u.Path, []string{u.Scheme}), nil)
}
97 changes: 97 additions & 0 deletions cmd/korrel8rcli/operations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright: This file is part of korrel8r, released under https://github.com/korrel8r/korrel8r/blob/main/LICENSE

package main

import (
"os"

"github.com/korrel8r/korrel8r/client/pkg/swagger/client/operations"
"github.com/korrel8r/korrel8r/client/pkg/swagger/models"
"github.com/spf13/cobra"
)

// Common flags
var (
queries []string
class string
objects []string
goals []string
depth int64
rules bool
)

func makeStart() *models.Start {
return &models.Start{
Class: class,
Constraint: nil, // TODO support for constraints.
Objects: objects,
Queries: queries,
}
}

var domainsCmd = &cobra.Command{
Use: "domains URL",
Short: "Get a list of domains and store configuration",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
c := newClient(args[0])
ok, err := c.Operations.GetDomains(&operations.GetDomainsParams{})
check(err)
NewPrinter(output.String(), os.Stdout)(ok.Payload)
},
}

func init() {
rootCmd.AddCommand(domainsCmd)
}

var neighboursCmd = &cobra.Command{
Use: "neighbours URL [FLAGS]",
Short: "Get graph of nearest neighbours",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
c := newClient(args[0])
ok, err := c.Operations.PostGraphsNeighbours(&operations.PostGraphsNeighboursParams{
Request: &models.Neighbours{
Depth: depth,
Start: makeStart(),
},
Rules: &rules,
})
check(err)
NewPrinter(output.String(), os.Stdout)(ok.Payload)
},
}

var goalsCmd = &cobra.Command{
Use: "goals URL [FLAGS]",
Short: "Get graph of nearest goals",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
c := newClient(args[0])
ok, err := c.Operations.PostGraphsGoals(&operations.PostGraphsGoalsParams{
Request: &models.Goals{
Goals: goals,
Start: makeStart(),
},
Rules: &rules,
})
check(err)
NewPrinter(output.String(), os.Stdout)(ok.Payload)
},
}

func commonFlags(cmd *cobra.Command) {
rootCmd.AddCommand(cmd)
cmd.Flags().StringArrayVar(&queries, "query", nil, "Query string for start objects, can be multiple.")
cmd.Flags().StringVar(&class, "class", "", "Class for serialized objects")
cmd.Flags().StringArrayVar(&objects, "object", nil, "Serialized start object, can be multiple.")
cmd.Flags().BoolVar(&rules, "rules", false, "Include per-rule information in returned graph.")
}

func init() {
commonFlags(neighboursCmd)
neighboursCmd.Flags().Int64Var(&depth, "depth", 2, "Depth of neighbourhood search.")
commonFlags(goalsCmd)
goalsCmd.Flags().StringArrayVar(&goals, "goal", nil, "Serialized start goal, can be multiple.")
}
38 changes: 38 additions & 0 deletions cmd/korrel8rcli/output.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright: This file is part of korrel8r, released under https://github.com/korrel8r/korrel8r/blob/main/LICENSE

package main

import (
"encoding/json"
"fmt"
"io"

"github.com/korrel8r/korrel8r/internal/pkg/must"
"sigs.k8s.io/yaml"
)

// NewPrinter returns a function that prints in the chose format to writer.
func NewPrinter(format string, w io.Writer) func(any) {
switch format {

case "json":
return func(v any) {
if b, err := json.Marshal(v); err != nil {
fmt.Fprintf(w, "%v\n", err)
} else {
fmt.Fprintf(w, "%v\n", string(b))
}
}

case "json-pretty":
encoder := json.NewEncoder(w)
encoder.SetIndent("", " ")
return func(v any) { must.Must(encoder.Encode(v)) }

case "yaml":
return func(v any) { fmt.Fprintf(w, "%s", must.Must1(yaml.Marshal(v))) }

default:
return func(v any) { fmt.Fprintf(w, "%v", v) }
}
}
47 changes: 47 additions & 0 deletions cmd/korrel8rcli/web.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright: This file is part of korrel8r, released under https://github.com/korrel8r/korrel8r/blob/main/LICENSE

package main

import (
"log"
"net/http"

"github.com/gin-gonic/gin"
"github.com/korrel8r/korrel8r/client/pkg/browser"
"github.com/spf13/cobra"
)

var webCmd = &cobra.Command{
Use: "web REMOTE-URL [LISTEN-ADDR]",
Short: "Connect to REMOTE-URL and run an HTTP server listening on LISTEN-ADDR (default :8080)",
Args: cobra.RangeArgs(1, 2),
RunE: func(_ *cobra.Command, args []string) error {
remoteURL := args[0]
gin.DefaultWriter = log.Writer()
gin.SetMode(gin.ReleaseMode)
gin.DisableConsoleColor()
router := gin.New()
router.Use(gin.Recovery())
router.Use(gin.Logger())
b, err := browser.New(newClient(remoteURL), router)
if err != nil {
return err
}
defer b.Close()
s := http.Server{
Addr: *addr,
Handler: router,
}
log.Printf("Listening on %v, connected to %v\n", *addr, remoteURL)
return s.ListenAndServe()
},
}

var (
addr *string
)

func init() {
rootCmd.AddCommand(webCmd)
addr = webCmd.Flags().StringP("addr", "a", ":8080", "Listeing address for web server")
}
Loading

0 comments on commit d0b1c1b

Please sign in to comment.