github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/provider/lxd/environ.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // +build go1.3 5 6 package lxd 7 8 import ( 9 "sync" 10 11 "github.com/juju/errors" 12 13 "github.com/juju/juju/environs" 14 "github.com/juju/juju/environs/config" 15 "github.com/juju/juju/environs/tags" 16 "github.com/juju/juju/instance" 17 "github.com/juju/juju/provider/common" 18 "github.com/juju/juju/tools/lxdclient" 19 ) 20 21 const bootstrapMessage = `To configure your system to better support LXD containers, please see: https://github.com/lxc/lxd/blob/master/doc/production-setup.md` 22 23 type baseProvider interface { 24 // BootstrapEnv bootstraps a Juju environment. 25 BootstrapEnv(environs.BootstrapContext, environs.BootstrapParams) (*environs.BootstrapResult, error) 26 27 // DestroyEnv destroys the provided Juju environment. 28 DestroyEnv() error 29 } 30 31 type environ struct { 32 name string 33 uuid string 34 raw *rawProvider 35 base baseProvider 36 37 // namespace is used to create the machine and device hostnames. 38 namespace instance.Namespace 39 40 lock sync.Mutex 41 ecfg *environConfig 42 } 43 44 type newRawProviderFunc func(environs.CloudSpec) (*rawProvider, error) 45 46 func newEnviron(spec environs.CloudSpec, cfg *config.Config, newRawProvider newRawProviderFunc) (*environ, error) { 47 ecfg, err := newValidConfig(cfg) 48 if err != nil { 49 return nil, errors.Annotate(err, "invalid config") 50 } 51 52 namespace, err := instance.NewNamespace(cfg.UUID()) 53 if err != nil { 54 return nil, errors.Trace(err) 55 } 56 57 raw, err := newRawProvider(spec) 58 if err != nil { 59 return nil, errors.Trace(err) 60 } 61 62 env := &environ{ 63 name: ecfg.Name(), 64 uuid: ecfg.UUID(), 65 raw: raw, 66 namespace: namespace, 67 ecfg: ecfg, 68 } 69 env.base = common.DefaultProvider{Env: env} 70 71 //TODO(wwitzel3) make sure we are also cleaning up profiles during destroy 72 if err := env.initProfile(); err != nil { 73 return nil, errors.Trace(err) 74 } 75 76 return env, nil 77 } 78 79 var defaultProfileConfig = map[string]string{ 80 "boot.autostart": "true", 81 "security.nesting": "true", 82 } 83 84 func (env *environ) initProfile() error { 85 hasProfile, err := env.raw.HasProfile(env.profileName()) 86 if err != nil { 87 return errors.Trace(err) 88 } 89 90 if hasProfile { 91 return nil 92 } 93 94 return env.raw.CreateProfile(env.profileName(), defaultProfileConfig) 95 } 96 97 func (env *environ) profileName() string { 98 return "juju-" + env.ecfg.Name() 99 } 100 101 // Name returns the name of the environment. 102 func (env *environ) Name() string { 103 return env.name 104 } 105 106 // Provider returns the environment provider that created this env. 107 func (*environ) Provider() environs.EnvironProvider { 108 return providerInstance 109 } 110 111 // SetConfig updates the env's configuration. 112 func (env *environ) SetConfig(cfg *config.Config) error { 113 env.lock.Lock() 114 defer env.lock.Unlock() 115 ecfg, err := newValidConfig(cfg) 116 if err != nil { 117 return errors.Trace(err) 118 } 119 env.ecfg = ecfg 120 return nil 121 } 122 123 // Config returns the configuration data with which the env was created. 124 func (env *environ) Config() *config.Config { 125 env.lock.Lock() 126 cfg := env.ecfg.Config 127 env.lock.Unlock() 128 return cfg 129 } 130 131 // PrepareForBootstrap implements environs.Environ. 132 func (env *environ) PrepareForBootstrap(ctx environs.BootstrapContext) error { 133 if err := lxdclient.EnableHTTPSListener(env.raw); err != nil { 134 return errors.Annotate(err, "enabling HTTPS listener") 135 } 136 return nil 137 } 138 139 // Create implements environs.Environ. 140 func (env *environ) Create(environs.CreateParams) error { 141 return nil 142 } 143 144 // Bootstrap implements environs.Environ. 145 func (env *environ) Bootstrap(ctx environs.BootstrapContext, params environs.BootstrapParams) (*environs.BootstrapResult, error) { 146 // Using the Bootstrap func from provider/common should be fine. 147 // Local provider does its own thing because it has to deal directly 148 // with localhost rather than using SSH. 149 return env.base.BootstrapEnv(ctx, params) 150 } 151 152 // BootstrapMessage is part of the Environ interface. 153 func (env *environ) BootstrapMessage() string { 154 return bootstrapMessage 155 } 156 157 // Destroy shuts down all known machines and destroys the rest of the 158 // known environment. 159 func (env *environ) Destroy() error { 160 ports, err := env.Ports() 161 if err != nil { 162 return errors.Trace(err) 163 } 164 if len(ports) > 0 { 165 if err := env.ClosePorts(ports); err != nil { 166 return errors.Trace(err) 167 } 168 } 169 if err := env.base.DestroyEnv(); err != nil { 170 return errors.Trace(err) 171 } 172 return nil 173 } 174 175 // DestroyController implements the Environ interface. 176 func (env *environ) DestroyController(controllerUUID string) error { 177 if err := env.Destroy(); err != nil { 178 return errors.Trace(err) 179 } 180 return env.destroyHostedModelResources(controllerUUID) 181 } 182 183 func (env *environ) destroyHostedModelResources(controllerUUID string) error { 184 // Destroy all instances with juju-controller-uuid 185 // matching the specified UUID. 186 const prefix = "juju-" 187 instances, err := env.prefixedInstances(prefix) 188 if err != nil { 189 return errors.Annotate(err, "listing instances") 190 } 191 logger.Debugf("instances: %v", instances) 192 var names []string 193 for _, inst := range instances { 194 metadata := inst.raw.Metadata() 195 if metadata[tags.JujuModel] == env.uuid { 196 continue 197 } 198 if metadata[tags.JujuController] != controllerUUID { 199 continue 200 } 201 names = append(names, string(inst.Id())) 202 } 203 if err := removeInstances(env.raw, prefix, names); err != nil { 204 return errors.Annotate(err, "removing hosted model instances") 205 } 206 return nil 207 }