github.com/nats-io/nsc@v0.0.0-20221206222106-35db9400b257/cmd/defaults.go (about) 1 /* 2 * Copyright 2018-2022 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 "fmt" 21 "os" 22 "path/filepath" 23 "strings" 24 "time" 25 26 "github.com/nats-io/nsc/home" 27 28 "github.com/nats-io/jwt/v2" 29 "github.com/nats-io/nats.go" 30 31 "github.com/nats-io/nsc/cmd/store" 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 config ToolConfig 49 var rootCAsNats nats.Option // Will be skipped, when nil and passed to a connection 50 var tlsKeyNats nats.Option // Will be skipped, when nil and passed to a connection 51 var tlsCertNats nats.Option // Will be skipped, when nil and passed to a connection 52 var rootCAsFile string 53 54 func GetToolName() string { 55 return "nsc" 56 } 57 58 // GetConfig returns the global config 59 func GetConfig() *ToolConfig { 60 return &config 61 } 62 63 func GetCwdCtx() *ContextConfig { 64 var ctx ContextConfig 65 cwd, err := os.Getwd() 66 if err != nil { 67 return nil 68 } 69 dir := cwd 70 info, ok, err := isOperatorDir(dir) 71 if err != nil { 72 return nil 73 } 74 if ok { 75 ctx.StoreRoot = filepath.Dir(dir) 76 ctx.Operator = info.Name 77 return &ctx 78 } 79 80 // search down 81 dirEntries, err := os.ReadDir(dir) 82 if err != nil { 83 return nil 84 } 85 for _, v := range dirEntries { 86 if !v.IsDir() { 87 continue 88 } 89 _, ok, err := isOperatorDir(filepath.Join(dir, v.Name())) 90 if err != nil { 91 return nil 92 } 93 if ok { 94 ctx.StoreRoot = dir 95 return &ctx 96 } 97 } 98 99 // search up 100 dir = cwd 101 for { 102 info, ok, err := isOperatorDir(dir) 103 if err != nil { 104 return nil 105 } 106 if ok { 107 ctx.StoreRoot = filepath.Dir(dir) 108 ctx.Operator = info.Name 109 sep := string(os.PathSeparator) 110 name := fmt.Sprintf("%s%s%s%s%s", sep, info.Name, sep, store.Accounts, sep) 111 idx := strings.Index(cwd, name) 112 if idx != -1 { 113 prefix := cwd[:idx+len(name)] 114 sub, err := filepath.Rel(prefix, cwd) 115 if err == nil && len(sub) > 0 { 116 names := strings.Split(sub, sep) 117 118 if len(names) > 0 { 119 ctx.Account = names[0] 120 } 121 } 122 } 123 return &ctx 124 } 125 pdir := filepath.Dir(dir) 126 if pdir == dir { 127 // not found 128 return nil 129 } 130 dir = pdir 131 } 132 } 133 134 func isOperatorDir(dir string) (store.Info, bool, error) { 135 var v store.Info 136 fi, err := os.Stat(dir) 137 if os.IsNotExist(err) { 138 return v, false, nil 139 } 140 if err != nil { 141 return v, false, err 142 } 143 if !fi.IsDir() { 144 return v, false, nil 145 } 146 tf := filepath.Join(dir, ".nsc") 147 fi, err = os.Stat(tf) 148 if os.IsNotExist(err) { 149 return v, false, nil 150 } 151 if err != nil { 152 return v, false, nil 153 } 154 if !fi.IsDir() { 155 d, err := os.ReadFile(tf) 156 if err != nil { 157 return v, false, err 158 } 159 err = json.Unmarshal(d, &v) 160 if err != nil { 161 return v, false, err 162 } 163 if v.Name != "" && v.Kind == jwt.OperatorClaim { 164 return v, true, nil 165 } 166 } 167 return v, false, nil 168 } 169 170 func GetConfigDir() string { 171 if ConfigDirFlag == "" { 172 // this is running under a test... 173 return os.Getenv(NscHomeEnv) 174 } 175 return ConfigDirFlag 176 } 177 178 func LoadOrInit(configDir string, dataDir string, keystoreDir string) (*ToolConfig, error) { 179 const github = "nats-io/nsc" 180 181 if configDir == "" { 182 configDir = home.NscConfigHome() 183 } 184 ConfigDirFlag = configDir 185 if err := MaybeMakeDir(ConfigDirFlag); err != nil { 186 return nil, err 187 } 188 189 dataDirFlagSet := dataDir != "" 190 if dataDir == "" { 191 dataDir = home.NscDataHome(home.StoresSubDirName) 192 } 193 // dir created if files written 194 DataDirFlag = dataDir 195 196 if keystoreDir == "" { 197 keystoreDir = home.NscDataHome(home.KeysSubDirName) 198 } 199 // dir created if keys added 200 KeysDirFlag = keystoreDir 201 store.KeyStorePath = KeysDirFlag 202 203 if err := config.load(); err != nil { 204 return nil, err 205 } 206 207 // is the struct modified from the file 208 if (ToolConfig{}) == config { 209 config.GithubUpdates = github 210 211 config.LastUpdate = time.Now().UTC().Unix() // this is not "true" but avoids the initial check 212 config.StoreRoot = dataDir 213 if err := MaybeMakeDir(config.StoreRoot); err != nil { 214 return nil, fmt.Errorf("error creating store root: %v", err) 215 } 216 217 // load any default entries if there are any 218 config.SetDefaults() 219 220 if err := config.Save(); err != nil { 221 return nil, err 222 } 223 } else { 224 ctx := GetCwdCtx() 225 if ctx != nil { 226 config.StoreRoot = ctx.StoreRoot 227 if ctx.Operator != "" { 228 config.Operator = ctx.Operator 229 if ctx.Account != "" { 230 config.Account = ctx.Account 231 } 232 } 233 config.SetDefaults() 234 } 235 } 236 if dataDirFlagSet { 237 config.StoreRoot = dataDir 238 } 239 240 // trigger updating defaults 241 config.SetDefaults() 242 243 return &config, nil 244 } 245 246 func SetVersion(version string) { 247 // sem version gets very angry if there's a v in the release 248 if strings.HasPrefix(version, "v") || strings.HasPrefix(version, "V") { 249 version = version[1:] 250 } 251 GetRootCmd().Version = version 252 } 253 254 func (d *ToolConfig) SetVersion(version string) { 255 SetVersion(version) 256 } 257 258 func (d *ToolConfig) load() error { 259 if NscCwdOnly { 260 // don't read it 261 return nil 262 } 263 err := ReadJson(d.configFile(), &config) 264 if err != nil && os.IsNotExist(err) { 265 return nil 266 } 267 return err 268 } 269 270 func (d *ToolConfig) Save() error { 271 d.SetDefaults() 272 if !NscCwdOnly { 273 return WriteJson(d.configFile(), d) 274 } 275 return nil 276 } 277 278 func (d *ToolConfig) configFile() string { 279 return filepath.Join(GetConfigDir(), "nsc.json") 280 }