Skip to content

Commit

Permalink
feat(cmd/cup): apply command and various fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
GeorgeMac committed Jul 28, 2023
1 parent 4138604 commit 8ff2d36
Show file tree
Hide file tree
Showing 12 changed files with 377 additions and 46 deletions.
3 changes: 3 additions & 0 deletions cmd/cup/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ func configCommand() *cli.Command {

type config struct {
CurrentContext string `json:"current_context"`
Output string `json:"-"`
Contexts map[string]*context `json:"contexts"`
}

Expand Down Expand Up @@ -110,6 +111,8 @@ func parseConfig(ctx *cli.Context) (config, error) {
return config{}, err
}

conf.Output = ctx.String("output")

return conf, nil
}

Expand Down
145 changes: 137 additions & 8 deletions cmd/cup/ctl.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package main

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path"
"sort"
"strings"
"text/tabwriter"
Expand Down Expand Up @@ -75,10 +77,10 @@ func definitions(cfg config, client *http.Client) error {
return wr.Flush()
}

func list(cfg config, client *http.Client, typ string) error {
func get(cfg config, client *http.Client, typ string, args ...string) error {
group, version, kind, err := getGVK(cfg, client, typ)
if err != nil {
return fmt.Errorf("list: %w", err)
return fmt.Errorf("get: %w", err)
}

endpoint := fmt.Sprintf("%s/apis/%s/%s/%s/%s/namespaces/%s",
Expand All @@ -90,6 +92,11 @@ func list(cfg config, client *http.Client, typ string) error {
cfg.Namespace(),
)

if len(args) == 1 {
// get with a single argument will use get instead of list
endpoint += "/" + args[0]
}

req, err := http.NewRequest(http.MethodGet, endpoint, nil)
if err != nil {
return err
Expand All @@ -109,19 +116,141 @@ func list(cfg config, client *http.Client, typ string) error {
return fmt.Errorf("unexpected status: %q", resp.Status)
}

enc := encoding.NewJSONEncoding[core.Resource]()
resources, err := encoding.DecodeAll(enc.NewDecoder(resp.Body))
dec := encoding.NewJSONDecoder[core.Resource](resp.Body)
resources, err := encoding.DecodeAll[core.Resource](dec)
if err != nil {
return fmt.Errorf("decoding resources: %w", err)
}

wr := writer()
fmt.Fprintln(wr, "NAMESPACE\tNAME\t")
var out encoding.TypedEncoder[core.Resource]
switch cfg.Output {
case "table":
table := newTableEncoding(
func(r *core.Resource) []string {
return []string{r.Metadata.Namespace, r.Metadata.Name}
},
"NAMESPACE", "NAME",
)

out = table

defer table.Flush()
case "json":
enc := encoding.NewJSONEncoder[core.Resource](os.Stdout)
enc.SetIndent("", " ")
out = enc
default:
return fmt.Errorf("unexpected output type: %q", cfg.Output)
}

for _, resource := range resources {
fmt.Fprintf(wr, "%s\t%s\t\n", resource.Metadata.Namespace, resource.Metadata.Name)
// filter by name client side
if len(args) > 0 {
var found bool
for _, name := range args {
if found = resource.Metadata.Name == name; found {
break
}
}

if !found {
continue
}
}

if err := out.Encode(resource); err != nil {
return err
}
}

return wr.Flush()
return nil
}

func apply(cfg config, client *http.Client, source string) (err error) {
var (
buf = &bytes.Buffer{}
rd = os.Stdin
)

if source != "-" {
rd, err = os.Open(source)
if err != nil {
return
}
}

resource, err := encoding.
NewJSONDecoder[core.Resource](io.TeeReader(rd, buf)).
Decode()
if err != nil {
return err
}

defs, err := getDefintions(cfg, client)
if err != nil {
return err
}

gvk := path.Join(resource.APIVersion, resource.Kind)
def, ok := defs[gvk]
if !ok {
return fmt.Errorf("unexpected resource kind: %q", gvk)
}

group, version, _ := strings.Cut(resource.APIVersion, "/")
endpoint := fmt.Sprintf("%s/apis/%s/%s/%s/%s/namespaces/%s/%s",
cfg.Address(),
cfg.Source(),
group,
version,
def.Names.Plural,
resource.Metadata.Namespace,
resource.Metadata.Name,
)

req, err := http.NewRequest(http.MethodPut, endpoint, buf)
if err != nil {
return err
}

resp, err := client.Do(req)
if err != nil {
return err
}

defer func() {
_, _ = io.Copy(io.Discard, resp.Body)
_ = resp.Body.Close()
}()

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("unexpected status: %q", resp.Status)
}

return nil
}

type tableEncoding[T any] struct {
*tabwriter.Writer

headers []string
rowFn func(*T) []string
headerPrinted bool
}

func newTableEncoding[T any](rowFn func(*T) []string, headers ...string) *tableEncoding[T] {
return &tableEncoding[T]{Writer: writer(), rowFn: rowFn, headers: headers}
}

func (e *tableEncoding[T]) Encode(t *T) error {
if !e.headerPrinted {
fmt.Fprintln(e, strings.Join(e.headers, "\t")+"\t")
e.headerPrinted = true
}

_, err := fmt.Fprintln(e, strings.Join(e.rowFn(t), "\t")+"\t")

return err
}

func getGVK(cfg config, client *http.Client, typ string) (group, version, kind string, err error) {
Expand Down
40 changes: 36 additions & 4 deletions cmd/cup/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ func main() {
Aliases: []string{"c"},
Value: path.Join(dir, "config.json"),
},
&cli.StringFlag{
Name: "output",
Aliases: []string{"o"},
Value: "table",
},
},
Commands: []*cli.Command{
{
Expand Down Expand Up @@ -69,17 +74,44 @@ func main() {
},
},
{
Name: "list",
Aliases: []string{"l"},
Name: "get",
Aliases: []string{"g"},
Category: "resource",
Usage: "List the available resources of a given definition for the target source",
Usage: "Get one or more resources",
Action: func(ctx *cli.Context) error {
cfg, err := parseConfig(ctx)
if err != nil {
return err
}

return get(cfg,
http.DefaultClient,
ctx.Args().First(),
ctx.Args().Tail()...)
},
},
{
Name: "apply",
Category: "resource",
Usage: "Put a resource from file on stdin",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "f",
Value: "-",
Usage: "Path to source to apply to target cupd",
DefaultText: "(- STDIN)",
},
},
Action: func(ctx *cli.Context) error {
cfg, err := parseConfig(ctx)
if err != nil {
return err
}

return list(cfg, http.DefaultClient, ctx.Args().First())
return apply(cfg,
http.DefaultClient,
ctx.String("f"),
)
},
},
},
Expand Down
20 changes: 18 additions & 2 deletions cmd/cupd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import (
"os"

"code.gitea.io/sdk/gitea"
githttp "github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/urfave/cli/v2"
"go.flipt.io/cup/pkg/api"
"go.flipt.io/cup/pkg/config"
"go.flipt.io/cup/pkg/controllers/template"
"go.flipt.io/cup/pkg/controllers/wasm"
"go.flipt.io/cup/pkg/fs/git"
scmgitea "go.flipt.io/cup/pkg/fs/git/scm/gitea"
"go.flipt.io/cup/pkg/fs/local"
Expand Down Expand Up @@ -39,6 +41,8 @@ func serve(ctx *cli.Context) error {

switch src.Type {
case config.SourceTypeGit:
user, pass := src.Git.Credentials()

var scm git.SCM
switch src.Git.SCM {
case config.SCMTypeGitea:
Expand All @@ -47,7 +51,7 @@ func serve(ctx *cli.Context) error {
return err
}

client, err := gitea.NewClient(src.Git.Host())
client, err := gitea.NewClient(src.Git.Host(), gitea.SetBasicAuth(user, pass))
if err != nil {
return err
}
Expand All @@ -57,7 +61,12 @@ func serve(ctx *cli.Context) error {
return fmt.Errorf("scm type not supported: %q", src.Git.SCM)
}

fs, err = git.NewFilesystem(ctx.Context, scm, src.Git.URL.String())
fs, err = git.NewFilesystem(ctx.Context, scm, src.Git.URL.String(), git.WithAuth(
&githttp.BasicAuth{
Username: user,
Password: pass,
},
))
if err != nil {
return err
}
Expand All @@ -75,6 +84,13 @@ func serve(ctx *cli.Context) error {
switch resource.Controller.Type {
case config.ControllerTypeTemplate:
controller = template.New()
case config.ControllerTypeWASM:
exec, err := os.ReadFile(resource.Controller.WASM.Executable)
if err != nil {
return err
}

controller = wasm.New(ctx.Context, exec)
default:
return fmt.Errorf("controller type not supported: %q", resource.Controller.Type)
}
Expand Down
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ go 1.20

require (
code.gitea.io/sdk/gitea v0.15.1
github.com/go-chi/chi v1.5.4
github.com/go-chi/chi/v5 v5.0.10
github.com/go-git/go-billy/v5 v5.4.1
github.com/go-git/go-git/v5 v5.8.0
github.com/oklog/ulid/v2 v2.1.0
github.com/stretchr/testify v1.8.4
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1
github.com/tetratelabs/wazero v1.3.1
github.com/urfave/cli/v2 v2.25.7
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090
)

require (
Expand All @@ -20,7 +23,6 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-chi/chi v1.5.4 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/hashicorp/go-version v1.2.1 // indirect
Expand All @@ -32,8 +34,6 @@ require (
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/skeema/knownhosts v1.1.1 // indirect
github.com/tetratelabs/wazero v1.3.1 // indirect
github.com/urfave/cli/v2 v2.25.7 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/crypto v0.10.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
Expand Down
Loading

0 comments on commit 8ff2d36

Please sign in to comment.