github.com/dcarley/cf-cli@v6.24.1-0.20170220111324-4225ff346898+incompatible/util/configv3/config.go (about) 1 // Package configv3 package contains everything related to the CF CLI Configuration. 2 package configv3 3 4 import ( 5 "encoding/json" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "strconv" 10 "time" 11 12 "code.cloudfoundry.org/cli/version" 13 ) 14 15 const ( 16 // DefaultStagingTimeout is the default timeout for application staging. 17 DefaultStagingTimeout = 15 * time.Minute 18 19 // DefaultStartupTimeout is the default timeout for application starting. 20 DefaultStartupTimeout = 5 * time.Minute 21 // DefaultPingerThrottle = 5 * time.Second 22 23 // DefaultDialTimeout is the default timeout for the dail. 24 DefaultDialTimeout = 5 * time.Second 25 26 // DefaultOverallPollingTimeout is the default maximum time that the CLI will 27 // poll a job running on the Cloud Controller. By default it's infinit, which 28 // is represented by MaxInt64. 29 DefaultOverallPollingTimeout = time.Duration(1<<63 - 1) // math.MaxInt64 30 // Developer note about constant above ^^^ do not replace with math.MaxInt64 31 // This will require the math package which is a dynamically linked library. 32 33 // DefaultTarget is the default CFConfig value for Target. 34 DefaultTarget = "" 35 36 // DefaultUAAOAuthClient is the default client ID for the CLI when 37 // communicating with the UAA. 38 DefaultUAAOAuthClient = "cf" 39 40 // DefaultCFOClientSecret is the default client secret for the CLI when 41 // communicating with the UAA. 42 DefaultUAAOAuthClientSecret = "" 43 ) 44 45 // LoadConfig loads the config from the .cf/config.json and os.ENV. If the 46 // config.json does not exists, it will use a default config in it's place. 47 // Takes in an optional FlagOverride, will only use the first one passed, that 48 // can override the given flag values. 49 // 50 // The '.cf' directory will be read in one of the following locations on UNIX 51 // Systems: 52 // 1. $CF_HOME/.cf if $CF_HOME is set 53 // 2. $HOME/.cf as the default 54 // 55 // The '.cf' directory will be read in one of the following locations on 56 // Windows Systems: 57 // 1. CF_HOME\.cf if CF_HOME is set 58 // 2. HOMEDRIVE\HOMEPATH\.cf if HOMEDRIVE or HOMEPATH is set 59 // 3. USERPROFILE\.cf as the default 60 func LoadConfig(flags ...FlagOverride) (*Config, error) { 61 filePath := ConfigFilePath() 62 63 var config Config 64 if _, err := os.Stat(filePath); os.IsNotExist(err) { 65 config = Config{ 66 ConfigFile: CFConfig{ 67 ConfigVersion: 3, 68 Target: DefaultTarget, 69 ColorEnabled: DefaultColorEnabled, 70 PluginRepos: []PluginRepos{{ 71 Name: DefaultPluginRepoName, 72 URL: DefaultPluginRepoURL, 73 }}, 74 UAAOAuthClient: DefaultUAAOAuthClient, 75 UAAOAuthClientSecret: DefaultUAAOAuthClientSecret, 76 }, 77 } 78 } else { 79 file, err := ioutil.ReadFile(filePath) 80 if err != nil { 81 return nil, err 82 } 83 84 err = json.Unmarshal(file, &config.ConfigFile) 85 if err != nil { 86 return nil, err 87 } 88 89 if config.ConfigFile.UAAOAuthClient == "" { 90 config.ConfigFile.UAAOAuthClient = DefaultUAAOAuthClient 91 config.ConfigFile.UAAOAuthClientSecret = DefaultUAAOAuthClientSecret 92 } 93 } 94 95 config.ENV = EnvOverride{ 96 BinaryName: filepath.Base(os.Args[0]), 97 CFColor: os.Getenv("CF_COLOR"), 98 CFPluginHome: os.Getenv("CF_PLUGIN_HOME"), 99 CFStagingTimeout: os.Getenv("CF_STAGING_TIMEOUT"), 100 CFStartupTimeout: os.Getenv("CF_STARTUP_TIMEOUT"), 101 CFTrace: os.Getenv("CF_TRACE"), 102 HTTPSProxy: os.Getenv("https_proxy"), 103 Lang: os.Getenv("LANG"), 104 LCAll: os.Getenv("LC_ALL"), 105 Experimental: os.Getenv("CF_CLI_EXPERIMENTAL"), 106 CFDialTimeout: os.Getenv("CF_DIAL_TIMEOUT"), 107 } 108 109 pluginFilePath := filepath.Join(config.PluginHome(), "config.json") 110 if _, err := os.Stat(pluginFilePath); os.IsNotExist(err) { 111 config.pluginConfig = PluginsConfig{} 112 } else { 113 file, err := ioutil.ReadFile(pluginFilePath) 114 if err != nil { 115 return nil, err 116 } 117 118 err = json.Unmarshal(file, &config.pluginConfig) 119 if err != nil { 120 return nil, err 121 } 122 } 123 124 if len(flags) > 0 { 125 config.Flags = flags[0] 126 } 127 128 return &config, nil 129 } 130 131 // WriteConfig creates the .cf directory and then writes the config.json. The 132 // location of .cf directory is written in the same way LoadConfig reads .cf 133 // directory. 134 func WriteConfig(c *Config) error { 135 rawConfig, err := json.MarshalIndent(c.ConfigFile, "", " ") 136 if err != nil { 137 return err 138 } 139 140 err = os.MkdirAll(filepath.Join(homeDirectory(), ".cf"), 0700) 141 if err != nil { 142 return err 143 } 144 145 return ioutil.WriteFile(ConfigFilePath(), rawConfig, 0600) 146 } 147 148 // Config combines the settings taken from the .cf/config.json, os.ENV, and the 149 // plugin config. 150 type Config struct { 151 // ConfigFile stores the configuration from the .cf/config 152 ConfigFile CFConfig 153 154 // ENV stores the configuration from os.ENV 155 ENV EnvOverride 156 157 // Flags stores the configuration from gobal flags 158 Flags FlagOverride 159 160 pluginConfig PluginsConfig 161 } 162 163 // CFConfig represents .cf/config.json 164 type CFConfig struct { 165 ConfigVersion int `json:"ConfigVersion"` 166 Target string `json:"Target"` 167 APIVersion string `json:"APIVersion"` 168 AuthorizationEndpoint string `json:"AuthorizationEndpoint"` 169 DopplerEndpoint string `json:"DopplerEndPoint"` 170 UAAEndpoint string `json:"UaaEndpoint"` 171 RoutingEndpoint string `json:"RoutingAPIEndpoint"` 172 AccessToken string `json:"AccessToken"` 173 SSHOAuthClient string `json:"SSHOAuthClient"` 174 UAAOAuthClient string `json:"UAAOAuthClient"` 175 UAAOAuthClientSecret string `json:"UAAOAuthClientSecret"` 176 RefreshToken string `json:"RefreshToken"` 177 TargetedOrganization Organization `json:"OrganizationFields"` 178 TargetedSpace Space `json:"SpaceFields"` 179 SkipSSLValidation bool `json:"SSLDisabled"` 180 AsyncTimeout int `json:"AsyncTimeout"` 181 Trace string `json:"Trace"` 182 ColorEnabled string `json:"ColorEnabled"` 183 Locale string `json:"Locale"` 184 PluginRepos []PluginRepos `json:"PluginRepos"` 185 MinCLIVersion string `json:"MinCLIVersion"` 186 MinRecommendedCLIVersion string `json:"MinRecommendedCLIVersion"` 187 } 188 189 // Organization contains basic information about the targeted organization 190 type Organization struct { 191 GUID string `json:"GUID"` 192 Name string `json:"Name"` 193 QuotaDefinition QuotaDefinition `json:"QuotaDefinition"` 194 } 195 196 // QuotaDefinition contains information about the organization's quota 197 type QuotaDefinition struct { 198 GUID string `json:"guid"` 199 Name string `json:"name"` 200 MemoryLimit int `json:"memory_limit"` 201 InstanceMemoryLimit int `json:"instance_memory_limit"` 202 TotalRoutes int `json:"total_routes"` 203 TotalServices int `json:"total_services"` 204 NonBasicServicesAllowed bool `json:"non_basic_services_allowed"` 205 AppInstanceLimit int `json:"app_instance_limit"` 206 TotalReservedRoutePorts int `json:"total_reserved_route_ports"` 207 } 208 209 // Space contains basic information about the targeted space 210 type Space struct { 211 GUID string `json:"GUID"` 212 Name string `json:"Name"` 213 AllowSSH bool `json:"AllowSSH"` 214 } 215 216 // EnvOverride represents all the environment variables read by the CF CLI 217 type EnvOverride struct { 218 BinaryName string 219 CFColor string 220 CFHome string 221 CFPluginHome string 222 CFStagingTimeout string 223 CFStartupTimeout string 224 CFTrace string 225 HTTPSProxy string 226 Lang string 227 LCAll string 228 Experimental string 229 CFDialTimeout string 230 } 231 232 // FlagOverride represents all the global flags passed to the CF CLI 233 type FlagOverride struct { 234 Verbose bool 235 } 236 237 // Target returns the CC API URL 238 func (config *Config) Target() string { 239 return config.ConfigFile.Target 240 } 241 242 // PollingInterval returns the time between polls. 243 func (config *Config) PollingInterval() time.Duration { 244 return 5 * time.Second 245 } 246 247 // OverallPollingTimeout returns the overall polling timeout for async 248 // operations. The time is based off of: 249 // 1. The config file's AsyncTimeout value (integer) is > 0 250 // 2. Defaults to the DefaultOverallPollingTimeout 251 func (config *Config) OverallPollingTimeout() time.Duration { 252 if config.ConfigFile.AsyncTimeout == 0 { 253 return DefaultOverallPollingTimeout 254 } 255 return time.Duration(config.ConfigFile.AsyncTimeout) * time.Minute 256 } 257 258 // SkipSSLValidation returns whether or not to skip SSL validation when 259 // targeting an API endpoint 260 func (config *Config) SkipSSLValidation() bool { 261 return config.ConfigFile.SkipSSLValidation 262 } 263 264 // AccessToken returns the access token for making authenticated API calls 265 func (config *Config) AccessToken() string { 266 return config.ConfigFile.AccessToken 267 } 268 269 // RefreshToken returns the refresh token for getting a new access token 270 func (config *Config) RefreshToken() string { 271 return config.ConfigFile.RefreshToken 272 } 273 274 // UAAOAuthClient returns the CLI's UAA client ID 275 func (config *Config) UAAOAuthClient() string { 276 return config.ConfigFile.UAAOAuthClient 277 } 278 279 // UAAOAuthClientSecret returns the CLI's UAA client secret 280 func (config *Config) UAAOAuthClientSecret() string { 281 return config.ConfigFile.UAAOAuthClientSecret 282 } 283 284 // APIVersion returns the CC API Version 285 func (config *Config) APIVersion() string { 286 return config.ConfigFile.APIVersion 287 } 288 289 // MinCLIVersion returns the minimum CLI version requried by the CC 290 func (config *Config) MinCLIVersion() string { 291 return config.ConfigFile.MinCLIVersion 292 } 293 294 // TargetedOrganization returns the currently targeted organization 295 func (config *Config) TargetedOrganization() Organization { 296 return config.ConfigFile.TargetedOrganization 297 } 298 299 // TargetedSpace returns the currently targeted space 300 func (config *Config) TargetedSpace() Space { 301 return config.ConfigFile.TargetedSpace 302 } 303 304 // StagingTimeout returns the max time an application staging should take. The 305 // time is based off of: 306 // 1. The $CF_STAGING_TIMEOUT environment variable if set 307 // 2. Defaults to the DefaultStagingTimeout 308 func (config *Config) StagingTimeout() time.Duration { 309 if config.ENV.CFStagingTimeout != "" { 310 val, err := strconv.ParseInt(config.ENV.CFStagingTimeout, 10, 64) 311 if err == nil { 312 return time.Duration(val) * time.Minute 313 } 314 } 315 316 return DefaultStagingTimeout 317 } 318 319 // StartupTimeout returns the max time an application should take to start. The 320 // time is based off of: 321 // 1. The $CF_STARTUP_TIMEOUT environment variable if set 322 // 2. Defaults to the DefaultStartupTimeout 323 func (config *Config) StartupTimeout() time.Duration { 324 if config.ENV.CFStartupTimeout != "" { 325 val, err := strconv.ParseInt(config.ENV.CFStartupTimeout, 10, 64) 326 if err == nil { 327 return time.Duration(val) * time.Minute 328 } 329 } 330 331 return DefaultStartupTimeout 332 } 333 334 // HTTPSProxy returns the proxy url that the CLI should use. The url is based 335 // off of: 336 // 1. The $https_proxy environment variable if set 337 // 2. Defaults to the empty string 338 func (config *Config) HTTPSProxy() string { 339 if config.ENV.HTTPSProxy != "" { 340 return config.ENV.HTTPSProxy 341 } 342 343 return "" 344 } 345 346 // BinaryName returns the running name of the CF CLI 347 func (config *Config) BinaryName() string { 348 return config.ENV.BinaryName 349 } 350 351 // Experimental returns whether or not to run experimental CLI commands. This 352 // is based off of: 353 // 1. The $CF_CLI_EXPERIMENTAL environment variable if set 354 // 2. Defaults to false 355 func (config *Config) Experimental() bool { 356 if config.ENV.Experimental != "" { 357 envVal, err := strconv.ParseBool(config.ENV.Experimental) 358 if err == nil { 359 return envVal 360 } 361 } 362 363 return false 364 } 365 366 // Verbose returns true if verbose should be displayed to terminal and a 367 // location to log to. This is based off of: 368 // - The config file's trace value (true/false/file path) 369 // - The $CF_TRACE enviroment variable if set (true/false/file path) 370 // - The '-v/--verbose' global flag 371 // - Defaults to false 372 func (config *Config) Verbose() (bool, []string) { 373 var ( 374 verbose bool 375 envOverride bool 376 filePath []string 377 ) 378 if config.ENV.CFTrace != "" { 379 envVal, err := strconv.ParseBool(config.ENV.CFTrace) 380 verbose = envVal 381 if err != nil { 382 filePath = []string{config.ENV.CFTrace} 383 } else { 384 envOverride = true 385 } 386 } 387 if config.ConfigFile.Trace != "" { 388 envVal, err := strconv.ParseBool(config.ConfigFile.Trace) 389 if !envOverride { 390 verbose = envVal || verbose 391 } 392 if err != nil { 393 filePath = append(filePath, config.ConfigFile.Trace) 394 } 395 } 396 verbose = config.Flags.Verbose || verbose 397 398 return verbose, filePath 399 } 400 401 // DialTimeout returns the timeout to use when dialing. This is based off of: 402 // 1. The $CF_DIAL_TIMEOUT environment variable if set 403 // 2. Defaults to 5 seconds 404 func (config *Config) DialTimeout() time.Duration { 405 if config.ENV.CFDialTimeout != "" { 406 envVal, err := strconv.ParseInt(config.ENV.CFDialTimeout, 10, 64) 407 if err == nil { 408 return time.Duration(envVal) * time.Second 409 } 410 } 411 412 return DefaultDialTimeout 413 } 414 415 func (config *Config) BinaryVersion() string { 416 return version.VersionString() 417 } 418 419 // HasTargetedOrganization returns true if the organization is set 420 func (config *Config) HasTargetedOrganization() bool { 421 return config.ConfigFile.TargetedOrganization.GUID != "" 422 } 423 424 // HasTargetedSpace returns true if the space is set 425 func (config *Config) HasTargetedSpace() bool { 426 return config.ConfigFile.TargetedSpace.GUID != "" 427 } 428 429 // SetOrganizationInformation sets the currently targeted organization 430 func (config *Config) SetOrganizationInformation(guid string, name string) { 431 config.ConfigFile.TargetedOrganization.GUID = guid 432 config.ConfigFile.TargetedOrganization.Name = name 433 config.ConfigFile.TargetedOrganization.QuotaDefinition = QuotaDefinition{} 434 } 435 436 // SetSpaceInformation sets the currently targeted space 437 func (config *Config) SetSpaceInformation(guid string, name string, allowSSH bool) { 438 config.ConfigFile.TargetedSpace.GUID = guid 439 config.ConfigFile.TargetedSpace.Name = name 440 config.ConfigFile.TargetedSpace.AllowSSH = allowSSH 441 } 442 443 // SetTargetInformation sets the currently targeted CC API and related other 444 // related API URLs 445 func (config *Config) SetTargetInformation(api string, apiVersion string, auth string, minCLIVersion string, doppler string, uaa string, routing string, skipSSLValidation bool) { 446 config.ConfigFile.Target = api 447 config.ConfigFile.APIVersion = apiVersion 448 config.ConfigFile.AuthorizationEndpoint = auth 449 config.ConfigFile.MinCLIVersion = minCLIVersion 450 config.ConfigFile.DopplerEndpoint = doppler 451 config.ConfigFile.UAAEndpoint = uaa 452 config.ConfigFile.RoutingEndpoint = routing 453 config.ConfigFile.SkipSSLValidation = skipSSLValidation 454 455 config.UnsetOrganizationInformation() 456 config.UnsetSpaceInformation() 457 } 458 459 // SetTokenInformation sets the current token/user information 460 func (config *Config) SetTokenInformation(accessToken string, refreshToken string, sshOAuthClient string) { 461 config.ConfigFile.AccessToken = accessToken 462 config.ConfigFile.RefreshToken = refreshToken 463 config.ConfigFile.SSHOAuthClient = sshOAuthClient 464 } 465 466 // SetAccessToken sets the current access token 467 func (config *Config) SetAccessToken(accessToken string) { 468 config.ConfigFile.AccessToken = accessToken 469 } 470 471 // SetRefreshToken sets the current refresh token 472 func (config *Config) SetRefreshToken(refreshToken string) { 473 config.ConfigFile.RefreshToken = refreshToken 474 } 475 476 // UnsetSpaceInformation resets the space values to default 477 func (config *Config) UnsetSpaceInformation() { 478 config.SetSpaceInformation("", "", false) 479 } 480 481 // UnsetOrganizationInformation resets the organization values to default 482 func (config *Config) UnsetOrganizationInformation() { 483 config.SetOrganizationInformation("", "") 484 }