github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/pkg/cmdconfig/viper.go (about) 1 package cmdconfig 2 3 import ( 4 "fmt" 5 "github.com/turbot/steampipe/pkg/filepaths" 6 "log" 7 "os" 8 9 filehelpers "github.com/turbot/go-kit/files" 10 "github.com/turbot/steampipe/pkg/steampipeconfig" 11 12 "github.com/spf13/cobra" 13 "github.com/spf13/viper" 14 "github.com/turbot/go-kit/types" 15 "github.com/turbot/steampipe/pkg/constants" 16 ) 17 18 // Viper fetches the global viper instance 19 func Viper() *viper.Viper { 20 return viper.GetViper() 21 } 22 23 // bootstrapViper sets up viper with the essential path config (workspace-chdir and install-dir) 24 func bootstrapViper(loader *steampipeconfig.WorkspaceProfileLoader, cmd *cobra.Command) error { 25 // set defaults for keys which do not have a corresponding command flag 26 if err := setBaseDefaults(); err != nil { 27 return err 28 } 29 30 // set defaults from defaultWorkspaceProfile 31 SetDefaultsFromConfig(loader.DefaultProfile.ConfigMap(cmd)) 32 33 // set defaults for install dir and mod location from env vars 34 // this needs to be done since the workspace profile definitions exist in the 35 // default install dir 36 setDirectoryDefaultsFromEnv() 37 38 // NOTE: if an explicit workspace profile was set, default the mod location and install dir _now_ 39 // All other workspace profile values are defaults _after defaulting to the connection config options 40 // to give them higher precedence, but these must be done now as subsequent operations depend on them 41 // (and they cannot be set from hcl options) 42 if loader.ConfiguredProfile != nil { 43 if loader.ConfiguredProfile.ModLocation != nil { 44 log.Printf("[TRACE] setting mod location from configured profile '%s' to '%s'", loader.ConfiguredProfile.Name(), *loader.ConfiguredProfile.ModLocation) 45 viper.SetDefault(constants.ArgModLocation, *loader.ConfiguredProfile.ModLocation) 46 } 47 if loader.ConfiguredProfile.InstallDir != nil { 48 log.Printf("[TRACE] setting install dir from configured profile '%s' to '%s'", loader.ConfiguredProfile.Name(), *loader.ConfiguredProfile.InstallDir) 49 viper.SetDefault(constants.ArgInstallDir, *loader.ConfiguredProfile.InstallDir) 50 } 51 } 52 53 // tildefy all paths in viper 54 return tildefyPaths() 55 } 56 57 // tildefyPaths cleans all path config values and replaces '~' with the home directory 58 func tildefyPaths() error { 59 pathArgs := []string{ 60 constants.ArgModLocation, 61 constants.ArgInstallDir, 62 } 63 var err error 64 for _, argName := range pathArgs { 65 if argVal := viper.GetString(argName); argVal != "" { 66 if argVal, err = filehelpers.Tildefy(argVal); err != nil { 67 return err 68 } 69 if viper.IsSet(argName) { 70 // if the value was already set re-set 71 viper.Set(argName, argVal) 72 } else { 73 // otherwise just update the default 74 viper.SetDefault(argName, argVal) 75 } 76 } 77 } 78 return nil 79 } 80 81 // SetDefaultsFromConfig overrides viper default values from hcl config values 82 func SetDefaultsFromConfig(configMap map[string]interface{}) { 83 for k, v := range configMap { 84 viper.SetDefault(k, v) 85 } 86 } 87 88 // for keys which do not have a corresponding command flag, we need a separate defaulting mechanism 89 // any option setting, workspace profile property or env var which does not have a command line 90 // MUST have a default (unless we want the zero value to take effect) 91 // 92 // Do not add keys here which have command line defaults - the way this is setup, this value takes 93 // precedence over command line default 94 func setBaseDefaults() error { 95 pipesInstallDir, err := filehelpers.Tildefy(filepaths.DefaultPipesInstallDir) 96 if err != nil { 97 return err 98 } 99 defaults := map[string]interface{}{ 100 // global general options 101 constants.ArgTelemetry: constants.TelemetryInfo, 102 constants.ArgUpdateCheck: true, 103 constants.ArgPipesInstallDir: pipesInstallDir, 104 105 // workspace profile 106 constants.ArgAutoComplete: true, 107 constants.ArgIntrospection: constants.IntrospectionNone, 108 109 // from global database options 110 constants.ArgDatabasePort: constants.DatabaseDefaultPort, 111 constants.ArgDatabaseStartTimeout: constants.DBStartTimeout.Seconds(), 112 constants.ArgServiceCacheEnabled: true, 113 constants.ArgCacheMaxTtl: 300, 114 115 // dashboard 116 constants.ArgDashboardStartTimeout: constants.DashboardStartTimeout.Seconds(), 117 118 // memory 119 constants.ArgMemoryMaxMbPlugin: 1024, 120 constants.ArgMemoryMaxMb: 1024, 121 } 122 123 for k, v := range defaults { 124 viper.SetDefault(k, v) 125 } 126 return nil 127 } 128 129 type envMapping struct { 130 configVar []string 131 varType EnvVarType 132 } 133 134 // set default values of INSTALL_DIR and ModLocation from env vars 135 func setDirectoryDefaultsFromEnv() { 136 envMappings := map[string]envMapping{ 137 constants.EnvInstallDir: {[]string{constants.ArgInstallDir}, String}, 138 constants.EnvWorkspaceChDir: {[]string{constants.ArgModLocation}, String}, 139 constants.EnvModLocation: {[]string{constants.ArgModLocation}, String}, 140 } 141 142 for envVar, mapping := range envMappings { 143 setConfigFromEnv(envVar, mapping.configVar, mapping.varType) 144 } 145 } 146 147 // setDefaultsFromEnv sets default values from env vars 148 func setDefaultsFromEnv() { 149 // NOTE: EnvWorkspaceProfile has already been set as a viper default as we have already loaded workspace profiles 150 // (EnvInstallDir has already been set at same time but we set it again to make sure it has the correct precedence) 151 152 // a map of known environment variables to map to viper keys 153 envMappings := map[string]envMapping{ 154 constants.EnvInstallDir: {[]string{constants.ArgInstallDir}, String}, 155 constants.EnvWorkspaceChDir: {[]string{constants.ArgModLocation}, String}, 156 constants.EnvModLocation: {[]string{constants.ArgModLocation}, String}, 157 constants.EnvIntrospection: {[]string{constants.ArgIntrospection}, String}, 158 constants.EnvTelemetry: {[]string{constants.ArgTelemetry}, String}, 159 constants.EnvUpdateCheck: {[]string{constants.ArgUpdateCheck}, Bool}, 160 // deprecated 161 constants.EnvCloudHost: {[]string{constants.ArgPipesHost}, String}, 162 constants.EnvCloudToken: {[]string{constants.ArgPipesToken}, String}, 163 constants.EnvPipesHost: {[]string{constants.ArgPipesHost}, String}, 164 constants.EnvPipesToken: {[]string{constants.ArgPipesToken}, String}, 165 constants.EnvSnapshotLocation: {[]string{constants.ArgSnapshotLocation}, String}, 166 constants.EnvWorkspaceDatabase: {[]string{constants.ArgWorkspaceDatabase}, String}, 167 constants.EnvServicePassword: {[]string{constants.ArgServicePassword}, String}, 168 constants.EnvDisplayWidth: {[]string{constants.ArgDisplayWidth}, Int}, 169 constants.EnvMaxParallel: {[]string{constants.ArgMaxParallel}, Int}, 170 constants.EnvQueryTimeout: {[]string{constants.ArgDatabaseQueryTimeout}, Int}, 171 constants.EnvDatabaseStartTimeout: {[]string{constants.ArgDatabaseStartTimeout}, Int}, 172 constants.EnvDatabaseSSLPassword: {[]string{constants.ArgDatabaseSSLPassword}, String}, 173 constants.EnvDashboardStartTimeout: {[]string{constants.ArgDashboardStartTimeout}, Int}, 174 constants.EnvCacheTTL: {[]string{constants.ArgCacheTtl}, Int}, 175 constants.EnvCacheMaxTTL: {[]string{constants.ArgCacheMaxTtl}, Int}, 176 constants.EnvMemoryMaxMb: {[]string{constants.ArgMemoryMaxMb}, Int}, 177 constants.EnvMemoryMaxMbPlugin: {[]string{constants.ArgMemoryMaxMbPlugin}, Int}, 178 179 // we need this value to go into different locations 180 constants.EnvCacheEnabled: {[]string{ 181 constants.ArgClientCacheEnabled, 182 constants.ArgServiceCacheEnabled, 183 }, Bool}, 184 } 185 186 for envVar, v := range envMappings { 187 setConfigFromEnv(envVar, v.configVar, v.varType) 188 } 189 } 190 191 func setConfigFromEnv(envVar string, configs []string, varType EnvVarType) { 192 for _, configVar := range configs { 193 SetDefaultFromEnv(envVar, configVar, varType) 194 } 195 } 196 197 func SetDefaultFromEnv(k string, configVar string, varType EnvVarType) { 198 if val, ok := os.LookupEnv(k); ok { 199 switch varType { 200 case String: 201 viper.SetDefault(configVar, val) 202 case Bool: 203 if boolVal, err := types.ToBool(val); err == nil { 204 viper.SetDefault(configVar, boolVal) 205 } 206 case Int: 207 if intVal, err := types.ToInt64(val); err == nil { 208 viper.SetDefault(configVar, intVal) 209 } 210 default: 211 // must be an invalid value in the map above 212 panic(fmt.Sprintf("invalid env var mapping type: %s", varType)) 213 } 214 } 215 }