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