Skip to content

Commit

Permalink
Merge pull request #849 from jlandowner/resoruce-keep
Browse files Browse the repository at this point in the history
Feature to change delete policy on Dashboard UI
  • Loading branch information
oruharo authored Jul 21, 2024
2 parents e142e61 + b085826 commit 32a4f82
Show file tree
Hide file tree
Showing 52 changed files with 2,492 additions and 786 deletions.
41 changes: 41 additions & 0 deletions api/v1alpha1/utils.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
package v1alpha1

import (
"fmt"
"reflect"
"slices"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/utils/ptr"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
)

// LabelControllerManaged is a label on all resources managed by the controllers
const LabelControllerManaged = "cosmo-workspace.github.io/controller-managed"

Expand Down Expand Up @@ -37,6 +49,35 @@ func KeepResourceDeletePolicy(obj AnnotationHolder) bool {
return v == ResourceAnnEnumDeletePolicyKeep
}

func SetOwnerReferenceIfNotKeepPolicy(owner metav1.Object, obj metav1.Object, scheme *runtime.Scheme) error {
if !KeepResourceDeletePolicy(owner) && !KeepResourceDeletePolicy(obj) {
// Set owner reference
err := ctrl.SetControllerReference(owner, obj, scheme)
if err != nil {
return fmt.Errorf("failed to set owner reference on %s: %w", obj.(runtime.Object).GetObjectKind().GroupVersionKind(), err)
}
return nil

} else {
// Remove owner reference
if len(obj.GetOwnerReferences()) > 0 {
gvk, _ := apiutil.GVKForObject(owner.(runtime.Object), scheme)
refs := slices.DeleteFunc(obj.GetOwnerReferences(), func(v metav1.OwnerReference) bool {
return reflect.DeepEqual(v, metav1.OwnerReference{
APIVersion: gvk.GroupVersion().String(),
Kind: gvk.Kind,
Name: owner.GetName(),
UID: owner.GetUID(),
Controller: ptr.To(true),
BlockOwnerDeletion: ptr.To(true),
})
})
obj.SetOwnerReferences(refs)
}
return nil
}
}

const (
EventAnnKeyUserName = "cosmo-workspace.github.io/user"
EventAnnKeyInstanceName = LabelKeyInstanceName
Expand Down
12 changes: 10 additions & 2 deletions internal/cmd/user/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,14 @@ func printAddonWithVars(addons []*dashv1alpha1.UserAddon) string {
return strings.Join(arr, " ")
}

func printDeletePolicy(deletePolicy *dashv1alpha1.DeletePolicy) string {
if deletePolicy != nil {
return deletePolicy.String()
} else {
return dashv1alpha1.DeletePolicy_delete.String()
}
}

func OutputTable(out io.Writer, users []*dashv1alpha1.User) {
data := [][]string{}

Expand All @@ -242,11 +250,11 @@ func OutputWideTable(out io.Writer, users []*dashv1alpha1.User) {
data := [][]string{}

for _, v := range users {
data = append(data, []string{v.Name, v.DisplayName, strings.Join(v.Roles, ","), v.AuthType, cosmov1alpha1.UserNamespace(v.Name), v.Status, printAddonWithVars(v.Addons)})
data = append(data, []string{v.Name, v.DisplayName, strings.Join(v.Roles, ","), v.AuthType, cosmov1alpha1.UserNamespace(v.Name), v.Status, printDeletePolicy(v.DeletePolicy), printAddonWithVars(v.Addons)})
}

cli.OutputTable(out,
[]string{"NAME", "DISPLAYNAME", "ROLES", "AUTHTYPE", "NAMESPACE", "PHASE", "ADDONS"},
[]string{"NAME", "DISPLAYNAME", "ROLES", "AUTHTYPE", "NAMESPACE", "PHASE", "DELETEPOLOCY", "ADDONS"},
data)
}

Expand Down
5 changes: 5 additions & 0 deletions internal/cmd/user/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,10 @@ func UpdateCmd(cmd *cobra.Command, o *cli.RootOptions) *cobra.Command {
Aliases: []string{"addon", "useraddon", "user-addon"},
Short: "Update addon",
}, o))
cmd.AddCommand(UpdateDeletePolicyCmd(&cobra.Command{
Use: "deletepolicy USER_NAME [delete|keep]",
Aliases: []string{"delete-policy"},
Short: "Update delete polocy",
}, o))
return cmd
}
122 changes: 122 additions & 0 deletions internal/cmd/user/update_deletepolicy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package user

