github.com/kbehouse/nsc@v0.0.6/cmd/defaults.go (about) 1 /* 2 * Copyright 2018-2020 The NATS Authors 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package cmd 17 18 import ( 19 "encoding/json" 20 "errors" 21 "fmt" 22 "io/ioutil" 23 "os" 24 "path/filepath" 25 "strings" 26 "time" 27 28 "github.com/kbehouse/nsc/cmd/store" 29 "github.com/mitchellh/go-homedir" 30 "github.com/nats-io/jwt/v2" 31 "github.com/nats-io/nats.go" 32 ) 33 34 //NscHomeEnv the folder for the config file 35 const NscHomeEnv = "NSC_HOME" 36 const NscCwdOnlyEnv = "NSC_CWD_ONLY" 37 const NscNoGitIgnoreEnv = "NSC_NO_GIT_IGNORE" 38 const NscRootCasNatsEnv = "NATS_CA" 39 const NscTlsKeyNatsEnv = "NATS_KEY" 40 const NscTlsCertNatsEnv = "NATS_CERT" 41 42 type ToolConfig struct { 43 ContextConfig 44 GithubUpdates string `json:"github_updates"` // git hub repo 45 LastUpdate int64 `json:"last_update"` 46 } 47 48 // var toolName = strings.ReplaceAll(filepath.Base(os.Args[0]), ".exe", "") 49 var toolName = "nsc" 50 var config ToolConfig 51 var toolHome string 52 var homeEnv string 53 var rootCAsNats nats.Option // Will be skipped, when nil and passed to a connection 54 var tlsKeyNats nats.Option // Will be skipped, when nil and passed to a connection 55 var tlsCertNats nats.Option // Will be skipped, when nil and passed to a connection 56 57 var rootCAsFile string 58 59 func SetToolName(name string) { 60 toolName = name 61 } 62 63 func GetToolName() string { 64 return toolName 65 } 66 67 // GetConfig returns the global config 68 func GetConfig() *ToolConfig { 69 return &config 70 } 71 72 func GetCwdCtx() *ContextConfig { 73 var ctx ContextConfig 74 cwd, err := os.Getwd() 75 if err != nil { 76 return nil 77 } 78 dir := cwd 79 info, ok, err := isOperatorDir(dir) 80 if err != nil { 81 return nil 82 } 83 if ok { 84 ctx.StoreRoot = filepath.Dir(dir) 85 ctx.Operator = info.Name 86 return &ctx 87 } 88 89 // search down 90 infos, err := ioutil.ReadDir(dir) 91 if err != nil { 92 return nil 93 } 94 for _, v := range infos { 95 if !v.IsDir() { 96 continue 97 } 98 _, ok, err := isOperatorDir(filepath.Join(dir, v.Name())) 99 if err != nil { 100 return nil 101 } 102 if ok { 103 ctx.StoreRoot = dir 104 return &ctx 105 } 106 } 107 108 // search up 109 dir = cwd 110 for { 111 info, ok, err := isOperatorDir(dir) 112 if err != nil { 113 return nil 114 } 115 if ok { 116 ctx.StoreRoot = filepath.Dir(dir) 117 ctx.Operator = info.Name 118 sep := string(os.PathSeparator) 119 name := fmt.Sprintf("%s%s%s%s%s", sep, info.Name, sep, store.Accounts, sep) 120 idx := strings.Index(cwd, name) 121 if idx != -1 { 122 prefix := cwd[:idx+len(name)] 123 sub, err := filepath.Rel(prefix, cwd) 124 if err == nil && len(sub) > 0 { 125 names := strings.Split(sub, sep) 126 127 if len(names) > 0 { 128 ctx.Account = names[0] 129 } 130 } 131 } 132 return &ctx 133 } 134 pdir := filepath.Dir(dir) 135 if pdir == dir { 136 // not found 137 return nil 138 } 139 dir = pdir 140 } 141 } 142 143 func isOperatorDir(dir string) (store.Info, bool, error) { 144 var v store.Info 145 fi, err := os.Stat(dir) 146 if os.IsNotExist(err) { 147 return v, false, nil 148 } 149 if err != nil { 150 return v, false, err 151 } 152 if !fi.IsDir() { 153 return v, false, nil 154 } 155 tf := filepath.Join(dir, ".nsc") 156 fi, err = os.Stat(tf) 157 if os.IsNotExist(err) { 158 return v, false, nil 159 } 160 if err != nil { 161 return v, false, nil 162 } 163 if !fi.IsDir() { 164 d, err := ioutil.ReadFile(tf) 165 if err != nil { 166 return v, false, err 167 } 168 err = json.Unmarshal(d, &v) 169 if err != nil { 170 return v, false, err 171 } 172 if v.Name != "" && v.Kind == jwt.OperatorClaim { 173 return v, true, nil 174 } 175 } 176 return v, false, nil 177 } 178 179 func LoadOrInit(github string, toolHomeEnvName string) (*ToolConfig, error) { 180 var err error 181 if toolHomeEnvName == "" { 182 return nil, errors.New("toolHomeEnv is required") 183 } 184 homeEnv = toolHomeEnvName 185 186 toolHome, err = initToolHome(toolHomeEnvName) 187 if err != nil { 188 return nil, err 189 } 190 191 if err := config.load(); err != nil { 192 return nil, err 193 } 194 195 // is the struct modified from the file 196 if (ToolConfig{}) == config { 197 config.GithubUpdates = github 198 199 config.LastUpdate = time.Now().UTC().Unix() // this is not "true" but avoids the initial check 200 // ~/.ngs_cli/nats 201 config.StoreRoot = filepath.Join(toolHome, "nats") 202 if err := MaybeMakeDir(config.StoreRoot); err != nil { 203 return nil, fmt.Errorf("error creating store root: %v", err) 204 } 205 206 // load any default entries if there are any 207 config.SetDefaults() 208 209 if err := config.Save(); err != nil { 210 return nil, err 211 } 212 } else { 213 ctx := GetCwdCtx() 214 if ctx != nil { 215 config.StoreRoot = ctx.StoreRoot 216 if ctx.Operator != "" { 217 config.Operator = ctx.Operator 218 if ctx.Account != "" { 219 config.Account = ctx.Account 220 } 221 } 222 config.SetDefaults() 223 } 224 } 225 226 // trigger updating defaults 227 config.SetDefaults() 228 229 return &config, nil 230 } 231 232 func (d *ToolConfig) SetVersion(version string) { 233 // sem version gets very angry if there's a v in the release 234 if strings.HasPrefix(version, "v") || strings.HasPrefix(version, "V") { 235 version = version[1:] 236 } 237 GetRootCmd().Version = version 238 } 239 240 func (d *ToolConfig) load() error { 241 if NscCwdOnly { 242 // don't read it 243 return nil 244 } 245 err := ReadJson(d.configFile(), &config) 246 if err != nil && os.IsNotExist(err) { 247 return nil 248 } 249 return err 250 } 251 252 func (d *ToolConfig) Save() error { 253 d.SetDefaults() 254 if !NscCwdOnly { 255 return WriteJson(d.configFile(), d) 256 } 257 return nil 258 } 259 260 // GetToolHome returns the . folder used fro this CLIs config and optionally the projects 261 func initToolHome(envVarName string) (string, error) { 262 toolHome = os.Getenv(envVarName) 263 264 if toolHome == "" { 265 dir, err := homedir.Dir() 266 if err != nil { 267 return "", fmt.Errorf("error getting homedir: %v", err.Error()) 268 } 269 toolHome = filepath.Join(dir, fmt.Sprintf(".%s", GetToolName())) 270 } 271 272 if err := MaybeMakeDir(toolHome); err != nil { 273 return "", fmt.Errorf("error creating tool home %#q: %v", toolHome, err) 274 } 275 276 return toolHome, nil 277 278 } 279 280 func (d *ToolConfig) configFile() string { 281 configFileName := fmt.Sprintf("%s.json", GetToolName()) 282 return filepath.Join(toolHome, configFileName) 283 }