@@ -18,27 +18,28 @@ limitations under the License.
1818package cmd
1919
2020import (
21- "encoding/json"
22- "fmt"
23- "io/ioutil"
24- "log"
25- "net/http"
26- "os"
27- "path/filepath"
28- "strconv"
29-
30- "github.com/spf13/cobra"
21+ "bytes"
22+ "encoding/json"
23+ "fmt"
24+ "io/ioutil"
25+ "log"
26+ "net/http"
27+ "os"
28+ "path/filepath"
29+ "strconv"
30+
31+ "github.com/spf13/cobra"
3132)
3233
3334var (
34- // Used for flags and config
35- configDir string
36- configPath string
37- defaultConfigPath string
38- user string
39- host string
40- port string
41- password string
35+ // Used for flags and config
36+ configDir string
37+ configPath string
38+ defaultConfigPath string
39+ user string
40+ host string
41+ port string
42+ password string
4243)
4344
4445const clientVersion = "1.0.0"
@@ -48,7 +49,7 @@ const apiVersion = "v0"
4849var rootCmd = & cobra.Command {
4950 Use : "rdctl" ,
5051 Short : "A CLI for Rancher Desktop" ,
51- Long : `The eventual goal of this CLI is to enable any UI-based operation to be done from the command-line as well.` ,
52+ Long : `The eventual goal of this CLI is to enable any UI-based operation to be done from the command-line as well.` ,
5253}
5354
5455// Execute adds all child commands to the root command and sets flags appropriately.
@@ -61,101 +62,120 @@ func Execute() {
6162}
6263
6364func init () {
64- var err error
65-
66- cobra .OnInitialize (initConfig )
67- configDir , err = os .UserConfigDir ()
68- if err != nil {
69- log .Fatal ("Can't get config-dir: " , err )
70- }
71- defaultConfigPath = filepath .Join (configDir , "rancher-desktop" , "rd-engine.json" )
72- rootCmd .PersistentFlags ().StringVar (& configPath , "config-path" , "" , fmt .Sprintf ("config file (default %s)" , defaultConfigPath ))
73- rootCmd .PersistentFlags ().StringVar (& user , "user" , "" , "overrides the user setting in the config file" )
74- rootCmd .PersistentFlags ().StringVar (& host , "host" , "" , "default is localhost; most useful for WSL" )
75- rootCmd .PersistentFlags ().StringVar (& port , "port" , "" , "overrides the port setting in the config file" )
76- rootCmd .PersistentFlags ().StringVar (& password , "password" , "" , "overrides the password setting in the config file" )
65+ var err error
66+
67+ cobra .OnInitialize (initConfig )
68+ configDir , err = os .UserConfigDir ()
69+ if err != nil {
70+ log .Fatal ("Can't get config-dir: " , err )
71+ }
72+ defaultConfigPath = filepath .Join (configDir , "rancher-desktop" , "rd-engine.json" )
73+ rootCmd .PersistentFlags ().StringVar (& configPath , "config-path" , "" , fmt .Sprintf ("config file (default %s)" , defaultConfigPath ))
74+ rootCmd .PersistentFlags ().StringVar (& user , "user" , "" , "overrides the user setting in the config file" )
75+ rootCmd .PersistentFlags ().StringVar (& host , "host" , "" , "default is localhost; most useful for WSL" )
76+ rootCmd .PersistentFlags ().StringVar (& port , "port" , "" , "overrides the port setting in the config file" )
77+ rootCmd .PersistentFlags ().StringVar (& password , "password" , "" , "overrides the password setting in the config file" )
7778}
7879
79- func doRequest (method string , command string ) error {
80- req , err := getRequestObject (method , command )
81- if err != nil {
82- return err
83- }
84- return doRestOfRequest (req )
80+ func doRequest (method string , command string ) ([]byte , error ) {
81+ req , err := getRequestObject (method , command )
82+ if err != nil {
83+ return nil , err
84+ }
85+ return doRestOfRequest (req )
86+ }
87+
88+ func doRequestWithPayload (method string , command string , payload * bytes.Buffer ) ([]byte , error ) {
89+ req , err := http .NewRequest (method , fmt .Sprintf ("http://%s:%s/%s/%s" , host , port , apiVersion , command ), payload )
90+ if err != nil {
91+ return nil , err
92+ }
93+ req .SetBasicAuth (user , password )
94+ req .Header .Add ("Content-Type" , "application/json" )
95+ req .Close = true
96+ return doRestOfRequest (req )
8597}
8698
8799func getRequestObject (method string , command string ) (* http.Request , error ) {
88- req , err := http .NewRequest (method , fmt .Sprintf ("http://%s:%s/%s/%s" , host , port , apiVersion , command ), nil )
89- if err != nil {
90- return nil , err
91- }
92- req .SetBasicAuth (user , password )
93- req .Header .Add ("Content-Type" , "text/plain" )
94- req .Close = true
95- return req , nil
100+ req , err := http .NewRequest (method , fmt .Sprintf ("http://%s:%s/%s/%s" , host , port , apiVersion , command ), nil )
101+ if err != nil {
102+ return nil , err
103+ }
104+ req .SetBasicAuth (user , password )
105+ req .Header .Add ("Content-Type" , "text/plain" )
106+ req .Close = true
107+ return req , nil
96108}
97109
98- func doRestOfRequest (req * http.Request ) error {
99- client := http.Client {}
100- response , err := client .Do (req )
101- if err != nil {
102- return err
103- }
104- if response .StatusCode < 200 || response .StatusCode >= 300 {
105- switch (response .StatusCode ) {
106- case 401 :
107- return fmt .Errorf ("user/password not accepted" )
108- case 500 :
109- return fmt .Errorf ("server-side problem: please consult the server logs for more information." )
110- }
111- return fmt .Errorf ("%s" , response .Status )
112- }
113-
114- defer response .Body .Close ()
115-
116- body , err := ioutil .ReadAll (response .Body )
117- if err != nil {
118- return err
119- }
120-
121- fmt .Println (string (body ))
122- return nil
110+ func doRestOfRequest (req * http.Request ) ([]byte , error ) {
111+ client := http.Client {}
112+ response , err := client .Do (req )
113+ if err != nil {
114+ return nil , err
115+ }
116+ statusMessage := ""
117+ if response .StatusCode < 200 || response .StatusCode >= 300 {
118+ switch response .StatusCode {
119+ case 400 :
120+ statusMessage = response .Status
121+ // Prefer the error message in the body written by the command-server, not the one from the http server.
122+ break
123+ case 401 :
124+ return nil , fmt .Errorf ("user/password not accepted" )
125+ case 500 :
126+ return nil , fmt .Errorf ("server-side problem: please consult the server logs for more information" )
127+ default :
128+ return nil , fmt .Errorf ("server error return-code %d: %s" , response .StatusCode , response .Status )
129+ }
130+ }
131+
132+ defer response .Body .Close ()
133+
134+ body , err := ioutil .ReadAll (response .Body )
135+ if err != nil {
136+ if statusMessage != "" {
137+ return nil , fmt .Errorf ("server error return-code %d: %s" , response .StatusCode , statusMessage )
138+ }
139+ return nil , err
140+ } else if statusMessage != "" {
141+ return nil , fmt .Errorf ("%s" , string (body ))
142+ }
143+
144+ return body , nil
123145}
124146
125147// The CLIConfig struct is used to store the json data read from the config file.
126148type CLIConfig struct {
127- User string
128- Password string
129- Port int
149+ User string
150+ Password string
151+ Port int
130152}
131153
132154func initConfig () {
133- if configPath == "" {
134- configPath = defaultConfigPath
135- }
136- content , err := ioutil .ReadFile (configPath )
137- if err != nil {
138- log .Fatalf ("Error trying to read file %s: %v" , configPath , err )
139- }
140-
141- var settings CLIConfig
142- err = json .Unmarshal (content , & settings )
143- if err != nil {
144- log .Fatalf ("Error trying to json-load file %s: %v" , configPath , err )
145- }
146-
147- if user == "" {
148- user = settings .User
149- }
150- if password == "" {
151- password = settings .Password
152- }
153- if host == "" {
154- host = "localhost"
155- }
156- if port == "" {
157- port = strconv .Itoa (settings .Port )
158- }
159- }
155+ if configPath == "" {
156+ configPath = defaultConfigPath
157+ }
158+ content , err := ioutil .ReadFile (configPath )
159+ if err != nil {
160+ log .Fatalf ("Error trying to read file %s: %v" , configPath , err )
161+ }
160162
163+ var settings CLIConfig
164+ err = json .Unmarshal (content , & settings )
165+ if err != nil {
166+ log .Fatalf ("Error trying to json-load file %s: %v" , configPath , err )
167+ }
161168
169+ if user == "" {
170+ user = settings .User
171+ }
172+ if password == "" {
173+ password = settings .Password
174+ }
175+ if host == "" {
176+ host = "localhost"
177+ }
178+ if port == "" {
179+ port = strconv .Itoa (settings .Port )
180+ }
181+ }
0 commit comments