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 }