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  }