1
// Copyright 2012, 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
12
"github.com/juju/errors"
13
goyaml "gopkg.in/yaml.v2"
15
"github.com/juju/juju/environs"
16
"github.com/juju/juju/environs/storage"
17
"github.com/juju/juju/instance"
20
// StateFile is the name of the file where the provider's state is stored.
21
const StateFile = "provider-state"
23
// BootstrapState is the state information that is stored in StateFile.
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"`
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)))
40
// CreateStateFile creates an empty state file on the given storage, and
42
func CreateStateFile(stor storage.Storage) (string, error) {
43
err := putState(stor, []byte{})
45
return "", fmt.Errorf("cannot create initial state file: %v", err)
47
return stor.URL(StateFile)
50
// DeleteStateFile deletes the state file on the given storage.
51
func DeleteStateFile(stor storage.Storage) error {
52
return stor.Remove(StateFile)
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)
61
return putState(storage, data)
64
// LoadState reads state from the given storage.
65
func LoadState(stor storage.StorageReader) (*BootstrapState, error) {
66
r, err := storage.Get(stor, StateFile)
68
if errors.IsNotFound(err) {
69
return nil, environs.ErrNotBootstrapped
76
func loadState(r io.ReadCloser) (*BootstrapState, error) {
78
data, err := ioutil.ReadAll(r)
80
return nil, fmt.Errorf("error reading %q: %v", StateFile, err)
82
var state BootstrapState
83
err = goyaml.Unmarshal(data, &state)
85
return nil, fmt.Errorf("error unmarshalling %q: %v", StateFile, err)
90
// AddStateInstance adds a controller instance ID to the provider-state
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")
99
state.StateInstances = append(state.StateInstances, id)
100
return SaveState(stor, state)
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 {
110
} else if err != nil {
111
return errors.Annotate(err, "cannot remove recorded state instance-id")
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...)
129
return SaveState(stor, state)
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)
141
return st.StateInstances, nil