github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/cmd/modelcmd/files.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package modelcmd
     5  
     6  import (
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/juju/errors"
    14  	"github.com/juju/utils/fslock"
    15  
    16  	"github.com/juju/juju/juju/osenv"
    17  )
    18  
    19  const (
    20  	CurrentControllerFilename = "current-controller"
    21  
    22  	lockName = "current.lock"
    23  )
    24  
    25  var (
    26  	// 5 seconds should be way more than enough to write or read any files
    27  	// even on heavily loaded controllers.
    28  	lockTimeout = 5 * time.Second
    29  )
    30  
    31  // NOTE: synchronisation across functions in this file.
    32  //
    33  // Each of the read and write functions use a fslock to synchronise calls
    34  // across both the current executable and across different executables.
    35  
    36  func getCurrentControllerFilePath() string {
    37  	return filepath.Join(osenv.JujuXDGDataHome(), CurrentControllerFilename)
    38  }
    39  
    40  // ReadCurrentController reads the file $JUJU_DATA/current-controller and
    41  // return the value stored there. If the file doesn't exist an empty string is
    42  // returned and no error.
    43  func ReadCurrentController() (string, error) {
    44  	lock, err := acquireEnvironmentLock("read current-controller")
    45  	if err != nil {
    46  		return "", errors.Trace(err)
    47  	}
    48  	defer lock.Unlock()
    49  
    50  	current, err := ioutil.ReadFile(getCurrentControllerFilePath())
    51  	if err != nil {
    52  		if os.IsNotExist(err) {
    53  			return "", nil
    54  		}
    55  		return "", errors.Trace(err)
    56  	}
    57  	return strings.TrimSpace(string(current)), nil
    58  }
    59  
    60  // WriteCurrentController writes the controllerName to the file
    61  // $JUJU_DATA/current-controller file.
    62  func WriteCurrentController(controllerName string) error {
    63  	lock, err := acquireEnvironmentLock("write current-controller")
    64  	if err != nil {
    65  		return errors.Trace(err)
    66  	}
    67  	defer lock.Unlock()
    68  
    69  	path := getCurrentControllerFilePath()
    70  	err = ioutil.WriteFile(path, []byte(controllerName+"\n"), 0644)
    71  	if err != nil {
    72  		return errors.Errorf("unable to write to the controller file: %q, %s", path, err)
    73  	}
    74  	return nil
    75  }
    76  
    77  func acquireEnvironmentLock(operation string) (*fslock.Lock, error) {
    78  	// NOTE: any reading or writing from the directory should be done with a
    79  	// fslock to make sure we have a consistent read or write.  Also worth
    80  	// noting, we should use a very short timeout.
    81  	lock, err := fslock.NewLock(osenv.JujuXDGDataHome(), lockName, fslock.Defaults())
    82  	if err != nil {
    83  		return nil, errors.Trace(err)
    84  	}
    85  	err = lock.LockWithTimeout(lockTimeout, operation)
    86  	if err != nil {
    87  		return nil, errors.Trace(err)
    88  	}
    89  	return lock, nil
    90  }