diff --git a/cmd/add.go b/cmd/add.go index cad0722b..7044a33a 100644 --- a/cmd/add.go +++ b/cmd/add.go @@ -81,7 +81,7 @@ func (ac *AddCommand) runAdd(cmd *cobra.Command, args []string) error { } } else { // check path - file, err := CheckAndTransformFilePath(file) + file, err := CheckAndTransformFilePath(file, cfgCreate) if err != nil { return err } diff --git a/cmd/merge.go b/cmd/merge.go index 88e5cf1f..03363de3 100644 --- a/cmd/merge.go +++ b/cmd/merge.go @@ -52,7 +52,7 @@ func (mc MergeCommand) runMerge(command *cobra.Command, args []string) error { } if folder != "" { - folder, err = CheckAndTransformFilePath(folder) + folder, err = CheckAndTransformDirPath(folder) if err != nil { return err } diff --git a/cmd/root.go b/cmd/root.go index 45e6cc77..5a3babd7 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -33,10 +33,12 @@ import ( ) var ( + // cfgFile represents the path to the configuration file. cfgFile string uiSize int macNotify bool silenceTable bool + cfgCreate bool ) // Cli cmd struct @@ -68,6 +70,7 @@ func (cli *Cli) setFlags() { } flags := cli.rootCmd.PersistentFlags() flags.StringVar(&cfgFile, "config", kubeconfig, "path of kubeconfig") + flags.BoolVar(&cfgCreate, "create", false, "Create a new kubeconfig file if not exists") // let the `make doc-gen` command generate consistent output rather than parsing different $HOME environment variables for different users. flags.Lookup("config").DefValue = "$HOME/.kube/config" flags.IntVarP(&uiSize, "ui-size", "u", 4, "number of list items to show in menu at once") @@ -78,7 +81,7 @@ func (cli *Cli) setFlags() { // Run command func (cli *Cli) Run() error { // check and format kubeconfig path - config, err := CheckAndTransformFilePath(cfgFile) + config, err := CheckAndTransformFilePath(cfgFile, cfgCreate) if err != nil { return err } diff --git a/cmd/utils.go b/cmd/utils.go index d6854e82..addad10f 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -340,7 +340,7 @@ func WriteConfig(cover bool, file string, outConfig *clientcmdapi.Config) error // UpdateConfigFile update kubeconfig func UpdateConfigFile(file string, updateConfig *clientcmdapi.Config) error { - file, err := CheckAndTransformFilePath(file) + file, err := CheckAndTransformFilePath(file, cfgCreate) if err != nil { return err } @@ -416,10 +416,31 @@ func appendConfig(c1, c2 *clientcmdapi.Config) *clientcmdapi.Config { } // CheckAndTransformFilePath return converted path -func CheckAndTransformFilePath(path string) (string, error) { +func CheckAndTransformFilePath(path string, autoCreate bool) (string, error) { if strings.HasPrefix(path, "~/") { path = filepath.Join(homeDir(), path[2:]) } + if IsFile(path) { + printYellow(os.Stdout, path+" Path Exist\n") + } else { + if !autoCreate { + return path, errors.New("path Not Exist") + } + printYellow(os.Stdout, "Createing Directory: "+filepath.Dir(path)+"\n") + printYellow(os.Stdout, path+" Path is Not Absolute, setting path to home dir\n") + pathDir := filepath.Join(homeDir(), ".kube") + path = filepath.Join(pathDir, "config") + err := os.MkdirAll(pathDir, 0777) + if err != nil { + return path, err + } + file, err := os.Create(path) + if err != nil { + return path, err + } + defer file.Close() + return path, err + } // read files info _, err := os.Stat(path) if err != nil { @@ -481,3 +502,25 @@ func validateContextTemplate(contextTemplate []string) error { } return nil } + +// checkes if a path exists +func IsFile(path string) bool { + info, err := os.Stat(path) + if err != nil { + return false + } + return !info.IsDir() +} + +// CheckAndTransformFilePath return converted path +func CheckAndTransformDirPath(path string) (string, error) { + if strings.HasPrefix(path, "~/") { + path = filepath.Join(homeDir(), path[2:]) + } + // read files info + _, err := os.Stat(path) + if err != nil { + return "", err + } + return path, nil +} diff --git a/cmd/utils_test.go b/cmd/utils_test.go index ddb0f881..c4383892 100644 --- a/cmd/utils_test.go +++ b/cmd/utils_test.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "os/user" + "path/filepath" "reflect" "strings" "testing" @@ -195,9 +196,10 @@ func TestExitOption(t *testing.T) { } func TestCheckAndTransformFilePath(t *testing.T) { - wantPath := homeDir() + wantPath := filepath.Join(homeDir(), ".kube", "config") type args struct { - path string + path string + cfgCreate bool } tests := []struct { name string @@ -206,11 +208,13 @@ func TestCheckAndTransformFilePath(t *testing.T) { wantErr bool }{ // TODO: Add test cases. - {"test-~", args{path: "~/"}, wantPath, false}, + {"test -~ with no auto create", args{path: "~/", cfgCreate: false}, homeDir(), true}, + {"test -~ with auto create enabled", args{path: "~/", cfgCreate: true}, wantPath, false}, + {"test - false config path no auto create", args{path: "", cfgCreate: false}, "", true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := CheckAndTransformFilePath(tt.args.path) + got, err := CheckAndTransformFilePath(tt.args.path, tt.args.cfgCreate) if (err != nil) != tt.wantErr { t.Errorf("CheckAndTransformFilePath() error = %v, wantErr %v", err, tt.wantErr) return @@ -219,6 +223,73 @@ func TestCheckAndTransformFilePath(t *testing.T) { t.Errorf("CheckAndTransformFilePath() got = %v, want %v", got, tt.want) } }) + if tt.args.cfgCreate { + t.Cleanup(func() { + // Remove the file from wantPath after the test run is done + os.RemoveAll(filepath.Join(homeDir(), ".kube")) + }) + } + } +} +func TestCheckAndTransformDirPath(t *testing.T) { + type args struct { + path string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + // TODO: Add test cases. + {"test -~ home dir - should pass", args{path: "~/"}, homeDir(), false}, + {"test -~ with auto create enabled", args{path: ""}, "", true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := CheckAndTransformDirPath(tt.args.path) + if (err != nil) != tt.wantErr { + t.Errorf("CheckAndTransformDirPath() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("CheckAndTransformDirPath() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestIsFile(t *testing.T) { + type args struct { + path string + } + tests := []struct { + name string + args args + want bool + }{ + // TODO: Add test cases. + {"test -~ not a file", args{path: "."}, false}, + {"test - is a file", args{path: "./test.file"}, true}, + {"test - is a config file", args{path: "./config"}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.want { + // Create a file at the path + _ = os.WriteFile(tt.args.path, []byte{}, 0666) + } + got := IsFile(tt.args.path) + if got != tt.want { + t.Errorf("IsFile() got = %v, want %v", got, tt.want) + } + }) + if tt.want { + t.Cleanup(func() { + // Remove the file from wantPath after the test run is done + os.Remove(tt.args.path) + }) + } } }