launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/provider/local/environprovider.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package local 5 6 import ( 7 "fmt" 8 "net" 9 "os" 10 "os/user" 11 "syscall" 12 13 "github.com/loggo/loggo" 14 15 "launchpad.net/juju-core/environs" 16 "launchpad.net/juju-core/environs/config" 17 "launchpad.net/juju-core/instance" 18 "launchpad.net/juju-core/juju/osenv" 19 "launchpad.net/juju-core/provider" 20 "launchpad.net/juju-core/utils" 21 "launchpad.net/juju-core/version" 22 ) 23 24 var logger = loggo.GetLogger("juju.provider.local") 25 26 var _ environs.EnvironProvider = (*environProvider)(nil) 27 28 type environProvider struct{} 29 30 var providerInstance = &environProvider{} 31 32 func init() { 33 environs.RegisterProvider(provider.Local, providerInstance) 34 } 35 36 var userCurrent = user.Current 37 38 // Open implements environs.EnvironProvider.Open. 39 func (environProvider) Open(cfg *config.Config) (environs.Environ, error) { 40 logger.Infof("opening environment %q", cfg.Name()) 41 if _, ok := cfg.AgentVersion(); !ok { 42 newCfg, err := cfg.Apply(map[string]interface{}{ 43 "agent-version": version.Current.Number.String(), 44 }) 45 if err != nil { 46 return nil, err 47 } 48 cfg = newCfg 49 } 50 // Set the "namespace" attribute. We do this here, and not in Prepare, 51 // for backwards compatibility: older versions did not store the namespace 52 // in config. 53 if namespace, _ := cfg.UnknownAttrs()["namespace"].(string); namespace == "" { 54 var err error 55 namespace = fmt.Sprintf("%s-%s", os.Getenv("USER"), cfg.Name()) 56 cfg, err = cfg.Apply(map[string]interface{}{"namespace": namespace}) 57 if err != nil { 58 return nil, fmt.Errorf("failed to create namespace: %v", err) 59 } 60 } 61 // Do the initial validation on the config. 62 localConfig, err := providerInstance.newConfig(cfg) 63 if err != nil { 64 return nil, err 65 } 66 if err := VerifyPrerequisites(localConfig.container()); err != nil { 67 logger.Errorf("failed verification of local provider prerequisites: %v", err) 68 return nil, err 69 } 70 environ := &localEnviron{name: cfg.Name()} 71 if err := environ.SetConfig(cfg); err != nil { 72 logger.Errorf("failure setting config: %v", err) 73 return nil, err 74 } 75 return environ, nil 76 } 77 78 var detectAptProxies = utils.DetectAptProxies 79 80 // Prepare implements environs.EnvironProvider.Prepare. 81 func (p environProvider) Prepare(cfg *config.Config) (environs.Environ, error) { 82 // The user must not set bootstrap-ip; this is determined by the provider, 83 // and its presence used to determine whether the environment has yet been 84 // bootstrapped. 85 if _, ok := cfg.UnknownAttrs()["bootstrap-ip"]; ok { 86 return nil, fmt.Errorf("bootstrap-ip must not be specified") 87 } 88 err := checkLocalPort(cfg.StatePort(), "state port") 89 if err != nil { 90 return nil, err 91 } 92 err = checkLocalPort(cfg.APIPort(), "API port") 93 if err != nil { 94 return nil, err 95 } 96 // If the user has specified no values for any of the three normal 97 // proxies, then look in the environment and set them. 98 attrs := make(map[string]interface{}) 99 setIfNotBlank := func(key, value string) { 100 if value != "" { 101 attrs[key] = value 102 } 103 } 104 logger.Tracef("Look for proxies?") 105 if cfg.HttpProxy() == "" && 106 cfg.HttpsProxy() == "" && 107 cfg.FtpProxy() == "" { 108 proxy := osenv.DetectProxies() 109 logger.Tracef("Proxies detected %#v", proxy) 110 setIfNotBlank("http-proxy", proxy.Http) 111 setIfNotBlank("https-proxy", proxy.Https) 112 setIfNotBlank("ftp-proxy", proxy.Ftp) 113 } 114 if cfg.AptHttpProxy() == "" && 115 cfg.AptHttpsProxy() == "" && 116 cfg.AptFtpProxy() == "" { 117 proxy, err := detectAptProxies() 118 if err != nil { 119 return nil, err 120 } 121 setIfNotBlank("apt-http-proxy", proxy.Http) 122 setIfNotBlank("apt-https-proxy", proxy.Https) 123 setIfNotBlank("apt-ftp-proxy", proxy.Ftp) 124 } 125 if len(attrs) > 0 { 126 cfg, err = cfg.Apply(attrs) 127 if err != nil { 128 return nil, err 129 } 130 } 131 132 return p.Open(cfg) 133 } 134 135 // checkLocalPort checks that the passed port is not used so far. 136 var checkLocalPort = func(port int, description string) error { 137 logger.Infof("checking %s", description) 138 // Try to connect the port on localhost. 139 address := fmt.Sprintf("localhost:%d", port) 140 // TODO(mue) Add a timeout? 141 conn, err := net.Dial("tcp", address) 142 if err != nil { 143 if nerr, ok := err.(*net.OpError); ok { 144 if nerr.Err == syscall.ECONNREFUSED { 145 // No connection, so everything is fine. 146 return nil 147 } 148 } 149 return err 150 } 151 // Connected, so port is in use. 152 err = conn.Close() 153 if err != nil { 154 return err 155 } 156 return fmt.Errorf("cannot use %d as %s, already in use", port, description) 157 } 158 159 // Validate implements environs.EnvironProvider.Validate. 160 func (provider environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) { 161 // Check for valid changes for the base config values. 162 if err := config.Validate(cfg, old); err != nil { 163 return nil, err 164 } 165 validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) 166 if err != nil { 167 logger.Errorf("failed to validate unknown attrs: %v", err) 168 return nil, err 169 } 170 localConfig := newEnvironConfig(cfg, validated) 171 // Before potentially creating directories, make sure that the 172 // root directory has not changed. 173 if old != nil { 174 oldLocalConfig, err := provider.newConfig(old) 175 if err != nil { 176 return nil, fmt.Errorf("old config is not a valid local config: %v", old) 177 } 178 if localConfig.container() != oldLocalConfig.container() { 179 return nil, fmt.Errorf("cannot change container from %q to %q", 180 oldLocalConfig.container(), 181 localConfig.container()) 182 } 183 if localConfig.rootDir() != oldLocalConfig.rootDir() { 184 return nil, fmt.Errorf("cannot change root-dir from %q to %q", 185 oldLocalConfig.rootDir(), 186 localConfig.rootDir()) 187 } 188 if localConfig.networkBridge() != oldLocalConfig.networkBridge() { 189 return nil, fmt.Errorf("cannot change network-bridge from %q to %q", 190 oldLocalConfig.rootDir(), 191 localConfig.rootDir()) 192 } 193 if localConfig.storagePort() != oldLocalConfig.storagePort() { 194 return nil, fmt.Errorf("cannot change storage-port from %v to %v", 195 oldLocalConfig.storagePort(), 196 localConfig.storagePort()) 197 } 198 } 199 // Currently only supported containers are "lxc" and "kvm". 200 if localConfig.container() != instance.LXC && localConfig.container() != instance.KVM { 201 return nil, fmt.Errorf("unsupported container type: %q", localConfig.container()) 202 } 203 dir, err := utils.NormalizePath(localConfig.rootDir()) 204 if err != nil { 205 return nil, err 206 } 207 if dir == "." { 208 dir = osenv.JujuHomePath(cfg.Name()) 209 } 210 // Always assign the normalized path. 211 localConfig.attrs["root-dir"] = dir 212 213 // Apply the coerced unknown values back into the config. 214 return cfg.Apply(localConfig.attrs) 215 } 216 217 // BoilerplateConfig implements environs.EnvironProvider.BoilerplateConfig. 218 func (environProvider) BoilerplateConfig() string { 219 return ` 220 # https://juju.ubuntu.com/docs/config-local.html 221 local: 222 type: local 223 # Override the directory that is used for the storage files and database. 224 # The default location is $JUJU_HOME/<ENV>. 225 226 # $JUJU_HOME defaults to ~/.juju 227 # root-dir: ~/.juju/local 228 229 # Override the storage port if you have multiple local providers, or if the 230 # default port is used by another program. 231 # storage-port: 8040 232 233 # Override the network bridge if you have changed the default lxc bridge 234 # network-bridge: lxcbr0 235 236 `[1:] 237 } 238 239 // SecretAttrs implements environs.EnvironProvider.SecretAttrs. 240 func (environProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) { 241 // don't have any secret attrs 242 return nil, nil 243 } 244 245 // Location specific methods that are able to be called by any instance that 246 // has been created by this provider type. So a machine agent may well call 247 // these methods to find out its own address or instance id. 248 249 // PublicAddress implements environs.EnvironProvider.PublicAddress. 250 func (environProvider) PublicAddress() (string, error) { 251 // Get the IPv4 address from eth0 252 return getAddressForInterface("eth0") 253 } 254 255 // PrivateAddress implements environs.EnvironProvider.PrivateAddress. 256 func (environProvider) PrivateAddress() (string, error) { 257 // Get the IPv4 address from eth0 258 return getAddressForInterface("eth0") 259 } 260 261 func (p environProvider) newConfig(cfg *config.Config) (*environConfig, error) { 262 valid, err := p.Validate(cfg, nil) 263 if err != nil { 264 return nil, err 265 } 266 return newEnvironConfig(valid, valid.UnknownAttrs()), nil 267 }