Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add used-by command #13

Merged
merged 7 commits into from
Jan 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,11 @@ linters:
- errorlint
- dogsled
- nilnil
- gocyclo
- gocognit
- funlen
- dupl
- gocritic

linters-settings:
gocyclo:
min-complexity: 10
gocognit:
min-complexity: 20
funlen:
lines: 120
statements: 120
Expand Down
33 changes: 25 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,34 @@ $ kubectl mapr-ticket --help

## Usage

Currently, `kubectl-mapr-ticket` supports only the `list` command. This command will list all MapR tickets deployed in the current namespace. The output will include the name of the secret, the MapR cluster name, the username, and the expiry date of the ticket.
The plugin can be invoked using the `kubectl mapr-ticket` command. The plugin supports the following subcommands:

- `list` - List all MapR tickets deployed in the current namespace.
- `used-by` - List all Persistent Volumes that are using a specific MapR ticket.

### List

The `list` subcommand will list all MapR tickets deployed in the current namespace. The output by default will be a table with the following columns. Additional flags can be used to customize the output, see `kubectl mapr-ticket list --help` for more details.

```console
$ kubectl mapr-ticket list
NAME MAPR CLUSTER USER EXPIRATION
mapr-dev-ticket-user-a demo.dev.mapr.com user_a 2028-11-09T15:50:57+01:00 (Expired)
mapr-dev-ticket-user-b demo.dev.mapr.com user_b 2028-11-09T15:50:55+01:00 (Expired)
mapr-dev-ticket-user-c demo.dev.mapr.com user_c 2028-11-09T15:50:51+01:00 (Expired)
mapr-prod-ticket-user-a demo.prod.mapr.com user_a 2023-11-19T08:47:05+01:00
mapr-prod-ticket-user-b demo.prod.mapr.com user_b 2023-11-19T08:47:03+01:00
mapr-prod-ticket-user-c demo.prod.mapr.com user_c 2023-11-19T08:47:02+01:00
NAME MAPR CLUSTER USER STATUS AGE
mapr-dev-ticket-user-a demo.dev.mapr.com user_a Valid (4y left) 75d
mapr-dev-ticket-user-b demo.dev.mapr.com user_b Valid (4y left) 75d
mapr-dev-ticket-user-c demo.dev.mapr.com user_c Valid (4y left) 75d
mapr-prod-ticket-user-a demo.prod.mapr.com user_a Expired (43d ago) 73d
mapr-prod-ticket-user-b demo.prod.mapr.com user_b Expired (43d ago) 73d
mapr-prod-ticket-user-c demo.prod.mapr.com user_c Expired (43d ago) 73d
```

### Used By

The `used-by` subcommand will list all Persistent Volumes that are using a specific MapR ticket or any ticket in the current namespace if `--all` is specified. The output by default will be a table with the following columns. Additional flags can be used to customize the output, see `kubectl mapr-ticket used-by --help` for more details.

```console
$ kubectl mapr-ticket mapr-ticket-secret -n test-csi
NAME SECRET NAMESPACE SECRET CLAIM NAMESPACE CLAIM AGE
test-static-pv test-csi mapr-ticket-secret 13h
```

## Does this require a connection to a MapR cluster?
Expand Down
18 changes: 18 additions & 0 deletions cmd/cli/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ type ListOptions struct {
// FilterByMaprGID indicates whether to filter secrets to only those that have
// a ticket for the specified GID
FilterByMaprGID uint32

// FilterByInUse indicates whether to filter secrets to only those that are
// in use by a persistent volume
FilterByInUse bool

// ShowInUse indicates whether to show only secrets that are in use by a
// persistent volume
ShowInUse bool
}

