Skip to content

Commit

Permalink
Add the alpha <resource> get command (#2332)
Browse files Browse the repository at this point in the history
  • Loading branch information
pPrecel authored Jan 23, 2025
1 parent 99785db commit 4a65ea2
Show file tree
Hide file tree
Showing 19 changed files with 513 additions and 56 deletions.
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ require (
github.com/gboddin/go-www-authenticate-parser v0.0.0-20230926203616-ec0b649bb077
github.com/go-test/deep v1.1.1
github.com/google/go-containerregistry v0.20.2
github.com/itchyny/gojq v0.12.17
github.com/kyma-project/api-gateway v0.0.0-20241120132533-7d29d687f9f0
github.com/moby/term v0.5.2
github.com/olekukonko/tablewriter v0.0.5
github.com/pkg/errors v0.9.1
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.10.0
gopkg.in/yaml.v3 v3.0.1
istio.io/client-go v1.24.2
Expand Down Expand Up @@ -111,6 +113,7 @@ require (
github.com/heroku/color v0.0.6 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/itchyny/timefmt-go v0.1.6 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
Expand Down Expand Up @@ -147,12 +150,11 @@ require (
github.com/prometheus/common v0.60.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8 // indirect
github.com/rivo/uniseg v0.4.3 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/vbatts/tar-split v0.11.5 // indirect
github.com/x448/float16 v0.8.4 // indirect
Expand Down
7 changes: 6 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/itchyny/gojq v0.12.17 h1:8av8eGduDb5+rvEdaOO+zQUjA04MS0m3Ps8HiD+fceg=
github.com/itchyny/gojq v0.12.17/go.mod h1:WBrEMkgAfAGO1LUcGOckBl5O726KPp+OlkKug0I/FEY=
github.com/itchyny/timefmt-go v0.1.6 h1:ia3s54iciXDdzWzwaVKXZPbiXzxxnv1SPGFfM/myJ5Q=
github.com/itchyny/timefmt-go v0.1.6/go.mod h1:RRDZYC5s9ErkjQvTvvU7keJjxUYzIISJGxm9/mAERQg=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
Expand Down Expand Up @@ -391,8 +395,9 @@ github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoG
github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8 h1:xe+mmCnDN82KhC010l3NfYlA8ZbOuzbXAzSYBa6wbMc=
github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8/go.mod h1:WIfMkQNY+oq/mWwtsjOYHIZBuwthioY2srOmljJkTnk=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw=
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
Expand Down
1 change: 1 addition & 0 deletions internal/cmd/alpha/alpha.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func NewAlphaCMD() (*cobra.Command, clierror.Error) {
cmds := kymaConfig.BuildExtensions(&cmdcommon.TemplateCommandsList{
// list of template commands deffinitions
Explain: templates.BuildExplainCommand,
Get: templates.BuildGetCommand,
Create: templates.BuildCreateCommand,
Delete: templates.BuildDeleteCommand,
}, cmdcommon.CoreCommandsMap{
Expand Down
14 changes: 7 additions & 7 deletions internal/cmd/alpha/templates/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,28 @@ type CreateOptions struct {
ResourceInfo types.ResourceInfo
}

func BuildCreateCommand(clientGetter KubeClientGetter, createOptions *CreateOptions) *cobra.Command {
return buildCreateCommand(os.Stdout, clientGetter, createOptions)
func BuildCreateCommand(clientGetter KubeClientGetter, options *CreateOptions) *cobra.Command {
return buildCreateCommand(os.Stdout, clientGetter, options)
}

func buildCreateCommand(out io.Writer, clientGetter KubeClientGetter, createOptions *CreateOptions) *cobra.Command {
func buildCreateCommand(out io.Writer, clientGetter KubeClientGetter, options *CreateOptions) *cobra.Command {
extraValues := []parameters.Value{}
cmd := &cobra.Command{
Use: "create",
Short: createOptions.Description,
Long: createOptions.DescriptionLong,
Short: options.Description,
Long: options.DescriptionLong,
Run: func(cmd *cobra.Command, args []string) {
clierror.Check(createResource(&createArgs{
out: out,
ctx: cmd.Context(),
clientGetter: clientGetter,
createOptions: createOptions,
createOptions: options,
extraValues: extraValues,
}))
},
}

flags := append(createOptions.CustomFlags, commonResourceFlags(createOptions.ResourceInfo.Scope)...)
flags := append(options.CustomFlags, commonResourceFlags(options.ResourceInfo.Scope)...)
for _, flag := range flags {
value := parameters.NewTyped(flag.Type, flag.Path, flag.DefaultValue)
cmd.Flags().VarP(value, flag.Name, flag.Shorthand, flag.Description)
Expand Down
8 changes: 4 additions & 4 deletions internal/cmd/alpha/templates/explain.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ func BuildExplainCommand(explainOptions *ExplainOptions) *cobra.Command {
return buildExplainCommand(os.Stdout, explainOptions)
}

func buildExplainCommand(out io.Writer, explainOptions *ExplainOptions) *cobra.Command {
func buildExplainCommand(out io.Writer, options *ExplainOptions) *cobra.Command {
return &cobra.Command{
Use: "explain",
Short: explainOptions.Description,
Long: explainOptions.DescriptionLong,
Short: options.Description,
Long: options.DescriptionLong,
Run: func(_ *cobra.Command, _ []string) {
fmt.Fprintln(out, explainOptions.Output)
fmt.Fprintln(out, options.Output)
},
}
}
180 changes: 180 additions & 0 deletions internal/cmd/alpha/templates/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
package templates

import (
"cmp"
"context"
"fmt"
"io"
"os"
"slices"
"strings"

"github.com/itchyny/gojq"
"github.com/kyma-project/cli.v3/internal/clierror"
"github.com/kyma-project/cli.v3/internal/cmd/alpha/templates/types"
"github.com/kyma-project/cli.v3/internal/kube/rootlessdynamic"
"github.com/kyma-project/cli.v3/internal/render"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
)

type GetOptions struct {
types.GetCommand
ResourceInfo types.ResourceInfo
}

func BuildGetCommand(clientGetter KubeClientGetter, options *GetOptions) *cobra.Command {
return buildGetCommand(os.Stdout, clientGetter, options)
}

func buildGetCommand(out io.Writer, clientGetter KubeClientGetter, options *GetOptions) *cobra.Command {
flags := flags{}
cmd := &cobra.Command{
Use: "get",
Short: options.Description,
Long: options.DescriptionLong,
Run: func(cmd *cobra.Command, args []string) {
clierror.Check(getResources(&getArgs{
out: out,
ctx: cmd.Context(),
getOptions: options,
clientGetter: clientGetter,
flags: flags,
}))
},
}

cmd.Flags().StringVar(&flags.name, "name", "", "resource of the resource")

if options.ResourceInfo.Scope == types.NamespaceScope {
cmd.Flags().StringVar(&flags.namespace, "namespace", "default", "resource namespace")
cmd.Flags().BoolVarP(&flags.allNamespaces, "all-namespaces", "A", false, "get from all namespaces")
}

return cmd
}

type getArgs struct {
out io.Writer
ctx context.Context
getOptions *GetOptions
clientGetter KubeClientGetter
flags flags
}

type flags struct {
allNamespaces bool
name string
namespace string
}

func getResources(args *getArgs) clierror.Error {
u := &unstructured.Unstructured{}
u.SetNamespace(args.flags.namespace)
u.SetGroupVersionKind(schema.GroupVersionKind{
Group: args.getOptions.ResourceInfo.Group,
Version: args.getOptions.ResourceInfo.Version,
Kind: args.getOptions.ResourceInfo.Kind,
})

client, clierr := args.clientGetter.GetKubeClientWithClierr()
if clierr != nil {
return clierr
}

nameSelector := ""
if args.flags.name != "" {
// set name field selector to get only one resource
nameSelector = fmt.Sprintf("metadata.name==%s", args.flags.name)
}

resources, err := client.RootlessDynamic().List(args.ctx, u, &rootlessdynamic.ListOptions{
AllNamespaces: args.flags.allNamespaces,
FieldSelector: nameSelector,
})
if err != nil {
return clierror.Wrap(err, clierror.New("failed to get resource"))
}

tableInfo := buildTableInfo(args.getOptions)
renderTable(args.out, resources.Items, tableInfo)
return nil
}

func buildTableInfo(opts *GetOptions) TableInfo {
Headers := []string{"NAME"}
fieldConverters := []FieldConverter{
genericFieldConverter(".metadata.name"),
}

if opts.ResourceInfo.Scope == types.NamespaceScope {
Headers = append(Headers, "NAMESPACE")
fieldConverters = append(fieldConverters, genericFieldConverter(".metadata.namespace"))
}

for _, param := range opts.Parameters {
Headers = append(Headers, strings.ToUpper(param.Name))
fieldConverters = append(fieldConverters, genericFieldConverter(param.Path))
}

return TableInfo{
Header: Headers,
RowConverter: func(u unstructured.Unstructured) []string {
row := make([]string, len(fieldConverters))
for i := range fieldConverters {
row[i] = fieldConverters[i](u)
}

return row
},
}
}

func genericFieldConverter(path string) func(u unstructured.Unstructured) string {
return func(u unstructured.Unstructured) string {
query, err := gojq.Parse(path)
if err != nil {
// ignore result because path is incorrect
return ""
}

value, ok := query.Run(u.Object).Next()
_, isError := value.(error)
if !ok || isError {
// ignore result because of an unexpected error
return ""
}

return fmt.Sprintf("%v", value)
}
}

func renderTable(writer io.Writer, resources []unstructured.Unstructured, tableInfo TableInfo) {
render.Table(
writer,
convertResourcesToTable(resources, tableInfo.RowConverter),
tableInfo.Header,
)
}

type FieldConverter func(u unstructured.Unstructured) string

type RowConverter func(unstructured.Unstructured) []string

type TableInfo struct {
Header []string
RowConverter RowConverter
}

func convertResourcesToTable(resources []unstructured.Unstructured, rowConverter RowConverter) [][]string {
slices.SortFunc(resources, func(a, b unstructured.Unstructured) int {
return cmp.Compare(a.GetNamespace(), b.GetNamespace())
})

var result [][]string
for _, resource := range resources {
result = append(result, rowConverter(resource))
}
return result
}
Loading

0 comments on commit 4a65ea2

Please sign in to comment.