import (
"context"
"errors"
"fmt"
"time"

"github.com/fatih/color"
"github.com/spf13/cobra"
"k8s.io/utils/ptr"

"github.com/cosmo-workspace/cosmo/pkg/apiconv"
"github.com/cosmo-workspace/cosmo/pkg/cli"
"github.com/cosmo-workspace/cosmo/pkg/clog"
"github.com/cosmo-workspace/cosmo/pkg/kosmo"
dashv1alpha1 "github.com/cosmo-workspace/cosmo/proto/gen/dashboard/v1alpha1"
)

type UpdateDeletePolicyOption struct {
*cli.RootOptions

UserName string
DeletePolicy string
}

func UpdateDeletePolicyCmd(cmd *cobra.Command, cliOpt *cli.RootOptions) *cobra.Command {
o := &UpdateDeletePolicyOption{RootOptions: cliOpt}
cmd.RunE = cli.ConnectErrorHandler(o)
return cmd
}

func (o *UpdateDeletePolicyOption) Validate(cmd *cobra.Command, args []string) error {
if err := o.RootOptions.Validate(cmd, args); err != nil {
return err
}
if len(args) < 2 {
return errors.New("invalid args")
}
return nil
}

func (o *UpdateDeletePolicyOption) Complete(cmd *cobra.Command, args []string) error {
if err := o.RootOptions.Complete(cmd, args); err != nil {
return err
}

o.UserName = args[0]
o.DeletePolicy = args[1]

cmd.SilenceErrors = true
cmd.SilenceUsage = true
return nil
}

func (o *UpdateDeletePolicyOption) RunE(cmd *cobra.Command, args []string) error {
if err := o.Validate(cmd, args); err != nil {
return fmt.Errorf("validation error: %w", err)
}
if err := o.Complete(cmd, args); err != nil {
return fmt.Errorf("invalid options: %w", err)
}

ctx, cancel := context.WithTimeout(o.Ctx, time.Second*10)
defer cancel()
ctx = clog.IntoContext(ctx, o.Logr)

o.Logr.Info("updating user delete polocy", "userName", o.UserName, "deletepolicy", o.DeletePolicy)

var (
user *dashv1alpha1.User
err error
)
if o.UseKubeAPI {
user, err = o.UpdateUserDeletePolicyWithKubeClient(ctx)
} else {
user, err = o.UpdateUserDeletePolicyWithDashClient(ctx)
}
if err != nil {
return err
}
fmt.Fprintln(cmd.OutOrStdout(), color.GreenString("Successfully updated user %s", o.UserName))
OutputWideTable(cmd.OutOrStdout(), []*dashv1alpha1.User{user})

return nil
}

func (o *UpdateDeletePolicyOption) UpdateUserDeletePolicyWithDashClient(ctx context.Context) (*dashv1alpha1.User, error) {
delPolicy := apiconv.C2D_DeletePolicy(o.DeletePolicy)
if delPolicy == nil {
delPolicy = ptr.To(dashv1alpha1.DeletePolicy_delete)
}

req := &dashv1alpha1.UpdateUserDeletePolicyRequest{
UserName: o.UserName,
DeletePolicy: *delPolicy,
}
o.Logr.DebugAll().Info("UserServiceClient.UpdateUserDeletePolicy", "req", req)
c := o.CosmoDashClient
res, err := c.UserServiceClient.UpdateUserDeletePolicy(ctx, cli.NewRequestWithToken(req, o.CliConfig))
if err != nil {
return nil, fmt.Errorf("failed to connect dashboard server: %w", err)
}
o.Logr.DebugAll().Info("UserServiceClient.UpdateUserDeletePolicy", "res", res)

return res.Msg.User, nil
}

