github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/client/cli/util/util.go (about) 1 // Package cliutil contains methods used across all cli commands 2 // @todo: get rid of os.Exits and use errors instread 3 package util 4 5 import ( 6 "encoding/json" 7 "fmt" 8 "regexp" 9 "strings" 10 11 merrors "github.com/tickoalcantara12/micro/v3/service/errors" 12 13 "github.com/tickoalcantara12/micro/v3/util/config" 14 "github.com/urfave/cli/v2" 15 ) 16 17 const ( 18 // EnvLocal is a builtin environment, it represents your local `micro server` 19 EnvLocal = "local" 20 // EnvPlatform is a builtin highly available environment in the cloud, 21 EnvPlatform = "platform" 22 ) 23 24 const ( 25 // localProxyAddress is the default proxy address for environment server 26 localProxyAddress = "127.0.0.1:8081" 27 // platformProxyAddress is the default proxy address for environment platform 28 platformProxyAddress = "proxy.m3o.com" 29 ) 30 31 var ( 32 // list of services managed 33 // TODO: make use server/server list 34 services = []string{ 35 // runtime services 36 "network", // :8085 (peer), :8443 (proxy) 37 "runtime", // :8088 38 "registry", // :8000 39 "config", // :8001 40 "store", // :8002 41 "broker", // :8003 42 "router", // :8084 43 "auth", // :8010 44 "proxy", // :8081 45 "api", // :8080 46 "events", 47 } 48 ) 49 50 var defaultEnvs = map[string]Env{ 51 EnvLocal: { 52 Name: EnvLocal, 53 ProxyAddress: localProxyAddress, 54 Description: "Local running Micro Server", 55 }, 56 EnvPlatform: { 57 Name: EnvPlatform, 58 ProxyAddress: platformProxyAddress, 59 Description: "Cloud hosted Micro Platform", 60 }, 61 } 62 63 func IsBuiltInService(command string) bool { 64 for _, service := range services { 65 if command == service { 66 return true 67 } 68 } 69 return false 70 } 71 72 // CLIProxyAddress returns the proxy address which should be set for the client 73 func CLIProxyAddress(ctx *cli.Context) (string, error) { 74 switch ctx.Args().First() { 75 case "new", "server", "help", "env": 76 return "", nil 77 } 78 79 // fix for "micro service [command]", e.g "micro service auth" 80 if ctx.Args().First() == "service" && IsBuiltInService(ctx.Args().Get(1)) { 81 return "", nil 82 } 83 84 // don't set the proxy address on the proxy 85 if ctx.Args().First() == "proxy" { 86 return "", nil 87 } 88 89 env, err := GetEnv(ctx) 90 if err != nil { 91 return "", err 92 } 93 addr := env.ProxyAddress 94 if !strings.Contains(addr, ":") { 95 return fmt.Sprintf("%v:443", addr), nil 96 } 97 return addr, nil 98 } 99 100 type Env struct { 101 Name string 102 ProxyAddress string 103 Description string 104 } 105 106 func AddEnv(env Env) error { 107 envs, err := getEnvs() 108 if err != nil { 109 return err 110 } 111 envs[env.Name] = env 112 return setEnvs(envs) 113 } 114 115 func getEnvs() (map[string]Env, error) { 116 envsJSON, err := config.Get("envs") 117 if err != nil { 118 return nil, fmt.Errorf("Error getting environment: %v", err) 119 } 120 envs := map[string]Env{} 121 if len(envsJSON) > 0 { 122 err := json.Unmarshal([]byte(envsJSON), &envs) 123 if err != nil { 124 return nil, err 125 } 126 } 127 for k, v := range defaultEnvs { 128 envs[k] = v 129 } 130 return envs, nil 131 } 132 133 func setEnvs(envs map[string]Env) error { 134 envsJSON, err := json.Marshal(envs) 135 if err != nil { 136 return err 137 } 138 return config.Set("envs", string(envsJSON)) 139 } 140 141 // GetEnv returns the current selected environment 142 // Does not take 143 func GetEnv(ctx *cli.Context) (Env, error) { 144 var envName string 145 if len(ctx.String("env")) > 0 { 146 envName = ctx.String("env") 147 } else { 148 env, err := config.Get("env") 149 if err != nil { 150 return Env{}, err 151 } 152 if env == "" { 153 env = EnvLocal 154 } 155 envName = env 156 } 157 158 return GetEnvByName(envName) 159 } 160 161 func GetEnvByName(env string) (Env, error) { 162 envs, err := getEnvs() 163 if err != nil { 164 return Env{}, err 165 } 166 envir, ok := envs[env] 167 if !ok { 168 return Env{}, fmt.Errorf("Env \"%s\" not found. See `micro env` for available environments.", env) 169 } 170 return envir, nil 171 } 172 173 func GetEnvs() ([]Env, error) { 174 envs, err := getEnvs() 175 if err != nil { 176 return nil, err 177 } 178 179 var ret []Env 180 181 // populate the default environments 182 for _, env := range defaultEnvs { 183 ret = append(ret, env) 184 } 185 186 var nonDefaults []Env 187 188 for _, env := range envs { 189 if _, isDefault := defaultEnvs[env.Name]; !isDefault { 190 nonDefaults = append(nonDefaults, env) 191 } 192 } 193 194 // @todo order nondefault envs alphabetically 195 ret = append(ret, nonDefaults...) 196 197 return ret, nil 198 } 199 200 // SetEnv selects an environment to be used. 201 func SetEnv(envName string) error { 202 envs, err := getEnvs() 203 if err != nil { 204 return err 205 } 206 _, ok := envs[envName] 207 if !ok { 208 return fmt.Errorf("Environment '%v' does not exist", envName) 209 } 210 return config.Set("env", envName) 211 } 212 213 // DelEnv deletes an env from config 214 func DelEnv(ctx *cli.Context, envName string) error { 215 env, err := GetEnv(ctx) 216 if err != nil { 217 return err 218 } 219 if env.Name == envName { 220 return fmt.Errorf("Environment '%v' is your current environment. Before deleting it, please change your current environment.", envName) 221 } 222 envs, err := getEnvs() 223 if err != nil { 224 return err 225 } 226 _, ok := envs[envName] 227 if !ok { 228 return fmt.Errorf("Environment '%v' does not exist", envName) 229 } 230 delete(envs, envName) 231 return setEnvs(envs) 232 } 233 234 func IsPlatform(ctx *cli.Context) bool { 235 env, err := GetEnv(ctx) 236 if err == nil && env.Name == EnvPlatform { 237 return true 238 } 239 return false 240 } 241 242 type Exec func(*cli.Context, []string) ([]byte, error) 243 244 func Print(e Exec) func(*cli.Context) error { 245 return func(c *cli.Context) error { 246 rsp, err := e(c, c.Args().Slice()) 247 if err != nil { 248 return CliError(err) 249 } 250 if len(rsp) > 0 { 251 fmt.Printf("%s\n", string(rsp)) 252 } 253 return nil 254 } 255 } 256 257 // CliError returns a user friendly message from error. If we can't determine a good one returns an error with code 128 258 func CliError(err error) cli.ExitCoder { 259 if err == nil { 260 return nil 261 } 262 // if it's already a cli.ExitCoder we use this 263 cerr, ok := err.(cli.ExitCoder) 264 if ok { 265 return cerr 266 } 267 268 // grpc errors 269 if mname := regexp.MustCompile(`malformed method name: \\?"(\w+)\\?"`).FindStringSubmatch(err.Error()); len(mname) > 0 { 270 return cli.Exit(fmt.Sprintf(`Method name "%s" invalid format. Expecting service.endpoint`, mname[1]), 3) 271 } 272 if service := regexp.MustCompile(`service ([\w\.]+): route not found`).FindStringSubmatch(err.Error()); len(service) > 0 { 273 return cli.Exit(fmt.Sprintf(`Service "%s" not found`, service[1]), 4) 274 } 275 if service := regexp.MustCompile(`unknown service ([\w\.]+)`).FindStringSubmatch(err.Error()); len(service) > 0 { 276 if strings.Contains(service[0], ".") { 277 return cli.Exit(fmt.Sprintf(`Service method "%s" not found`, service[1]), 5) 278 } 279 return cli.Exit(fmt.Sprintf(`Service "%s" not found`, service[1]), 5) 280 } 281 if address := regexp.MustCompile(`Error while dialing dial tcp.*?([\w]+\.[\w:\.]+): `).FindStringSubmatch(err.Error()); len(address) > 0 { 282 return cli.Exit(fmt.Sprintf(`Failed to connect to micro server at %s`, address[1]), 4) 283 } 284 285 merr, ok := err.(*merrors.Error) 286 if !ok { 287 return cli.Exit(err, 128) 288 } 289 290 switch merr.Code { 291 case 408: 292 return cli.Exit("Request timed out", 1) 293 case 401: 294 // TODO check if not signed in, prompt to sign in 295 return cli.Exit("Not authorized to perform this request", 2) 296 } 297 298 // fallback to using the detail from the merr 299 return cli.Exit(merr.Detail, 127) 300 }