Skip to content

Commit

Permalink
feat(cloud): adding support for cloud resources (#3101)
Browse files Browse the repository at this point in the history
* feature(cli): adding CLI cloud login and auth logic

* feature(cli): adding CLI cloud login and auth logic

* feat(cloud): adding support for cloud resources

* feat(cloud): adding select command for cloud resources
  • Loading branch information
xoscar authored Aug 28, 2023
1 parent 7caa9b7 commit 6f204b7
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 22 deletions.
78 changes: 78 additions & 0 deletions cli/cmd/resource_select_cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package cmd

import (
"context"
"errors"
"fmt"
"strings"

"github.com/kubeshop/tracetest/cli/config"
"github.com/kubeshop/tracetest/cli/pkg/resourcemanager"
"github.com/spf13/cobra"
)

type selectableFn func(config config.Config, ID string) config.Config

var (
selectParams = &resourceIDParameters{}
selectCmd *cobra.Command
selectable = strings.Join([]string{"environment", "organization"}, "|")
selectableMap = map[string]selectableFn{
"environment": func(config config.Config, ID string) config.Config {
config.EnvironmentID = ID
return config
},
"organization": func(config config.Config, ID string) config.Config {
config.OrganizationID = ID
return config
}}
)

func init() {
selectCmd = &cobra.Command{
GroupID: cmdGroupCloud.ID,
Use: "select " + selectable,
Short: "select resources",
Long: "Select resources to your Tracetest CLI config",
PreRun: setupCommand(),
Run: WithResourceMiddleware(func(_ *cobra.Command, args []string) (string, error) {
resourceType := resourceParams.ResourceName
ctx := context.Background()

selectableFn, ok := selectableMap[resourceType]
if !ok {
return "", fmt.Errorf("resource type %s not selectable. Selectable resources are %s", resourceType, selectable)
}

resourceClient, err := resources.Get(resourceType)
if err != nil {
return "", err
}

resultFormat, err := resourcemanager.Formats.GetWithFallback(output, "yaml")
if err != nil {
return "", err
}

result, err := resourceClient.Get(ctx, selectParams.ResourceID, resultFormat)
if errors.Is(err, resourcemanager.ErrNotFound) {
return result, nil
}
if err != nil {
return "", err
}

cliConfig = selectableFn(cliConfig, selectParams.ResourceID)
err = config.Save(ctx, cliConfig, config.ConfigureConfig{})
if err != nil {
return "", err
}

return fmt.Sprintf("✔ Resource %s of type %s has been stored to your configuration file", selectParams.ResourceID, resourceType), nil
}, selectParams),
PostRun: teardownCommand,
}

selectCmd.Flags().StringVar(&selectParams.ResourceID, "id", "", "id of the resource to select")
rootCmd.AddCommand(selectCmd)
}
38 changes: 30 additions & 8 deletions cli/cmd/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,33 @@ var (
var (
httpClient = &resourcemanager.HTTPClient{}

organizationsClient = resourcemanager.NewClient(
httpClient, cliLogger,
"organization", "organizations",
resourcemanager.WithTableConfig(resourcemanager.TableConfig{
Cells: []resourcemanager.TableCellConfig{
{Header: "ID", Path: "id"},
{Header: "NAME", Path: "name"},
},
}),
resourcemanager.WithListPath("elements"),
)

environmentClient = resourcemanager.NewClient(
httpClient, cliLogger,
"environment", "environments",
resourcemanager.WithTableConfig(resourcemanager.TableConfig{
Cells: []resourcemanager.TableCellConfig{
{Header: "ID", Path: "id"},
{Header: "NAME", Path: "name"},
},
}),
resourcemanager.WithPrefixGetter(func() string { return fmt.Sprintf("/organizations/%s/", cliConfig.OrganizationID) }),
resourcemanager.WithListPath("elements"),
)

// resourcemanager.WithPrefixGetter(func() string { return cliConfig.EnvironmentID }),

variableSetPreprocessor = preprocessor.VariableSet(cliLogger)
variableSetClient = resourcemanager.NewClient(
httpClient, cliLogger,
Expand Down Expand Up @@ -129,13 +156,6 @@ var (
resourcemanager.WithDeprecatedAlias("Transaction"),
)

// deprecated resources
deprecatedEnvironmentClient = resourcemanager.NewClient(
httpClient, cliLogger,
"environment", "environments",
resourcemanager.WithProxyResource("variableset"),
)

deprecatedTransactionsClient = resourcemanager.NewClient(
httpClient, cliLogger,
"transaction", "transactions",
Expand Down Expand Up @@ -253,9 +273,11 @@ var (
Register(variableSetClient).
Register(testSuiteClient).
Register(testClient).
Register(organizationsClient).
Register(environmentClient).

// deprecated resources
Register(deprecatedEnvironmentClient).
// Register(deprecatedEnvironmentClient).
Register(deprecatedTransactionsClient)
)

Expand Down
4 changes: 2 additions & 2 deletions cli/pkg/resourcemanager/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func (c Client) Apply(ctx context.Context, inputFile fileutil.File, requestedFor
zap.String("contents", string(inputFile.Contents())),
)

url := c.client.url(c.resourceNamePlural)
url := c.client.url(c.resourceNamePlural, "")
req, err := http.NewRequestWithContext(ctx, http.MethodPut, url.String(), inputFile.Reader())
if err != nil {
return "", fmt.Errorf("cannot build Apply request: %w", err)
Expand Down Expand Up @@ -160,5 +160,5 @@ func (c Client) Apply(ctx context.Context, inputFile fileutil.File, requestedFor
}
}

return requestedFormat.Format(string(body), c.options.tableConfig)
return requestedFormat.Format(string(body), c.options.tableConfig, c.options.listPath)
}
4 changes: 2 additions & 2 deletions cli/pkg/resourcemanager/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ func NewHTTPClient(baseURL string, extraHeaders http.Header) *HTTPClient {
}
}

func (c HTTPClient) url(resourceName string, extra ...string) *url.URL {
urlStr := c.baseURL + path.Join("/", resourceName, strings.Join(extra, "/"))
func (c HTTPClient) url(resourceName, prefix string, extra ...string) *url.URL {
urlStr := c.baseURL + path.Join("/", prefix, resourceName, strings.Join(extra, "/"))
url, _ := url.Parse(urlStr)
return url
}
Expand Down
22 changes: 17 additions & 5 deletions cli/pkg/resourcemanager/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,15 +138,27 @@ func (p prettyFormat) String() string {
// The path is a dot-separated list of keys, e.g. "metadata.name". See github.com/Jeffail/gabs.
func (p prettyFormat) Format(data string, opts ...any) (string, error) {
// we expect only one option - TableConfig
if len(opts) != 1 {
return "", fmt.Errorf("expected 1 option, got %d", len(opts))
if len(opts) != 2 {
return "", fmt.Errorf("expected 2 options, got %d", len(opts))
}

tableConfig, ok := opts[0].(TableConfig)
if !ok {
return "", fmt.Errorf("expected option to be a []TableCellConfig, got %T", opts[0])
}

listPath := ""
if len(opts) > 1 {
listPath, ok = opts[1].(string)
if !ok {
return "", fmt.Errorf("expected option to be a string, got %T", opts[1])
}
}

if listPath == "" {
listPath = "items"
}

parsed, err := gabs.ParseJSON([]byte(data))
if err != nil {
return "", err
Expand All @@ -161,7 +173,7 @@ func (p prettyFormat) Format(data string, opts ...any) (string, error) {
}

// iterate over parsed data and build table body
body := buildTableBody(parsed, tableConfig)
body := buildTableBody(parsed, tableConfig, listPath)

// configure output table
table := simpletable.New()
Expand All @@ -172,8 +184,8 @@ func (p prettyFormat) Format(data string, opts ...any) (string, error) {
return table.String(), nil
}

func buildTableBody(parsed *gabs.Container, tableConfig TableConfig) [][]*simpletable.Cell {
items := parsed.Path("items")
func buildTableBody(parsed *gabs.Container, tableConfig TableConfig, listPath string) [][]*simpletable.Cell {
items := parsed.Path(listPath)
// if items is nil, we assume that the parsed data is a single item
if items == nil {
body := make([][]*simpletable.Cell, 0, 1)
Expand Down
11 changes: 8 additions & 3 deletions cli/pkg/resourcemanager/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,14 @@ import (

const VerbGet Verb = "get"

type prefixGetterFn func() string

func (c Client) Get(ctx context.Context, id string, format Format) (string, error) {
url := c.client.url(c.resourceNamePlural, id)
prefix := ""
if c.options.prefixGetterFn != nil {
prefix = c.options.prefixGetterFn()
}
url := c.client.url(c.resourceNamePlural, prefix, id)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url.String(), nil)
if err != nil {
return "", fmt.Errorf("cannot build Get request: %w", err)
Expand Down Expand Up @@ -47,13 +53,12 @@ func (c Client) Get(ctx context.Context, id string, format Format) (string, erro
}

return "", fmt.Errorf("could not Get resource: %w", err)

}

body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("cannot read Get response: %w", err)
}

return format.Format(string(body), c.options.tableConfig)
return format.Format(string(body), c.options.tableConfig, c.options.listPath)
}
8 changes: 6 additions & 2 deletions cli/pkg/resourcemanager/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ type ListOption struct {
const VerbList Verb = "list"

func (c Client) List(ctx context.Context, opt ListOption, format Format) (string, error) {
url := c.client.url(c.resourceNamePlural)
prefix := ""
if c.options.prefixGetterFn != nil {
prefix = c.options.prefixGetterFn()
}
url := c.client.url(c.resourceNamePlural, prefix)

q := url.Query()
q.Add("skip", fmt.Sprintf("%d", opt.Skip))
Expand Down Expand Up @@ -55,5 +59,5 @@ func (c Client) List(ctx context.Context, opt ListOption, format Format) (string
return "", fmt.Errorf("cannot read List response: %w", err)
}

return format.Format(string(body), c.options.tableConfig)
return format.Format(string(body), c.options.tableConfig, c.options.listPath)
}
14 changes: 14 additions & 0 deletions cli/pkg/resourcemanager/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package resourcemanager

type options struct {
applyPreProcessor applyPreProcessorFn
prefixGetterFn prefixGetterFn
tableConfig TableConfig
listPath string
deleteSuccessMsg string
resourceType string
deprecatedAlias string
Expand All @@ -17,6 +19,18 @@ func WithApplyPreProcessor(preProcessor applyPreProcessorFn) option {
}
}

func WithPrefixGetter(prefixGetterFn prefixGetterFn) option {
return func(o *options) {
o.prefixGetterFn = prefixGetterFn
}
}

func WithListPath(path string) option {
return func(o *options) {
o.listPath = path
}
}

func WithDeleteSuccessMessage(deleteSuccessMssg string) option {
return func(o *options) {
o.deleteSuccessMsg = deleteSuccessMssg
Expand Down

0 comments on commit 6f204b7

Please sign in to comment.