func (o *UpdateDeletePolicyOption) UpdateUserDeletePolicyWithKubeClient(ctx context.Context) (*dashv1alpha1.User, error) {
c := o.KosmoClient
opts := kosmo.UpdateUserOpts{
DeletePolicy: &o.DeletePolicy,
}
o.Logr.DebugAll().Info("UpdateUser", "opts", opts)
user, err := c.UpdateUser(ctx, o.UserName, opts)
if err != nil {
return nil, err
}
d := apiconv.C2D_User(*user)

return d, nil
}
5 changes: 2 additions & 3 deletions internal/cmd/user/update_display_name.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ type UpdateDisplayNameOption struct {
func UpdateDisplayNameCmd(cmd *cobra.Command, cliOpt *cli.RootOptions) *cobra.Command {
o := &UpdateDisplayNameOption{RootOptions: cliOpt}
cmd.RunE = cli.ConnectErrorHandler(o)
cmd.Flags().StringVar(&o.DisplayName, "display-name", "", "user display name (Required)")
cmd.MarkFlagRequired("display-name")
cmd.Flags().BoolVar(&o.Force, "force", false, "not ask confirmation")
return cmd
}
Expand All @@ -39,7 +37,7 @@ func (o *UpdateDisplayNameOption) Validate(cmd *cobra.Command, args []string) er
if err := o.RootOptions.Validate(cmd, args); err != nil {
return err
}
if len(args) < 1 {
if len(args) < 2 {
return errors.New("invalid args")
}
return nil
Expand All @@ -51,6 +49,7 @@ func (o *UpdateDisplayNameOption) Complete(cmd *cobra.Command, args []string) er
}

o.UserName = args[0]
o.DisplayName = args[1]

cmd.SilenceErrors = true
cmd.SilenceUsage = true
Expand Down
36 changes: 26 additions & 10 deletions internal/cmd/workspace/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,22 +237,38 @@ func OutputWideTable(out io.Writer, username string, workspaces []*dashv1alpha1.
data := [][]string{}

for _, v := range workspaces {
mainURL := v.Status.MainUrl
if v.OwnerName != username {
mainURL = `[shared workspace. see shared URLs by "cosmoctl ws get-network"]`
}
vars := make([]string, 0, len(v.Spec.Vars))
for k, vv := range v.Spec.Vars {
vars = append(vars, fmt.Sprintf("%s=%s", k, vv))
}
data = append(data, []string{v.OwnerName, v.Name, v.Spec.Template, strings.Join(vars, ","), v.Status.Phase, mainURL})
data = append(data, []string{v.OwnerName, v.Name, v.Spec.Template, printVars(v.Spec.Vars), v.Status.Phase, printDeletePolicy(v.DeletePolicy), printMainURL(v, username)})
}

cli.OutputTable(out,
[]string{"USER", "NAME", "TEMPLATE", "VARS", "PHASE", "MAINURL"},
[]string{"USER", "NAME", "TEMPLATE", "VARS", "PHASE", "DELETEPOLICY", "MAINURL"},
data)
}

func printMainURL(v *dashv1alpha1.Workspace, username string) string {
mainURL := v.Status.MainUrl
if v.OwnerName != username {
mainURL = `[shared workspace. see shared URLs by "cosmoctl ws get-network"]`
}
return mainURL
}

func printVars(vars map[string]string) string {
varSlice := make([]string, 0, len(vars))
for k, vv := range vars {
varSlice = append(varSlice, fmt.Sprintf("%s=%s", k, vv))
}
return strings.Join(varSlice, ",")
}

func printDeletePolicy(deletePolicy *dashv1alpha1.DeletePolicy) string {
if deletePolicy != nil {
return deletePolicy.String()
} else {
return dashv1alpha1.DeletePolicy_delete.String()
}
}

func (o *GetOption) listWorkspacesByKubeClient(ctx context.Context, userName string, includeShared bool) ([]*dashv1alpha1.Workspace, error) {
c := o.KosmoClient
workspaces, err := c.ListWorkspacesByUserName(ctx, userName, func(o *kosmo.ListWorkspacesOptions) {
Expand Down
22 changes: 22 additions & 0 deletions internal/cmd/workspace/suspend.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"strings"
"time"

"github.com/fatih/color"
Expand All @@ -21,6 +22,7 @@ type SuspendOption struct {

WorkspaceNames []string
UserName string
Force bool
}

func SuspendCmd(cmd *cobra.Command, cliOpt *cli.RootOptions) *cobra.Command {
Expand All @@ -29,6 +31,7 @@ func SuspendCmd(cmd *cobra.Command, cliOpt *cli.RootOptions) *cobra.Command {
cmd.RunE = cli.ConnectErrorHandler(o)

cmd.Flags().StringVarP(&o.UserName, "user", "u", "", "user name (defualt: login user)")
cmd.Flags().BoolVar(&o.Force, "force", false, "not ask confirmation")

return cmd
}
Expand Down Expand Up @@ -69,6 +72,25 @@ func (o *SuspendOption) RunE(cmd *cobra.Command, args []string) error {
return fmt.Errorf("invalid options: %w", err)
}

o.Logr.Info("suspend workspaces", "workspaces", o.WorkspaceNames)

if !o.Force {
AskLoop:
for {
input, err := cli.AskInput("Confirm? [y/n] ", false)
if err != nil {
return err
}
switch strings.ToLower(input) {
case "y":
break AskLoop
case "n":
fmt.Println("canceled")
return nil
}
}
}

ctx, cancel := context.WithTimeout(o.Ctx, time.Second*10)
defer cancel()
ctx = clog.IntoContext(ctx, o.Logr)
Expand Down
Loading

0 comments on commit 32a4f82

Please sign in to comment.