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