~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/provider/common/state.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}