func NewListOptions(rootOpts *rootCmdOptions) *ListOptions {
Expand Down Expand Up @@ -89,6 +97,8 @@ some information about them.`,
cmd.Flags().StringVarP(&o.FilterByMaprUser, "mapr-user", "u", "", "Only show secrets with tickets for the specified MapR user")
cmd.Flags().Uint32Var(&o.FilterByMaprUID, "mapr-uid", 0, "Only show secrets with tickets for the specified UID")
cmd.Flags().Uint32Var(&o.FilterByMaprGID, "mapr-gid", 0, "Only show secrets with tickets for the specified GID")
cmd.Flags().BoolVarP(&o.FilterByInUse, "in-use", "I", false, "If true, only show secrets that are in use by a persistent volume")
cmd.Flags().BoolVarP(&o.ShowInUse, "show-in-use", "i", false, "If true, add a column to the output indicating whether the secret is in use by a persistent volume")
cmd.MarkFlagsMutuallyExclusive("only-expired", "only-unexpired")

return cmd
Expand Down Expand Up @@ -153,6 +163,14 @@ func (o *ListOptions) Run(cmd *cobra.Command, args []string) error {
opts = append(opts, list.WithFilterByGID(o.FilterByMaprGID))
}

if cmd.Flags().Changed("in-use") && o.FilterByInUse {
opts = append(opts, list.WithFilterByInUse())
}

if cmd.Flags().Changed("show-in-use") && o.ShowInUse {
opts = append(opts, list.WithShowInUse())
}

// create lister
lister := list.NewLister(client, *o.kubernetesConfigFlags.Namespace, opts...)

Expand Down
1 change: 1 addition & 0 deletions cmd/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ requiring access to the MapR cluster.`,
rootCmd.AddCommand(
newListCmd(rootOpts),
newVersionCmd(rootOpts),
newUsedByCmd(rootOpts),
)

return rootCmd
Expand Down
125 changes: 125 additions & 0 deletions cmd/cli/usedby.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package cli

import (
"fmt"

"github.com/nobbs/kubectl-mapr-ticket/internal/util"
"github.com/nobbs/kubectl-mapr-ticket/internal/volumes"
"github.com/spf13/cobra"
)

type UsedByOptions struct {
*rootCmdOptions

// Args are the arguments passed to the command
args []string

// SecretName is the name of the secret to find persistent volumes for
SecretName string

// AllSecrets indicates whether to find persistent volumes for all secrets
// in the current namespace
AllSecrets bool

// OutputFormat is the format to use for output
OutputFormat string
}

func NewUsedByOptions(rootOpts *rootCmdOptions) *UsedByOptions {
return &UsedByOptions{
rootCmdOptions: rootOpts,
}
}

func newUsedByCmd(rootOpts *rootCmdOptions) *cobra.Command {
o := NewUsedByOptions(rootOpts)

cmd := &cobra.Command{
Use: "used-by {secret-name|--all} [flags]",
Short: "List all persistent volumes that use the specified MapR ticket secret",
Long: `List all persistent volumes that use the specified MapR ticket secret and print
some information about them.`,
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if err := o.Complete(cmd, args); err != nil {
return err
}

if err := o.Validate(); err != nil {
return err
}

if err := o.Run(cmd, args); err != nil {
return err
}

return nil
},
}

// set IOStreams for the command
cmd.SetIn(o.IOStreams.In)
cmd.SetOut(o.IOStreams.Out)
cmd.SetErr(o.IOStreams.ErrOut)

// add flags
cmd.Flags().StringVarP(&o.OutputFormat, "output", "o", "table", "Output format. One of: table|wide")
cmd.Flags().BoolVarP(&o.AllSecrets, "all", "a", false, "List persistent volumes for all MapR ticket secrets in the current namespace")

return cmd
}

func (o *UsedByOptions) Complete(cmd *cobra.Command, args []string) error {
o.args = args

if len(args) > 0 {
o.SecretName = args[0]
}

return nil
}

func (o *UsedByOptions) Validate() error {
// ensure that the secret name was provided
if !o.AllSecrets && o.SecretName == "" {
return fmt.Errorf("either --all or a secret name must be provided")
}

// ensure that the output format is valid
if o.OutputFormat != "table" && o.OutputFormat != "wide" {
return fmt.Errorf("output format %s is not valid", o.OutputFormat)
}

return nil
}

func (o *UsedByOptions) Run(cmd *cobra.Command, args []string) error {
client, err := util.ClientFromFlags(o.kubernetesConfigFlags)
if err != nil {
return err
}

// create list options
opts := []volumes.ListerOption{}

// if we are listing volumes for all secrets in the namespace, create an option to do so
if o.AllSecrets {
opts = append(opts, volumes.WithAllSecrets())
}

// create lister
lister := volumes.NewLister(client, o.SecretName, *o.kubernetesConfigFlags.Namespace, opts...)

// run the lister
pvs, err := lister.Run()
if err != nil {
return err
}

// print the volumes
if err := volumes.Print(cmd, pvs); err != nil {
return err
}

return nil
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
k8s.io/apimachinery v0.29.0
k8s.io/cli-runtime v0.29.0
k8s.io/client-go v0.29.0
sigs.k8s.io/yaml v1.3.0
)

require (
Expand Down Expand Up @@ -66,5 +67,4 @@ require (
sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect
sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
Loading