github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/provider/vsphere/environ.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package vsphere 5 6 import ( 7 "fmt" 8 "path" 9 "sync" 10 11 "github.com/juju/errors" 12 "github.com/juju/version" 13 "golang.org/x/net/context" 14 15 "github.com/juju/juju/core/instance" 16 "github.com/juju/juju/environs" 17 "github.com/juju/juju/environs/config" 18 callcontext "github.com/juju/juju/environs/context" 19 "github.com/juju/juju/provider/common" 20 ) 21 22 // Note: This provider/environment does *not* implement storage. 23 24 type environ struct { 25 name string 26 cloud environs.CloudSpec 27 provider *environProvider 28 29 // namespace is used to create the machine and device hostnames. 30 namespace instance.Namespace 31 32 lock sync.Mutex // lock protects access the following fields. 33 ecfg *environConfig 34 } 35 36 func newEnviron( 37 provider *environProvider, 38 cloud environs.CloudSpec, 39 cfg *config.Config, 40 ) (*environ, error) { 41 ecfg, err := newValidConfig(cfg) 42 if err != nil { 43 return nil, errors.Annotate(err, "invalid config") 44 } 45 46 namespace, err := instance.NewNamespace(cfg.UUID()) 47 if err != nil { 48 return nil, errors.Trace(err) 49 } 50 51 env := &environ{ 52 name: ecfg.Name(), 53 cloud: cloud, 54 provider: provider, 55 ecfg: ecfg, 56 namespace: namespace, 57 } 58 return env, nil 59 } 60 61 func (env *environ) withClient(ctx context.Context, callCtx callcontext.ProviderCallContext, f func(Client) error) error { 62 client, err := env.dialClient(ctx) 63 if err != nil { 64 HandleCredentialError(err, callCtx) 65 return errors.Annotate(err, "dialing client") 66 } 67 defer client.Close(ctx) 68 return f(client) 69 } 70 71 func (env *environ) dialClient(ctx context.Context) (Client, error) { 72 return dialClient(ctx, env.cloud, env.provider.dial) 73 } 74 75 // Name is part of the environs.Environ interface. 76 func (env *environ) Name() string { 77 return env.name 78 } 79 80 // Provider is part of the environs.Environ interface. 81 func (env *environ) Provider() environs.EnvironProvider { 82 return env.provider 83 } 84 85 // SetConfig is part of the environs.Environ interface. 86 func (env *environ) SetConfig(cfg *config.Config) error { 87 env.lock.Lock() 88 defer env.lock.Unlock() 89 90 if env.ecfg == nil { 91 return errors.New("cannot set config on uninitialized env") 92 } 93 94 if err := env.ecfg.update(cfg); err != nil { 95 return errors.Annotate(err, "invalid config change") 96 } 97 return nil 98 } 99 100 // Config is part of the environs.Environ interface. 101 func (env *environ) Config() *config.Config { 102 env.lock.Lock() 103 cfg := env.ecfg.Config 104 env.lock.Unlock() 105 return cfg 106 } 107 108 // PrepareForBootstrap implements environs.Environ. 109 func (env *environ) PrepareForBootstrap(ctx environs.BootstrapContext) error { 110 return nil 111 } 112 113 // Create implements environs.Environ. 114 func (env *environ) Create(ctx callcontext.ProviderCallContext, args environs.CreateParams) error { 115 return env.withSession(ctx, func(env *sessionEnviron) error { 116 return env.Create(ctx, args) 117 }) 118 } 119 120 // Create implements environs.Environ. 121 func (env *sessionEnviron) Create(ctx callcontext.ProviderCallContext, args environs.CreateParams) error { 122 return env.ensureVMFolder(args.ControllerUUID, ctx) 123 } 124 125 //this variable is exported, because it has to be rewritten in external unit tests 126 var Bootstrap = common.Bootstrap 127 128 // Bootstrap is part of the environs.Environ interface. 129 func (env *environ) Bootstrap( 130 ctx environs.BootstrapContext, 131 callCtx callcontext.ProviderCallContext, 132 args environs.BootstrapParams, 133 ) (result *environs.BootstrapResult, err error) { 134 // NOTE(axw) we must not pass a sessionEnviron to common.Bootstrap, 135 // as the Environ will be used during instance finalization after 136 // the Bootstrap method returns, and the session will be invalid. 137 if err := env.withSession(callCtx, func(env *sessionEnviron) error { 138 return env.ensureVMFolder(args.ControllerConfig.ControllerUUID(), callCtx) 139 }); err != nil { 140 return nil, errors.Trace(err) 141 } 142 return Bootstrap(ctx, env, callCtx, args) 143 } 144 145 func (env *sessionEnviron) Bootstrap( 146 ctx environs.BootstrapContext, 147 callCtx callcontext.ProviderCallContext, 148 args environs.BootstrapParams, 149 ) (result *environs.BootstrapResult, err error) { 150 return nil, errors.Errorf("sessionEnviron.Bootstrap should never be called") 151 } 152 153 func (env *sessionEnviron) ensureVMFolder(controllerUUID string, ctx callcontext.ProviderCallContext) error { 154 _, err := env.client.EnsureVMFolder(env.ctx, path.Join( 155 controllerFolderName(controllerUUID), 156 env.modelFolderName(), 157 )) 158 HandleCredentialError(err, ctx) 159 return errors.Trace(err) 160 } 161 162 //this variable is exported, because it has to be rewritten in external unit tests 163 var DestroyEnv = common.Destroy 164 165 // AdoptResources is part of the Environ interface. 166 func (env *environ) AdoptResources(ctx callcontext.ProviderCallContext, controllerUUID string, fromVersion version.Number) error { 167 // Move model folder into the controller's folder. 168 return env.withSession(ctx, func(env *sessionEnviron) error { 169 return env.AdoptResources(ctx, controllerUUID, fromVersion) 170 }) 171 } 172 173 // AdoptResources is part of the Environ interface. 174 func (env *sessionEnviron) AdoptResources(ctx callcontext.ProviderCallContext, controllerUUID string, fromVersion version.Number) error { 175 err := env.client.MoveVMFolderInto(env.ctx, 176 controllerFolderName(controllerUUID), 177 path.Join( 178 controllerFolderName("*"), 179 env.modelFolderName(), 180 ), 181 ) 182 HandleCredentialError(err, ctx) 183 return err 184 } 185 186 // Destroy is part of the environs.Environ interface. 187 func (env *environ) Destroy(ctx callcontext.ProviderCallContext) error { 188 return env.withSession(ctx, func(env *sessionEnviron) error { 189 return env.Destroy(ctx) 190 }) 191 } 192 193 // Destroy is part of the environs.Environ interface. 194 func (env *sessionEnviron) Destroy(ctx callcontext.ProviderCallContext) error { 195 if err := DestroyEnv(env, ctx); err != nil { 196 // We don't need to worry about handling credential errors 197 // here - this is implemented in terms of common operations 198 // that call back into this provider, so we'll handle them 199 // further down the stack. 200 return errors.Trace(err) 201 } 202 err := env.client.DestroyVMFolder(env.ctx, path.Join( 203 controllerFolderName("*"), 204 env.modelFolderName(), 205 )) 206 HandleCredentialError(err, ctx) 207 return err 208 } 209 210 // DestroyController implements the Environ interface. 211 func (env *environ) DestroyController(ctx callcontext.ProviderCallContext, controllerUUID string) error { 212 return env.withSession(ctx, func(env *sessionEnviron) error { 213 return env.DestroyController(ctx, controllerUUID) 214 }) 215 } 216 217 // DestroyController implements the Environ interface. 218 func (env *sessionEnviron) DestroyController(ctx callcontext.ProviderCallContext, controllerUUID string) error { 219 if err := env.Destroy(ctx); err != nil { 220 return errors.Trace(err) 221 } 222 controllerFolderName := controllerFolderName(controllerUUID) 223 if err := env.client.RemoveVirtualMachines(env.ctx, path.Join( 224 controllerFolderName, 225 modelFolderName("*", "*"), 226 "*", 227 )); err != nil { 228 HandleCredentialError(err, ctx) 229 return errors.Annotate(err, "removing VMs") 230 } 231 if err := env.client.DestroyVMFolder(env.ctx, controllerFolderName); err != nil { 232 HandleCredentialError(err, ctx) 233 return errors.Annotate(err, "destroying VM folder") 234 } 235 236 // Remove VMDK cache(s). The user can specify the datastore, and can 237 // change it over time; or if not specified, any accessible datastore 238 // will be used. We must check them all. 239 datastores, err := env.client.Datastores(env.ctx) 240 if err != nil { 241 HandleCredentialError(err, ctx) 242 return errors.Annotate(err, "listing datastores") 243 } 244 for _, ds := range datastores { 245 if !ds.Summary.Accessible { 246 continue 247 } 248 datastorePath := fmt.Sprintf("[%s] %s", ds.Name, vmdkDirectoryName(controllerUUID)) 249 logger.Debugf("deleting: %s", datastorePath) 250 if err := env.client.DeleteDatastoreFile(env.ctx, datastorePath); err != nil { 251 HandleCredentialError(err, ctx) 252 return errors.Annotatef(err, "deleting VMDK cache from datastore %q", ds.Name) 253 } 254 } 255 return nil 256 }