<commit_before>// Copyright 2011, 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package ec2

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"sync"
	"time"

	"launchpad.net/goamz/aws"
	"launchpad.net/goamz/ec2"
	"launchpad.net/goamz/s3"
	"launchpad.net/loggo"

	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/cloudinit"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/environs/imagemetadata"
	"launchpad.net/juju-core/environs/instances"
	"launchpad.net/juju-core/environs/simplestreams"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/provider"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api"
	"launchpad.net/juju-core/tools"
	"launchpad.net/juju-core/utils"
)

var logger = loggo.GetLogger("juju.provider.ec2")

// Use shortAttempt to poll for short-term events.
var shortAttempt = utils.AttemptStrategy{
	Total: 5 * time.Second,
	Delay: 200 * time.Millisecond,
}

func init() {
	environs.RegisterProvider("ec2", environProvider{})
}

type environProvider struct{}

var providerInstance environProvider

type environ struct {
	name string

	// ecfgMutex protects the *Unlocked fields below.
	ecfgMutex             sync.Mutex
	ecfgUnlocked          *environConfig
	ec2Unlocked           *ec2.EC2
	s3Unlocked            *s3.S3
	storageUnlocked       environs.Storage
	publicStorageUnlocked environs.StorageReader // optional.
}

var _ environs.Environ = (*environ)(nil)

type ec2Instance struct {
	e *environ
	*ec2.Instance
	arch     *string
	instType *instances.InstanceType
}

func (inst *ec2Instance) String() string {
	return inst.InstanceId
}

var _ instance.Instance = (*ec2Instance)(nil)

func (inst *ec2Instance) Id() instance.Id {
	return instance.Id(inst.InstanceId)
}

func (inst *ec2Instance) Status() string {
	return inst.State.Name
}

func (inst *ec2Instance) hardwareCharacteristics() *instance.HardwareCharacteristics {
	hc := &instance.HardwareCharacteristics{Arch: inst.arch}
	if inst.instType != nil {
		hc.Mem = &inst.instType.Mem
		hc.RootDisk = &inst.instType.RootDisk
		hc.CpuCores = &inst.instType.CpuCores
		hc.CpuPower = inst.instType.CpuPower
	}
	return hc
}

// refreshInstance requeries the Instance details over the ec2 api
func (inst *ec2Instance) refreshInstance() error {
	insts, err := inst.e.Instances([]instance.Id{inst.Id()})
	if err != nil {
		return err
	}
	inst.Instance = insts[0].(*ec2Instance).Instance
	return nil
}

// Addresses implements instance.Addresses() returning generic address
// details for the instance, and requerying the ec2 api if required.
func (inst *ec2Instance) Addresses() ([]instance.Address, error) {
	var addresses []instance.Address
	// TODO(gz): Stop relying on this requerying logic, maybe remove error
	if inst.Instance.DNSName == "" {
		// Fetch the instance information again, in case
		// the DNS information has become available.
		err := inst.refreshInstance()
		if err != nil {
			return nil, err
		}
	}
	possibleAddresses := []instance.Address{
		{
			Value:        inst.Instance.DNSName,
			Type:         instance.HostName,
			NetworkScope: instance.NetworkPublic,
		},
		{
			Value:        inst.Instance.PrivateDNSName,
			Type:         instance.HostName,
			NetworkScope: instance.NetworkCloudLocal,
		},
		{
			Value:        inst.Instance.IPAddress,
			Type:         instance.Ipv4Address,
			NetworkScope: instance.NetworkPublic,
		},
		{
			Value:        inst.Instance.PrivateIPAddress,
			Type:         instance.Ipv4Address,
			NetworkScope: instance.NetworkCloudLocal,
		},
	}
	for _, address := range possibleAddresses {
		if address.Value != "" {
			addresses = append(addresses, address)
		}
	}
	return addresses, nil
}

func (inst *ec2Instance) DNSName() (string, error) {
	addresses, err := inst.Addresses()
	if err != nil {
		return "", err
	}
	addr := instance.SelectPublicAddress(addresses)
	if addr == "" {
		return "", instance.ErrNoDNSName
	}
	return addr, nil

}

func (inst *ec2Instance) WaitDNSName() (string, error) {
	return provider.WaitDNSName(inst)
}

func (p environProvider) BoilerplateConfig() string {
	return `
## https://juju.ubuntu.com/get-started/amazon/
amazon:
  type: ec2
  admin-secret: {{rand}}
  # globally unique S3 bucket name
  control-bucket: juju-{{rand}}
  # override if your workstation is running a different series to which you are deploying
  # default-series: precise
  # region defaults to us-east-1, override if required
  # region: us-east-1
  # Usually set via the env variable AWS_ACCESS_KEY_ID, but can be specified here
  # access-key: <secret>
  # Usually set via the env variable AWS_SECRET_ACCESS_KEY, but can be specified here
  # secret-key: <secret>

`[1:]
}

