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