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