LEFT | RIGHT |
(no file at all) | |
| 1 // Stub provider for OpenStack, using goose will be implemented here |
| 2 |
| 3 package openstack |
| 4 |
| 5 import ( |
| 6 "fmt" |
| 7 "io/ioutil" |
| 8 "launchpad.net/juju-core/environs" |
| 9 "launchpad.net/juju-core/environs/config" |
| 10 "launchpad.net/juju-core/log" |
| 11 "launchpad.net/juju-core/state" |
| 12 "launchpad.net/juju-core/trivial" |
| 13 "net/http" |
| 14 "strings" |
| 15 "sync" |
| 16 "time" |
| 17 ) |
| 18 |
| 19 type environProvider struct{} |
| 20 |
| 21 var _ environs.EnvironProvider = (*environProvider)(nil) |
| 22 |
| 23 var providerInstance environProvider |
| 24 |
| 25 // A request may fail to due "eventual consistency" semantics, which |
| 26 // should resolve fairly quickly. A request may also fail due to a slow |
| 27 // state transition (for instance an instance taking a while to release |
| 28 // a security group after termination). The former failure mode is |
| 29 // dealt with by shortAttempt, the latter by longAttempt. |
| 30 var shortAttempt = trivial.AttemptStrategy{ |
| 31 Total: 5 * time.Second, |
| 32 Delay: 200 * time.Millisecond, |
| 33 } |
| 34 |
| 35 var longAttempt = trivial.AttemptStrategy{ |
| 36 Total: 3 * time.Minute, |
| 37 Delay: 1 * time.Second, |
| 38 } |
| 39 |
| 40 func init() { |
| 41 environs.RegisterProvider("openstack", environProvider{}) |
| 42 } |
| 43 |
| 44 func (p environProvider) Open(cfg *config.Config) (environs.Environ, error) { |
| 45 log.Printf("environs/openstack: opening environment %q", cfg.Name()) |
| 46 e := new(environ) |
| 47 err := e.SetConfig(cfg) |
| 48 if err != nil { |
| 49 return nil, err |
| 50 } |
| 51 return e, nil |
| 52 } |
| 53 |
| 54 func (p environProvider) SecretAttrs(cfg *config.Config) (map[string]interface{}
, error) { |
| 55 m := make(map[string]interface{}) |
| 56 ecfg, err := providerInstance.newConfig(cfg) |
| 57 if err != nil { |
| 58 return nil, err |
| 59 } |
| 60 m["username"] = ecfg.username() |
| 61 m["password"] = ecfg.password() |
| 62 m["tenant-name"] = ecfg.tenantName() |
| 63 return m, nil |
| 64 } |
| 65 |
| 66 func (p environProvider) PublicAddress() (string, error) { |
| 67 return fetchMetadata("public-hostname") |
| 68 } |
| 69 |
| 70 func (p environProvider) PrivateAddress() (string, error) { |
| 71 return fetchMetadata("local-hostname") |
| 72 } |
| 73 |
| 74 type environ struct { |
| 75 name string |
| 76 |
| 77 ecfgMutex sync.Mutex |
| 78 ecfgUnlocked *environConfig |
| 79 } |
| 80 |
| 81 var _ environs.Environ = (*environ)(nil) |
| 82 |
| 83 func (e *environ) ecfg() *environConfig { |
| 84 e.ecfgMutex.Lock() |
| 85 ecfg := e.ecfgUnlocked |
| 86 e.ecfgMutex.Unlock() |
| 87 return ecfg |
| 88 } |
| 89 |
| 90 func (e *environ) Name() string { |
| 91 return e.name |
| 92 } |
| 93 |
| 94 func (e *environ) Bootstrap(uploadTools bool, stateServerPEM []byte) error { |
| 95 panic("not implemented") |
| 96 } |
| 97 |
| 98 func (e *environ) StateInfo() (*state.Info, error) { |
| 99 panic("not implemented") |
| 100 } |
| 101 |
| 102 func (e *environ) Config() *config.Config { |
| 103 panic("not implemented") |
| 104 } |
| 105 |
| 106 func (e *environ) SetConfig(cfg *config.Config) error { |
| 107 ecfg, err := providerInstance.newConfig(cfg) |
| 108 if err != nil { |
| 109 return err |
| 110 } |
| 111 e.ecfgMutex.Lock() |
| 112 defer e.ecfgMutex.Unlock() |
| 113 e.name = ecfg.Name() |
| 114 e.ecfgUnlocked = ecfg |
| 115 |
| 116 // TODO(dimitern): setup the goose client auth/compute, etc. here |
| 117 return nil |
| 118 } |
| 119 |
| 120 func (e *environ) StartInstance(machineId int, info *state.Info, tools *state.To
ols) (environs.Instance, error) { |
| 121 panic("not implemented") |
| 122 } |
| 123 |
| 124 func (e *environ) StopInstances([]environs.Instance) error { |
| 125 panic("not implemented") |
| 126 } |
| 127 |
| 128 func (e *environ) Instances(ids []string) ([]environs.Instance, error) { |
| 129 panic("not implemented") |
| 130 } |
| 131 |
| 132 func (e *environ) AllInstances() ([]environs.Instance, error) { |
| 133 panic("not implemented") |
| 134 } |
| 135 |
| 136 func (e *environ) Storage() environs.Storage { |
| 137 panic("not implemented") |
| 138 } |
| 139 |
| 140 func (e *environ) PublicStorage() environs.StorageReader { |
| 141 panic("not implemented") |
| 142 } |
| 143 |
| 144 func (e *environ) Destroy(insts []environs.Instance) error { |
| 145 panic("not implemented") |
| 146 } |
| 147 |
| 148 func (e *environ) AssignmentPolicy() state.AssignmentPolicy { |
| 149 panic("not implemented") |
| 150 } |
| 151 |
| 152 func (e *environ) OpenPorts(ports []state.Port) error { |
| 153 panic("not implemented") |
| 154 } |
| 155 |
| 156 func (e *environ) ClosePorts(ports []state.Port) error { |
| 157 panic("not implemented") |
| 158 } |
| 159 |
| 160 func (e *environ) Ports() ([]state.Port, error) { |
| 161 panic("not implemented") |
| 162 } |
| 163 |
| 164 func (e *environ) Provider() environs.EnvironProvider { |
| 165 return &providerInstance |
| 166 } |
| 167 |
| 168 // metadataHost holds the address of the instance metadata service. |
| 169 // It is a variable so that tests can change it to refer to a local |
| 170 // server when needed. |
| 171 var metadataHost = "http://169.254.169.254" |
| 172 |
| 173 // fetchMetadata fetches a single atom of data from the openstack instance metad
ata service. |
| 174 // http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/AESDG-chapter-insta
ncedata.html |
| 175 // (the same specs is implemented in OpenStack, hence the reference) |
| 176 func fetchMetadata(name string) (value string, err error) { |
| 177 uri := fmt.Sprintf("%s/2011-01-01/meta-data/%s", metadataHost, name) |
| 178 for a := shortAttempt.Start(); a.Next(); { |
| 179 var resp *http.Response |
| 180 resp, err = http.Get(uri) |
| 181 if err != nil { |
| 182 continue |
| 183 } |
| 184 defer resp.Body.Close() |
| 185 if resp.StatusCode != http.StatusOK { |
| 186 err = fmt.Errorf("bad http response %v", resp.Status) |
| 187 continue |
| 188 } |
| 189 var data []byte |
| 190 data, err = ioutil.ReadAll(resp.Body) |
| 191 if err != nil { |
| 192 continue |
| 193 } |
| 194 return strings.TrimSpace(string(data)), nil |
| 195 } |
| 196 if err != nil { |
| 197 return "", fmt.Errorf("cannot get %q: %v", uri, err) |
| 198 } |
| 199 return |
| 200 } |
LEFT | RIGHT |