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