func (p environProvider) Open(cfg *config.Config) (environs.Environ, error) {
	logger.Infof("opening environment %q", cfg.Name())<commit_msg>Update ec2 boilerplate doc link<commit_after>// Copyright 2011, 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package ec2

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"sync"
	"time"

	"launchpad.net/goamz/aws"
	"launchpad.net/goamz/ec2"
	"launchpad.net/goamz/s3"
	"launchpad.net/loggo"

	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/cloudinit"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/environs/imagemetadata"
	"launchpad.net/juju-core/environs/instances"
	"launchpad.net/juju-core/environs/simplestreams"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/provider"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api"
	"launchpad.net/juju-core/tools"
	"launchpad.net/juju-core/utils"
)

var logger = loggo.GetLogger("juju.provider.ec2")

// Use shortAttempt to poll for short-term events.
var shortAttempt = utils.AttemptStrategy{
	Total: 5 * time.Second,
	Delay: 200 * time.Millisecond,
}

func init() {
	environs.RegisterProvider("ec2", environProvider{})
}

type environProvider struct{}

var providerInstance environProvider

type environ struct {
	name string

	// ecfgMutex protects the *Unlocked fields below.
	ecfgMutex             sync.Mutex
	ecfgUnlocked          *environConfig
	ec2Unlocked           *ec2.EC2
	s3Unlocked            *s3.S3
	storageUnlocked       environs.Storage
	publicStorageUnlocked environs.StorageReader // optional.
}

var _ environs.Environ = (*environ)(nil)

type ec2Instance struct {
	e *environ
	*ec2.Instance
	arch     *string
	instType *instances.InstanceType
}

func (inst *ec2Instance) String() string {
	return inst.InstanceId
}

var _ instance.Instance = (*ec2Instance)(nil)

func (inst *ec2Instance) Id() instance.Id {
	return instance.Id(inst.InstanceId)
}

func (inst *ec2Instance) Status() string {
	return inst.State.Name
}

func (inst *ec2Instance) hardwareCharacteristics() *instance.HardwareCharacteristics {
	hc := &instance.HardwareCharacteristics{Arch: inst.arch}
	if inst.instType != nil {
		hc.Mem = &inst.instType.Mem
		hc.RootDisk = &inst.instType.RootDisk
		hc.CpuCores = &inst.instType.CpuCores
		hc.CpuPower = inst.instType.CpuPower
	}
	return hc
}

// refreshInstance requeries the Instance details over the ec2 api
func (inst *ec2Instance) refreshInstance() error {
	insts, err := inst.e.Instances([]instance.Id{inst.Id()})
	if err != nil {
		return err
	}
	inst.Instance = insts[0].(*ec2Instance).Instance
	return nil
}

// Addresses implements instance.Addresses() returning generic address
// details for the instance, and requerying the ec2 api if required.
func (inst *ec2Instance) Addresses() ([]instance.Address, error) {
	var addresses []instance.Address
	// TODO(gz): Stop relying on this requerying logic, maybe remove error
	if inst.Instance.DNSName == "" {
		// Fetch the instance information again, in case
		// the DNS information has become available.
		err := inst.refreshInstance()
		if err != nil {
			return nil, err
		}
	}
	possibleAddresses := []instance.Address{
		{
			Value:        inst.Instance.DNSName,
			Type:         instance.HostName,
			NetworkScope: instance.NetworkPublic,
		},
		{
			Value:        inst.Instance.PrivateDNSName,
			Type:         instance.HostName,
			NetworkScope: instance.NetworkCloudLocal,
		},
		{
			Value:        inst.Instance.IPAddress,
			Type:         instance.Ipv4Address,
			NetworkScope: instance.NetworkPublic,
		},
		{
			Value:        inst.Instance.PrivateIPAddress,
			Type:         instance.Ipv4Address,
			NetworkScope: instance.NetworkCloudLocal,
		},
	}
	for _, address := range possibleAddresses {
		if address.Value != "" {
			addresses = append(addresses, address)
		}
	}
	return addresses, nil
}

func (inst *ec2Instance) DNSName() (string, error) {
	addresses, err := inst.Addresses()
	if err != nil {
		return "", err
	}
	addr := instance.SelectPublicAddress(addresses)
	if addr == "" {
		return "", instance.ErrNoDNSName
	}
	return addr, nil

}

func (inst *ec2Instance) WaitDNSName() (string, error) {
	return provider.WaitDNSName(inst)
}

func (p environProvider) BoilerplateConfig() string {
	return `
## https://juju.ubuntu.com/docs/config-aws.html
amazon:
  type: ec2
  admin-secret: {{rand}}
  # globally unique S3 bucket name
  control-bucket: juju-{{rand}}
  # override if your workstation is running a different series to which you are deploying
  # default-series: precise
  # region defaults to us-east-1, override if required
  # region: us-east-1
  # Usually set via the env variable AWS_ACCESS_KEY_ID, but can be specified here
  # access-key: <secret>
  # Usually set via the env variable AWS_SECRET_ACCESS_KEY, but can be specified here
  # secret-key: <secret>

`[1:]
}

func (p environProvider) Open(cfg *config.Config) (environs.Environ, error) {
	logger.Infof("opening environment %q", cfg.Name())
{'fraction_non_alphanumeric': 0.11850797845309483, 'fraction_numerical': 0.010265270860859843, 'mean_word_length': 3.8544647261963494, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1159688', 'n_tokens_mistral': 3557, 'n_tokens_neox': 3307, 'n_words': 785}