Skip to content
Draft
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
25 changes: 18 additions & 7 deletions cmd/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"strings"

"github.com/google/uuid"
"github.com/outscale/octl/cmd/prerun"
"github.com/outscale/octl/pkg/builder"
"github.com/outscale/octl/pkg/config"
"github.com/outscale/octl/pkg/debug"
Expand All @@ -20,6 +21,7 @@ import (
"github.com/outscale/octl/pkg/preferences"
"github.com/outscale/octl/pkg/runner"
"github.com/outscale/osc-sdk-go/v3/pkg/oks"
"github.com/outscale/osc-sdk-go/v3/pkg/profile"
"github.com/samber/lo"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -51,11 +53,11 @@ func init() {

// Add --project flag to kube cluster commands
clusterCmd, _ := lo.Find(oksCmd.Commands(), func(c *cobra.Command) bool { return c.Name() == "cluster" })
clusterCmd.PersistentFlags().String("project", preferences.Preferences.Kube.DefaultProject, "Name or ID of project")
clusterCmd.PersistentFlags().String("project", "", "Name or ID of project")
_ = flags.MarkAsNoForward(clusterCmd.PersistentFlags(), "project")
clusterCmd.PersistentPreRunE = clusterArgToID
clusterCreateCmd, _ := lo.Find(clusterCmd.Commands(), func(c *cobra.Command) bool { return c.Name() == "create" })
_ = flags.SetDefault(clusterCreateCmd.Flags(), "project", preferences.Preferences.Kube.DefaultProject)
_ = flags.SetDefault(clusterCreateCmd.Flags(), "project", "")

// Remap <cluster ls --project name> to <project clusters name>
projectCmd, _ := lo.Find(oksCmd.Commands(), func(c *cobra.Command) bool { return c.Name() == "project" })
Expand Down Expand Up @@ -116,6 +118,7 @@ func projectArgToID(cmd *cobra.Command, args []string) error {
if cmd.Name() == "use" {
return nil
}

debug.Println("projectArgToID")
p := loadProfile(cmd)
cl, err := oks.NewClient(p, sdkOptions(cmd)...)
Expand Down Expand Up @@ -168,6 +171,9 @@ func flagNamesToID(cmd *cobra.Command, args []string) error {
}

func projectNameToID(ctx context.Context, name string, cl *oks.Client) (string, error) {
if name == "" {
name = prerun.PreferencesFrom(ctx).Kube.DefaultProject
}
if name == "" {
return "", nil
}
Expand Down Expand Up @@ -222,19 +228,24 @@ func clusterNameToID(ctx context.Context, name, project string, cl *oks.Client)
}
}

func useProject(c *cobra.Command, args []string) {
func useProject(cmd *cobra.Command, args []string) {
var def string
if len(args) > 0 {
def = args[0]
}
preferences.Preferences.Kube.DefaultProject = def
err := preferences.Preferences.Save()
prof, _ := cmd.Flags().GetString("profile")
if prof == "" {
prof = profile.DefaultProfile
}
// retro compatibility
_ = preferences.SetGlobal(func(prefs *preferences.Preferences) { prefs.Kube.DefaultProject = "" })
err := preferences.Set(prof, func(prefs *preferences.Preferences) { prefs.Kube.DefaultProject = def })
if err != nil {
messages.ExitErr(err)
}
if def == "" {
messages.Success("The default project has been reset")
messages.Success("The default project has been reset for profile %q", prof)
} else {
messages.Success("%q is now the default project for all octl kube commands", def)
messages.Success("%q is now the default project for profile %q", def, prof)
}
}
3 changes: 1 addition & 2 deletions cmd/kube_kubeapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/outscale/octl/pkg/config"
"github.com/outscale/octl/pkg/flags"
"github.com/outscale/octl/pkg/messages"
"github.com/outscale/octl/pkg/preferences"
"github.com/outscale/octl/pkg/runner"
"github.com/outscale/osc-sdk-go/v3/pkg/oks"
"github.com/outscale/osc-sdk-go/v3/pkg/osc"
Expand All @@ -34,7 +33,7 @@ func buildKubeAPI[Client any](provider string, cmd, parent *cobra.Command, getcl
_ = child.MarkPersistentFlagRequired("cluster")
} else {
child.Flags().String("cluster", "", "[REQUIRED] Name or ID of cluster")
child.Flags().String("project", preferences.Preferences.Kube.DefaultProject, "Name or ID of project")
child.Flags().String("project", "", "Name or ID of project")
_ = child.MarkFlagRequired("cluster")
_ = flags.MarkAsNoForward(child.Flags(), "project")
}
Expand Down
3 changes: 1 addition & 2 deletions cmd/kube_kubectl.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/outscale/octl/pkg/debug"
"github.com/outscale/octl/pkg/flags"
"github.com/outscale/octl/pkg/messages"
"github.com/outscale/octl/pkg/preferences"
"github.com/outscale/osc-sdk-go/v3/pkg/oks"
"github.com/spf13/cobra"
"k8s.io/client-go/tools/clientcmd"
Expand Down Expand Up @@ -120,6 +119,6 @@ func init() {
oksCmd.AddCommand(kubectlCmd)
kubectlCmd.Flags().String("cluster", "", "Name or ID of cluster")
_ = kubectlCmd.MarkFlagRequired("cluster")
kubectlCmd.Flags().String("project", preferences.Preferences.Kube.DefaultProject, "Name or ID of project")
kubectlCmd.Flags().String("project", "", "Name or ID of project")
_ = flags.MarkAsNoForward(kubectlCmd.Flags(), "project")
}
22 changes: 22 additions & 0 deletions cmd/kube_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,26 @@ func TestKube(t *testing.T) {
t.Run("Kubectl fails with an invalid --project", func(t *testing.T) {
runWithError(t, []string{"kube", "kubectl", "--cluster", cluster, "--project", "foobarbaz", "--", "get", "nodes"}, nil)
})

t.Run("A default project can be set", func(t *testing.T) {
_ = run(t, []string{"kube", "project", "use", emptyProject}, nil)
t.Cleanup(func() {
_ = run(t, []string{"kube", "project", "use"}, nil)
})
runWithError(t, []string{"kube", "kubectl", "--cluster", cluster, "--", "get", "nodes"}, nil)
})
t.Run("A default project is ignored if flag si set", func(t *testing.T) {
_ = run(t, []string{"kube", "project", "use", emptyProject}, nil)
t.Cleanup(func() {
_ = run(t, []string{"kube", "project", "use"}, nil)
})
_ = run(t, []string{"kube", "kubectl", "--cluster", cluster, "--project", project, "--", "get", "nodes"}, nil)
})
t.Run("A default project is ignored when set on another profile", func(t *testing.T) {
_ = run(t, []string{"kube", "project", "use", emptyProject, "--profile", "foo"}, nil)
t.Cleanup(func() {
_ = run(t, []string{"kube", "project", "use", "--profile", "foo"}, nil)
})
_ = run(t, []string{"kube", "kubectl", "--cluster", cluster, "--", "get", "nodes"}, nil)
})
}
36 changes: 36 additions & 0 deletions cmd/prerun/preferences.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package prerun

import (
"context"

"github.com/outscale/octl/pkg/messages"
"github.com/outscale/octl/pkg/preferences"
"github.com/outscale/osc-sdk-go/v3/pkg/profile"
"github.com/spf13/cobra"
)

type preferenceKey struct{}

func contextWithPreferences(ctx context.Context, p preferences.Preferences) context.Context {
return context.WithValue(ctx, preferenceKey{}, p)
}

func PreferencesFrom(ctx context.Context) preferences.Preferences {
p := ctx.Value(preferenceKey{})
if p == nil {
return preferences.Preferences{}
}
return p.(preferences.Preferences)
}

func LoadPreferences(cmd *cobra.Command) {
prof, _ := cmd.Flags().GetString("profile")
if prof == "" {
prof = profile.DefaultProfile
}
prefs, err := preferences.Get(prof)
if err != nil {
messages.Err(err.Error())
}
cmd.SetContext(contextWithPreferences(cmd.Context(), prefs))
}
4 changes: 4 additions & 0 deletions cmd/profile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,7 @@ func TestProfilePriority(t *testing.T) {
assert.Equal(t, "priority_region", p.Region)
})
}

func TestProfilePreferences(t *testing.T) {

}
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ var rootCmd = &cobra.Command{
PersistentPreRun: func(cmd *cobra.Command, args []string) {
prerun.CheckFalse(cmd, args)
prerun.CheckUpdate(cmd, args)
prerun.LoadPreferences(cmd)
},
Run: root,
SilenceErrors: true, // do not display errors when an error occurred, we do it
Expand Down
63 changes: 53 additions & 10 deletions pkg/preferences/preferences.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package preferences

import (
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"

"dario.cat/mergo"
"github.com/goccy/go-yaml"
"github.com/outscale/octl/pkg/debug"
"github.com/outscale/octl/pkg/messages"
Expand All @@ -15,36 +17,77 @@ type Kube struct {
DefaultProject string `yaml:"default_project,omitempty"`
}

type All struct {
type Preferences struct {
Kube Kube `yaml:"kube,omitzero"`
}

var Preferences All
type File struct {
Global Preferences `yaml:"global,omitzero"`
PerProfile map[string]Preferences `yaml:"per_profile,omitzero"`
}

func init() {
func Load() (File, error) {
root, err := os.UserConfigDir()
if err != nil {
debug.Println("Unable to compute user preferences dir", err)
return
return File{}, err
}
path := filepath.Join(root, "octl", "preferences.yaml")
fd, err := os.Open(path) //nolint:gosec
if errors.Is(err, fs.ErrNotExist) {
debug.Println("no user config file found", path)
return
return File{}, err
}
if err != nil {
messages.Err("Unable to open preferences path: %w")
return
return File{}, err
}
debug.Println("loading user config from", path)
err = yaml.NewDecoder(fd).Decode(&Preferences)
var pf File
err = yaml.NewDecoder(fd).Decode(&pf)
if err != nil {
return File{}, err
}
return pf, nil
}

func Get(profile string) (Preferences, error) {
pf, err := Load()
if err != nil {
return Preferences{}, fmt.Errorf("unable to load preferences: %w", err)
}
if pf.PerProfile == nil {
return pf.Global, nil
}
prefs := pf.PerProfile[profile]
mergo.Merge(&prefs, pf.Global)
return prefs, nil
}

func Set(profile string, fn func(prefs *Preferences)) error {
pf, err := Load()
if err != nil {
return fmt.Errorf("unable to load preferences: %w", err)
}
if pf.PerProfile == nil {
pf.PerProfile = map[string]Preferences{}
}
pref := pf.PerProfile[profile]
fn(&pref)
pf.PerProfile[profile] = pref
return pf.Save()
}

func SetGlobal(fn func(prefs *Preferences)) error {
pf, err := Load()
if err != nil {
messages.Err("Unable to load preferences: %w")
return fmt.Errorf("unable to load preferences: %w", err)
}
fn(&pf.Global)
return pf.Save()
}

func (p *All) Save() (err error) {
func (pf *File) Save() (err error) {
root, err := os.UserConfigDir()
if err != nil {
debug.Println("Unable to compute user preferences dir", err)
Expand All @@ -67,5 +110,5 @@ func (p *All) Save() (err error) {
}
}()
debug.Println("saving user preferences to", path)
return yaml.NewEncoder(fd).Encode(p)
return yaml.NewEncoder(fd).Encode(pf)
}