github.com/scaleway/scaleway-cli@v1.11.1/pkg/cli/main.go (about) 1 // Copyright (C) 2015 Scaleway. All rights reserved. 2 // Use of this source code is governed by a MIT-style 3 // license that can be found in the LICENSE.md file. 4 5 package cli 6 7 import ( 8 "fmt" 9 "io/ioutil" 10 "net/http" 11 "os" 12 "path/filepath" 13 "strings" 14 "time" 15 16 "github.com/Sirupsen/logrus" 17 flag "github.com/docker/docker/pkg/mflag" 18 19 version "github.com/hashicorp/go-version" 20 "github.com/scaleway/scaleway-cli/pkg/api" 21 "github.com/scaleway/scaleway-cli/pkg/clilogger" 22 "github.com/scaleway/scaleway-cli/pkg/commands" 23 "github.com/scaleway/scaleway-cli/pkg/config" 24 "github.com/scaleway/scaleway-cli/pkg/scwversion" 25 "github.com/scaleway/scaleway-cli/pkg/utils" 26 ) 27 28 // global options 29 var ( 30 flDebug = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode") 31 flVerbose = flag.Bool([]string{"V", "-verbose"}, false, "Enable verbose mode") 32 flVersion = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit") 33 flQuiet = flag.Bool([]string{"q", "-quiet"}, false, "Enable quiet mode") 34 flSensitive = flag.Bool([]string{"-sensitive"}, false, "Show sensitive data in outputs, i.e. API Token/Organization") 35 flRegion = flag.String([]string{"-region"}, "par1", "Change the default region (e.g. ams1)") 36 ) 37 38 // Start is the entrypoint 39 func Start(rawArgs []string, streams *commands.Streams) (int, error) { 40 checkVersion() 41 if streams == nil { 42 streams = &commands.Streams{ 43 Stdin: os.Stdin, 44 Stdout: os.Stdout, 45 Stderr: os.Stderr, 46 } 47 } 48 flag.CommandLine.Parse(rawArgs) 49 50 config, cfgErr := config.GetConfig() 51 if cfgErr != nil && !os.IsNotExist(cfgErr) { 52 return 1, fmt.Errorf("unable to open .scwrc config file: %v", cfgErr) 53 } 54 55 if *flVersion { 56 fmt.Fprintf(streams.Stderr, "scw version %s, build %s\n", scwversion.VERSION, scwversion.GITCOMMIT) 57 return 0, nil 58 } 59 60 if *flSensitive { 61 os.Setenv("SCW_SENSITIVE", "1") 62 } 63 64 if *flDebug { 65 os.Setenv("DEBUG", "1") 66 } 67 68 if *flVerbose { 69 os.Setenv("SCW_VERBOSE_API", "1") 70 } 71 72 utils.Quiet(*flQuiet) 73 initLogging(os.Getenv("DEBUG") != "", *flVerbose, streams) 74 75 args := flag.Args() 76 if len(args) < 1 { 77 CmdHelp.Exec(CmdHelp, []string{}) 78 return 1, nil 79 } 80 name := args[0] 81 82 args = args[1:] 83 84 // Apply default values 85 for _, cmd := range Commands { 86 cmd.streams = streams 87 } 88 89 for _, cmd := range Commands { 90 if cmd.Name() == name { 91 cmd.Flag.SetOutput(ioutil.Discard) 92 err := cmd.Flag.Parse(args) 93 if err != nil { 94 return 1, fmt.Errorf("usage: scw %s", cmd.UsageLine) 95 } 96 switch cmd.Name() { 97 case "login", "help", "version": 98 // commands that don't need API 99 case "_userdata": 100 // commands that may need API 101 api, _ := getScalewayAPI(*flRegion) 102 cmd.API = api 103 default: 104 // commands that do need API 105 if cfgErr != nil { 106 if name != "login" && config == nil { 107 logrus.Debugf("cfgErr: %v", cfgErr) 108 fmt.Fprintf(streams.Stderr, "You need to login first: 'scw login'\n") 109 return 1, nil 110 } 111 } 112 api, errGet := getScalewayAPI(*flRegion) 113 if errGet != nil { 114 return 1, fmt.Errorf("unable to initialize scw api: %v", errGet) 115 } 116 cmd.API = api 117 } 118 // clean cache between versions 119 if cmd.API != nil && config.Version != scwversion.VERSION { 120 cmd.API.ClearCache() 121 config.Save() 122 } 123 err = cmd.Exec(cmd, cmd.Flag.Args()) 124 switch err { 125 case nil: 126 case ErrExitFailure: 127 return 1, nil 128 case ErrExitSuccess: 129 return 0, nil 130 default: 131 return 1, fmt.Errorf("cannot execute '%s': %v", cmd.Name(), err) 132 } 133 if cmd.API != nil { 134 cmd.API.Sync() 135 } 136 return 0, nil 137 } 138 } 139 return 1, fmt.Errorf("scw: unknown subcommand %s\nRun 'scw help' for usage", name) 140 } 141 142 // getScalewayAPI returns a ScalewayAPI using the user config file 143 func getScalewayAPI(region string) (*api.ScalewayAPI, error) { 144 // We already get config globally, but whis way we can get explicit error when trying to create a ScalewayAPI object 145 config, err := config.GetConfig() 146 if err != nil { 147 return nil, err 148 } 149 return api.NewScalewayAPI(config.Organization, config.Token, scwversion.UserAgent(), region, clilogger.SetupLogger) 150 } 151 152 func initLogging(debug bool, verbose bool, streams *commands.Streams) { 153 logrus.SetOutput(streams.Stderr) 154 if debug { 155 logrus.SetLevel(logrus.DebugLevel) 156 } else if verbose { 157 logrus.SetLevel(logrus.InfoLevel) 158 } else { 159 logrus.SetLevel(logrus.WarnLevel) 160 } 161 } 162 163 func checkVersion() { 164 if os.Getenv("SCW_NOCHECKVERSION") == "1" { 165 return 166 } 167 homeDir, err := config.GetHomeDir() 168 if err != nil { 169 return 170 } 171 updateFiles := []string{"/var/run/.scw-update", "/tmp/.scw-update", filepath.Join(homeDir, ".scw-update")} 172 updateFile := "" 173 174 callAPI := false 175 for _, file := range updateFiles { 176 if stat, err := os.Stat(file); err == nil { 177 updateFile = file 178 callAPI = stat.ModTime().Before(time.Now().AddDate(0, 0, -1)) 179 break 180 } 181 } 182 if updateFile == "" { 183 for _, file := range updateFiles { 184 if scwupdate, err := os.OpenFile(file, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0600); err == nil { 185 scwupdate.Close() 186 updateFile = file 187 callAPI = true 188 break 189 } 190 } 191 } 192 if callAPI { 193 scwupdate, err := os.OpenFile(updateFile, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0600) 194 if err != nil { 195 return 196 } 197 scwupdate.Close() 198 req := http.Client{ 199 Timeout: 1 * time.Second, 200 } 201 resp, err := req.Get("https://fr-1.storage.online.net/scaleway/scaleway-cli/VERSION") 202 if resp != nil { 203 defer resp.Body.Close() 204 } 205 if err != nil { 206 return 207 } 208 body, err := ioutil.ReadAll(resp.Body) 209 if err != nil { 210 return 211 } 212 if scwversion.VERSION == "" { 213 return 214 } 215 ver := scwversion.VERSION 216 if ver[0] == 'v' { 217 ver = string([]byte(ver)[1:]) 218 } 219 actual, err1 := version.NewVersion(ver) 220 update, err2 := version.NewVersion(strings.Trim(string(body), "\n")) 221 if err1 != nil || err2 != nil { 222 return 223 } 224 if actual.LessThan(update) { 225 logrus.Infof("A new version of scw is available (%v), beware that you are currently running %v", update, actual) 226 } 227 } 228 }