github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/provider/common/state.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package common
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"io"
    10  	"io/ioutil"
    11  
    12  	"github.com/juju/errors"
    13  	goyaml "gopkg.in/yaml.v2"
    14  
    15  	"github.com/juju/juju/environs"
    16  	"github.com/juju/juju/environs/storage"
    17  	"github.com/juju/juju/instance"
    18  )
    19  
    20  // StateFile is the name of the file where the provider's state is stored.
    21  const StateFile = "provider-state"
    22  
    23  // BootstrapState is the state information that is stored in StateFile.
    24  //
    25  // Individual providers may define their own state structures instead of
    26  // this one, and use their own code for loading and saving those, but this is
    27  // the definition that most practically useful providers share unchanged.
    28  type BootstrapState struct {
    29  	// StateInstances are the controllers.
    30  	StateInstances []instance.Id `yaml:"state-instances"`
    31  }
    32  
    33  // putState writes the given data to the state file on the given storage.
    34  // The file's name is as defined in StateFile.
    35  func putState(stor storage.StorageWriter, data []byte) error {
    36  	logger.Debugf("putting %q to bootstrap storage %T", StateFile, stor)
    37  	return stor.Put(StateFile, bytes.NewBuffer(data), int64(len(data)))
    38  }
    39  
    40  // CreateStateFile creates an empty state file on the given storage, and
    41  // returns its URL.
    42  func CreateStateFile(stor storage.Storage) (string, error) {
    43  	err := putState(stor, []byte{})
    44  	if err != nil {
    45  		return "", fmt.Errorf("cannot create initial state file: %v", err)
    46  	}
    47  	return stor.URL(StateFile)
    48  }
    49  
    50  // DeleteStateFile deletes the state file on the given storage.
    51  func DeleteStateFile(stor storage.Storage) error {
    52  	return stor.Remove(StateFile)
    53  }
    54  
    55  // SaveState writes the given state to the given storage.
    56  func SaveState(storage storage.StorageWriter, state *BootstrapState) error {
    57  	data, err := goyaml.Marshal(state)
    58  	if err != nil {
    59  		return err
    60  	}
    61  	return putState(storage, data)
    62  }
    63  
    64  // LoadState reads state from the given storage.
    65  func LoadState(stor storage.StorageReader) (*BootstrapState, error) {
    66  	r, err := storage.Get(stor, StateFile)
    67  	if err != nil {
    68  		if errors.IsNotFound(err) {
    69  			return nil, environs.ErrNotBootstrapped
    70  		}
    71  		return nil, err
    72  	}
    73  	return loadState(r)
    74  }
    75  
    76  func loadState(r io.ReadCloser) (*BootstrapState, error) {
    77  	defer r.Close()
    78  	data, err := ioutil.ReadAll(r)
    79  	if err != nil {
    80  		return nil, fmt.Errorf("error reading %q: %v", StateFile, err)
    81  	}
    82  	var state BootstrapState
    83  	err = goyaml.Unmarshal(data, &state)
    84  	if err != nil {
    85  		return nil, fmt.Errorf("error unmarshalling %q: %v", StateFile, err)
    86  	}
    87  	return &state, nil
    88  }
    89  
    90  // AddStateInstance adds a controller instance ID to the provider-state
    91  // file in storage.
    92  func AddStateInstance(stor storage.Storage, id instance.Id) error {
    93  	state, err := LoadState(stor)
    94  	if err == environs.ErrNotBootstrapped {
    95  		state = &BootstrapState{}
    96  	} else if err != nil {
    97  		return errors.Annotate(err, "cannot record state instance-id")
    98  	}
    99  	state.StateInstances = append(state.StateInstances, id)
   100  	return SaveState(stor, state)
   101  }
   102  
   103  // RemoveStateInstances removes controller instance IDs from the
   104  // provider-state file in storage. Instance IDs that are not found
   105  // in the file are ignored.
   106  func RemoveStateInstances(stor storage.Storage, ids ...instance.Id) error {
   107  	state, err := LoadState(stor)
   108  	if err == environs.ErrNotBootstrapped {
   109  		return nil
   110  	} else if err != nil {
   111  		return errors.Annotate(err, "cannot remove recorded state instance-id")
   112  	}
   113  	var anyFound bool
   114  	for i := 0; i < len(state.StateInstances); i++ {
   115  		for _, id := range ids {
   116  			if state.StateInstances[i] == id {
   117  				head := state.StateInstances[:i]
   118  				tail := state.StateInstances[i+1:]
   119  				state.StateInstances = append(head, tail...)
   120  				anyFound = true
   121  				i--
   122  				break
   123  			}
   124  		}
   125  	}
   126  	if !anyFound {
   127  		return nil
   128  	}
   129  	return SaveState(stor, state)
   130  }
   131  
   132  // ProviderStateInstances extracts the instance IDs from provider-state.
   133  func ProviderStateInstances(
   134  	env environs.Environ,
   135  	stor storage.StorageReader,
   136  ) ([]instance.Id, error) {
   137  	st, err := LoadState(stor)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  	return st.StateInstances, nil
   142  }