Added k3kcli cluster list and k3kcli policy list commands (#368)

* list commands

* go mod tidy

* moved logic to separate file, small refactor
This commit is contained in:
Enrico Candino
2025-05-30 15:35:31 +02:00
committed by GitHub
parent 2655d792cc
commit 5758b880a5
11 changed files with 248 additions and 14 deletions
+5 -1
View File
@@ -14,7 +14,11 @@ spec:
singular: cluster
scope: Namespaced
versions:
- name: v1alpha1
- additionalPrinterColumns:
- jsonPath: .spec.mode
name: Mode
type: string
name: v1alpha1
schema:
openAPIV3Schema:
description: |-
@@ -20,9 +20,6 @@ spec:
- jsonPath: .spec.allowedMode
name: Mode
type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1alpha1
schema:
openAPIV3Schema:
+1
View File
@@ -11,6 +11,7 @@ func NewClusterCmd(appCtx *AppContext) *cli.Command {
Subcommands: []*cli.Command{
NewClusterCreateCmd(appCtx),
NewClusterDeleteCmd(appCtx),
NewClusterListCmd(appCtx),
},
}
}
+51
View File
@@ -0,0 +1,51 @@
package cmds
import (
"context"
"github.com/rancher/k3k/pkg/apis/k3k.io/v1alpha1"
"github.com/urfave/cli/v2"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/cli-runtime/pkg/printers"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
)
func NewClusterListCmd(appCtx *AppContext) *cli.Command {
return &cli.Command{
Name: "list",
Usage: "List all the existing cluster",
UsageText: "k3kcli cluster list [command options]",
Action: list(appCtx),
Flags: WithCommonFlags(appCtx),
HideHelpCommand: true,
}
}
func list(appCtx *AppContext) cli.ActionFunc {
return func(clx *cli.Context) error {
ctx := context.Background()
client := appCtx.Client
if clx.NArg() > 0 {
return cli.ShowSubcommandHelp(clx)
}
var clusters v1alpha1.ClusterList
if err := client.List(ctx, &clusters, ctrlclient.InNamespace(appCtx.namespace)); err != nil {
return err
}
crd := &apiextensionsv1.CustomResourceDefinition{}
if err := client.Get(ctx, types.NamespacedName{Name: "clusters.k3k.io"}, crd); err != nil {
return err
}
items := toPointerSlice(clusters.Items)
table := createTable(crd, items)
printer := printers.NewTablePrinter(printers.PrintOptions{WithNamespace: true})
return printer.PrintObj(table, clx.App.Writer)
}
}
+1
View File
@@ -11,6 +11,7 @@ func NewPolicyCmd(appCtx *AppContext) *cli.Command {
Subcommands: []*cli.Command{
NewPolicyCreateCmd(appCtx),
NewPolicyDeleteCmd(appCtx),
NewPolicyListCmd(appCtx),
},
}
}
+50
View File
@@ -0,0 +1,50 @@
package cmds
import (
"context"
"github.com/rancher/k3k/pkg/apis/k3k.io/v1alpha1"
"github.com/urfave/cli/v2"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/cli-runtime/pkg/printers"
)
func NewPolicyListCmd(appCtx *AppContext) *cli.Command {
return &cli.Command{
Name: "list",
Usage: "List all the existing policies",
UsageText: "k3kcli policy list [command options]",
Action: policyList(appCtx),
Flags: WithCommonFlags(appCtx),
HideHelpCommand: true,
}
}
func policyList(appCtx *AppContext) cli.ActionFunc {
return func(clx *cli.Context) error {
ctx := context.Background()
client := appCtx.Client
if clx.NArg() > 0 {
return cli.ShowSubcommandHelp(clx)
}
var policies v1alpha1.VirtualClusterPolicyList
if err := client.List(ctx, &policies); err != nil {
return err
}
crd := &apiextensionsv1.CustomResourceDefinition{}
if err := client.Get(ctx, types.NamespacedName{Name: "virtualclusterpolicies.k3k.io"}, crd); err != nil {
return err
}
items := toPointerSlice(policies.Items)
table := createTable(crd, items)
printer := printers.NewTablePrinter(printers.PrintOptions{})
return printer.PrintObj(table, clx.App.Writer)
}
}
+3
View File
@@ -7,6 +7,7 @@ import (
"github.com/rancher/k3k/pkg/buildinfo"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
@@ -45,6 +46,7 @@ func NewApp() *cli.App {
scheme := runtime.NewScheme()
_ = clientgoscheme.AddToScheme(scheme)
_ = v1alpha1.AddToScheme(scheme)
_ = apiextensionsv1.AddToScheme(scheme)
ctrlClient, err := client.New(restConfig, client.Options{Scheme: scheme})
if err != nil {
@@ -109,6 +111,7 @@ func WithCommonFlags(appCtx *AppContext, flags ...cli.Flag) []cli.Flag {
&cli.StringFlag{
Name: "namespace",
Usage: "namespace to create the k3k cluster in",
Aliases: []string{"n"},
Destination: &appCtx.namespace,
},
}
+103
View File
@@ -0,0 +1,103 @@
package cmds
import (
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/util/jsonpath"
)
// createTable creates a table to print from the printerColumn defined in the CRD spec, plus the name at the beginning
func createTable[T runtime.Object](crd *apiextensionsv1.CustomResourceDefinition, objs []T) *metav1.Table {
printerColumns := getPrinterColumnsFromCRD(crd)
return &metav1.Table{
TypeMeta: metav1.TypeMeta{APIVersion: "meta.k8s.io/v1", Kind: "Table"},
ColumnDefinitions: convertToTableColumns(printerColumns),
Rows: createTableRows(objs, printerColumns),
}
}
func getPrinterColumnsFromCRD(crd *apiextensionsv1.CustomResourceDefinition) []apiextensionsv1.CustomResourceColumnDefinition {
printerColumns := []apiextensionsv1.CustomResourceColumnDefinition{
{Name: "Name", Type: "string", Format: "name", Description: "Name of the Resource", JSONPath: ".metadata.name"},
}
for _, version := range crd.Spec.Versions {
if version.Name == "v1alpha1" {
printerColumns = append(printerColumns, version.AdditionalPrinterColumns...)
break
}
}
return printerColumns
}
func convertToTableColumns(printerColumns []apiextensionsv1.CustomResourceColumnDefinition) []metav1.TableColumnDefinition {
var columnDefinitions []metav1.TableColumnDefinition
for _, col := range printerColumns {
columnDefinitions = append(columnDefinitions, metav1.TableColumnDefinition{
Name: col.Name,
Type: col.Type,
Format: col.Format,
Description: col.Description,
Priority: col.Priority,
})
}
return columnDefinitions
}
func createTableRows[T runtime.Object](objs []T, printerColumns []apiextensionsv1.CustomResourceColumnDefinition) []metav1.TableRow {
var rows []metav1.TableRow
for _, obj := range objs {
objMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&obj)
if err != nil {
rows = append(rows, metav1.TableRow{Cells: []any{"<error: " + err.Error() + ">"}})
continue
}
rows = append(rows, metav1.TableRow{
Cells: buildRowCells(objMap, printerColumns),
Object: runtime.RawExtension{Object: obj},
})
}
return rows
}
func buildRowCells(objMap map[string]any, printerColumns []apiextensionsv1.CustomResourceColumnDefinition) []any {
var cells []any
for _, printCol := range printerColumns {
j := jsonpath.New(printCol.Name)
err := j.Parse("{" + printCol.JSONPath + "}")
if err != nil {
cells = append(cells, "<error>")
continue
}
results, err := j.FindResults(objMap)
if err != nil || len(results) == 0 || len(results[0]) == 0 {
cells = append(cells, "<none>")
continue
}
cells = append(cells, results[0][0].Interface())
}
return cells
}
func toPointerSlice[T any](v []T) []*T {
var vPtr = make([]*T, len(v))
for i := range v {
vPtr[i] = &v[i]
}
return vPtr
}
+31 -7
View File
@@ -9,7 +9,7 @@ k3kcli
```
[--debug]
[--kubeconfig]=[value]
[--namespace]=[value]
[--namespace|-n]=[value]
```
**Usage**:
@@ -24,7 +24,7 @@ k3kcli [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
**--kubeconfig**="": kubeconfig path (default: $HOME/.kube/config or $KUBECONFIG if set)
**--namespace**="": namespace to create the k3k cluster in
**--namespace, -n**="": namespace to create the k3k cluster in
# COMMANDS
@@ -55,7 +55,7 @@ Create new cluster
**--mode**="": k3k mode type (shared, virtual) (default: "shared")
**--namespace**="": namespace to create the k3k cluster in
**--namespace, -n**="": namespace to create the k3k cluster in
**--persistence-type**="": persistence mode for the nodes (dynamic, ephemeral, static) (default: "dynamic")
@@ -87,7 +87,19 @@ Delete an existing cluster
**--kubeconfig**="": kubeconfig path (default: $HOME/.kube/config or $KUBECONFIG if set)
**--namespace**="": namespace to create the k3k cluster in
**--namespace, -n**="": namespace to create the k3k cluster in
### list
List all the existing cluster
>k3kcli cluster list [command options]
**--debug**: Turn on debug logs
**--kubeconfig**="": kubeconfig path (default: $HOME/.kube/config or $KUBECONFIG if set)
**--namespace, -n**="": namespace to create the k3k cluster in
## policy
@@ -105,7 +117,7 @@ Create new policy
**--mode**="": The allowed mode type of the policy (default: "shared")
**--namespace**="": namespace to create the k3k cluster in
**--namespace, -n**="": namespace to create the k3k cluster in
### delete
@@ -117,7 +129,19 @@ Delete an existing policy
**--kubeconfig**="": kubeconfig path (default: $HOME/.kube/config or $KUBECONFIG if set)
**--namespace**="": namespace to create the k3k cluster in
**--namespace, -n**="": namespace to create the k3k cluster in
### list
List all the existing policies
>k3kcli policy list [command options]
**--debug**: Turn on debug logs
**--kubeconfig**="": kubeconfig path (default: $HOME/.kube/config or $KUBECONFIG if set)
**--namespace, -n**="": namespace to create the k3k cluster in
## kubeconfig
@@ -143,6 +167,6 @@ Generate kubeconfig for clusters
**--name**="": cluster name
**--namespace**="": namespace to create the k3k cluster in
**--namespace, -n**="": namespace to create the k3k cluster in
**--org**="": Organization name (ORG) of the generated certificates for the kubeconfig
+2 -2
View File
@@ -28,8 +28,10 @@ require (
gopkg.in/yaml.v2 v2.4.0
helm.sh/helm/v3 v3.14.4
k8s.io/api v0.29.11
k8s.io/apiextensions-apiserver v0.29.11
k8s.io/apimachinery v0.29.11
k8s.io/apiserver v0.29.11
k8s.io/cli-runtime v0.29.11
k8s.io/client-go v0.29.11
k8s.io/component-base v0.29.11
k8s.io/component-helpers v0.29.11
@@ -203,8 +205,6 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.29.11 // indirect
k8s.io/cli-runtime v0.29.11 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kms v0.29.11 // indirect
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
+1 -1
View File
@@ -10,6 +10,7 @@ import (
// +kubebuilder:object:root=true
// +kubebuilder:storageversion
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:JSONPath=".spec.mode",name=Mode,type=string
// Cluster defines a virtual Kubernetes cluster managed by k3k.
// It specifies the desired state of a virtual cluster, including version, node configuration, and networking.
@@ -334,7 +335,6 @@ type ClusterList struct {
// +kubebuilder:subresource:status
// +kubebuilder:object:root=true
// +kubebuilder:printcolumn:JSONPath=".spec.allowedMode",name=Mode,type=string
// +kubebuilder:printcolumn:JSONPath=".metadata.creationTimestamp",name=Age,type=date
// +kubebuilder:resource:scope=Cluster,shortName=vcp
// VirtualClusterPolicy allows defining common configurations and constraints