github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/jujuclient/file.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // Package jujuclient provides functionality to support
     5  // connections to Juju such as controllers cache, accounts cache, etc.
     6  
     7  package jujuclient
     8  
     9  import (
    10  	"time"
    11  
    12  	"github.com/juju/errors"
    13  	"github.com/juju/loggo"
    14  	"github.com/juju/mutex"
    15  	"github.com/juju/utils/clock"
    16  
    17  	"github.com/juju/juju/cloud"
    18  )
    19  
    20  var _ ClientStore = (*store)(nil)
    21  
    22  var logger = loggo.GetLogger("juju.jujuclient")
    23  
    24  // A second should be enough to write or read any files. But
    25  // some disks are slow when under load, so lets give the disk a
    26  // reasonable time to get the lock.
    27  var lockTimeout = 5 * time.Second
    28  
    29  // NewFileClientStore returns a new filesystem-based client store
    30  // that manages files in $XDG_DATA_HOME/juju.
    31  func NewFileClientStore() ClientStore {
    32  	return &store{}
    33  }
    34  
    35  // NewFileCredentialStore returns a new filesystem-based credentials store
    36  // that manages credentials in $XDG_DATA_HOME/juju.
    37  func NewFileCredentialStore() CredentialStore {
    38  	return &store{}
    39  }
    40  
    41  type store struct{}
    42  
    43  func (s *store) acquireLock() (mutex.Releaser, error) {
    44  	const lockName = "store-lock"
    45  	spec := mutex.Spec{
    46  		Name:    lockName,
    47  		Clock:   clock.WallClock,
    48  		Delay:   20 * time.Millisecond,
    49  		Timeout: lockTimeout,
    50  	}
    51  	releaser, err := mutex.Acquire(spec)
    52  	if err != nil {
    53  		return nil, errors.Trace(err)
    54  	}
    55  	return releaser, nil
    56  }
    57  
    58  // AllControllers implements ControllersGetter.
    59  func (s *store) AllControllers() (map[string]ControllerDetails, error) {
    60  	releaser, err := s.acquireLock()
    61  	if err != nil {
    62  		return nil, errors.Annotate(err, "cannot read all controllers")
    63  	}
    64  	defer releaser.Release()
    65  	controllers, err := ReadControllersFile(JujuControllersPath())
    66  	if err != nil {
    67  		return nil, errors.Trace(err)
    68  	}
    69  	return controllers.Controllers, nil
    70  }
    71  
    72  // CurrentController implements ControllersGetter.
    73  func (s *store) CurrentController() (string, error) {
    74  	releaser, err := s.acquireLock()
    75  	if err != nil {
    76  		return "", errors.Annotate(err, "cannot get current controller name")
    77  	}
    78  	defer releaser.Release()
    79  	controllers, err := ReadControllersFile(JujuControllersPath())
    80  	if err != nil {
    81  		return "", errors.Trace(err)
    82  	}
    83  	if controllers.CurrentController == "" {
    84  		return "", errors.NotFoundf("current controller")
    85  	}
    86  	return controllers.CurrentController, nil
    87  }
    88  
    89  // ControllerByName implements ControllersGetter.
    90  func (s *store) ControllerByName(name string) (*ControllerDetails, error) {
    91  	if err := ValidateControllerName(name); err != nil {
    92  		return nil, errors.Trace(err)
    93  	}
    94  
    95  	releaser, err := s.acquireLock()
    96  	if err != nil {
    97  		return nil, errors.Annotatef(err, "cannot read controller %v", name)
    98  	}
    99  	defer releaser.Release()
   100  
   101  	controllers, err := ReadControllersFile(JujuControllersPath())
   102  	if err != nil {
   103  		return nil, errors.Trace(err)
   104  	}
   105  	if result, ok := controllers.Controllers[name]; ok {
   106  		return &result, nil
   107  	}
   108  	return nil, errors.NotFoundf("controller %s", name)
   109  }
   110  
   111  // AddController implements ControllerUpdater.
   112  func (s *store) AddController(name string, details ControllerDetails) error {
   113  	if err := ValidateControllerName(name); err != nil {
   114  		return errors.Trace(err)
   115  	}
   116  	if err := ValidateControllerDetails(details); err != nil {
   117  		return errors.Trace(err)
   118  	}
   119  
   120  	releaser, err := s.acquireLock()
   121  	if err != nil {
   122  		return errors.Annotatef(err, "cannot add controller %v", name)
   123  	}
   124  	defer releaser.Release()
   125  
   126  	all, err := ReadControllersFile(JujuControllersPath())
   127  	if err != nil {
   128  		return errors.Annotate(err, "cannot get controllers")
   129  	}
   130  
   131  	if len(all.Controllers) == 0 {
   132  		all.Controllers = make(map[string]ControllerDetails)
   133  	}
   134  
   135  	if _, ok := all.Controllers[name]; ok {
   136  		return errors.AlreadyExistsf("controller with name %s", name)
   137  	}
   138  
   139  	for k, v := range all.Controllers {
   140  		if v.ControllerUUID == details.ControllerUUID {
   141  			return errors.AlreadyExistsf("controller with UUID %s (%s)",
   142  				details.ControllerUUID, k)
   143  		}
   144  	}
   145  
   146  	all.Controllers[name] = details
   147  	return WriteControllersFile(all)
   148  }
   149  
   150  // UpdateController implements ControllerUpdater.
   151  func (s *store) UpdateController(name string, details ControllerDetails) error {
   152  	if err := ValidateControllerName(name); err != nil {
   153  		return errors.Trace(err)
   154  	}
   155  	if err := ValidateControllerDetails(details); err != nil {
   156  		return errors.Trace(err)
   157  	}
   158  
   159  	releaser, err := s.acquireLock()
   160  	if err != nil {
   161  		return errors.Annotatef(err, "cannot update controller %v", name)
   162  	}
   163  	defer releaser.Release()
   164  
   165  	all, err := ReadControllersFile(JujuControllersPath())
   166  	if err != nil {
   167  		return errors.Annotate(err, "cannot get controllers")
   168  	}
   169  
   170  	if len(all.Controllers) == 0 {
   171  		return errors.NotFoundf("controllers")
   172  	}
   173  
   174  	for k, v := range all.Controllers {
   175  		if v.ControllerUUID == details.ControllerUUID && k != name {
   176  			return errors.AlreadyExistsf("controller %s with UUID %s",
   177  				k, v.ControllerUUID)
   178  		}
   179  	}
   180  
   181  	if _, ok := all.Controllers[name]; !ok {
   182  		return errors.NotFoundf("controller %s", name)
   183  	}
   184  
   185  	all.Controllers[name] = details
   186  	return WriteControllersFile(all)
   187  }
   188  
   189  // SetCurrentController implements ControllerUpdater.
   190  func (s *store) SetCurrentController(name string) error {
   191  	if err := ValidateControllerName(name); err != nil {
   192  		return errors.Trace(err)
   193  	}
   194  
   195  	releaser, err := s.acquireLock()
   196  	if err != nil {
   197  		return errors.Annotate(err, "cannot set current controller name")
   198  	}
   199  	defer releaser.Release()
   200  
   201  	controllers, err := ReadControllersFile(JujuControllersPath())
   202  	if err != nil {
   203  		return errors.Trace(err)
   204  	}
   205  	if _, ok := controllers.Controllers[name]; !ok {
   206  		return errors.NotFoundf("controller %v", name)
   207  	}
   208  	if controllers.CurrentController == name {
   209  		return nil
   210  	}
   211  	controllers.CurrentController = name
   212  	return WriteControllersFile(controllers)
   213  }
   214  
   215  // RemoveController implements ControllersRemover
   216  func (s *store) RemoveController(name string) error {
   217  	if err := ValidateControllerName(name); err != nil {
   218  		return errors.Trace(err)
   219  	}
   220  
   221  	releaser, err := s.acquireLock()
   222  	if err != nil {
   223  		return errors.Annotatef(err, "cannot remove controller %v", name)
   224  	}
   225  	defer releaser.Release()
   226  
   227  	controllers, err := ReadControllersFile(JujuControllersPath())
   228  	if err != nil {
   229  		return errors.Annotate(err, "cannot get controllers")
   230  	}
   231  
   232  	// We remove all controllers with the same UUID as the named one.
   233  	namedControllerDetails, ok := controllers.Controllers[name]
   234  	if !ok {
   235  		return nil
   236  	}
   237  	var names []string
   238  	for name, details := range controllers.Controllers {
   239  		if details.ControllerUUID == namedControllerDetails.ControllerUUID {
   240  			names = append(names, name)
   241  			delete(controllers.Controllers, name)
   242  			if controllers.CurrentController == name {
   243  				controllers.CurrentController = ""
   244  			}
   245  		}
   246  	}
   247  
   248  	// Remove models for the controller.
   249  	controllerModels, err := ReadModelsFile(JujuModelsPath())
   250  	if err != nil {
   251  		return errors.Trace(err)
   252  	}
   253  	for _, name := range names {
   254  		if _, ok := controllerModels[name]; ok {
   255  			delete(controllerModels, name)
   256  			if err := WriteModelsFile(controllerModels); err != nil {
   257  				return errors.Trace(err)
   258  			}
   259  		}
   260  	}
   261  
   262  	// Remove accounts for the controller.
   263  	controllerAccounts, err := ReadAccountsFile(JujuAccountsPath())
   264  	if err != nil {
   265  		return errors.Trace(err)
   266  	}
   267  	for _, name := range names {
   268  		if _, ok := controllerAccounts[name]; ok {
   269  			delete(controllerAccounts, name)
   270  			if err := WriteAccountsFile(controllerAccounts); err != nil {
   271  				return errors.Trace(err)
   272  			}
   273  		}
   274  	}
   275  
   276  	// Remove bootstrap config for the controller.
   277  	bootstrapConfigurations, err := ReadBootstrapConfigFile(JujuBootstrapConfigPath())
   278  	if err != nil {
   279  		return errors.Trace(err)
   280  	}
   281  	for _, name := range names {
   282  		if _, ok := bootstrapConfigurations[name]; ok {
   283  			delete(bootstrapConfigurations, name)
   284  			if err := WriteBootstrapConfigFile(bootstrapConfigurations); err != nil {
   285  				return errors.Trace(err)
   286  			}
   287  		}
   288  	}
   289  
   290  	// Finally, remove the controllers. This must be done last
   291  	// so we don't end up with dangling entries in other files.
   292  	return WriteControllersFile(controllers)
   293  }
   294  
   295  // UpdateModel implements ModelUpdater.
   296  func (s *store) UpdateModel(controllerName, modelName string, details ModelDetails) error {
   297  	if err := ValidateControllerName(controllerName); err != nil {
   298  		return errors.Trace(err)
   299  	}
   300  	if err := ValidateModelName(modelName); err != nil {
   301  		return errors.Trace(err)
   302  	}
   303  	if err := ValidateModelDetails(details); err != nil {
   304  		return errors.Trace(err)
   305  	}
   306  
   307  	releaser, err := s.acquireLock()
   308  	if err != nil {
   309  		return errors.Trace(err)
   310  	}
   311  	defer releaser.Release()
   312  
   313  	return errors.Trace(updateModels(
   314  		controllerName,
   315  		func(models *ControllerModels) (bool, error) {
   316  			oldDetails, ok := models.Models[modelName]
   317  			if ok && details == oldDetails {
   318  				return false, nil
   319  			}
   320  			models.Models[modelName] = details
   321  			return true, nil
   322  		},
   323  	))
   324  }
   325  
   326  // SetCurrentModel implements ModelUpdater.
   327  func (s *store) SetCurrentModel(controllerName, modelName string) error {
   328  	if err := ValidateControllerName(controllerName); err != nil {
   329  		return errors.Trace(err)
   330  	}
   331  	if err := ValidateModelName(modelName); err != nil {
   332  		return errors.Trace(err)
   333  	}
   334  
   335  	releaser, err := s.acquireLock()
   336  	if err != nil {
   337  		return errors.Trace(err)
   338  	}
   339  	defer releaser.Release()
   340  
   341  	return errors.Trace(updateModels(
   342  		controllerName,
   343  		func(models *ControllerModels) (bool, error) {
   344  			if models.CurrentModel == modelName {
   345  				return false, nil
   346  			}
   347  			if _, ok := models.Models[modelName]; !ok {
   348  				return false, errors.NotFoundf(
   349  					"model %s:%s",
   350  					controllerName,
   351  					modelName,
   352  				)
   353  			}
   354  			models.CurrentModel = modelName
   355  			return true, nil
   356  		},
   357  	))
   358  }
   359  
   360  // AllModels implements ModelGetter.
   361  func (s *store) AllModels(controllerName string) (map[string]ModelDetails, error) {
   362  	if err := ValidateControllerName(controllerName); err != nil {
   363  		return nil, errors.Trace(err)
   364  	}
   365  
   366  	releaser, err := s.acquireLock()
   367  	if err != nil {
   368  		return nil, errors.Trace(err)
   369  	}
   370  	defer releaser.Release()
   371  
   372  	all, err := ReadModelsFile(JujuModelsPath())
   373  	if err != nil {
   374  		return nil, errors.Trace(err)
   375  	}
   376  	controllerModels, ok := all[controllerName]
   377  	if !ok {
   378  		return nil, errors.NotFoundf(
   379  			"models for controller %s",
   380  			controllerName,
   381  		)
   382  	}
   383  	return controllerModels.Models, nil
   384  }
   385  
   386  // CurrentModel implements ModelGetter.
   387  func (s *store) CurrentModel(controllerName string) (string, error) {
   388  	if err := ValidateControllerName(controllerName); err != nil {
   389  		return "", errors.Trace(err)
   390  	}
   391  
   392  	releaser, err := s.acquireLock()
   393  	if err != nil {
   394  		return "", errors.Trace(err)
   395  	}
   396  	defer releaser.Release()
   397  
   398  	all, err := ReadModelsFile(JujuModelsPath())
   399  	if err != nil {
   400  		return "", errors.Trace(err)
   401  	}
   402  	controllerModels, ok := all[controllerName]
   403  	if !ok {
   404  		return "", errors.NotFoundf(
   405  			"current model for controller %s",
   406  			controllerName,
   407  		)
   408  	}
   409  	if controllerModels.CurrentModel == "" {
   410  		return "", errors.NotFoundf(
   411  			"current model for controller %s",
   412  			controllerName,
   413  		)
   414  	}
   415  	return controllerModels.CurrentModel, nil
   416  }
   417  
   418  // ModelByName implements ModelGetter.
   419  func (s *store) ModelByName(controllerName, modelName string) (*ModelDetails, error) {
   420  	if err := ValidateControllerName(controllerName); err != nil {
   421  		return nil, errors.Trace(err)
   422  	}
   423  	if err := ValidateModelName(modelName); err != nil {
   424  		return nil, errors.Trace(err)
   425  	}
   426  
   427  	releaser, err := s.acquireLock()
   428  	if err != nil {
   429  		return nil, errors.Trace(err)
   430  	}
   431  	defer releaser.Release()
   432  
   433  	all, err := ReadModelsFile(JujuModelsPath())
   434  	if err != nil {
   435  		return nil, errors.Trace(err)
   436  	}
   437  	controllerModels, ok := all[controllerName]
   438  	if !ok {
   439  		return nil, errors.NotFoundf(
   440  			"models for controller %s",
   441  			controllerName,
   442  		)
   443  	}
   444  	details, ok := controllerModels.Models[modelName]
   445  	if !ok {
   446  		return nil, errors.NotFoundf(
   447  			"model %s:%s",
   448  			controllerName,
   449  			modelName,
   450  		)
   451  	}
   452  	return &details, nil
   453  }
   454  
   455  // RemoveModel implements ModelRemover.
   456  func (s *store) RemoveModel(controllerName, modelName string) error {
   457  	if err := ValidateControllerName(controllerName); err != nil {
   458  		return errors.Trace(err)
   459  	}
   460  	if err := ValidateModelName(modelName); err != nil {
   461  		return errors.Trace(err)
   462  	}
   463  
   464  	releaser, err := s.acquireLock()
   465  	if err != nil {
   466  		return errors.Trace(err)
   467  	}
   468  	defer releaser.Release()
   469  
   470  	return errors.Trace(updateModels(
   471  		controllerName,
   472  		func(models *ControllerModels) (bool, error) {
   473  			if _, ok := models.Models[modelName]; !ok {
   474  				return false, errors.NotFoundf(
   475  					"model %s:%s",
   476  					controllerName,
   477  					modelName,
   478  				)
   479  			}
   480  			delete(models.Models, modelName)
   481  			if models.CurrentModel == modelName {
   482  				models.CurrentModel = ""
   483  			}
   484  			return true, nil
   485  		},
   486  	))
   487  }
   488  
   489  func updateModels(
   490  	controllerName string,
   491  	update func(*ControllerModels) (bool, error),
   492  ) error {
   493  	all, err := ReadModelsFile(JujuModelsPath())
   494  	if err != nil {
   495  		return errors.Trace(err)
   496  	}
   497  	controllerModels, ok := all[controllerName]
   498  	if !ok {
   499  		if all == nil {
   500  			all = make(map[string]*ControllerModels)
   501  		}
   502  		controllerModels = &ControllerModels{}
   503  		all[controllerName] = controllerModels
   504  	}
   505  	if controllerModels.Models == nil {
   506  		controllerModels.Models = make(map[string]ModelDetails)
   507  	}
   508  	updated, err := update(controllerModels)
   509  	if err != nil {
   510  		return errors.Trace(err)
   511  	}
   512  	if updated {
   513  		return errors.Trace(WriteModelsFile(all))
   514  	}
   515  	return nil
   516  }
   517  
   518  // UpdateAccount implements AccountUpdater.
   519  func (s *store) UpdateAccount(controllerName string, details AccountDetails) error {
   520  	if err := ValidateControllerName(controllerName); err != nil {
   521  		return errors.Trace(err)
   522  	}
   523  	if err := ValidateAccountDetails(details); err != nil {
   524  		return errors.Trace(err)
   525  	}
   526  
   527  	releaser, err := s.acquireLock()
   528  	if err != nil {
   529  		return errors.Trace(err)
   530  	}
   531  	defer releaser.Release()
   532  
   533  	accounts, err := ReadAccountsFile(JujuAccountsPath())
   534  	if err != nil {
   535  		return errors.Trace(err)
   536  	}
   537  	if accounts == nil {
   538  		accounts = make(map[string]AccountDetails)
   539  	}
   540  	if oldDetails, ok := accounts[controllerName]; ok && details == oldDetails {
   541  		return nil
   542  	} else {
   543  		// Only update last known access if it has a value.
   544  		if details.LastKnownAccess == "" {
   545  			details.LastKnownAccess = oldDetails.LastKnownAccess
   546  		}
   547  	}
   548  
   549  	accounts[controllerName] = details
   550  	return errors.Trace(WriteAccountsFile(accounts))
   551  }
   552  
   553  // AccountByName implements AccountGetter.
   554  func (s *store) AccountDetails(controllerName string) (*AccountDetails, error) {
   555  	if err := ValidateControllerName(controllerName); err != nil {
   556  		return nil, errors.Trace(err)
   557  	}
   558  
   559  	releaser, err := s.acquireLock()
   560  	if err != nil {
   561  		return nil, errors.Trace(err)
   562  	}
   563  	defer releaser.Release()
   564  
   565  	accounts, err := ReadAccountsFile(JujuAccountsPath())
   566  	if err != nil {
   567  		return nil, errors.Trace(err)
   568  	}
   569  	details, ok := accounts[controllerName]
   570  	if !ok {
   571  		return nil, errors.NotFoundf("account details for controller %s", controllerName)
   572  	}
   573  	return &details, nil
   574  }
   575  
   576  // RemoveAccount implements AccountRemover.
   577  func (s *store) RemoveAccount(controllerName string) error {
   578  	if err := ValidateControllerName(controllerName); err != nil {
   579  		return errors.Trace(err)
   580  	}
   581  
   582  	releaser, err := s.acquireLock()
   583  	if err != nil {
   584  		return errors.Trace(err)
   585  	}
   586  	defer releaser.Release()
   587  
   588  	accounts, err := ReadAccountsFile(JujuAccountsPath())
   589  	if err != nil {
   590  		return errors.Trace(err)
   591  	}
   592  	if _, ok := accounts[controllerName]; !ok {
   593  		return errors.NotFoundf("account details for controller %s", controllerName)
   594  	}
   595  
   596  	delete(accounts, controllerName)
   597  	return errors.Trace(WriteAccountsFile(accounts))
   598  }
   599  
   600  // UpdateCredential implements CredentialUpdater.
   601  func (s *store) UpdateCredential(cloudName string, details cloud.CloudCredential) error {
   602  	releaser, err := s.acquireLock()
   603  	if err != nil {
   604  		return errors.Annotatef(err, "cannot update credentials for %v", cloudName)
   605  	}
   606  	defer releaser.Release()
   607  
   608  	all, err := ReadCredentialsFile(JujuCredentialsPath())
   609  	if err != nil {
   610  		return errors.Annotate(err, "cannot get credentials")
   611  	}
   612  
   613  	if len(all) == 0 {
   614  		all = make(map[string]cloud.CloudCredential)
   615  	}
   616  
   617  	// Clear the default credential if we are removing that one.
   618  	if existing, ok := all[cloudName]; ok && existing.DefaultCredential != "" {
   619  		stillHaveDefault := false
   620  		for name := range details.AuthCredentials {
   621  			if name == existing.DefaultCredential {
   622  				stillHaveDefault = true
   623  				break
   624  			}
   625  		}
   626  		if !stillHaveDefault {
   627  			details.DefaultCredential = ""
   628  		}
   629  	}
   630  
   631  	all[cloudName] = details
   632  	return WriteCredentialsFile(all)
   633  }
   634  
   635  // CredentialForCloud implements CredentialGetter.
   636  func (s *store) CredentialForCloud(cloudName string) (*cloud.CloudCredential, error) {
   637  	cloudCredentials, err := s.AllCredentials()
   638  	if err != nil {
   639  		return nil, errors.Trace(err)
   640  	}
   641  	credentials, ok := cloudCredentials[cloudName]
   642  	if !ok {
   643  		return nil, errors.NotFoundf("credentials for cloud %s", cloudName)
   644  	}
   645  	return &credentials, nil
   646  }
   647  
   648  // AllCredentials implements CredentialGetter.
   649  func (s *store) AllCredentials() (map[string]cloud.CloudCredential, error) {
   650  	cloudCredentials, err := ReadCredentialsFile(JujuCredentialsPath())
   651  	if err != nil {
   652  		return nil, errors.Trace(err)
   653  	}
   654  	return cloudCredentials, nil
   655  }
   656  
   657  // UpdateBootstrapConfig implements BootstrapConfigUpdater.
   658  func (s *store) UpdateBootstrapConfig(controllerName string, cfg BootstrapConfig) error {
   659  	if err := ValidateControllerName(controllerName); err != nil {
   660  		return errors.Trace(err)
   661  	}
   662  	if err := ValidateBootstrapConfig(cfg); err != nil {
   663  		return errors.Trace(err)
   664  	}
   665  
   666  	releaser, err := s.acquireLock()
   667  	if err != nil {
   668  		return errors.Annotatef(err, "cannot update bootstrap config for controller %s", controllerName)
   669  	}
   670  	defer releaser.Release()
   671  
   672  	all, err := ReadBootstrapConfigFile(JujuBootstrapConfigPath())
   673  	if err != nil {
   674  		return errors.Annotate(err, "cannot get bootstrap config")
   675  	}
   676  
   677  	if all == nil {
   678  		all = make(map[string]BootstrapConfig)
   679  	}
   680  	all[controllerName] = cfg
   681  	return WriteBootstrapConfigFile(all)
   682  }
   683  
   684  // BootstrapConfigForController implements BootstrapConfigGetter.
   685  func (s *store) BootstrapConfigForController(controllerName string) (*BootstrapConfig, error) {
   686  	configs, err := ReadBootstrapConfigFile(JujuBootstrapConfigPath())
   687  	if err != nil {
   688  		return nil, errors.Trace(err)
   689  	}
   690  	cfg, ok := configs[controllerName]
   691  	if !ok {
   692  		return nil, errors.NotFoundf("bootstrap config for controller %s", controllerName)
   693  	}
   694  	if cfg.CloudType == "" {
   695  		// TODO(axw) 2016-07-25 #1603841
   696  		// Drop this when we get to 2.0. This exists only for
   697  		// compatibility with previous beta releases.
   698  		cfg.CloudType, _ = cfg.Config["type"].(string)
   699  	}
   700  	return &cfg, nil
   701  }