github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/provider/oracle/environ.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package oracle
     5  
     6  import (
     7  	"fmt"
     8  	"math/rand"
     9  	"strconv"
    10  	"strings"
    11  	"sync"
    12  	"time"
    13  
    14  	"github.com/juju/clock"
    15  	"github.com/juju/collections/set"
    16  	"github.com/juju/errors"
    17  	oci "github.com/juju/go-oracle-cloud/api"
    18  	ociCommon "github.com/juju/go-oracle-cloud/common"
    19  	ociResponse "github.com/juju/go-oracle-cloud/response"
    20  	"github.com/juju/os"
    21  	jujuseries "github.com/juju/os/series"
    22  	"github.com/juju/utils/arch"
    23  	"github.com/juju/version"
    24  
    25  	"github.com/juju/juju/cloudconfig/cloudinit"
    26  	"github.com/juju/juju/cloudconfig/instancecfg"
    27  	"github.com/juju/juju/cloudconfig/providerinit"
    28  	"github.com/juju/juju/core/constraints"
    29  	"github.com/juju/juju/core/instance"
    30  	"github.com/juju/juju/environs"
    31  	"github.com/juju/juju/environs/config"
    32  	"github.com/juju/juju/environs/context"
    33  	envinstance "github.com/juju/juju/environs/instances"
    34  	"github.com/juju/juju/environs/tags"
    35  	"github.com/juju/juju/network"
    36  	"github.com/juju/juju/provider/common"
    37  	commonProvider "github.com/juju/juju/provider/oracle/common"
    38  	oraclenet "github.com/juju/juju/provider/oracle/network"
    39  	"github.com/juju/juju/tools"
    40  )
    41  
    42  // OracleEnviron implements the environs.Environ interface
    43  type OracleEnviron struct {
    44  	environs.Networking
    45  	oraclenet.Firewaller
    46  
    47  	mutex     *sync.Mutex
    48  	p         *EnvironProvider
    49  	spec      environs.CloudSpec
    50  	cfg       *config.Config
    51  	client    EnvironAPI
    52  	namespace instance.Namespace
    53  	clock     clock.Clock
    54  	rand      *rand.Rand
    55  }
    56  
    57  // EnvironAPI provides interface to access and make operation
    58  // inside a oracle environ
    59  type EnvironAPI interface {
    60  	commonProvider.Instancer
    61  	commonProvider.InstanceAPI
    62  	commonProvider.Authenticater
    63  	commonProvider.Shaper
    64  	commonProvider.Imager
    65  	commonProvider.IpReservationAPI
    66  	commonProvider.IpAssociationAPI
    67  	commonProvider.IpNetworkExchanger
    68  	commonProvider.IpNetworker
    69  	commonProvider.VnicSetAPI
    70  
    71  	commonProvider.RulesAPI
    72  	commonProvider.AclAPI
    73  	commonProvider.SecIpAPI
    74  	commonProvider.IpAddressPrefixSetAPI
    75  	commonProvider.SecListAPI
    76  	commonProvider.ApplicationsAPI
    77  	commonProvider.SecRulesAPI
    78  	commonProvider.AssociationAPI
    79  
    80  	StorageAPI
    81  }
    82  
    83  // AvailabilityZones is defined in the common.ZonedEnviron interface
    84  func (o *OracleEnviron) AvailabilityZones(ctx context.ProviderCallContext) ([]common.AvailabilityZone, error) {
    85  	return []common.AvailabilityZone{
    86  		oraclenet.NewAvailabilityZone("default"),
    87  	}, nil
    88  }
    89  
    90  // InstanceAvailabilityzoneNames is defined in the common.ZonedEnviron interface
    91  func (o *OracleEnviron) InstanceAvailabilityZoneNames(ctx context.ProviderCallContext, ids []instance.Id) ([]string, error) {
    92  	instances, err := o.Instances(ctx, ids)
    93  	if err != nil && err != environs.ErrPartialInstances {
    94  		return nil, err
    95  	}
    96  	zones := make([]string, len(instances))
    97  	for idx := range instances {
    98  		zones[idx] = "default"
    99  	}
   100  	return zones, nil
   101  }
   102  
   103  // NewOracleEnviron returns a new OracleEnviron
   104  func NewOracleEnviron(p *EnvironProvider, args environs.OpenParams, client EnvironAPI, c clock.Clock) (env *OracleEnviron, err error) {
   105  	if client == nil {
   106  		return nil, errors.NotFoundf("oracle client")
   107  	}
   108  	if p == nil {
   109  		return nil, errors.NotFoundf("environ proivder")
   110  	}
   111  	env = &OracleEnviron{
   112  		p:      p,
   113  		spec:   args.Cloud,
   114  		cfg:    args.Config,
   115  		mutex:  &sync.Mutex{},
   116  		client: client,
   117  		clock:  c,
   118  	}
   119  	env.namespace, err = instance.NewNamespace(env.cfg.UUID())
   120  	if err != nil {
   121  		return nil, errors.Trace(err)
   122  	}
   123  	env.Firewaller = oraclenet.NewFirewall(env, client, c)
   124  	env.Networking = oraclenet.NewEnviron(client, env)
   125  
   126  	source := rand.NewSource(env.clock.Now().UTC().UnixNano())
   127  	r := rand.New(source)
   128  	env.rand = r
   129  
   130  	return env, nil
   131  }
   132  
   133  // PrepareForBootstrap is part of the Environ interface.
   134  func (o *OracleEnviron) PrepareForBootstrap(ctx environs.BootstrapContext) error {
   135  	if ctx.ShouldVerifyCredentials() {
   136  		logger.Infof("Logging into the oracle cloud infrastructure")
   137  		if err := o.client.Authenticate(); err != nil {
   138  			return errors.Trace(err)
   139  		}
   140  	}
   141  
   142  	return nil
   143  }
   144  
   145  // Bootstrap is part of the Environ interface.
   146  func (o *OracleEnviron) Bootstrap(ctx environs.BootstrapContext, callCtx context.ProviderCallContext, args environs.BootstrapParams) (*environs.BootstrapResult, error) {
   147  	return common.Bootstrap(ctx, o, callCtx, args)
   148  }
   149  
   150  // Create is part of the Environ interface.
   151  func (o *OracleEnviron) Create(ctx context.ProviderCallContext, params environs.CreateParams) error {
   152  	if err := o.client.Authenticate(); err != nil {
   153  		return errors.Trace(err)
   154  	}
   155  
   156  	return nil
   157  }
   158  
   159  // AdoptResources is part of the Environ interface.
   160  func (e *OracleEnviron) AdoptResources(ctx context.ProviderCallContext, controllerUUID string, fromVersion version.Number) error {
   161  	//TODO (gsamfira): Implement AdoptResources. Controller tag for all resources needs to
   162  	// be changed
   163  	return nil
   164  }
   165  
   166  // getCloudInitConfig returns a CloudConfig instance. this function only exists because of a
   167  // limitation that currently exists in cloud-init see bug report:
   168  // https://bugs.launchpad.net/cloud-init/+bug/1675370
   169  func (e *OracleEnviron) getCloudInitConfig(series string, networks map[string]oci.Networker) (cloudinit.CloudConfig, error) {
   170  	// TODO (gsamfira): remove this function when the above mention bug is fixed
   171  	cloudcfg, err := cloudinit.New(series)
   172  	if err != nil {
   173  		return nil, errors.Annotate(err, "cannot create cloudinit template")
   174  	}
   175  	operatingSystem, err := jujuseries.GetOSFromSeries(series)
   176  	if err != nil {
   177  		return nil, errors.Trace(err)
   178  	}
   179  	renderer := cloudcfg.ShellRenderer()
   180  	// by default windows has all NICs set on DHCP, so no need to configure
   181  	// anything.
   182  	// gsamfira: I could not find a CentOS image in the oracle compute
   183  	// marketplace, so not doing anything CentOS specific here.
   184  	var scripts []string
   185  	switch operatingSystem {
   186  	case os.Ubuntu:
   187  		for key := range networks {
   188  			if key == defaultNicName {
   189  				continue
   190  			}
   191  			fileBaseName := fmt.Sprintf("%s.cfg", key)
   192  			fileContents := fmt.Sprintf(ubuntuInterfaceTemplate, key, key)
   193  			fileName := renderer.Join(
   194  				renderer.FromSlash(interfacesConfigDir), fileBaseName)
   195  			scripts = append(scripts,
   196  				renderer.WriteFile(fileName, []byte(fileContents))...)
   197  			scripts = append(scripts, fmt.Sprintf("/sbin/ifup %s", key))
   198  			scripts = append(scripts, "")
   199  
   200  		}
   201  		if len(scripts) > 0 {
   202  			cloudcfg.AddScripts(strings.Join(scripts, "\n"))
   203  		}
   204  	}
   205  	return cloudcfg, nil
   206  }
   207  
   208  // buildSpacesMap builds a map with juju converted names from provider space names
   209  //
   210  // shamelessly copied from the MAAS provider
   211  func (e *OracleEnviron) buildSpacesMap(ctx context.ProviderCallContext) (map[string]network.SpaceInfo, map[string]string, error) {
   212  	empty := set.Strings{}
   213  	providerIdMap := map[string]string{}
   214  	// NOTE (gsamfira): This seems brittle to me, and I would much rather get this
   215  	// from state, as that information should already be there from the discovered spaces
   216  	// and that is the information that gets presented to the user when running:
   217  	// juju spaces
   218  	// However I have not found a clean way to access that info from the provider,
   219  	// without creating a facade. Someone with more knowledge on this might be able to chip in.
   220  	spaces, err := e.Spaces(ctx)
   221  	if err != nil {
   222  		return nil, providerIdMap, errors.Trace(err)
   223  	}
   224  	spaceMap := make(map[string]network.SpaceInfo)
   225  	for _, space := range spaces {
   226  		jujuName := network.ConvertSpaceName(space.Name, empty)
   227  		spaceMap[jujuName] = space
   228  		empty.Add(jujuName)
   229  		providerIdMap[string(space.ProviderId)] = space.Name
   230  	}
   231  	return spaceMap, providerIdMap, nil
   232  
   233  }
   234  
   235  func (e *OracleEnviron) getInstanceNetworks(
   236  	ctx context.ProviderCallContext,
   237  	args environs.StartInstanceParams,
   238  	secLists, vnicSets []string,
   239  ) (map[string]oci.Networker, error) {
   240  
   241  	// gsamfira: We add a default NIC attached o the shared network provided
   242  	// by the oracle cloud. This NIC is used for outbound traffic.
   243  	// While you can attach just a VNIC to the instance, and assign a
   244  	// public IP to it, I have not been able to get any outbound traffic
   245  	// through the thing.
   246  	// NOTE (gsamfira): The NIC ordering inside the instance is determined
   247  	// by the order in which the API returns the network interfaces. It seems
   248  	// the API orders the NICs alphanumerically. When adding new network
   249  	// interfaces, make sure they are ordered to come after eth0
   250  	networking := map[string]oci.Networker{
   251  		defaultNicName: oci.SharedNetwork{
   252  			Seclists: secLists,
   253  		},
   254  	}
   255  	spaces := set.Strings{}
   256  	if s := args.Constraints.IncludeSpaces(); len(s) != 0 {
   257  		for _, val := range s {
   258  			logger.Debugf("Adding space from constraints %s", val)
   259  			spaces.Add(val)
   260  		}
   261  	}
   262  
   263  	// NOTE (gsamfira): The way spaces works seems really iffy to me. We currently
   264  	// rely on two sources of truth to determine the spaces to which we should attach
   265  	// an instance. This becomes evident then we try to use --constraints and --bind
   266  	// to specify the space.
   267  	// In both cases, the user specifies the space name that comes up in juju spaces
   268  	// When fetching the space using constraints inside the provider, we get the actual
   269  	// space name passed in by the user. However, when we go through args.EndpointBindings
   270  	// we get a mapping between the endpoint binding name (not the space name) and the ProviderID
   271  	// of the space (unlike constraints where you get the name). All this without being able to access
   272  	// the source of truth the user used when selecting the space, which is juju state. So we need to:
   273  	// 1) fetch spaces from the provider
   274  	// 2) parse the name and mutate it to match what the discover spaces worker does (and hope
   275  	// that the API returns the spaces in the same order every time)
   276  	// 3) create a map of those spaces both name-->space and providerID-->name to be able to match
   277  	// both cases. This all seems really brittle to me.
   278  	providerSpaces, providerIds, err := e.buildSpacesMap(ctx)
   279  	if err != nil {
   280  		return map[string]oci.Networker{}, err
   281  	}
   282  
   283  	if len(args.EndpointBindings) != 0 {
   284  		for _, providerID := range args.EndpointBindings {
   285  			if name, ok := providerIds[string(providerID)]; ok {
   286  				logger.Debugf("Adding space from bindings %s", name)
   287  				spaces.Add(name)
   288  			}
   289  		}
   290  	}
   291  
   292  	//start from 1. eth0 is the default nic that gets attached by default.
   293  	idx := 1
   294  	for _, space := range spaces.Values() {
   295  		providerSpace, ok := providerSpaces[space]
   296  		if !ok {
   297  			return map[string]oci.Networker{},
   298  				errors.Errorf("Could not find space %q", space)
   299  		}
   300  		if len(providerSpace.Subnets) == 0 {
   301  			return map[string]oci.Networker{},
   302  				errors.Errorf("No usable subnets found in space %q", space)
   303  		}
   304  
   305  		ipNet := providerSpace.Subnets[e.rand.Intn(len(providerSpace.Subnets))]
   306  		vnic := oci.IPNetwork{
   307  			Ipnetwork: string(ipNet.ProviderId),
   308  			Vnicsets:  vnicSets,
   309  		}
   310  		nicName := fmt.Sprintf("%s%s", nicPrefix, strconv.Itoa(idx))
   311  		networking[nicName] = vnic
   312  		idx++
   313  	}
   314  	logger.Debugf("returning networking interfaces: %v", networking)
   315  	return networking, nil
   316  }
   317  
   318  // StartInstance is part of the InstanceBroker interface.
   319  func (o *OracleEnviron) StartInstance(ctx context.ProviderCallContext, args environs.StartInstanceParams) (*environs.StartInstanceResult, error) {
   320  	if args.ControllerUUID == "" {
   321  		return nil, errors.NotFoundf("Controller UUID")
   322  	}
   323  
   324  	series := args.Tools.OneSeries()
   325  	arches := args.Tools.Arches()
   326  
   327  	// take all instance types from the oracle cloud provider
   328  	types, err := instanceTypes(o.client)
   329  	if err != nil {
   330  		return nil, errors.Trace(err)
   331  	}
   332  
   333  	// check if we find an image that is compliant with the
   334  	// constraints provided in the oracle cloud account
   335  	if args.ImageMetadata, err = checkImageList(o.client); err != nil {
   336  		return nil, errors.Trace(err)
   337  	}
   338  
   339  	// find the best suitable instance based on
   340  	// the oracle cloud instance types,
   341  	// the images that already matched the juju constrains
   342  	spec, imagelist, err := findInstanceSpec(
   343  		o.client,
   344  		args.ImageMetadata,
   345  		types,
   346  		&envinstance.InstanceConstraint{
   347  			Region:      o.spec.Region,
   348  			Series:      series,
   349  			Arches:      arches,
   350  			Constraints: args.Constraints,
   351  		},
   352  	)
   353  	if err != nil {
   354  		return nil, errors.Trace(err)
   355  	}
   356  
   357  	tools, err := args.Tools.Match(tools.Filter{Arch: spec.Image.Arch})
   358  	if err != nil {
   359  		return nil, errors.Trace(err)
   360  	}
   361  	logger.Tracef("agent binaries: %v", tools)
   362  	if err = args.InstanceConfig.SetTools(tools); err != nil {
   363  		return nil, errors.Trace(err)
   364  	}
   365  
   366  	if err = instancecfg.FinishInstanceConfig(
   367  		args.InstanceConfig,
   368  		o.Config(),
   369  	); err != nil {
   370  		return nil, errors.Trace(err)
   371  	}
   372  
   373  	hostname, err := o.namespace.Hostname(args.InstanceConfig.MachineId)
   374  	if err != nil {
   375  		return nil, errors.Trace(err)
   376  	}
   377  
   378  	machineName := o.client.ComposeName(hostname)
   379  	imageName := o.client.ComposeName(imagelist)
   380  
   381  	tags := make([]string, 0, len(args.InstanceConfig.Tags)+1)
   382  	for k, v := range args.InstanceConfig.Tags {
   383  		if k == "" || v == "" {
   384  			continue
   385  		}
   386  		t := tagValue{k, v}
   387  		tags = append(tags, t.String())
   388  	}
   389  	tags = append(tags, machineName)
   390  
   391  	var apiPort int
   392  	var desiredStatus ociCommon.InstanceState
   393  	if args.InstanceConfig.Controller != nil {
   394  		apiPort = args.InstanceConfig.Controller.Config.APIPort()
   395  		desiredStatus = ociCommon.StateRunning
   396  	} else {
   397  		// All ports are the same so pick the first.
   398  		apiPort = args.InstanceConfig.APIInfo.Ports()[0]
   399  		desiredStatus = ociCommon.StateStarting
   400  	}
   401  
   402  	// create a new seclists
   403  	secLists, err := o.CreateMachineSecLists(
   404  		args.InstanceConfig.MachineId, apiPort)
   405  	if err != nil {
   406  		return nil, errors.Trace(err)
   407  	}
   408  	logger.Debugf("Creating vnic sets")
   409  	vnicSets, err := o.ensureVnicSet(ctx, args.InstanceConfig.MachineId, tags)
   410  	if err != nil {
   411  		return nil, errors.Trace(err)
   412  	}
   413  	// fetch instance network card configuration
   414  	logger.Debugf("Getting instance networks")
   415  	networking, err := o.getInstanceNetworks(ctx, args, secLists, []string{vnicSets.Name})
   416  	if err != nil {
   417  		return nil, errors.Trace(err)
   418  	}
   419  
   420  	logger.Debugf("Getting cloud config")
   421  	cloudcfg, err := o.getCloudInitConfig(args.InstanceConfig.Series, networking)
   422  	if err != nil {
   423  		return nil, errors.Annotate(err, "cannot create cloudinit template")
   424  	}
   425  
   426  	// compose userdata with the cloud config template
   427  	logger.Debugf("Composing userdata")
   428  	userData, err := providerinit.ComposeUserData(
   429  		args.InstanceConfig,
   430  		cloudcfg,
   431  		OracleRenderer{},
   432  	)
   433  	if err != nil {
   434  		return nil, errors.Annotate(err, "cannot make user data")
   435  	}
   436  
   437  	logger.Debugf("oracle user data: %d bytes", len(userData))
   438  
   439  	attributes := map[string]interface{}{
   440  		"userdata": string(userData),
   441  	}
   442  
   443  	// create the instance based on the instance params
   444  	instance, err := o.createInstance(oci.InstanceParams{
   445  		Instances: []oci.Instances{
   446  			{
   447  				Shape:       spec.InstanceType.Name,
   448  				Imagelist:   imageName,
   449  				Name:        machineName,
   450  				Label:       args.InstanceConfig.MachineAgentServiceName,
   451  				Hostname:    hostname,
   452  				Tags:        tags,
   453  				Attributes:  attributes,
   454  				Reverse_dns: true,
   455  				Networking:  networking,
   456  			},
   457  		},
   458  	})
   459  	if err != nil {
   460  		return nil, errors.Trace(err)
   461  	}
   462  
   463  	instance.arch = &spec.Image.Arch
   464  	instance.instType = &spec.InstanceType
   465  
   466  	machineId := instance.machine.Name
   467  	timeout := 10 * time.Minute
   468  	if err := instance.waitForMachineStatus(desiredStatus, timeout); err != nil {
   469  		return nil, errors.Trace(err)
   470  	}
   471  	logger.Infof("started instance %q", machineId)
   472  
   473  	//TODO: add config option for public IP allocation
   474  	logger.Debugf("Associating public IP to instance %q", machineId)
   475  	if err := instance.associatePublicIP(); err != nil {
   476  		return nil, errors.Trace(err)
   477  	}
   478  	result := &environs.StartInstanceResult{
   479  		Instance: instance,
   480  		Hardware: instance.hardwareCharacteristics(),
   481  	}
   482  
   483  	return result, nil
   484  }
   485  
   486  // StopInstances is part of the InstanceBroker interface.
   487  func (o *OracleEnviron) StopInstances(ctx context.ProviderCallContext, ids ...instance.Id) error {
   488  	oracleInstances, err := o.getOracleInstances(ids...)
   489  	if err == environs.ErrNoInstances {
   490  		return nil
   491  	} else if err != nil {
   492  		return err
   493  	}
   494  
   495  	logger.Debugf("terminating instances %v", ids)
   496  	if err := o.terminateInstances(oracleInstances...); err != nil {
   497  		return err
   498  	}
   499  
   500  	return nil
   501  }
   502  
   503  func (o *OracleEnviron) terminateInstances(instances ...*oracleInstance) error {
   504  	wg := sync.WaitGroup{}
   505  	wg.Add(len(instances))
   506  	errs := []error{}
   507  	instIds := []instance.Id{}
   508  	for _, oInst := range instances {
   509  		inst := oInst
   510  		go func() {
   511  			defer wg.Done()
   512  			if err := inst.deleteInstanceAndResources(true); err != nil {
   513  				if !oci.IsNotFound(err) {
   514  					instIds = append(instIds, instance.Id(inst.name))
   515  					errs = append(errs, err)
   516  				}
   517  			}
   518  		}()
   519  	}
   520  	wg.Wait()
   521  	switch len(errs) {
   522  	case 0:
   523  		return nil
   524  	case 1:
   525  		return errors.Annotatef(errs[0], "failed to stop instance %s", instIds[0])
   526  	default:
   527  		return errors.Errorf(
   528  			"failed to stop instances %s: %s",
   529  			instIds, errs,
   530  		)
   531  	}
   532  }
   533  
   534  type tagValue struct {
   535  	tag, value string
   536  }
   537  
   538  func (t *tagValue) String() string {
   539  	return fmt.Sprintf("%s=%s", t.tag, t.value)
   540  }
   541  
   542  // allControllerManagedInstances returns all instances managed by this
   543  // environment's controller, matching the optionally specified filter.
   544  func (o *OracleEnviron) allControllerManagedInstances(controllerUUID string) ([]*oracleInstance, error) {
   545  	return o.allInstances(tagValue{
   546  		tag:   tags.JujuController,
   547  		value: controllerUUID,
   548  	})
   549  }
   550  
   551  // getOracleInstances attempts to fetch information from the oracle API for the
   552  // specified IDs.
   553  func (o *OracleEnviron) getOracleInstances(ids ...instance.Id) ([]*oracleInstance, error) {
   554  	ret := make([]*oracleInstance, 0, len(ids))
   555  	resp, err := o.client.AllInstances(nil)
   556  	if err != nil {
   557  		return nil, errors.Trace(err)
   558  	}
   559  
   560  	if len(resp.Result) == 0 {
   561  		return nil, environs.ErrNoInstances
   562  	}
   563  
   564  	for _, val := range resp.Result {
   565  		for _, id := range ids {
   566  			oInst, err := newInstance(val, o)
   567  			if err != nil {
   568  				return nil, errors.Trace(err)
   569  			}
   570  			if oInst.Id() == id {
   571  				ret = append(ret, oInst)
   572  				break
   573  			}
   574  		}
   575  	}
   576  	if len(ret) < len(ids) {
   577  		return ret, environs.ErrPartialInstances
   578  	}
   579  	return ret, nil
   580  }
   581  
   582  func (o *OracleEnviron) getOracleInstancesAsMap(ids ...instance.Id) (map[string]*oracleInstance, error) {
   583  	instances, err := o.getOracleInstances(ids...)
   584  	if err != nil {
   585  		return map[string]*oracleInstance{}, errors.Trace(err)
   586  	}
   587  	ret := map[string]*oracleInstance{}
   588  	for _, val := range instances {
   589  		ret[string(val.Id())] = val
   590  	}
   591  	return ret, nil
   592  }
   593  
   594  // AllInstances is part of the InstanceBroker interface.
   595  func (o *OracleEnviron) AllInstances(ctx context.ProviderCallContext) ([]envinstance.Instance, error) {
   596  	tagFilter := tagValue{tags.JujuModel, o.Config().UUID()}
   597  	all, err := o.allInstances(tagFilter)
   598  	if err != nil {
   599  		return nil, err
   600  	}
   601  
   602  	ret := make([]envinstance.Instance, len(all))
   603  	for i, val := range all {
   604  		ret[i] = val
   605  	}
   606  	return ret, nil
   607  }
   608  
   609  func (o *OracleEnviron) allInstances(tagFilter tagValue) ([]*oracleInstance, error) {
   610  	filter := []oci.Filter{
   611  		{
   612  			Arg:   "tags",
   613  			Value: tagFilter.String(),
   614  		},
   615  	}
   616  	logger.Infof("Looking for instances with tags: %v", filter)
   617  	resp, err := o.client.AllInstances(filter)
   618  	if err != nil {
   619  		return nil, err
   620  	}
   621  
   622  	n := len(resp.Result)
   623  	instances := make([]*oracleInstance, 0, n)
   624  	for _, val := range resp.Result {
   625  		oracleInstance, err := newInstance(val, o)
   626  		if err != nil {
   627  			return nil, errors.Trace(err)
   628  		}
   629  		instances = append(instances, oracleInstance)
   630  	}
   631  
   632  	return instances, nil
   633  }
   634  
   635  // MaintainInstance is part of the InstanceBroker interface.
   636  func (o *OracleEnviron) MaintainInstance(ctx context.ProviderCallContext, args environs.StartInstanceParams) error {
   637  	return nil
   638  }
   639  
   640  // Config is part of the Environ interface.
   641  func (o *OracleEnviron) Config() *config.Config {
   642  	o.mutex.Lock()
   643  	defer o.mutex.Unlock()
   644  	return o.cfg
   645  }
   646  
   647  // ConstraintsValidator is part of the environs.Environ interface.
   648  func (o *OracleEnviron) ConstraintsValidator(ctx context.ProviderCallContext) (constraints.Validator, error) {
   649  	// list of unsupported oracle provider constraints
   650  	unsupportedConstraints := []string{
   651  		constraints.Container,
   652  		constraints.CpuPower,
   653  		constraints.RootDisk,
   654  		constraints.VirtType,
   655  	}
   656  
   657  	// we choose to use the default validator implementation
   658  	validator := constraints.NewValidator()
   659  	// we must feed the validator that the oracle cloud
   660  	// provider does not support these constraints
   661  	validator.RegisterUnsupported(unsupportedConstraints)
   662  	validator.RegisterVocabulary(constraints.Arch, []string{arch.I386, arch.AMD64})
   663  	logger.Infof("Returning constraints validator: %v", validator)
   664  	return validator, nil
   665  }
   666  
   667  // SetConfig is part of the environs.Environ interface.
   668  func (o *OracleEnviron) SetConfig(cfg *config.Config) error {
   669  	o.mutex.Lock()
   670  	defer o.mutex.Unlock()
   671  
   672  	var old *config.Config
   673  	if o.cfg != nil {
   674  		old = o.cfg
   675  	}
   676  	if err := config.Validate(cfg, old); err != nil {
   677  		return errors.Trace(err)
   678  	}
   679  	o.cfg = cfg
   680  	return nil
   681  }
   682  
   683  func (o *OracleEnviron) Details(id instance.Id) (ociResponse.Instance, error) {
   684  	inst, err := o.getOracleInstances(id)
   685  	if err != nil {
   686  		return ociResponse.Instance{}, err
   687  	}
   688  
   689  	return inst[0].machine, nil
   690  }
   691  
   692  // Instances is part of the environs.Environ interface.
   693  func (o *OracleEnviron) Instances(ctx context.ProviderCallContext, ids []instance.Id) ([]envinstance.Instance, error) {
   694  	if len(ids) == 0 {
   695  		return nil, nil
   696  	}
   697  	instances, err := o.getOracleInstances(ids...)
   698  	if err != nil {
   699  		return nil, err
   700  	}
   701  
   702  	ret := []envinstance.Instance{}
   703  	for _, val := range instances {
   704  		ret = append(ret, val)
   705  	}
   706  	return ret, nil
   707  }
   708  
   709  // ControllerInstances is part of the environs.Environ interface.
   710  func (o *OracleEnviron) ControllerInstances(ctx context.ProviderCallContext, controllerUUID string) ([]instance.Id, error) {
   711  	instances, err := o.allControllerManagedInstances(controllerUUID)
   712  	if err != nil {
   713  		return nil, errors.Trace(err)
   714  	}
   715  
   716  	filter := tagValue{tags.JujuIsController, "true"}
   717  	ids := make([]instance.Id, 0, 1)
   718  	for _, val := range instances {
   719  		found := false
   720  		for _, tag := range val.machine.Tags {
   721  			if tag == filter.String() {
   722  				found = true
   723  				break
   724  			}
   725  		}
   726  		if found == false {
   727  			continue
   728  		}
   729  		ids = append(ids, val.Id())
   730  	}
   731  
   732  	if len(ids) == 0 {
   733  		return nil, environs.ErrNoInstances
   734  	}
   735  
   736  	return ids, nil
   737  }
   738  
   739  // Destroy is part of the environs.Environ interface.
   740  func (o *OracleEnviron) Destroy(ctx context.ProviderCallContext) error {
   741  	return common.Destroy(o, ctx)
   742  }
   743  
   744  // DestroyController is part of the environs.Environ interface.
   745  func (o *OracleEnviron) DestroyController(ctx context.ProviderCallContext, controllerUUID string) error {
   746  	err := o.Destroy(ctx)
   747  	if err != nil {
   748  		logger.Errorf("Failed to destroy environment through controller: %s", errors.Trace(err))
   749  	}
   750  	instances, err := o.allControllerManagedInstances(controllerUUID)
   751  	if err != nil {
   752  		if err == environs.ErrNoInstances {
   753  			return nil
   754  		}
   755  		return errors.Trace(err)
   756  	}
   757  	ids := make([]instance.Id, len(instances))
   758  	for i, val := range instances {
   759  		ids[i] = val.Id()
   760  	}
   761  	return o.StopInstances(ctx, ids...)
   762  }
   763  
   764  // Provider is part of the environs.Environ interface.
   765  func (o *OracleEnviron) Provider() environs.EnvironProvider {
   766  	return o.p
   767  }
   768  
   769  // PrecheckInstance is part of the environs.Environ interface.
   770  func (o *OracleEnviron) PrecheckInstance(context.ProviderCallContext, environs.PrecheckInstanceParams) error {
   771  	return nil
   772  }
   773  
   774  // InstanceTypes is part of the environs.InstanceTypesFetcher interface.
   775  func (o *OracleEnviron) InstanceTypes(context.ProviderCallContext, constraints.Value) (envinstance.InstanceTypesWithCostMetadata, error) {
   776  	var i envinstance.InstanceTypesWithCostMetadata
   777  	return i, nil
   778  }
   779  
   780  // createInstance creates a new instance inside the oracle infrastructure
   781  func (e *OracleEnviron) createInstance(params oci.InstanceParams) (*oracleInstance, error) {
   782  	if len(params.Instances) > 1 {
   783  		return nil, errors.NotSupportedf("launching multiple instances")
   784  	}
   785  
   786  	logger.Debugf("running createInstance")
   787  	resp, err := e.client.CreateInstance(params)
   788  	if err != nil {
   789  		return nil, errors.Trace(err)
   790  	}
   791  
   792  	instance, err := newInstance(resp.Instances[0], e)
   793  	if err != nil {
   794  		return nil, errors.Trace(err)
   795  	}
   796  
   797  	return instance, nil
   798  }