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  }