183 lines
4.9 KiB
Go
183 lines
4.9 KiB
Go
package user
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
iamv1alpha2 "github.com/beclab/api/iam/v1alpha2"
|
|
"github.com/spf13/cobra"
|
|
"k8s.io/apimachinery/pkg/api/resource"
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
|
"log"
|
|
"slices"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
var sortFuncs = map[string]func(users []iamv1alpha2.User, i, j int) bool{
|
|
"name": func(users []iamv1alpha2.User, i, j int) bool {
|
|
return strings.Compare(users[i].Name, users[j].Name) == -1
|
|
},
|
|
"role": func(users []iamv1alpha2.User, i, j int) bool {
|
|
return strings.Compare(users[i].Annotations[annotationKeyRole], users[j].Annotations[annotationKeyRole]) == -1
|
|
},
|
|
"create-time": func(users []iamv1alpha2.User, i, j int) bool {
|
|
return users[i].CreationTimestamp.Before(&users[j].CreationTimestamp)
|
|
},
|
|
"memory": func(users []iamv1alpha2.User, i, j int) bool {
|
|
iMemoryStr, ok := users[i].Annotations[annotationKeyMemoryLimit]
|
|
if !ok || iMemoryStr == "" {
|
|
return false
|
|
}
|
|
jMemoryStr, ok := users[j].Annotations[annotationKeyMemoryLimit]
|
|
if !ok || jMemoryStr == "" {
|
|
return true
|
|
}
|
|
iMemory, err := resource.ParseQuantity(iMemoryStr)
|
|
if err != nil {
|
|
fmt.Printf("Warning: invalid memory limit '%s' is set on user '%s'\n", iMemoryStr, users[i].Name)
|
|
return false
|
|
}
|
|
jMemory, err := resource.ParseQuantity(jMemoryStr)
|
|
if err != nil {
|
|
fmt.Printf("Warning: invalid memory limit '%s' is set on user '%s'\n", jMemoryStr, users[j].Name)
|
|
return true
|
|
}
|
|
return iMemory.Cmp(jMemory) == -1
|
|
},
|
|
"cpu": func(users []iamv1alpha2.User, i, j int) bool {
|
|
iCPUStr, ok := users[i].Annotations[annotationKeyCPULimit]
|
|
if !ok || iCPUStr == "" {
|
|
return false
|
|
}
|
|
jCPUStr, ok := users[j].Annotations[annotationKeyCPULimit]
|
|
if !ok || jCPUStr == "" {
|
|
return true
|
|
}
|
|
iCPU, err := resource.ParseQuantity(iCPUStr)
|
|
if err != nil {
|
|
fmt.Printf("Warning: invalid cpu limit '%s' is set on user '%s'", iCPUStr, users[i].Name)
|
|
return false
|
|
}
|
|
jCPU, err := resource.ParseQuantity(jCPUStr)
|
|
if err != nil {
|
|
fmt.Printf("Warning: invalid cpu limit '%s' is set on user '%s'", jCPUStr, users[j].Name)
|
|
return true
|
|
}
|
|
return iCPU.Cmp(jCPU) == -1
|
|
},
|
|
}
|
|
|
|
var sortAliases = map[string]sets.Set[string]{
|
|
"name": sets.New[string]("n", "N", "Name"),
|
|
"role": sets.New[string]("r", "R", "Role"),
|
|
"create-time": sets.New[string]("creation", "created", "created-at", "createdat", "createtime"),
|
|
"cpu": sets.New[string]("c", "C", "CPU"),
|
|
"memory": sets.New[string]("m", "M", "Memory"),
|
|
}
|
|
|
|
func getSortFunc(sortBy string) func(users []iamv1alpha2.User, i, j int) bool {
|
|
if f, ok := sortFuncs[sortBy]; ok {
|
|
return f
|
|
}
|
|
for origin, sortAlias := range sortAliases {
|
|
if sortAlias.Has(sortBy) {
|
|
return sortFuncs[origin]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type listUsersOptions struct {
|
|
kubeConfig string
|
|
output string
|
|
noHeaders bool
|
|
sortBys []string
|
|
reverse bool
|
|
}
|
|
|
|
func NewCmdListUsers() *cobra.Command {
|
|
o := &listUsersOptions{}
|
|
cmd := &cobra.Command{
|
|
Use: "list",
|
|
Aliases: []string{"ls", "l"},
|
|
Short: "list all users",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
if err := o.Validate(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if err := o.Run(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
},
|
|
}
|
|
o.AddFlags(cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (o *listUsersOptions) AddFlags(cmd *cobra.Command) {
|
|
cmd.Flags().StringVar(&o.kubeConfig, "kubeconfig", "", "path to kubeconfig file")
|
|
cmd.Flags().StringVarP(&o.output, "output", "o", "table", "output format (table, json)")
|
|
cmd.Flags().BoolVar(&o.noHeaders, "no-headers", false, "disable headers")
|
|
cmd.Flags().StringSliceVar(&o.sortBys, "sort", []string{}, "sort output order by (name, role, create-time, memory, cpu)")
|
|
cmd.Flags().BoolVarP(&o.reverse, "reverse", "r", false, "reverse order")
|
|
}
|
|
|
|
func (o *listUsersOptions) Validate() error {
|
|
for _, sortBy := range o.sortBys {
|
|
f := getSortFunc(sortBy)
|
|
if f == nil {
|
|
return fmt.Errorf("unknown sort option: %s", sortBy)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (o *listUsersOptions) Run() error {
|
|
ctx := context.Background()
|
|
|
|
userClient, err := newUserClientFromKubeConfig(o.kubeConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var userList iamv1alpha2.UserList
|
|
err = userClient.List(ctx, &userList)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to list users: %w", err)
|
|
}
|
|
|
|
for _, sortBy := range o.sortBys {
|
|
sort.SliceStable(userList.Items, func(i, j int) bool {
|
|
f := getSortFunc(sortBy)
|
|
if f == nil {
|
|
log.Fatalf("unkown sort option: %s", sortBy)
|
|
}
|
|
return f(userList.Items, i, j)
|
|
})
|
|
}
|
|
|
|
if o.reverse {
|
|
slices.Reverse(userList.Items)
|
|
}
|
|
|
|
users := make([]userInfo, 0, len(userList.Items))
|
|
for _, user := range userList.Items {
|
|
users = append(users, convertUserObjectToUserInfo(user))
|
|
}
|
|
|
|
if o.output == "json" {
|
|
jsonOutput, _ := json.MarshalIndent(users, "", " ")
|
|
fmt.Println(string(jsonOutput))
|
|
} else {
|
|
if !o.noHeaders {
|
|
printUserTableHeaders()
|
|
}
|
|
for _, user := range users {
|
|
printUserTableRow(user)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|