diff --git a/README.md b/README.md index ac2a165..5b76307 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A CLI tool for common tasks relating to the Single Digital Presence hosting plat These commands simplify common cryptographic processes. -*Encrypt secrets to store in the project codebase. +*Encrypt secrets to store in the project codebase.* ``` cat /tmp/oauth.pem | bay kms encrypt \ --project content-foo-vic-gov-au \ @@ -20,3 +20,30 @@ This will store the encrypted file at `keys/production/oauth.pem.asc`. ``` cat oauth.pen.asc | bay kms decrypt > oauth.pem ``` + +## Project Map + +These commands provide a simple way to show how projects relate to eachother. + +*Show all frontends connected a specific backends* + +```sh +bay project-map by-backend content-vic +bay project-map by-backend content-health-vic-gov-au content-legalaid-vic-gov-au +bay project-map by-backend --all --output=json +``` + +*Show which backend a specific frontend connects to* + +```sh +bay project-map by-frontend vic-gov-au +bay project-map by-frontend --all +``` + +### Outputs + +These commands accept a `--output` flag which can be one of the following values: + +* `json` - a json object +* `table` - a cli table +* `go-template-file` - accepts another flag `--go-template-file` which indicates a go template to use for output. See `examples/templates` for ways to use this. \ No newline at end of file diff --git a/cmd/project-map/by-backend.go b/cmd/project-map/by-backend.go index 6fbb2ea..ae0cf8f 100644 --- a/cmd/project-map/by-backend.go +++ b/cmd/project-map/by-backend.go @@ -3,13 +3,17 @@ package project_map import ( "encoding/json" "fmt" + "github.com/pkg/errors" + "path" "strings" + "text/template" "github.com/alexeyco/simpletable" "github.com/urfave/cli/v2" "github.com/uselagoon/machinery/api/schema" "github.com/dpc-sdp/bay-cli/internal/helpers" + template_helpers "github.com/dpc-sdp/bay-cli/internal/template" ) type ByBackendResponse struct { @@ -21,6 +25,11 @@ type ByBackendResponseItem struct { FrontEnds []string `json:"frontends"` } +type ByBackendTemplateVars struct { + Inventory map[string]schema.Project + Items []ByBackendResponseItem +} + func ByBackend(c *cli.Context) error { client, err := helpers.NewLagoonClient(nil) if err != nil { @@ -28,6 +37,7 @@ func ByBackend(c *cli.Context) error { } output := ByBackendResponse{} + projectInventory := make(map[string]schema.Project) all := c.Bool("all") args := make([]string, 0) @@ -39,17 +49,20 @@ func ByBackend(c *cli.Context) error { } for _, p := range projects { args = append(args, p.Name) + + projectInventory[p.Name] = p.Project } } else { args = helpers.GetAllArgs(c) } for _, v := range args { - project := &schema.ProjectMetadata{} - err := client.ProjectByNameMetadata(c.Context, v, project) + project := &schema.Project{} + err := client.ProjectByNameExtended(c.Context, v, project) if err != nil { return err } + projectInventory[project.Name] = *project projects := make([]schema.ProjectMetadata, 0) err = client.ProjectsByMetadata(c.Context, "backend-project", v, &projects) @@ -64,14 +77,38 @@ func ByBackend(c *cli.Context) error { for _, p := range projects { item.FrontEnds = append(item.FrontEnds, p.Name) + projectInventory[p.Name] = p.Project } output.Items = append(output.Items, item) } - if c.String("output") == "json" { + // JSON output + outputFormat := c.String("output") + if outputFormat == "json" { a, _ := json.Marshal(output) fmt.Fprintf(c.App.Writer, string(a)) + } else if outputFormat == "go-template-file" { + pathOnDisk := c.String("go-template-file") + basePath := path.Base(pathOnDisk) + if pathOnDisk == "" { + return fmt.Errorf("--go-template-file flag was empty, but is a required field when using the --output=go-template-file option") + } + templateInputs := ByBackendTemplateVars{ + Inventory: projectInventory, + Items: output.Items, + } + + tmpl, err := template.New(basePath).Funcs(template.FuncMap{ + "convert_github_url_to_page": template_helpers.ConvertGithubUriToPage, + }).ParseFiles(pathOnDisk) + if err != nil { + return errors.Wrap(err, "could not load template file") + } + err = tmpl.Execute(c.App.Writer, templateInputs) + if err != nil { + return errors.Wrap(err, "could not render template") + } } else { table := simpletable.New() @@ -89,7 +126,7 @@ func ByBackend(c *cli.Context) error { } table.Body.Cells = append(table.Body.Cells, r) } - table.SetStyle(simpletable.StyleCompactLite) + table.SetStyle(simpletable.StyleMarkdown) fmt.Fprintf(c.App.Writer, table.String()) } diff --git a/cmd/project-map/by-frontend.go b/cmd/project-map/by-frontend.go index 34eadf6..6bff7c8 100644 --- a/cmd/project-map/by-frontend.go +++ b/cmd/project-map/by-frontend.go @@ -3,17 +3,27 @@ package project_map import ( "encoding/json" "fmt" + "path" + "text/template" + "github.com/alexeyco/simpletable" + "github.com/pkg/errors" "github.com/urfave/cli/v2" "github.com/uselagoon/machinery/api/schema" "github.com/dpc-sdp/bay-cli/internal/helpers" + template_helpers "github.com/dpc-sdp/bay-cli/internal/template" ) type ByFrontendResponse struct { Items map[string]string `json:"items"` } +type ByFrontendTemplateVars struct { + Inventory map[string]schema.Project + Items map[string]string +} + func ByFrontend(c *cli.Context) error { client, err := helpers.NewLagoonClient(nil) if err != nil { @@ -23,6 +33,7 @@ func ByFrontend(c *cli.Context) error { output := ByFrontendResponse{ Items: make(map[string]string, 0), } + projectInventory := make(map[string]schema.Project) all := c.Bool("all") args := make([]string, 0) @@ -35,6 +46,7 @@ func ByFrontend(c *cli.Context) error { } for _, p := range rippleProjects { args = append(args, p.Name) + projectInventory[p.Name] = p.Project } ripple2Projects := make([]schema.ProjectMetadata, 0) @@ -44,18 +56,22 @@ func ByFrontend(c *cli.Context) error { } for _, p := range ripple2Projects { args = append(args, p.Name) + projectInventory[p.Name] = p.Project } } else { args = helpers.GetAllArgs(c) } for _, v := range args { - project := &schema.ProjectMetadata{} - - err := client.ProjectByNameMetadata(c.Context, v, project) - if err != nil { - return err + if _, ok := projectInventory[v]; !ok { + project := &schema.ProjectMetadata{} + err := client.ProjectByNameMetadata(c.Context, v, project) + if err != nil { + return err + } + projectInventory[v] = project.Project } + if err != nil { return err } @@ -63,9 +79,32 @@ func ByFrontend(c *cli.Context) error { output.Items[v] = project.Metadata["backend-project"] } - if c.String("output") == "json" { + outputFormat := c.String("output") + if outputFormat == "json" { a, _ := json.Marshal(output) fmt.Fprintf(c.App.Writer, string(a)) + } else if outputFormat == "go-template-file" { + pathOnDisk := c.String("go-template-file") + basePath := path.Base(pathOnDisk) + if pathOnDisk == "" { + return fmt.Errorf("--go-template-file flag was empty, but is a required field when using the --output=go-template-file option") + } + + templateInputs := ByFrontendTemplateVars{ + Items: output.Items, + Inventory: + } + + tmpl, err := template.New(basePath).Funcs(template.FuncMap{ + "convert_github_url_to_page": template_helpers.ConvertGithubUriToPage, + }).ParseFiles(pathOnDisk) + if err != nil { + return errors.Wrap(err, "could not load template file") + } + err = tmpl.Execute(c.App.Writer, templateInputs) + if err != nil { + return errors.Wrap(err, "could not render template") + } } else { table := simpletable.New() diff --git a/examples/templates/project-map-by-backend-table.tmpl b/examples/templates/project-map-by-backend-table.tmpl new file mode 100644 index 0000000..21382f2 --- /dev/null +++ b/examples/templates/project-map-by-backend-table.tmpl @@ -0,0 +1,34 @@ +{{ $inventory := .Inventory }} + + + + + + + {{ range $index, $item := .Items }} + + + + + {{ end }} + +
+

Backend +

+ Frontends +
+ {{ with (index $inventory $item.Project)}} +
  • {{ .Name }}
  • + {{ end }} +
    +
      + {{ range $index2, $frontend := $item.FrontEnds }} + {{ with (index $inventory $frontend)}} +
    • {{ $frontend }}
    • + {{ end }} + {{ end }} +
    +
    + + + diff --git a/go.mod b/go.mod index 1efb1b9..5420e3b 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/dpc-sdp/bay-cli go 1.20 require ( + github.com/alexeyco/simpletable v1.0.0 github.com/aws/aws-sdk-go-v2/config v1.26.3 github.com/aws/aws-sdk-go-v2/service/kms v1.27.9 github.com/pkg/errors v0.9.1 @@ -13,7 +14,6 @@ require ( ) require ( - github.com/alexeyco/simpletable v1.0.0 // indirect github.com/aws/aws-sdk-go-v2 v1.24.1 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.16.14 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect diff --git a/internal/template/helpers.go b/internal/template/helpers.go new file mode 100644 index 0000000..45c3c22 --- /dev/null +++ b/internal/template/helpers.go @@ -0,0 +1,18 @@ +package template + +import ( + "fmt" + "regexp" +) + +func ConvertGithubUriToPage(input string) string { + re := regexp.MustCompile(`^git@github\.com:([\w-]+)/([\w-]+)\.git$`) + matches := re.FindStringSubmatch(input) + if len(matches) > 2 { + org := matches[1] + repo := matches[2] + return fmt.Sprintf("https://github.com/%s/%s", org, repo) + } else { + return input + } +} diff --git a/main.go b/main.go index 72b86f6..3ede140 100644 --- a/main.go +++ b/main.go @@ -60,9 +60,14 @@ func main() { }, &cli.StringFlag{ Name: "output", - Usage: "Output format - supports json, table", + Usage: "Output format - supports json, table, go-template-file", DefaultText: "table", }, + &cli.StringFlag{ + Name: "go-template-file", + Usage: "Path on disk to go template file", + Hidden: true, + }, }, Action: project_map.ByBackend, }, @@ -77,9 +82,14 @@ func main() { }, &cli.StringFlag{ Name: "output", - Usage: "Output format - supports json, table", + Usage: "Output format - supports json, table, go-template-file", DefaultText: "table", }, + &cli.StringFlag{ + Name: "go-template-file", + Usage: "Path on disk to go template file", + Hidden: true, + }, }, Action: project_map.ByFrontend, },