github.com/kbehouse/nsc@v0.0.6/cmd/root.go (about) 1 /* 2 * Copyright 2018-2021 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 "errors" 20 "fmt" 21 "io" 22 "os" 23 "path/filepath" 24 "strings" 25 26 "github.com/kbehouse/nsc/cmd/store" 27 "github.com/mitchellh/go-homedir" 28 cli "github.com/nats-io/cliprompts/v2" 29 "github.com/nats-io/nats.go" 30 "github.com/nats-io/nkeys" 31 "github.com/spf13/cobra" 32 "github.com/spf13/viper" 33 ) 34 35 const TestEnv = "NSC_TEST" 36 37 var KeyPathFlag string 38 var InteractiveFlag bool 39 var NscCwdOnly bool 40 var quietMode bool 41 42 var cfgFile string 43 44 var ErrNoOperator = errors.New("set an operator -- 'nsc env -o operatorName'") 45 46 type InterceptorFn func(ctx ActionCtx, params interface{}) error 47 48 func GetStoreForOperator(operator string) (*store.Store, error) { 49 config := GetConfig() 50 if config.StoreRoot == "" { 51 return nil, errors.New("no stores available") 52 } 53 if err := IsValidDir(config.StoreRoot); err != nil { 54 return nil, err 55 } 56 57 if operator != "" { 58 config.Operator = operator 59 } 60 61 if config.Operator == "" { 62 config.SetDefaults() 63 if config.Operator == "" { 64 return nil, ErrNoOperator 65 } 66 } 67 68 fp := filepath.Join(config.StoreRoot, config.Operator) 69 ngsStore, err := store.LoadStore(fp) 70 if err != nil { 71 return nil, err 72 } 73 74 if config.Account != "" { 75 ngsStore.DefaultAccount = config.Account 76 } 77 return ngsStore, nil 78 } 79 80 func GetStore() (*store.Store, error) { 81 return GetStoreForOperator("") 82 } 83 84 func ResolveKeyFlag() (nkeys.KeyPair, error) { 85 if KeyPathFlag != "" { 86 kp, err := store.ResolveKey(KeyPathFlag) 87 if err != nil { 88 return nil, err 89 } 90 return kp, nil 91 } 92 return nil, nil 93 } 94 95 func GetRootCmd() *cobra.Command { 96 return rootCmd 97 } 98 99 func EnterQuietMode() { 100 quietMode = true 101 } 102 103 func SetQuietMode(tf bool) { 104 quietMode = tf 105 } 106 107 func QuietMode() bool { 108 return quietMode 109 } 110 111 var rootCmd = &cobra.Command{ 112 Use: "nsc", 113 Short: "nsc creates NATS operators, accounts, users, and manage their permissions.", 114 PersistentPreRunE: func(cmd *cobra.Command, args []string) error { 115 if store, _ := GetStore(); store != nil { 116 if c, _ := store.ReadOperatorClaim(); c != nil && c.Version == 1 { 117 if c.Version > 2 { 118 return fmt.Errorf("the store %#q is at version %d. To upgrade nsc - type `%s update`", 119 store.GetName(), c.Version, os.Args[0]) 120 } else if c.Version == 1 { 121 allowCmdWithJWTV1Store := cmd.Name() == "upgrade-jwt" || cmd.Name() == "env" || cmd.Name() == "help" || cmd.Name() == "update" 122 if !allowCmdWithJWTV1Store && cmd.Name() == "operator" { 123 for _, v := range addCmd.Commands() { 124 if v == cmd { 125 allowCmdWithJWTV1Store = true 126 break 127 } 128 } 129 } 130 if !allowCmdWithJWTV1Store { 131 //lint:ignore ST1005 this message is shown to the user 132 return fmt.Errorf(`This version of nsc only supports jwtV2. 133 If you are using a managed service, check your provider for 134 instructions on how to update your project. In most cases 135 all you need to do is: 136 "%s add operator --force -u <url provided by your service>" 137 138 If your service is well known, such as Synadia's NGS: 139 "%s add operator --force -u synadia" 140 141 If you are the operator, and you have your operator key, to 142 upgrade the v1 store %#q - type: 143 "%s upgrade-jwt" 144 145 Alternatively you can downgrade' %q to a compatible version using: 146 "%s update --version 0.5.0" 147 `, 148 os.Args[0], os.Args[0], store.GetName(), os.Args[0], os.Args[0], os.Args[0]) 149 } 150 } 151 } 152 } 153 if cmd.Name() == "migrate" && cmd.Parent().Name() == "keys" { 154 return nil 155 } 156 // check if we need to perform any kind of migration 157 needsUpdate, err := store.KeysNeedMigration() 158 if err != nil { 159 return err 160 } 161 if needsUpdate { 162 cmd.SilenceUsage = true 163 return fmt.Errorf("the keystore %#q needs migration - type `%s keys migrate` to update", AbbrevHomePaths(store.GetKeysDir()), os.Args[0]) 164 } 165 166 return nil 167 }, 168 } 169 170 // Execute adds all child commands to the root command and sets flags appropriately. 171 // This is called by main.main(). It only needs to happen once to the rootCmd. 172 func Execute() { 173 err := ExecuteWithWriter(rootCmd.OutOrStderr()) 174 if err != nil { 175 os.Exit(1) 176 } 177 } 178 179 // ExecuteWithWriter adds all child commands to the root command and sets flags appropriately. 180 // This is called by main.main(). It only needs to happen once to the rootCmd. 181 // But writer is decided by the caller function 182 // returns error than os.Exit(1) 183 func ExecuteWithWriter(out io.Writer) error { 184 cli.SetOutput(out) 185 if err := GetRootCmd().Execute(); err != nil { 186 return err 187 } 188 return nil 189 } 190 191 func SetEnvOptions() { 192 if _, ok := os.LookupEnv(NscNoGitIgnoreEnv); ok { 193 store.NscNotGitIgnore = true 194 } 195 if _, ok := os.LookupEnv(NscCwdOnlyEnv); ok { 196 NscCwdOnly = true 197 } 198 if f, ok := os.LookupEnv(NscRootCasNatsEnv); ok { 199 rootCAsFile = strings.TrimSpace(f) 200 rootCAsNats = nats.RootCAs(rootCAsFile) 201 } 202 key, okKey := os.LookupEnv(NscTlsKeyNatsEnv) 203 cert, okCert := os.LookupEnv(NscTlsCertNatsEnv) 204 if okKey || okCert { 205 tlsKeyNats = nats.ClientCert(cert, key) 206 } 207 } 208 209 func init() { 210 SetEnvOptions() 211 cobra.OnInitialize(initConfig) 212 HoistRootFlags(GetRootCmd()) 213 } 214 215 // HoistRootFlags adds persistent flags that would be added by the cobra framework 216 // but are not because the unit tests are testing the command directly 217 func HoistRootFlags(cmd *cobra.Command) *cobra.Command { 218 cmd.PersistentFlags().StringVarP(&KeyPathFlag, "private-key", "K", "", "Key used to sign. Can be specified as role (where applicable), public key (private portion is retrieved) or file path to a private key or private key ") 219 cmd.PersistentFlags().BoolVarP(&InteractiveFlag, "interactive", "i", false, "ask questions for various settings") 220 return cmd 221 } 222 223 // initConfig reads in config file and ENV variables if set. 224 func initConfig() { 225 if cfgFile != "" { 226 // Use config file from the flag. 227 viper.SetConfigFile(cfgFile) 228 } else { 229 // Find home directory. 230 home, err := homedir.Dir() 231 if err != nil { 232 fmt.Println(err) 233 os.Exit(1) 234 } 235 236 // Search config in home directory with name ".nsc" (without extension). 237 viper.AddConfigPath(home) 238 viper.SetConfigName(".nsc") 239 } 240 241 viper.AutomaticEnv() // read in environment variables that match 242 243 // If a config file is found, read it in. 244 if err := viper.ReadInConfig(); err == nil { 245 fmt.Println("Using config file:", viper.ConfigFileUsed()) 246 } 247 }