<commit_before>package exoscale

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
	"text/template"
	"time"

	"github.com/codegangsta/cli"
	"github.com/docker/machine/drivers"
	"github.com/docker/machine/log"
	"github.com/docker/machine/state"
	"github.com/pyr/egoscale/src/egoscale"
)

type Driver struct {
	URL              string
	ApiKey           string
	ApiSecretKey     string
	InstanceProfile  string
	DiskSize         int
	Image            string
	SecurityGroup    string
	AvailabilityZone string
	MachineName      string
	KeyPair          string
	IPAddress        string
	PublicKey        string
	Id               string
	CaCertPath       string
	PrivateKeyPath   string
	DriverKeyPath    string
	SwarmMaster      bool
	SwarmHost        string
	SwarmDiscovery   string
	storePath        string
}

func init() {
	drivers.Register("exoscale", &drivers.RegisteredDriver{
		New:            NewDriver,
		GetCreateFlags: GetCreateFlags,
	})
}

// RegisterCreateFlags registers the flags this driver adds to
// "docker hosts create"
func GetCreateFlags() []cli.Flag {
	return []cli.Flag{
		cli.StringFlag{
			EnvVar: "EXOSCALE_ENDPOINT",
			Name:   "exoscale-url",
			Usage:  "exoscale API endpoint",
		},
		cli.StringFlag{
			EnvVar: "EXOSCALE_API_KEY",
			Name:   "exoscale-api-key",
			Usage:  "exoscale API key",
		},
		cli.StringFlag{
			EnvVar: "EXOSCALE_API_SECRET",
			Name:   "exoscale-api-secret-key",
			Usage:  "exoscale API secret key",
		},
		cli.StringFlag{
			EnvVar: "EXOSCALE_INSTANCE_PROFILE",
			Name:   "exoscale-instance-profile",
			Value:  "small",
			Usage:  "exoscale instance profile (small, medium, large, ...)",
		},
		cli.IntFlag{
			EnvVar: "EXOSCALE_DISK_SIZE",
			Name:   "exoscale-disk-size",
			Value:  50,
			Usage:  "exoscale disk size (10, 50, 100, 200, 400)",
		},
		cli.StringFlag{
			EnvVar: "EXSOCALE_IMAGE",
			Name:   "exoscale-image",
			Value:  "ubuntu-14.04",
			Usage:  "exoscale image template",
		},
		cli.StringFlag{
			EnvVar: "EXOSCALE_SECURITY_GROUP",
			Name:   "exoscale-security-group",
			Value:  "docker-machine",
			Usage:  "exoscale security group",
		},
		cli.StringFlag{
			EnvVar: "EXOSCALE_AVAILABILITY_ZONE",
			Name:   "exoscale-availability-zone",
			Value:  "ch-gva-2",
			Usage:  "exoscale availibility zone",
		},
	}
}

func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) {
	return &Driver{MachineName: machineName, storePath: storePath, CaCertPath: caCert, PrivateKeyPath: privateKey}, nil
}

func (d *Driver) AuthorizePort(ports []*drivers.Port) error {
	return nil
}

func (d *Driver) DeauthorizePort(ports []*drivers.Port) error {
	return nil
}

func (d *Driver) GetMachineName() string {
	return d.MachineName
}

func (d *Driver) GetSSHHostname() (string, error) {
	return d.GetIP()
}

func (d *Driver) GetSSHKeyPath() string {
	return filepath.Join(d.storePath, "id_rsa")
}

func (d *Driver) GetSSHPort() (int, error) {
	return 22, nil
}

func (d *Driver) GetSSHUsername() string {
	return "ubuntu"
}

func (d *Driver) DriverName() string {
	return "exoscale"
}

func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
	d.URL = flags.String("exoscale-endpoint")
	d.ApiKey = flags.String("exoscale-api-key")
	d.ApiSecretKey = flags.String("exoscale-api-secret-key")
	d.InstanceProfile = flags.String("exoscale-instance-profile")
	d.DiskSize = flags.Int("exoscale-disk-size")
	d.Image = flags.String("exoscale-image")
	d.SecurityGroup = flags.String("exoscale-security-group")
	d.AvailabilityZone = flags.String("exoscale-availability-zone")
	d.SwarmMaster = flags.Bool("swarm-master")
	d.SwarmHost = flags.String("swarm-host")
	d.SwarmDiscovery = flags.String("swarm-discovery")

	if d.URL == "" {
		d.URL = "https://api.exoscale.ch/compute"
	}
	if d.ApiKey == "" || d.ApiSecretKey == "" {
		return fmt.Errorf("Please specify an API key (--exoscale-api-key) and an API secret key (--exoscale-api-secret-key).")
	}

	return nil
}

func (d *Driver) GetURL() (string, error) {
	ip, err := d.GetIP()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("tcp://%s:2376", ip), nil
}

func (d *Driver) GetIP() (string, error) {
	if d.IPAddress == "" {
		return "", fmt.Errorf("IP address is not set")
	}
	return d.IPAddress, nil
}

func (d *Driver) GetState() (state.State, error) {
	client := egoscale.NewClient(d.URL, d.ApiKey, d.ApiSecretKey)
	vm, err := client.GetVirtualMachine(d.Id)
	if err != nil {
		return state.Error, err
	}
	switch vm.State {
	case "Starting":
		return state.Starting, nil
	case "Running":
		return state.Running, nil
	case "Stopping":
		return state.Running, nil
	case "Stopped":
		return state.Stopped, nil
	case "Destroyed":
		return state.Stopped, nil
	case "Expunging":
		return state.Stopped, nil
	case "Migrating":
		return state.Paused, nil
	case "Error":
		return state.Error, nil
	case "Unknown":
		return state.Error, nil
	case "Shutdowned":
		return state.Stopped, nil
	}
	return state.None, nil
}

func (d *Driver) PreCreateCheck() error {
	return nil
}

func (d *Driver) Create() error {
	log.Infof("Querying exoscale for the requested parameters...")
	client := egoscale.NewClient(d.URL, d.ApiKey, d.ApiSecretKey)
	topology, err := client.GetTopology()
	if err != nil {
		return err
	}

	// Availability zone UUID
	zone, ok := topology.Zones[d.AvailabilityZone]
	if !ok {
		return fmt.Errorf("Availability zone %v doesn't exist",
			d.AvailabilityZone)
	}
	log.Debugf("Availability zone %v = %s", d.AvailabilityZone, zone)

	// Image UUID
	var tpl string
	images, ok := topology.Images[strings.ToLower(d.Image)]
	if ok {
		tpl, ok = images[d.DiskSize]
	}
	if !ok {
		return fmt.Errorf("Unable to find image %v with size %d",
			d.Image, d.DiskSize)
	}
	log.Debugf("Image %v(%d) = %s", d.Image, d.DiskSize, tpl)

	// Profile UUID
	profile, ok := topology.Profiles[strings.ToLower(d.InstanceProfile)]
	if !ok {
		return fmt.Errorf("Unable to find the %s profile",
			d.InstanceProfile)
	}
	log.Debugf("Profile %v = %s", d.InstanceProfile, profile)

	// Security group
	sg, ok := topology.SecurityGroups[d.SecurityGroup]
	if !ok {
		log.Infof("Security group %v does not exist, create it",
			d.SecurityGroup)
		rules := []egoscale.SecurityGroupRule{
			{
				SecurityGroupId: "",
				Cidr:            "0.0.0.0/0",
				Protocol:        "TCP",
				Port:            22,
			},
			{
				SecurityGroupId: "",
				Cidr:            "0.0.0.0/0",
				Protocol:        "TCP",
				Port:            2376,
			},
			{
				SecurityGroupId: "",
				Cidr:            "0.0.0.0/0",
				Protocol:        "TCP",
				Port:            3376,
			},
			{
				SecurityGroupId: "",
				Cidr:            "0.0.0.0/0",
				Protocol:        "ICMP",
				IcmpType:        8,
				IcmpCode:        0,
			},
		}
		sgresp, err := client.CreateSecurityGroupWithRules(d.SecurityGroup,
			rules,
			make([]egoscale.SecurityGroupRule, 0, 0))
		if err != nil {
			return err
		}
		sg = sgresp.Id
	}
	log.Debugf("Security group %v = %s", d.SecurityGroup, sg)

	log.Infof("Generate an SSH keypair...")
	keypairName := fmt.Sprintf("docker-machine-%s", d.MachineName)
	kpresp, err := client.CreateKeypair(keypairName)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(d.GetSSHKeyPath(), []byte(kpresp.Privatekey), 0600)
	if err != nil {
		return err
	}
	d.KeyPair = keypairName

	log.Infof("Spawn exoscale host...")

	userdata, err := d.getCloudInit()
	if err != nil {
		return err
	}
	log.Debugf("Using the following cloud-init file:")
	log.Debugf("%s", userdata)

	machineProfile := egoscale.MachineProfile{
		Template:        tpl,
		ServiceOffering: profile,
		SecurityGroups:  []string{sg},
		Userdata:        userdata,
		Zone:            zone,
		Keypair:         d.KeyPair,
		Name:            d.MachineName,
	}

	cvmresp, err := client.CreateVirtualMachine(machineProfile)
	if err != nil {
		return err
	}

	vm, err := d.waitForVM(client, cvmresp)
	if err != nil {
		return err
	}
	d.IPAddress = vm.Nic[0].Ipaddress
	d.Id = vm.Id

	return nil
}

func (d *Driver) Start() error {
	vmstate, err := d.GetState()
	if err != nil {
		return err
	}
	if vmstate == state.Running || vmstate == state.Starting {
		log.Infof("Host is already running or starting")
		return nil
	}

	client := egoscale.NewClient(d.URL, d.ApiKey, d.ApiSecretKey)
	svmresp, err := client.StartVirtualMachine(d.Id)
	if err != nil {
		return err
	}
	_, err = d.waitForVM(client, svmresp)
	if err != nil {
		return err
	}
	return nil
}

func (d *Driver) Stop() error {
	vmstate, err := d.GetState()
	if err != nil {
		return err
	}
	if vmstate == state.Stopped {
		log.Infof("Host is already stopped")
		return nil
	}

	client := egoscale.NewClient(d.URL, d.ApiKey, d.ApiSecretKey)
	svmresp, err := client.StopVirtualMachine(d.Id)
	if err != nil {
		return err
	}
	if _, err = d.waitForVM(client, svmresp); err != nil {
		return err
	}
	return nil
}

func (d *Driver) Remove() error {
	client := egoscale.NewClient(d.URL, d.ApiKey, d.ApiSecretKey)

	// Destroy the SSH key
	if _, err := client.DeleteKeypair(d.KeyPair); err != nil {
		return err
	}

	// Destroy the virtual machine
	dvmresp, err := client.DestroyVirtualMachine(d.Id)
	if err != nil {
		return err
	}
	_, err = d.waitForVM(client, dvmresp)
	if err != nil {
		return err
	}
	return nil
}

func (d *Driver) Restart() error {
	vmstate, err := d.GetState()
	if err != nil {
		return err
	}
	if vmstate == state.Stopped {
		return fmt.Errorf("Host is stopped, use start command to start it")
	}

	client := egoscale.NewClient(d.URL, d.ApiKey, d.ApiSecretKey)
	svmresp, err := client.RebootVirtualMachine(d.Id)
	if err != nil {
		return err
	}
	if _, err = d.waitForVM(client, svmresp); err != nil {
		return err
	}

	return nil
}

func (d *Driver) Kill() error {
	return d.Stop()
}

func (d *Driver) waitForVM(client *egoscale.Client, jobid string) (*egoscale.DeployVirtualMachineResponse, error) {
	log.Infof("Waiting for VM...")
	maxRepeats := 60
	i := 0
	var resp *egoscale.QueryAsyncJobResultResponse
	var err error
WaitLoop:
	for ; i < maxRepeats; i++ {
		resp, err = client.PollAsyncJob(jobid)
		if err != nil {
			return nil, err
		}
		switch resp.Jobstatus {
		case 0: // Job is still in progress
			continue
		case 1: // Job has successfully completed
			break WaitLoop
		case 2: // Job has failed to complete
			return nil, fmt.Errorf("Operation failed to complete")
		default: // Some other code
			continue
		}
		time.Sleep(2 * time.Second)
	}
	if i == maxRepeats {
		return nil, fmt.Errorf("Timeout while waiting for VM")
	}
	vm, err := client.AsyncToVirtualMachine(*resp)
	if err != nil {
		return nil, err
	}

	return vm, nil
}

// Build a cloud-init user data string that will install and run
// docker.
func (d *Driver) getCloudInit() (string, error) {
	const tpl = `#cloud-config
manage_etc_hosts: true
fqdn: {{ .MachineName }}
resize_rootfs: true
`
	var buffer bytes.Buffer

	tmpl, err := template.New("cloud-init").Parse(tpl)
	if err != nil {
		return "", err
	}
	err = tmpl.Execute(&buffer, d)
	if err != nil {
		return "", err
	}
	return buffer.String(), nil
}
<commit_msg>exoscale: ensure we sleep a bit while waiting for the VM to be ready<commit_after>package exoscale

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
	"text/template"
	"time"

	"github.com/codegangsta/cli"
	"github.com/docker/machine/drivers"
	"github.com/docker/machine/log"
	"github.com/docker/machine/state"
	"github.com/pyr/egoscale/src/egoscale"
)

type Driver struct {
	URL              string
	ApiKey           string
	ApiSecretKey     string
	InstanceProfile  string
	DiskSize         int
	Image            string
	SecurityGroup    string
	AvailabilityZone string
	MachineName      string
	KeyPair          string
	IPAddress        string
	PublicKey        string
	Id               string
	CaCertPath       string
	PrivateKeyPath   string
	DriverKeyPath    string
	SwarmMaster      bool
	SwarmHost        string
	SwarmDiscovery   string
	storePath        string
}

func init() {
	drivers.Register("exoscale", &drivers.RegisteredDriver{
		New:            NewDriver,
		GetCreateFlags: GetCreateFlags,
	})
}

// RegisterCreateFlags registers the flags this driver adds to
// "docker hosts create"
func GetCreateFlags() []cli.Flag {
	return []cli.Flag{
		cli.StringFlag{
			EnvVar: "EXOSCALE_ENDPOINT",
			Name:   "exoscale-url",
			Usage:  "exoscale API endpoint",
		},
		cli.StringFlag{
			EnvVar: "EXOSCALE_API_KEY",
			Name:   "exoscale-api-key",
			Usage:  "exoscale API key",
		},
		cli.StringFlag{
			EnvVar: "EXOSCALE_API_SECRET",
			Name:   "exoscale-api-secret-key",
			Usage:  "exoscale API secret key",
		},
		cli.StringFlag{
			EnvVar: "EXOSCALE_INSTANCE_PROFILE",
			Name:   "exoscale-instance-profile",
			Value:  "small",
			Usage:  "exoscale instance profile (small, medium, large, ...)",
		},
		cli.IntFlag{
			EnvVar: "EXOSCALE_DISK_SIZE",
			Name:   "exoscale-disk-size",
			Value:  50,
			Usage:  "exoscale disk size (10, 50, 100, 200, 400)",
		},
		cli.StringFlag{
			EnvVar: "EXSOCALE_IMAGE",
			Name:   "exoscale-image",
			Value:  "ubuntu-14.04",
			Usage:  "exoscale image template",
		},
		cli.StringFlag{
			EnvVar: "EXOSCALE_SECURITY_GROUP",
			Name:   "exoscale-security-group",
			Value:  "docker-machine",
			Usage:  "exoscale security group",
		},
		cli.StringFlag{
			EnvVar: "EXOSCALE_AVAILABILITY_ZONE",
			Name:   "exoscale-availability-zone",
			Value:  "ch-gva-2",
			Usage:  "exoscale availibility zone",
		},
	}
}

func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) {
	return &Driver{MachineName: machineName, storePath: storePath, CaCertPath: caCert, PrivateKeyPath: privateKey}, nil
}

func (d *Driver) AuthorizePort(ports []*drivers.Port) error {
	return nil
}

func (d *Driver) DeauthorizePort(ports []*drivers.Port) error {
	return nil
}

func (d *Driver) GetMachineName() string {
	return d.MachineName
}

func (d *Driver) GetSSHHostname() (string, error) {
	return d.GetIP()
}

func (d *Driver) GetSSHKeyPath() string {
	return filepath.Join(d.storePath, "id_rsa")
}

func (d *Driver) GetSSHPort() (int, error) {
	return 22, nil
}

func (d *Driver) GetSSHUsername() string {
	return "ubuntu"
}

func (d *Driver) DriverName() string {
	return "exoscale"
}

func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
	d.URL = flags.String("exoscale-endpoint")
	d.ApiKey = flags.String("exoscale-api-key")
	d.ApiSecretKey = flags.String("exoscale-api-secret-key")
	d.InstanceProfile = flags.String("exoscale-instance-profile")
	d.DiskSize = flags.Int("exoscale-disk-size")
	d.Image = flags.String("exoscale-image")
	d.SecurityGroup = flags.String("exoscale-security-group")
	d.AvailabilityZone = flags.String("exoscale-availability-zone")
	d.SwarmMaster = flags.Bool("swarm-master")
	d.SwarmHost = flags.String("swarm-host")
	d.SwarmDiscovery = flags.String("swarm-discovery")

	if d.URL == "" {
		d.URL = "https://api.exoscale.ch/compute"
	}
	if d.ApiKey == "" || d.ApiSecretKey == "" {
		return fmt.Errorf("Please specify an API key (--exoscale-api-key) and an API secret key (--exoscale-api-secret-key).")
	}

	return nil
}

func (d *Driver) GetURL() (string, error) {
	ip, err := d.GetIP()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("tcp://%s:2376", ip), nil
}

func (d *Driver) GetIP() (string, error) {
	if d.IPAddress == "" {
		return "", fmt.Errorf("IP address is not set")
	}
	return d.IPAddress, nil
}

func (d *Driver) GetState() (state.State, error) {
	client := egoscale.NewClient(d.URL, d.ApiKey, d.ApiSecretKey)
	vm, err := client.GetVirtualMachine(d.Id)
	if err != nil {
		return state.Error, err
	}
	switch vm.State {
	case "Starting":
		return state.Starting, nil
	case "Running":
		return state.Running, nil
	case "Stopping":
		return state.Running, nil
	case "Stopped":
		return state.Stopped, nil
	case "Destroyed":
		return state.Stopped, nil
	case "Expunging":
		return state.Stopped, nil
	case "Migrating":
		return state.Paused, nil
	case "Error":
		return state.Error, nil
	case "Unknown":
		return state.Error, nil
	case "Shutdowned":
		return state.Stopped, nil
	}
	return state.None, nil
}

func (d *Driver) PreCreateCheck() error {
	return nil
}

func (d *Driver) Create() error {
	log.Infof("Querying exoscale for the requested parameters...")
	client := egoscale.NewClient(d.URL, d.ApiKey, d.ApiSecretKey)
	topology, err := client.GetTopology()
	if err != nil {
		return err
	}

	// Availability zone UUID
	zone, ok := topology.Zones[d.AvailabilityZone]
	if !ok {
		return fmt.Errorf("Availability zone %v doesn't exist",
			d.AvailabilityZone)
	}
	log.Debugf("Availability zone %v = %s", d.AvailabilityZone, zone)

	// Image UUID
	var tpl string
	images, ok := topology.Images[strings.ToLower(d.Image)]
	if ok {
		tpl, ok = images[d.DiskSize]
	}
	if !ok {
		return fmt.Errorf("Unable to find image %v with size %d",
			d.Image, d.DiskSize)
	}
	log.Debugf("Image %v(%d) = %s", d.Image, d.DiskSize, tpl)

	// Profile UUID
	profile, ok := topology.Profiles[strings.ToLower(d.InstanceProfile)]
	if !ok {
		return fmt.Errorf("Unable to find the %s profile",
			d.InstanceProfile)
	}
	log.Debugf("Profile %v = %s", d.InstanceProfile, profile)

	// Security group
	sg, ok := topology.SecurityGroups[d.SecurityGroup]
	if !ok {
		log.Infof("Security group %v does not exist, create it",
			d.SecurityGroup)
		rules := []egoscale.SecurityGroupRule{
			{
				SecurityGroupId: "",
				Cidr:            "0.0.0.0/0",
				Protocol:        "TCP",
				Port:            22,
			},
			{
				SecurityGroupId: "",
				Cidr:            "0.0.0.0/0",
				Protocol:        "TCP",
				Port:            2376,
			},
			{
				SecurityGroupId: "",
				Cidr:            "0.0.0.0/0",
				Protocol:        "TCP",
				Port:            3376,
			},
			{
				SecurityGroupId: "",
				Cidr:            "0.0.0.0/0",
				Protocol:        "ICMP",
				IcmpType:        8,
				IcmpCode:        0,
			},
		}
		sgresp, err := client.CreateSecurityGroupWithRules(d.SecurityGroup,
			rules,
			make([]egoscale.SecurityGroupRule, 0, 0))
		if err != nil {
			return err
		}
		sg = sgresp.Id
	}
	log.Debugf("Security group %v = %s", d.SecurityGroup, sg)

	log.Infof("Generate an SSH keypair...")
	keypairName := fmt.Sprintf("docker-machine-%s", d.MachineName)
	kpresp, err := client.CreateKeypair(keypairName)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(d.GetSSHKeyPath(), []byte(kpresp.Privatekey), 0600)
	if err != nil {
		return err
	}
	d.KeyPair = keypairName

	log.Infof("Spawn exoscale host...")

	userdata, err := d.getCloudInit()
	if err != nil {
		return err
	}
	log.Debugf("Using the following cloud-init file:")
	log.Debugf("%s", userdata)

	machineProfile := egoscale.MachineProfile{
		Template:        tpl,
		ServiceOffering: profile,
		SecurityGroups:  []string{sg},
		Userdata:        userdata,
		Zone:            zone,
		Keypair:         d.KeyPair,
		Name:            d.MachineName,
	}

	cvmresp, err := client.CreateVirtualMachine(machineProfile)
	if err != nil {
		return err
	}

	vm, err := d.waitForVM(client, cvmresp)
	if err != nil {
		return err
	}
	d.IPAddress = vm.Nic[0].Ipaddress
	d.Id = vm.Id

	return nil
}

func (d *Driver) Start() error {
	vmstate, err := d.GetState()
	if err != nil {
		return err
	}
	if vmstate == state.Running || vmstate == state.Starting {
		log.Infof("Host is already running or starting")
		return nil
	}

	client := egoscale.NewClient(d.URL, d.ApiKey, d.ApiSecretKey)
	svmresp, err := client.StartVirtualMachine(d.Id)
	if err != nil {
		return err
	}
	_, err = d.waitForVM(client, svmresp)
	if err != nil {
		return err
	}
	return nil
}

func (d *Driver) Stop() error {
	vmstate, err := d.GetState()
	if err != nil {
		return err
	}
	if vmstate == state.Stopped {
		log.Infof("Host is already stopped")
		return nil
	}

	client := egoscale.NewClient(d.URL, d.ApiKey, d.ApiSecretKey)
	svmresp, err := client.StopVirtualMachine(d.Id)
	if err != nil {
		return err
	}
	if _, err = d.waitForVM(client, svmresp); err != nil {
		return err
	}
	return nil
}

func (d *Driver) Remove() error {
	client := egoscale.NewClient(d.URL, d.ApiKey, d.ApiSecretKey)

	// Destroy the SSH key
	if _, err := client.DeleteKeypair(d.KeyPair); err != nil {
		return err
	}

	// Destroy the virtual machine
	dvmresp, err := client.DestroyVirtualMachine(d.Id)
	if err != nil {
		return err
	}
	_, err = d.waitForVM(client, dvmresp)
	if err != nil {
		return err
	}
	return nil
}

func (d *Driver) Restart() error {
	vmstate, err := d.GetState()
	if err != nil {
		return err
	}
	if vmstate == state.Stopped {
		return fmt.Errorf("Host is stopped, use start command to start it")
	}

	client := egoscale.NewClient(d.URL, d.ApiKey, d.ApiSecretKey)
	svmresp, err := client.RebootVirtualMachine(d.Id)
	if err != nil {
		return err
	}
	if _, err = d.waitForVM(client, svmresp); err != nil {
		return err
	}

	return nil
}

func (d *Driver) Kill() error {
	return d.Stop()
}

func (d *Driver) waitForVM(client *egoscale.Client, jobid string) (*egoscale.DeployVirtualMachineResponse, error) {
	log.Infof("Waiting for VM...")
	maxRepeats := 60
	i := 0
	var resp *egoscale.QueryAsyncJobResultResponse
	var err error
WaitLoop:
	for ; i < maxRepeats; i++ {
		resp, err = client.PollAsyncJob(jobid)
		if err != nil {
			return nil, err
		}
		switch resp.Jobstatus {
		case 0: // Job is still in progress
		case 1: // Job has successfully completed
			break WaitLoop
		case 2: // Job has failed to complete
			return nil, fmt.Errorf("Operation failed to complete")
		default: // Some other code
		}
		time.Sleep(2 * time.Second)
	}
	if i == maxRepeats {
		return nil, fmt.Errorf("Timeout while waiting for VM")
	}
	vm, err := client.AsyncToVirtualMachine(*resp)
	if err != nil {
		return nil, err
	}

	return vm, nil
}

// Build a cloud-init user data string that will install and run
// docker.
func (d *Driver) getCloudInit() (string, error) {
	const tpl = `#cloud-config
manage_etc_hosts: true
fqdn: {{ .MachineName }}
resize_rootfs: true
`
	var buffer bytes.Buffer

	tmpl, err := template.New("cloud-init").Parse(tpl)
	if err != nil {
		return "", err
	}
	err = tmpl.Execute(&buffer, d)
	if err != nil {
		return "", err
	}
	return buffer.String(), nil
}
<|endoftext|>


<commit_before><commit_msg>rename package<commit_after><|endoftext|>


<commit_before>
//----------------------------------------
// 代码由GenlibVcl工具自动生成。
// Copyright © ying32. All Rights Reserved.
//
//----------------------------------------

package vcl

import (
	. "github.com/ying32/govcl/vcl/api"
    . "github.com/ying32/govcl/vcl/types"
    "unsafe"
)

type TMenuItem struct {
    IComponent
    instance uintptr
    // 特殊情况下使用，主要应对Go的GC问题，与VCL没有太多关系。
    ptr unsafe.Pointer
}

// NewMenuItem
// CN: 创建一个新的对象。
// EN: Create a new object.
func NewMenuItem(owner IComponent) *TMenuItem {
    m := new(TMenuItem)
    m.instance = MenuItem_Create(CheckPtr(owner))
    m.ptr = unsafe.Pointer(m.instance)
    return m
}

// MenuItemFromInst
// CN: 新建一个对象来自已经存在的对象实例指针。
// EN: Create a new object from an existing object instance pointer.
func MenuItemFromInst(inst uintptr) *TMenuItem {
    m := new(TMenuItem)
    m.instance = inst
    m.ptr = unsafe.Pointer(inst)
    return m
}

// MenuItemFromObj
// CN: 新建一个对象来自已经存在的对象实例。
// EN: Create a new object from an existing object instance.
func MenuItemFromObj(obj IObject) *TMenuItem {
    m := new(TMenuItem)
    m.instance = CheckPtr(obj)
    m.ptr = unsafe.Pointer(m.instance)
    return m
}

// MenuItemFromUnsafePointer
// CN: 新建一个对象来自不安全的地址。注意：使用此函数可能造成一些不明情况，慎用。
// EN: Create a new object from an unsecure address. Note: Using this function may cause some unclear situations and be used with caution..
func MenuItemFromUnsafePointer(ptr unsafe.Pointer) *TMenuItem {
    m := new(TMenuItem)
    m.instance = uintptr(ptr)
    m.ptr = ptr
    return m
}

// Free
// CN: 释放对象。
// EN: Free object.
func (m *TMenuItem) Free() {
    if m.instance != 0 {
        MenuItem_Free(m.instance)
        m.instance = 0
        m.ptr = unsafe.Pointer(uintptr(0))
    }
}

// Instance
// CN: 返回对象实例指针。
// EN: Return object instance pointer.
func (m *TMenuItem) Instance() uintptr {
    return m.instance
}

// UnsafeAddr
// CN: 获取一个不安全的地址。
// EN: Get an unsafe address.
func (m *TMenuItem) UnsafeAddr() unsafe.Pointer {
    return m.ptr
}

// IsValid
// CN: 检测地址是否为空。
// EN: Check if the address is empty.
func (m *TMenuItem) IsValid() bool {
    return m.instance != 0
}

// TMenuItemClass
// CN: 获取类信息指针。
// EN: Get class information pointer.
func TMenuItemClass() TClass {
    return MenuItem_StaticClassType()
}

// Insert
func (m *TMenuItem) Insert(Index int32, Item IComponent) {
    MenuItem_Insert(m.instance, Index , CheckPtr(Item))
}

// Delete
func (m *TMenuItem) Delete(Index int32) {
    MenuItem_Delete(m.instance, Index)
}

// Clear
// CN: 清除。
// EN: .
func (m *TMenuItem) Clear() {
    MenuItem_Clear(m.instance)
}

// Click
// CN: 单击。
// EN: .
func (m *TMenuItem) Click() {
    MenuItem_Click(m.instance)
}

// IndexOf
func (m *TMenuItem) IndexOf(Item IComponent) int32 {
    return MenuItem_IndexOf(m.instance, CheckPtr(Item))
}

// HasParent
// CN: 是否有父容器。
// EN: Is there a parent container.
func (m *TMenuItem) HasParent() bool {
    return MenuItem_HasParent(m.instance)
}

// Add
func (m *TMenuItem) Add(Item IComponent) {
    MenuItem_Add(m.instance, CheckPtr(Item))
}

// FindComponent
// CN: 查找指定名称的组件。
// EN: Find the component with the specified name.
func (m *TMenuItem) FindComponent(AName string) *TComponent {
    return ComponentFromInst(MenuItem_FindComponent(m.instance, AName))
}

// GetNamePath
// CN: 获取类名路径。
// EN: Get the class name path.
func (m *TMenuItem) GetNamePath() string {
    return MenuItem_GetNamePath(m.instance)
}

// Assign
// CN: 复制一个对象，如果对象实现了此方法的话。
// EN: Copy an object, if the object implements this method.
func (m *TMenuItem) Assign(Source IObject) {
    MenuItem_Assign(m.instance, CheckPtr(Source))
}

// DisposeOf
// CN: 丢弃当前对象。
// EN: Discard the current object.
func (m *TMenuItem) DisposeOf() {
    MenuItem_DisposeOf(m.instance)
}

// ClassType
// CN: 获取类的类型信息。
// EN: Get class type information.
func (m *TMenuItem) ClassType() TClass {
    return MenuItem_ClassType(m.instance)
}

// ClassName
// CN: 获取当前对象类名称。
// EN: Get the current object class name.
func (m *TMenuItem) ClassName() string {
    return MenuItem_ClassName(m.instance)
}

// InstanceSize
// CN: 获取当前对象实例大小。
// EN: Get the current object instance size.
func (m *TMenuItem) InstanceSize() int32 {
    return MenuItem_InstanceSize(m.instance)
}

// InheritsFrom
// CN: 判断当前类是否继承自指定类。
// EN: Determine whether the current class inherits from the specified class.
func (m *TMenuItem) InheritsFrom(AClass TClass) bool {
    return MenuItem_InheritsFrom(m.instance, AClass)
}

// Equals
// CN: 与一个对象进行比较。
// EN: Compare with an object.
func (m *TMenuItem) Equals(Obj IObject) bool {
    return MenuItem_Equals(m.instance, CheckPtr(Obj))
}

// GetHashCode
// CN: 获取类的哈希值。
// EN: Get the hash value of the class.
func (m *TMenuItem) GetHashCode() int32 {
    return MenuItem_GetHashCode(m.instance)
}

// ToString
// CN: 文本类信息。
// EN: Text information.
func (m *TMenuItem) ToString() string {
    return MenuItem_ToString(m.instance)
}

// Handle
// CN: 获取控件句柄。
// EN: Get Control handle.
func (m *TMenuItem) Handle() HMENU {
    return MenuItem_GetHandle(m.instance)
}

// Count
func (m *TMenuItem) Count() int32 {
    return MenuItem_GetCount(m.instance)
}

// Parent
// CN: 获取控件父容器。
// EN: Get control parent container.
func (m *TMenuItem) Parent() *TMenuItem {
    return MenuItemFromInst(MenuItem_GetParent(m.instance))
}

// Action
func (m *TMenuItem) Action() *TAction {
    return ActionFromInst(MenuItem_GetAction(m.instance))
}

// SetAction
func (m *TMenuItem) SetAction(value IComponent) {
    MenuItem_SetAction(m.instance, CheckPtr(value))
}

// AutoHotkeys
func (m *TMenuItem) AutoHotkeys() TMenuItemAutoFlag {
    return MenuItem_GetAutoHotkeys(m.instance)
}

// SetAutoHotkeys
func (m *TMenuItem) SetAutoHotkeys(value TMenuItemAutoFlag) {
    MenuItem_SetAutoHotkeys(m.instance, value)
}

// Bitmap
func (m *TMenuItem) Bitmap() *TBitmap {
    return BitmapFromInst(MenuItem_GetBitmap(m.instance))
}

// SetBitmap
func (m *TMenuItem) SetBitmap(value *TBitmap) {
    MenuItem_SetBitmap(m.instance, CheckPtr(value))
}

// Break
func (m *TMenuItem) Break() TMenuBreak {
    return MenuItem_GetBreak(m.instance)
}

// SetBreak
func (m *TMenuItem) SetBreak(value TMenuBreak) {
    MenuItem_SetBreak(m.instance, value)
}

// Caption
// CN: 获取控件标题。
// EN: Get the control title.
func (m *TMenuItem) Caption() string {
    return MenuItem_GetCaption(m.instance)
}

// SetCaption
// CN: 设置控件标题。
// EN: Set the control title.
func (m *TMenuItem) SetCaption(value string) {
    MenuItem_SetCaption(m.instance, value)
}

// Checked
// CN: 获取是否选中。
// EN: .
func (m *TMenuItem) Checked() bool {
    return MenuItem_GetChecked(m.instance)
}

// SetChecked
// CN: 设置是否选中。
// EN: .
func (m *TMenuItem) SetChecked(value bool) {
    MenuItem_SetChecked(m.instance, value)
}

// Default
func (m *TMenuItem) Default() bool {
    return MenuItem_GetDefault(m.instance)
}

// SetDefault
func (m *TMenuItem) SetDefault(value bool) {
    MenuItem_SetDefault(m.instance, value)
}

// Enabled
// CN: 获取控件启用。
// EN: Get the control enabled.
func (m *TMenuItem) Enabled() bool {
    return MenuItem_GetEnabled(m.instance)
}

// SetEnabled
// CN: 设置控件启用。
// EN: Set the control enabled.
func (m *TMenuItem) SetEnabled(value bool) {
    MenuItem_SetEnabled(m.instance, value)
}

// GroupIndex
// CN: 获取团组索引。
// EN: .
func (m *TMenuItem) GroupIndex() uint8 {
    return MenuItem_GetGroupIndex(m.instance)
}

// SetGroupIndex
// CN: 设置团组索引。
// EN: .
func (m *TMenuItem) SetGroupIndex(value uint8) {
    MenuItem_SetGroupIndex(m.instance, value)
}

// Hint
// CN: 获取组件鼠标悬停提示。
// EN: Get component mouse hints.
func (m *TMenuItem) Hint() string {
    return MenuItem_GetHint(m.instance)
}

// SetHint
// CN: 设置组件鼠标悬停提示。
// EN: Set component mouse hints.
func (m *TMenuItem) SetHint(value string) {
    MenuItem_SetHint(m.instance, value)
}

// ImageIndex
// CN: 获取图像在images中的索引。
// EN: .
func (m *TMenuItem) ImageIndex() int32 {
    return MenuItem_GetImageIndex(m.instance)
}

// SetImageIndex
// CN: 设置图像在images中的索引。
// EN: .
func (m *TMenuItem) SetImageIndex(value int32) {
    MenuItem_SetImageIndex(m.instance, value)
}

// ShortCut
// CN: 获取快捷键。
// EN: .
func (m *TMenuItem) ShortCut() TShortCut {
    return MenuItem_GetShortCut(m.instance)
}

// SetShortCut
// CN: 设置快捷键。
// EN: .
func (m *TMenuItem) SetShortCut(value TShortCut) {
    MenuItem_SetShortCut(m.instance, value)
}

// Visible
// CN: 获取控件可视。
// EN: Get the control visible.
func (m *TMenuItem) Visible() bool {
    return MenuItem_GetVisible(m.instance)
}

// SetVisible
// CN: 设置控件可视。
// EN: Set the control visible.
func (m *TMenuItem) SetVisible(value bool) {
    MenuItem_SetVisible(m.instance, value)
}

// SetOnClick
// CN: 设置控件单击事件。
// EN: Set control click event.
func (m *TMenuItem) SetOnClick(fn TNotifyEvent) {
    MenuItem_SetOnClick(m.instance, fn)
}

// SetOnDrawItem
func (m *TMenuItem) SetOnDrawItem(fn TMenuDrawItemEvent) {
    MenuItem_SetOnDrawItem(m.instance, fn)
}

// SetOnMeasureItem
func (m *TMenuItem) SetOnMeasureItem(fn TMenuMeasureItemEvent) {
    MenuItem_SetOnMeasureItem(m.instance, fn)
}

// ComponentCount
// CN: 获取组件总数。
// EN: Get the total number of components.
func (m *TMenuItem) ComponentCount() int32 {
    return MenuItem_GetComponentCount(m.instance)
}

// ComponentIndex
// CN: 获取组件索引。
// EN: Get component index.
func (m *TMenuItem) ComponentIndex() int32 {
    return MenuItem_GetComponentIndex(m.instance)
}

// SetComponentIndex
// CN: 设置组件索引。
// EN: Set component index.
func (m *TMenuItem) SetComponentIndex(value int32) {
    MenuItem_SetComponentIndex(m.instance, value)
}

// Owner
// CN: 获取组件所有者。
// EN: Get component owner.
func (m *TMenuItem) Owner() *TComponent {
    return ComponentFromInst(MenuItem_GetOwner(m.instance))
}

// Name
// CN: 获取组件名称。
// EN: Get the component name.
func (m *TMenuItem) Name() string {
    return MenuItem_GetName(m.instance)
}

// SetName
// CN: 设置组件名称。
// EN: Set the component name.
func (m *TMenuItem) SetName(value string) {
    MenuItem_SetName(m.instance, value)
}

// Tag
// CN: 获取对象标记。
// EN: Get the control tag.
func (m *TMenuItem) Tag() int {
    return MenuItem_GetTag(m.instance)
}

// SetTag
// CN: 设置对象标记。
// EN: Set the control tag.
func (m *TMenuItem) SetTag(value int) {
    MenuItem_SetTag(m.instance, value)
}

// Items
func (m *TMenuItem) Items(Index int32) *TMenuItem {
    return MenuItemFromInst(MenuItem_GetItems(m.instance, Index))
}

// Components
// CN: 获取指定索引组件。
// EN: Get the specified index component.
func (m *TMenuItem) Components(AIndex int32) *TComponent {
    return ComponentFromInst(MenuItem_GetComponents(m.instance, AIndex))
}

<commit_msg>TMenuItem adds RadioItem property.<commit_after>
//----------------------------------------
// 代码由GenlibVcl工具自动生成。
// Copyright © ying32. All Rights Reserved.
//
//----------------------------------------

package vcl

import (
	. "github.com/ying32/govcl/vcl/api"
    . "github.com/ying32/govcl/vcl/types"
    "unsafe"
)

type TMenuItem struct {
    IComponent
    instance uintptr
    // 特殊情况下使用，主要应对Go的GC问题，与VCL没有太多关系。
    ptr unsafe.Pointer
}

// NewMenuItem
// CN: 创建一个新的对象。
// EN: Create a new object.
func NewMenuItem(owner IComponent) *TMenuItem {
    m := new(TMenuItem)
    m.instance = MenuItem_Create(CheckPtr(owner))
    m.ptr = unsafe.Pointer(m.instance)
    return m
}

// MenuItemFromInst
// CN: 新建一个对象来自已经存在的对象实例指针。
// EN: Create a new object from an existing object instance pointer.
func MenuItemFromInst(inst uintptr) *TMenuItem {
    m := new(TMenuItem)
    m.instance = inst
    m.ptr = unsafe.Pointer(inst)
    return m
}

// MenuItemFromObj
// CN: 新建一个对象来自已经存在的对象实例。
// EN: Create a new object from an existing object instance.
func MenuItemFromObj(obj IObject) *TMenuItem {
    m := new(TMenuItem)
    m.instance = CheckPtr(obj)
    m.ptr = unsafe.Pointer(m.instance)
    return m
}

// MenuItemFromUnsafePointer
// CN: 新建一个对象来自不安全的地址。注意：使用此函数可能造成一些不明情况，慎用。
// EN: Create a new object from an unsecure address. Note: Using this function may cause some unclear situations and be used with caution..
func MenuItemFromUnsafePointer(ptr unsafe.Pointer) *TMenuItem {
    m := new(TMenuItem)
    m.instance = uintptr(ptr)
    m.ptr = ptr
    return m
}

// Free
// CN: 释放对象。
// EN: Free object.
func (m *TMenuItem) Free() {
    if m.instance != 0 {
        MenuItem_Free(m.instance)
        m.instance = 0
        m.ptr = unsafe.Pointer(uintptr(0))
    }
}

// Instance
// CN: 返回对象实例指针。
// EN: Return object instance pointer.
func (m *TMenuItem) Instance() uintptr {
    return m.instance
}

// UnsafeAddr
// CN: 获取一个不安全的地址。
// EN: Get an unsafe address.
func (m *TMenuItem) UnsafeAddr() unsafe.Pointer {
    return m.ptr
}

// IsValid
// CN: 检测地址是否为空。
// EN: Check if the address is empty.
func (m *TMenuItem) IsValid() bool {
    return m.instance != 0
}

// TMenuItemClass
// CN: 获取类信息指针。
// EN: Get class information pointer.
func TMenuItemClass() TClass {
    return MenuItem_StaticClassType()
}

// Insert
func (m *TMenuItem) Insert(Index int32, Item IComponent) {
    MenuItem_Insert(m.instance, Index , CheckPtr(Item))
}

// Delete
func (m *TMenuItem) Delete(Index int32) {
    MenuItem_Delete(m.instance, Index)
}

// Clear
// CN: 清除。
// EN: .
func (m *TMenuItem) Clear() {
    MenuItem_Clear(m.instance)
}

// Click
// CN: 单击。
// EN: .
func (m *TMenuItem) Click() {
    MenuItem_Click(m.instance)
}

// IndexOf
func (m *TMenuItem) IndexOf(Item IComponent) int32 {
    return MenuItem_IndexOf(m.instance, CheckPtr(Item))
}

// HasParent
// CN: 是否有父容器。
// EN: Is there a parent container.
func (m *TMenuItem) HasParent() bool {
    return MenuItem_HasParent(m.instance)
}

// Add
func (m *TMenuItem) Add(Item IComponent) {
    MenuItem_Add(m.instance, CheckPtr(Item))
}

// FindComponent
// CN: 查找指定名称的组件。
// EN: Find the component with the specified name.
func (m *TMenuItem) FindComponent(AName string) *TComponent {
    return ComponentFromInst(MenuItem_FindComponent(m.instance, AName))
}

// GetNamePath
// CN: 获取类名路径。
// EN: Get the class name path.
func (m *TMenuItem) GetNamePath() string {
    return MenuItem_GetNamePath(m.instance)
}

// Assign
// CN: 复制一个对象，如果对象实现了此方法的话。
// EN: Copy an object, if the object implements this method.
func (m *TMenuItem) Assign(Source IObject) {
    MenuItem_Assign(m.instance, CheckPtr(Source))
}

// DisposeOf
// CN: 丢弃当前对象。
// EN: Discard the current object.
func (m *TMenuItem) DisposeOf() {
    MenuItem_DisposeOf(m.instance)
}

// ClassType
// CN: 获取类的类型信息。
// EN: Get class type information.
func (m *TMenuItem) ClassType() TClass {
    return MenuItem_ClassType(m.instance)
}

// ClassName
// CN: 获取当前对象类名称。
// EN: Get the current object class name.
func (m *TMenuItem) ClassName() string {
    return MenuItem_ClassName(m.instance)
}

// InstanceSize
// CN: 获取当前对象实例大小。
// EN: Get the current object instance size.
func (m *TMenuItem) InstanceSize() int32 {
    return MenuItem_InstanceSize(m.instance)
}

// InheritsFrom
// CN: 判断当前类是否继承自指定类。
// EN: Determine whether the current class inherits from the specified class.
func (m *TMenuItem) InheritsFrom(AClass TClass) bool {
    return MenuItem_InheritsFrom(m.instance, AClass)
}

// Equals
// CN: 与一个对象进行比较。
// EN: Compare with an object.
func (m *TMenuItem) Equals(Obj IObject) bool {
    return MenuItem_Equals(m.instance, CheckPtr(Obj))
}

// GetHashCode
// CN: 获取类的哈希值。
// EN: Get the hash value of the class.
func (m *TMenuItem) GetHashCode() int32 {
    return MenuItem_GetHashCode(m.instance)
}

// ToString
// CN: 文本类信息。
// EN: Text information.
func (m *TMenuItem) ToString() string {
    return MenuItem_ToString(m.instance)
}

// Handle
// CN: 获取控件句柄。
// EN: Get Control handle.
func (m *TMenuItem) Handle() HMENU {
    return MenuItem_GetHandle(m.instance)
}

// Count
func (m *TMenuItem) Count() int32 {
    return MenuItem_GetCount(m.instance)
}

// Parent
// CN: 获取控件父容器。
// EN: Get control parent container.
func (m *TMenuItem) Parent() *TMenuItem {
    return MenuItemFromInst(MenuItem_GetParent(m.instance))
}

// Action
func (m *TMenuItem) Action() *TAction {
    return ActionFromInst(MenuItem_GetAction(m.instance))
}

// SetAction
func (m *TMenuItem) SetAction(value IComponent) {
    MenuItem_SetAction(m.instance, CheckPtr(value))
}

// AutoHotkeys
func (m *TMenuItem) AutoHotkeys() TMenuItemAutoFlag {
    return MenuItem_GetAutoHotkeys(m.instance)
}

// SetAutoHotkeys
func (m *TMenuItem) SetAutoHotkeys(value TMenuItemAutoFlag) {
    MenuItem_SetAutoHotkeys(m.instance, value)
}

// Bitmap
func (m *TMenuItem) Bitmap() *TBitmap {
    return BitmapFromInst(MenuItem_GetBitmap(m.instance))
}

// SetBitmap
func (m *TMenuItem) SetBitmap(value *TBitmap) {
    MenuItem_SetBitmap(m.instance, CheckPtr(value))
}

// Break
func (m *TMenuItem) Break() TMenuBreak {
    return MenuItem_GetBreak(m.instance)
}

// SetBreak
func (m *TMenuItem) SetBreak(value TMenuBreak) {
    MenuItem_SetBreak(m.instance, value)
}

// Caption
// CN: 获取控件标题。
// EN: Get the control title.
func (m *TMenuItem) Caption() string {
    return MenuItem_GetCaption(m.instance)
}

// SetCaption
// CN: 设置控件标题。
// EN: Set the control title.
func (m *TMenuItem) SetCaption(value string) {
    MenuItem_SetCaption(m.instance, value)
}

// Checked
// CN: 获取是否选中。
// EN: .
func (m *TMenuItem) Checked() bool {
    return MenuItem_GetChecked(m.instance)
}

// SetChecked
// CN: 设置是否选中。
// EN: .
func (m *TMenuItem) SetChecked(value bool) {
    MenuItem_SetChecked(m.instance, value)
}

// Default
func (m *TMenuItem) Default() bool {
    return MenuItem_GetDefault(m.instance)
}

// SetDefault
func (m *TMenuItem) SetDefault(value bool) {
    MenuItem_SetDefault(m.instance, value)
}

// Enabled
// CN: 获取控件启用。
// EN: Get the control enabled.
func (m *TMenuItem) Enabled() bool {
    return MenuItem_GetEnabled(m.instance)
}

// SetEnabled
// CN: 设置控件启用。
// EN: Set the control enabled.
func (m *TMenuItem) SetEnabled(value bool) {
    MenuItem_SetEnabled(m.instance, value)
}

// GroupIndex
// CN: 获取团组索引。
// EN: .
func (m *TMenuItem) GroupIndex() uint8 {
    return MenuItem_GetGroupIndex(m.instance)
}

// SetGroupIndex
// CN: 设置团组索引。
// EN: .
func (m *TMenuItem) SetGroupIndex(value uint8) {
    MenuItem_SetGroupIndex(m.instance, value)
}

// Hint
// CN: 获取组件鼠标悬停提示。
// EN: Get component mouse hints.
func (m *TMenuItem) Hint() string {
    return MenuItem_GetHint(m.instance)
}

// SetHint
// CN: 设置组件鼠标悬停提示。
// EN: Set component mouse hints.
func (m *TMenuItem) SetHint(value string) {
    MenuItem_SetHint(m.instance, value)
}

// ImageIndex
// CN: 获取图像在images中的索引。
// EN: .
func (m *TMenuItem) ImageIndex() int32 {
    return MenuItem_GetImageIndex(m.instance)
}

// SetImageIndex
// CN: 设置图像在images中的索引。
// EN: .
func (m *TMenuItem) SetImageIndex(value int32) {
    MenuItem_SetImageIndex(m.instance, value)
}

// RadioItem
func (m *TMenuItem) RadioItem() bool {
    return MenuItem_GetRadioItem(m.instance)
}

// SetRadioItem
func (m *TMenuItem) SetRadioItem(value bool) {
    MenuItem_SetRadioItem(m.instance, value)
}

// ShortCut
// CN: 获取快捷键。
// EN: .
func (m *TMenuItem) ShortCut() TShortCut {
    return MenuItem_GetShortCut(m.instance)
}

// SetShortCut
// CN: 设置快捷键。
// EN: .
func (m *TMenuItem) SetShortCut(value TShortCut) {
    MenuItem_SetShortCut(m.instance, value)
}

// Visible
// CN: 获取控件可视。
// EN: Get the control visible.
func (m *TMenuItem) Visible() bool {
    return MenuItem_GetVisible(m.instance)
}

// SetVisible
// CN: 设置控件可视。
// EN: Set the control visible.
func (m *TMenuItem) SetVisible(value bool) {
    MenuItem_SetVisible(m.instance, value)
}

// SetOnClick
// CN: 设置控件单击事件。
// EN: Set control click event.
func (m *TMenuItem) SetOnClick(fn TNotifyEvent) {
    MenuItem_SetOnClick(m.instance, fn)
}

// SetOnDrawItem
func (m *TMenuItem) SetOnDrawItem(fn TMenuDrawItemEvent) {
    MenuItem_SetOnDrawItem(m.instance, fn)
}

// SetOnMeasureItem
func (m *TMenuItem) SetOnMeasureItem(fn TMenuMeasureItemEvent) {
    MenuItem_SetOnMeasureItem(m.instance, fn)
}

// ComponentCount
// CN: 获取组件总数。
// EN: Get the total number of components.
func (m *TMenuItem) ComponentCount() int32 {
    return MenuItem_GetComponentCount(m.instance)
}

// ComponentIndex
// CN: 获取组件索引。
// EN: Get component index.
func (m *TMenuItem) ComponentIndex() int32 {
    return MenuItem_GetComponentIndex(m.instance)
}

// SetComponentIndex
// CN: 设置组件索引。
// EN: Set component index.
func (m *TMenuItem) SetComponentIndex(value int32) {
    MenuItem_SetComponentIndex(m.instance, value)
}

// Owner
// CN: 获取组件所有者。
// EN: Get component owner.
func (m *TMenuItem) Owner() *TComponent {
    return ComponentFromInst(MenuItem_GetOwner(m.instance))
}

// Name
// CN: 获取组件名称。
// EN: Get the component name.
func (m *TMenuItem) Name() string {
    return MenuItem_GetName(m.instance)
}

// SetName
// CN: 设置组件名称。
// EN: Set the component name.
func (m *TMenuItem) SetName(value string) {
    MenuItem_SetName(m.instance, value)
}

// Tag
// CN: 获取对象标记。
// EN: Get the control tag.
func (m *TMenuItem) Tag() int {
    return MenuItem_GetTag(m.instance)
}

// SetTag
// CN: 设置对象标记。
// EN: Set the control tag.
func (m *TMenuItem) SetTag(value int) {
    MenuItem_SetTag(m.instance, value)
}

// Items
func (m *TMenuItem) Items(Index int32) *TMenuItem {
    return MenuItemFromInst(MenuItem_GetItems(m.instance, Index))
}

// Components
// CN: 获取指定索引组件。
// EN: Get the specified index component.
func (m *TMenuItem) Components(AIndex int32) *TComponent {
    return ComponentFromInst(MenuItem_GetComponents(m.instance, AIndex))
}

<|endoftext|>


<commit_before>/*
Copyright 2016 The Rook Authors. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package integration

import (
	"errors"

	"time"

	rgw "github.com/rook/rook/pkg/operator/ceph/object"
	"github.com/rook/rook/tests/framework/clients"
	"github.com/rook/rook/tests/framework/utils"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"
)

var (
	userid          = "rook-user"
	userdisplayname = "A rook RGW user"
	bucketname      = "smokebkt"
	objBody         = "Test Rook Object Data"
	objectKey       = "rookObj1"
	contentType     = "plain/text"
	obcName         = "smoke-delete-bucket"
	region          = "us-east-1"
)

// Smoke Test for ObjectStore - Test check the following operations on ObjectStore in order
// Create object store, Create User, Connect to Object Store, Create Bucket, Read/Write/Delete to bucket,
// Check issues in MGRs, Delete Bucket and Delete user
func runObjectE2ETest(helper *clients.TestClient, k8sh *utils.K8sHelper, s suite.Suite, namespace string) {
	storeName := "teststore"
	defer objectTestDataCleanUp(helper, k8sh, namespace, storeName)

	logger.Infof("Object Storage End To End Integration Test - Create Object Store, User,Bucket and read/write to bucket")
	logger.Infof("Running on Rook Cluster %s", namespace)

	logger.Infof("Step 0 : Create Object Store")
	cobsErr := helper.ObjectClient.Create(namespace, storeName, 3)
	// check that ObjectStore is created
	require.Nil(s.T(), cobsErr)
	logger.Infof("Object store created successfully")

	logger.Infof("Step 1 : Create Object Store User")
	cosuErr := helper.ObjectUserClient.Create(namespace, userid, userdisplayname, storeName)
	// check that ObjectUser is created
	require.Nil(s.T(), cosuErr)
	logger.Infof("Waiting 10 seconds to ensure user was created")
	time.Sleep(10 * time.Second)
	logger.Infof("Checking to see if the user secret has been created")
	i := 0
	for i = 0; i < 4 && helper.ObjectUserClient.UserSecretExists(namespace, storeName, userid) == false; i++ {
		logger.Infof("(%d) secret check sleeping for 5 seconds ...", i)
		time.Sleep(5 * time.Second)
	}
	assert.True(s.T(), helper.ObjectUserClient.UserSecretExists(namespace, storeName, userid))
	userInfo, err := helper.ObjectUserClient.GetUser(namespace, storeName, userid)
	require.NoError(s.T(), err)
	assert.Equal(s.T(), userid, userInfo.UserID)
	assert.Equal(s.T(), userdisplayname, *userInfo.DisplayName)
	logger.Infof("Done creating object store user")

	logger.Infof("Step 2 : Test Deleting User")
	dosuErr := helper.ObjectUserClient.Delete(namespace, userid)
	require.Nil(s.T(), dosuErr)
	logger.Infof("Object store user deleted successfully")
	logger.Infof("Checking to see if the user secret has been deleted")
	i = 0
	for i = 0; i < 4 && helper.ObjectUserClient.UserSecretExists(namespace, storeName, userid) == true; i++ {
		logger.Infof("(%d) secret check sleeping for 5 seconds ...", i)
		time.Sleep(5 * time.Second)
	}
	assert.False(s.T(), helper.ObjectUserClient.UserSecretExists(namespace, storeName, userid))

	logger.Infof("Check that MGRs are not in a crashloop")
	assert.True(s.T(), k8sh.CheckPodCountAndState("rook-ceph-mgr", namespace, 1, "Running"))
	logger.Infof("Ceph MGRs are running")

	// Testing creation/deletion of objects using Object Bucket Claim
	logger.Infof("Step 3 : Create Object Bucket Claim with reclaim policy delete")
	bucketStorageClassName := "rook-smoke-delete-bucket"
	cobErr := helper.BucketClient.CreateBucketStorageClass(namespace, storeName, bucketStorageClassName, "Delete", region)
	require.Nil(s.T(), cobErr)
	cobcErr := helper.BucketClient.CreateObc(obcName, bucketStorageClassName, bucketname, true)
	require.Nil(s.T(), cobcErr)

	for i = 0; i < 4 && !helper.BucketClient.CheckOBC(obcName, "created"); i++ {
		logger.Infof("(%d) obc created check, sleeping for 5 seconds ...", i)
		time.Sleep(5 * time.Second)
	}
	require.NotEqual(s.T(), i, 4)

	logger.Infof("Check if bucket was created")
	context := k8sh.MakeContext()
	rgwcontext := rgw.NewContext(context, storeName, namespace)
	var bkt rgw.ObjectBucket
	for i = 0; i < 4; i++ {
		b, _, err := rgw.GetBucket(rgwcontext, bucketname)
		if b != nil && err == nil {
			bkt = *b
			break
		}
		logger.Infof("(%d) check bucket exists, sleeping for 5 seconds ...", i)
		time.Sleep(5 * time.Second)
	}
	require.Equal(s.T(), bkt.Name, bucketname)
	logger.Infof("OBC, Secret and ConfigMap created")

	logger.Infof("Step 4 : Create s3 client")
	s3endpoint, _ := helper.ObjectClient.GetEndPointUrl(namespace, storeName)
	s3AccessKey, _ := helper.BucketClient.GetAccessKey(obcName)
	s3SecretKey, _ := helper.BucketClient.GetSecretKey(obcName)
	s3client := utils.CreateNewS3Helper(s3endpoint, s3AccessKey, s3SecretKey)
	logger.Infof("endpoint (%s) Accesskey (%s) secret (%s)", s3endpoint, s3AccessKey, s3SecretKey)

	logger.Infof("Step 5 : Put Object on bucket")
	_, poErr := s3client.PutObjectInBucket(bucketname, objBody, objectKey, contentType)
	require.Nil(s.T(), poErr)

	logger.Infof("Step 6 : Get Object from bucket")
	read, err := s3client.GetObjectInBucket(bucketname, objectKey)
	require.Nil(s.T(), err)
	require.Equal(s.T(), objBody, read)
	logger.Infof("Object Created and Retrieved on bucket successfully")

	logger.Infof("Step 7 : Delete object on bucket")
	_, delobjErr := s3client.DeleteObjectInBucket(bucketname, objectKey)
	require.Nil(s.T(), delobjErr)
	logger.Infof("Object deleted on bucket successfully")

	logger.Infof("Step 8 : Delete Object Bucket Claim")
	dobcErr := helper.BucketClient.DeleteObc(obcName, bucketStorageClassName, bucketname, true)
	require.Nil(s.T(), dobcErr)
	logger.Infof("Checking to see if the obc, secret and cm have all been deleted")
	for i = 0; i < 4 && !helper.BucketClient.CheckOBC(obcName, "deleted"); i++ {
		logger.Infof("(%d) obc deleted check, sleeping for 5 seconds ...", i)
		time.Sleep(5 * time.Second)
	}
	assert.NotEqual(s.T(), i, 4)

	logger.Infof("ensure bucket was deleted")
	var rgwErr int
	for i = 0; i < 4; i++ {
		_, rgwErr, _ = rgw.GetBucket(rgwcontext, bucketname)
		if rgwErr == rgw.RGWErrorNotFound {
			break
		}
		logger.Infof("(%d) check bucket deleted, sleeping for 5 seconds ...", i)
		time.Sleep(5 * time.Second)
	}
	assert.NotEqual(s.T(), i, 4)
	assert.Equal(s.T(), rgwErr, rgw.RGWErrorNotFound)

	dobErr := helper.BucketClient.DeleteBucketStorageClass(namespace, storeName, bucketStorageClassName, "Delete", region)
	assert.Nil(s.T(), dobErr)
	logger.Infof("Delete Object Bucket Claim successfully")

	// TODO : Add case for brownfield/cleanup s3 client

	logger.Infof("Delete Object Store")
	dobsErr := helper.ObjectClient.Delete(namespace, storeName)
	assert.Nil(s.T(), dobsErr)
	logger.Infof("Done deleting object store")
}

// Test Object StoreCreation on Rook that was installed via helm
func runObjectE2ETestLite(helper *clients.TestClient, k8sh *utils.K8sHelper, s suite.Suite, namespace string, name string, replicaSize int, deleteStore bool) {
	logger.Infof("Object Storage End To End Integration Test - Create Object Store and check if rgw service is Running")
	logger.Infof("Running on Rook Cluster %s", namespace)

	logger.Infof("Step 1 : Create Object Store")
	err := helper.ObjectClient.Create(namespace, name, int32(replicaSize))
	require.Nil(s.T(), err)

	logger.Infof("Step 2 : check rook-ceph-rgw service status and count")
	require.True(s.T(), k8sh.IsPodInExpectedState("rook-ceph-rgw", namespace, "Running"),
		"Make sure rook-ceph-rgw is in running state")

	assert.True(s.T(), k8sh.CheckPodCountAndState("rook-ceph-rgw", namespace, replicaSize, "Running"),
		"Make sure all rook-ceph-rgw pods are in Running state")

	require.True(s.T(), k8sh.IsServiceUp("rook-ceph-rgw-"+name, namespace))

	if deleteStore {
		logger.Infof("Delete Object Store")
		err = helper.ObjectClient.Delete(namespace, name)
		require.Nil(s.T(), err)
		logger.Infof("Done deleting object store")
	}

}

func objectTestDataCleanUp(helper *clients.TestClient, k8sh *utils.K8sHelper, namespace, storeName string) {
	logger.Infof("FIX: Cleaning up object store")
	/*oc := helper.ObjectClient
	userinfo, err := helper.ObjectClient.ObjectGetUser(storeName, userid)
	if err != nil {
		return //when user is not found
	}
	s3endpoint, _ := k8sh.GetRGWServiceURL(storeName, namespace)
	s3client := utils.CreateNewS3Helper(s3endpoint, *userinfo.AccessKey, *userinfo.SecretKey)
	s3client.DeleteObjectInBucket(bucketname, objectKey)
	s3client.DeleteBucket(bucketname)
	helper.ObjectClient.DeleteUser(storeName, userid)*/
}

func getBucket(bucketname string, bucketList []rgw.ObjectBucket) (rgw.ObjectBucket, error) {
	for _, bucket := range bucketList {
		if bucket.Name == bucketname {
			return bucket, nil
		}
	}
	return rgw.ObjectBucket{}, errors.New("Bucket not found")
}

func getBucketSizeAndObjects(bucketname string, bucketList []rgw.ObjectBucket) (uint64, uint64, error) {
	bkt, err := getBucket(bucketname, bucketList)
	if err != nil {
		return 0, 0, errors.New("Bucket not found")
	}
	return bkt.Size, bkt.NumberOfObjects, nil
}
<commit_msg>Ceph: integration test creates ObjectStoreUser before ObjectStore<commit_after>/*
Copyright 2016 The Rook Authors. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package integration

import (
	"errors"

	"time"

	rgw "github.com/rook/rook/pkg/operator/ceph/object"
	"github.com/rook/rook/tests/framework/clients"
	"github.com/rook/rook/tests/framework/utils"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"
)

var (
	userid          = "rook-user"
	userdisplayname = "A rook RGW user"
	bucketname      = "smokebkt"
	objBody         = "Test Rook Object Data"
	objectKey       = "rookObj1"
	contentType     = "plain/text"
	obcName         = "smoke-delete-bucket"
	region          = "us-east-1"
)

// Smoke Test for ObjectStore - Test check the following operations on ObjectStore in order
// Create object store, Create User, Connect to Object Store, Create Bucket, Read/Write/Delete to bucket,
// Check issues in MGRs, Delete Bucket and Delete user
func runObjectE2ETest(helper *clients.TestClient, k8sh *utils.K8sHelper, s suite.Suite, namespace string) {
	storeName := "teststore"
	defer objectTestDataCleanUp(helper, k8sh, namespace, storeName)

	logger.Infof("Object Storage End To End Integration Test - Create Object Store, User,Bucket and read/write to bucket")
	logger.Infof("Running on Rook Cluster %s", namespace)

	logger.Infof("Step 0 : Create Object Store User")
	cosuErr := helper.ObjectUserClient.Create(namespace, userid, userdisplayname, storeName)
	require.Nil(s.T(), cosuErr)

	logger.Infof("Step 1 : Create Object Store")
	cobsErr := helper.ObjectClient.Create(namespace, storeName, 3)
	require.Nil(s.T(), cobsErr)

	// check that ObjectStore is created
	logger.Infof("Check that RGW pods are Running")
	i := 0
	for i = 0; i < 24 && k8sh.CheckPodCountAndState("rook-ceph-rgw", namespace, 1, "Running") == false; i++ {
		logger.Infof("(%d) RGW pod check sleeping for 5 seconds ...", i)
		time.Sleep(5 * time.Second)
	}
	assert.True(s.T(), k8sh.CheckPodCountAndState("rook-ceph-rgw", namespace, 1, "Running"))
	logger.Infof("RGW pods are running")
	logger.Infof("Object store created successfully")

	// check that ObjectUser is created
	logger.Infof("Waiting 10 seconds to ensure user was created")
	time.Sleep(10 * time.Second)
	logger.Infof("Checking to see if the user secret has been created")
	i = 0
	for i = 0; i < 4 && helper.ObjectUserClient.UserSecretExists(namespace, storeName, userid) == false; i++ {
		logger.Infof("(%d) secret check sleeping for 5 seconds ...", i)
		time.Sleep(5 * time.Second)
	}
	assert.True(s.T(), helper.ObjectUserClient.UserSecretExists(namespace, storeName, userid))
	userInfo, err := helper.ObjectUserClient.GetUser(namespace, storeName, userid)
	require.NoError(s.T(), err)
	assert.Equal(s.T(), userid, userInfo.UserID)
	assert.Equal(s.T(), userdisplayname, *userInfo.DisplayName)
	logger.Infof("Done creating object store user")

	logger.Infof("Step 2 : Test Deleting User")
	dosuErr := helper.ObjectUserClient.Delete(namespace, userid)
	require.Nil(s.T(), dosuErr)
	logger.Infof("Object store user deleted successfully")
	logger.Infof("Checking to see if the user secret has been deleted")
	i = 0
	for i = 0; i < 4 && helper.ObjectUserClient.UserSecretExists(namespace, storeName, userid) == true; i++ {
		logger.Infof("(%d) secret check sleeping for 5 seconds ...", i)
		time.Sleep(5 * time.Second)
	}
	assert.False(s.T(), helper.ObjectUserClient.UserSecretExists(namespace, storeName, userid))

	logger.Infof("Check that MGRs are not in a crashloop")
	assert.True(s.T(), k8sh.CheckPodCountAndState("rook-ceph-mgr", namespace, 1, "Running"))
	logger.Infof("Ceph MGRs are running")

	// Testing creation/deletion of objects using Object Bucket Claim
	logger.Infof("Step 3 : Create Object Bucket Claim with reclaim policy delete")
	bucketStorageClassName := "rook-smoke-delete-bucket"
	cobErr := helper.BucketClient.CreateBucketStorageClass(namespace, storeName, bucketStorageClassName, "Delete", region)
	require.Nil(s.T(), cobErr)
	cobcErr := helper.BucketClient.CreateObc(obcName, bucketStorageClassName, bucketname, true)
	require.Nil(s.T(), cobcErr)

	for i = 0; i < 4 && !helper.BucketClient.CheckOBC(obcName, "created"); i++ {
		logger.Infof("(%d) obc created check, sleeping for 5 seconds ...", i)
		time.Sleep(5 * time.Second)
	}
	require.NotEqual(s.T(), i, 4)

	logger.Infof("Check if bucket was created")
	context := k8sh.MakeContext()
	rgwcontext := rgw.NewContext(context, storeName, namespace)
	var bkt rgw.ObjectBucket
	for i = 0; i < 4; i++ {
		b, _, err := rgw.GetBucket(rgwcontext, bucketname)
		if b != nil && err == nil {
			bkt = *b
			break
		}
		logger.Infof("(%d) check bucket exists, sleeping for 5 seconds ...", i)
		time.Sleep(5 * time.Second)
	}
	require.Equal(s.T(), bkt.Name, bucketname)
	logger.Infof("OBC, Secret and ConfigMap created")

	logger.Infof("Step 4 : Create s3 client")
	s3endpoint, _ := helper.ObjectClient.GetEndPointUrl(namespace, storeName)
	s3AccessKey, _ := helper.BucketClient.GetAccessKey(obcName)
	s3SecretKey, _ := helper.BucketClient.GetSecretKey(obcName)
	s3client := utils.CreateNewS3Helper(s3endpoint, s3AccessKey, s3SecretKey)
	logger.Infof("endpoint (%s) Accesskey (%s) secret (%s)", s3endpoint, s3AccessKey, s3SecretKey)

	logger.Infof("Step 5 : Put Object on bucket")
	_, poErr := s3client.PutObjectInBucket(bucketname, objBody, objectKey, contentType)
	require.Nil(s.T(), poErr)

	logger.Infof("Step 6 : Get Object from bucket")
	read, err := s3client.GetObjectInBucket(bucketname, objectKey)
	require.Nil(s.T(), err)
	require.Equal(s.T(), objBody, read)
	logger.Infof("Object Created and Retrieved on bucket successfully")

	logger.Infof("Step 7 : Delete object on bucket")
	_, delobjErr := s3client.DeleteObjectInBucket(bucketname, objectKey)
	require.Nil(s.T(), delobjErr)
	logger.Infof("Object deleted on bucket successfully")

	logger.Infof("Step 8 : Delete Object Bucket Claim")
	dobcErr := helper.BucketClient.DeleteObc(obcName, bucketStorageClassName, bucketname, true)
	require.Nil(s.T(), dobcErr)
	logger.Infof("Checking to see if the obc, secret and cm have all been deleted")
	for i = 0; i < 4 && !helper.BucketClient.CheckOBC(obcName, "deleted"); i++ {
		logger.Infof("(%d) obc deleted check, sleeping for 5 seconds ...", i)
		time.Sleep(5 * time.Second)
	}
	assert.NotEqual(s.T(), i, 4)

	logger.Infof("ensure bucket was deleted")
	var rgwErr int
	for i = 0; i < 4; i++ {
		_, rgwErr, _ = rgw.GetBucket(rgwcontext, bucketname)
		if rgwErr == rgw.RGWErrorNotFound {
			break
		}
		logger.Infof("(%d) check bucket deleted, sleeping for 5 seconds ...", i)
		time.Sleep(5 * time.Second)
	}
	assert.NotEqual(s.T(), i, 4)
	assert.Equal(s.T(), rgwErr, rgw.RGWErrorNotFound)

	dobErr := helper.BucketClient.DeleteBucketStorageClass(namespace, storeName, bucketStorageClassName, "Delete", region)
	assert.Nil(s.T(), dobErr)
	logger.Infof("Delete Object Bucket Claim successfully")

	// TODO : Add case for brownfield/cleanup s3 client

	logger.Infof("Delete Object Store")
	dobsErr := helper.ObjectClient.Delete(namespace, storeName)
	assert.Nil(s.T(), dobsErr)
	logger.Infof("Done deleting object store")
}

// Test Object StoreCreation on Rook that was installed via helm
func runObjectE2ETestLite(helper *clients.TestClient, k8sh *utils.K8sHelper, s suite.Suite, namespace string, name string, replicaSize int, deleteStore bool) {
	logger.Infof("Object Storage End To End Integration Test - Create Object Store and check if rgw service is Running")
	logger.Infof("Running on Rook Cluster %s", namespace)

	logger.Infof("Step 1 : Create Object Store")
	err := helper.ObjectClient.Create(namespace, name, int32(replicaSize))
	require.Nil(s.T(), err)

	logger.Infof("Step 2 : check rook-ceph-rgw service status and count")
	require.True(s.T(), k8sh.IsPodInExpectedState("rook-ceph-rgw", namespace, "Running"),
		"Make sure rook-ceph-rgw is in running state")

	assert.True(s.T(), k8sh.CheckPodCountAndState("rook-ceph-rgw", namespace, replicaSize, "Running"),
		"Make sure all rook-ceph-rgw pods are in Running state")

	require.True(s.T(), k8sh.IsServiceUp("rook-ceph-rgw-"+name, namespace))

	if deleteStore {
		logger.Infof("Delete Object Store")
		err = helper.ObjectClient.Delete(namespace, name)
		require.Nil(s.T(), err)
		logger.Infof("Done deleting object store")
	}

}

func objectTestDataCleanUp(helper *clients.TestClient, k8sh *utils.K8sHelper, namespace, storeName string) {
	logger.Infof("FIX: Cleaning up object store")
	/*oc := helper.ObjectClient
	userinfo, err := helper.ObjectClient.ObjectGetUser(storeName, userid)
	if err != nil {
		return //when user is not found
	}
	s3endpoint, _ := k8sh.GetRGWServiceURL(storeName, namespace)
	s3client := utils.CreateNewS3Helper(s3endpoint, *userinfo.AccessKey, *userinfo.SecretKey)
	s3client.DeleteObjectInBucket(bucketname, objectKey)
	s3client.DeleteBucket(bucketname)
	helper.ObjectClient.DeleteUser(storeName, userid)*/
}

func getBucket(bucketname string, bucketList []rgw.ObjectBucket) (rgw.ObjectBucket, error) {
	for _, bucket := range bucketList {
		if bucket.Name == bucketname {
			return bucket, nil
		}
	}
	return rgw.ObjectBucket{}, errors.New("Bucket not found")
}

func getBucketSizeAndObjects(bucketname string, bucketList []rgw.ObjectBucket) (uint64, uint64, error) {
	bkt, err := getBucket(bucketname, bucketList)
	if err != nil {
		return 0, 0, errors.New("Bucket not found")
	}
	return bkt.Size, bkt.NumberOfObjects, nil
}
<|endoftext|>


<commit_before>/* Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */

package go_kafka_client

import (
	"testing"
	"time"
)

var goodStrategy = func(_ *Worker, _ *Message, id TaskId) WorkerResult { return NewSuccessfulResult(id) }
var failStrategy = func(_ *Worker, _ *Message, id TaskId) WorkerResult { return NewProcessingFailedResult(id) }
var slowStrategy = func(_ *Worker, _ *Message, id TaskId) WorkerResult {
	time.Sleep(5 * time.Second)
	return NewSuccessfulResult(id)
}

func TestFailureCounter(t *testing.T) {
	threshold := int32(5)
	failTimeWindow := 2 * time.Second

	counter := NewFailureCounter(threshold, failTimeWindow)

	failed := false
	for i := 0; i < int(threshold); i++ {
		failed = failed || counter.Failed()
	}

	if !failed {
		t.Error("Failure counter should fail when threshold is reached")
	}

	counter = NewFailureCounter(threshold, failTimeWindow)
	failed = false
	for i := 0; i < int(threshold)-1; i++ {
		failed = failed || counter.Failed()
	}
	time.Sleep(failTimeWindow + (100 * time.Millisecond))
	failed = failed || counter.Failed()

	if failed {
		t.Error("Failure counter should not fail when threshold is not reached within a given time window")
	}
}

func TestWorker(t *testing.T) {
	outChannel := make(chan WorkerResult)
	taskTimeout := 1 * time.Second

	task := &Task{
		Msg: &Message{},
	}

	//test good case
	worker := &Worker{
		OutputChannel: outChannel,
		TaskTimeout:   taskTimeout,
	}
	worker.Start(task, goodStrategy)

	result := <-outChannel
	if !result.Success() {
		t.Error("Worker result with good strategy should be successful")
	}

	//test fail case
	worker2 := &Worker{
		OutputChannel: outChannel,
		TaskTimeout:   taskTimeout,
	}
	worker2.Start(task, failStrategy)
	result = <-outChannel
	if result.Success() {
		t.Error("Worker result with fail strategy should be unsuccessful")
	}

	//test timeout
	worker3 := &Worker{
		OutputChannel: outChannel,
		TaskTimeout:   taskTimeout,
	}
	worker3.Start(task, slowStrategy)
	result = <-outChannel
	if _, ok := result.(*TimedOutResult); !ok {
		t.Error("Worker with slow strategy should time out")
	}

	select {
	case <-outChannel:
		{
			t.Error("Worker should not produce any result after timeout")
		}
	case <-time.After(taskTimeout + time.Second):
	}
}

func TestWorkerManager(t *testing.T) {
	wmid := "test-WM"
	config := DefaultConsumerConfig()
	config.NumWorkers = 3
	config.Strategy = goodStrategy
	mockZk := newMockZookeeperCoordinator()
	config.Coordinator = mockZk
	config.OffsetStorage = mockZk
	topicPartition := TopicAndPartition{"fakeTopic", int32(0)}

	metrics := newConsumerMetrics(wmid, "")
	closeConsumer := make(chan bool)
	manager := NewWorkerManager(wmid, config, topicPartition, metrics, closeConsumer)

	go manager.Start()

	if len(manager.workers) != config.NumWorkers {
		t.Errorf("Number of workers of worker manager should be %d, actual: %d", config.NumWorkers, len(manager.workers))
	}

	checkAllWorkersAvailable(t, manager)

	batch := []*Message{
		&Message{Offset: 0},
		&Message{Offset: 1},
		&Message{Offset: 2},
		&Message{Offset: 3},
		&Message{Offset: 4},
		&Message{Offset: 5},
	}

	manager.inputChannel <- batch

	time.Sleep(1 * time.Second)
	checkAllWorkersAvailable(t, manager)

	<-manager.Stop()

	//make sure we don't lose our offsets
	if len(mockZk.commitHistory) != 1 {
		t.Errorf("Worker manager should commit offset only once")
	}
	if mockZk.commitHistory[topicPartition] != 5 {
		t.Errorf("Worker manager should commit offset 5")
	}
}

func checkAllWorkersAvailable(t *testing.T, wm *WorkerManager) {
	Trace("test", "Checking all workers availability")
	//if all workers are available we shouldn't be able to insert one more available worker
	select {
	case wm.availableWorkers <- &Worker{}:
		t.Error("Not all workers are available")
	case <-time.After(1 * time.Second):
	}
}

func benchmarkWorkerManager(b *testing.B, numWorkers int, msgsPerBatch int) {
	wmid := "test-WM"
	config := DefaultConsumerConfig()
	config.NumWorkers = numWorkers
	config.Strategy = goodStrategy
	mockZk := newMockZookeeperCoordinator()
	config.Coordinator = mockZk
	config.OffsetStorage = mockZk
	topicPartition := TopicAndPartition{"fakeTopic", int32(0)}

	metrics := newConsumerMetrics(wmid, "")
	closeConsumer := make(chan bool)
	manager := NewWorkerManager(wmid, config, topicPartition, metrics, closeConsumer)

	go manager.Start()

	batch := make([]*Message, msgsPerBatch)
	for i := range batch {
		batch[i] = &Message{Offset: int64(i)}
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		manager.inputChannel <- batch
	}
	<-manager.Stop()
}

func BenchmarkWorkerManager_SmallWorkerPoolSmallBatch(b *testing.B) {
	benchmarkWorkerManager(b, 2, 6)
}

func BenchmarkWorkerManager_MediumWorkerPoolSmallBatch(b *testing.B) {
	benchmarkWorkerManager(b, 30, 6)
}

func BenchmarkWorkerManager_LargeWorkerPoolSmallBatch(b *testing.B) {
	benchmarkWorkerManager(b, 100, 6)
}

func BenchmarkWorkerManager_SmallWorkerPoolMediumBatch(b *testing.B) {
	benchmarkWorkerManager(b, 2, 100)
}

func BenchmarkWorkerManager_MediumWorkerPoolMediumBatch(b *testing.B) {
	benchmarkWorkerManager(b, 30, 100)
}

func BenchmarkWorkerManager_LargeWorkerPoolMediumBatch(b *testing.B) {
	benchmarkWorkerManager(b, 100, 100)
}

func BenchmarkWorkerManager_SmallWorkerPoolLargeBatch(b *testing.B) {
	benchmarkWorkerManager(b, 2, 1000)
}

func BenchmarkWorkerManager_MediumWorkerPoolLargeBatch(b *testing.B) {
	benchmarkWorkerManager(b, 30, 1000)
}

func BenchmarkWorkerManager_LargeWorkerPoolLargeBatch(b *testing.B) {
	benchmarkWorkerManager(b, 100, 1000)
}
<commit_msg>comprehensive benchmarks<commit_after>/* Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */

package go_kafka_client

import (
	"testing"
	"time"
)

var goodStrategy = func(_ *Worker, _ *Message, id TaskId) WorkerResult { return NewSuccessfulResult(id) }
var failStrategy = func(_ *Worker, _ *Message, id TaskId) WorkerResult { return NewProcessingFailedResult(id) }
var slowStrategy = func(_ *Worker, _ *Message, id TaskId) WorkerResult {
	time.Sleep(5 * time.Second)
	return NewSuccessfulResult(id)
}

func sleepStrategy(sleepfor time.Duration) func(_ *Worker, _ *Message, id TaskId) WorkerResult {
	return func(_ *Worker, _ *Message, id TaskId) WorkerResult {
		time.Sleep(sleepfor)
		return NewSuccessfulResult(id)
	}
}

func TestFailureCounter(t *testing.T) {
	threshold := int32(5)
	failTimeWindow := 2 * time.Second

	counter := NewFailureCounter(threshold, failTimeWindow)

	failed := false
	for i := 0; i < int(threshold); i++ {
		failed = failed || counter.Failed()
	}

	if !failed {
		t.Error("Failure counter should fail when threshold is reached")
	}

	counter = NewFailureCounter(threshold, failTimeWindow)
	failed = false
	for i := 0; i < int(threshold)-1; i++ {
		failed = failed || counter.Failed()
	}
	time.Sleep(failTimeWindow + (100 * time.Millisecond))
	failed = failed || counter.Failed()

	if failed {
		t.Error("Failure counter should not fail when threshold is not reached within a given time window")
	}
}

func TestWorker(t *testing.T) {
	outChannel := make(chan WorkerResult)
	taskTimeout := 1 * time.Second

	task := &Task{
		Msg: &Message{},
	}

	//test good case
	worker := &Worker{
		OutputChannel: outChannel,
		TaskTimeout:   taskTimeout,
	}
	worker.Start(task, goodStrategy)

	result := <-outChannel
	if !result.Success() {
		t.Error("Worker result with good strategy should be successful")
	}

	//test fail case
	worker2 := &Worker{
		OutputChannel: outChannel,
		TaskTimeout:   taskTimeout,
	}
	worker2.Start(task, failStrategy)
	result = <-outChannel
	if result.Success() {
		t.Error("Worker result with fail strategy should be unsuccessful")
	}

	//test timeout
	worker3 := &Worker{
		OutputChannel: outChannel,
		TaskTimeout:   taskTimeout,
	}
	worker3.Start(task, slowStrategy)
	result = <-outChannel
	if _, ok := result.(*TimedOutResult); !ok {
		t.Error("Worker with slow strategy should time out")
	}

	select {
	case <-outChannel:
		{
			t.Error("Worker should not produce any result after timeout")
		}
	case <-time.After(taskTimeout + time.Second):
	}
}

func TestWorkerManager(t *testing.T) {
	wmid := "test-WM"
	config := DefaultConsumerConfig()
	config.NumWorkers = 3
	config.Strategy = goodStrategy
	mockZk := newMockZookeeperCoordinator()
	config.Coordinator = mockZk
	config.OffsetStorage = mockZk
	topicPartition := TopicAndPartition{"fakeTopic", int32(0)}

	metrics := newConsumerMetrics(wmid, "")
	closeConsumer := make(chan bool)
	manager := NewWorkerManager(wmid, config, topicPartition, metrics, closeConsumer)

	go manager.Start()

	if len(manager.workers) != config.NumWorkers {
		t.Errorf("Number of workers of worker manager should be %d, actual: %d", config.NumWorkers, len(manager.workers))
	}

	checkAllWorkersAvailable(t, manager)

	batch := []*Message{
		&Message{Offset: 0},
		&Message{Offset: 1},
		&Message{Offset: 2},
		&Message{Offset: 3},
		&Message{Offset: 4},
		&Message{Offset: 5},
	}

	manager.inputChannel <- batch

	time.Sleep(1 * time.Second)
	checkAllWorkersAvailable(t, manager)

	<-manager.Stop()

	//make sure we don't lose our offsets
	if len(mockZk.commitHistory) != 1 {
		t.Errorf("Worker manager should commit offset only once")
	}
	if mockZk.commitHistory[topicPartition] != 5 {
		t.Errorf("Worker manager should commit offset 5")
	}
}

func checkAllWorkersAvailable(t *testing.T, wm *WorkerManager) {
	Trace("test", "Checking all workers availability")
	//if all workers are available we shouldn't be able to insert one more available worker
	select {
	case wm.availableWorkers <- &Worker{}:
		t.Error("Not all workers are available")
	case <-time.After(1 * time.Second):
	}
}

func benchmarkWorkerManager(b *testing.B, numWorkers int, msgsPerBatch int, sleepFor time.Duration) {
	wmid := "test-WM"
	config := DefaultConsumerConfig()
	config.NumWorkers = numWorkers
	config.Strategy = sleepStrategy(sleepFor)
	mockZk := newMockZookeeperCoordinator()
	config.Coordinator = mockZk
	config.OffsetStorage = mockZk
	topicPartition := TopicAndPartition{"fakeTopic", int32(0)}

	metrics := newConsumerMetrics(wmid, "")
	closeConsumer := make(chan bool)
	manager := NewWorkerManager(wmid, config, topicPartition, metrics, closeConsumer)

	go manager.Start()
	batch := make([]*Message, msgsPerBatch)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		for j := range batch {
			batch[j] = &Message{Offset: int64(i*msgsPerBatch + j)}
		}
		manager.inputChannel <- batch
	}
	<-manager.Stop()

	// verify correctness
	if mockZk.commitHistory[topicPartition] != int64(b.N*msgsPerBatch-1) {
		b.Errorf("Worker manager commit - have=%d want=%d", mockZk.commitHistory[topicPartition], int64(b.N*msgsPerBatch-1))
	}
}

func BenchmarkWorkerManager_1worker_1msg_10us(b *testing.B) {
	benchmarkWorkerManager(b, 1, 1, 10*time.Microsecond)
}

func BenchmarkWorkerManager_1worker_1msg_100us(b *testing.B) {
	benchmarkWorkerManager(b, 1, 1, 100*time.Microsecond)
}

func BenchmarkWorkerManager_1worker_1msg_1000us(b *testing.B) {
	benchmarkWorkerManager(b, 1, 1, 1000*time.Microsecond)
}

func BenchmarkWorkerManager_1worker_10msg_10us(b *testing.B) {
	benchmarkWorkerManager(b, 1, 10, 10*time.Microsecond)
}

func BenchmarkWorkerManager_1worker_10msg_100us(b *testing.B) {
	benchmarkWorkerManager(b, 1, 10, 100*time.Microsecond)
}

func BenchmarkWorkerManager_1worker_10msg_1000us(b *testing.B) {
	benchmarkWorkerManager(b, 1, 10, 1000*time.Microsecond)
}

func BenchmarkWorkerManager_1worker_100msg_10us(b *testing.B) {
	benchmarkWorkerManager(b, 1, 10, 10*time.Microsecond)
}

func BenchmarkWorkerManager_1worker_100msg_100us(b *testing.B) {
	benchmarkWorkerManager(b, 1, 10, 100*time.Microsecond)
}

func BenchmarkWorkerManager_1worker_100msg_1000us(b *testing.B) {
	benchmarkWorkerManager(b, 1, 10, 1000*time.Microsecond)
}

func BenchmarkWorkerManager_5worker_1msg_10us(b *testing.B) {
	benchmarkWorkerManager(b, 5, 1, 10*time.Microsecond)
}

func BenchmarkWorkerManager_5worker_1msg_100us(b *testing.B) {
	benchmarkWorkerManager(b, 5, 1, 100*time.Microsecond)
}

func BenchmarkWorkerManager_5worker_1msg_1000us(b *testing.B) {
	benchmarkWorkerManager(b, 5, 1, 1000*time.Microsecond)
}

func BenchmarkWorkerManager_5worker_10msg_10us(b *testing.B) {
	benchmarkWorkerManager(b, 5, 10, 10*time.Microsecond)
}

func BenchmarkWorkerManager_5worker_10msg_100us(b *testing.B) {
	benchmarkWorkerManager(b, 5, 10, 100*time.Microsecond)
}

func BenchmarkWorkerManager_5worker_10msg_1000us(b *testing.B) {
	benchmarkWorkerManager(b, 5, 10, 1000*time.Microsecond)
}

func BenchmarkWorkerManager_5worker_100msg_10us(b *testing.B) {
	benchmarkWorkerManager(b, 5, 10, 10*time.Microsecond)
}

func BenchmarkWorkerManager_5worker_100msg_100us(b *testing.B) {
	benchmarkWorkerManager(b, 5, 10, 100*time.Microsecond)
}

func BenchmarkWorkerManager_5worker_100msg_1000us(b *testing.B) {
	benchmarkWorkerManager(b, 5, 10, 1000*time.Microsecond)
}

func BenchmarkWorkerManager_25worker_1msg_10us(b *testing.B) {
	benchmarkWorkerManager(b, 25, 1, 10*time.Microsecond)
}

func BenchmarkWorkerManager_25worker_1msg_100us(b *testing.B) {
	benchmarkWorkerManager(b, 25, 1, 100*time.Microsecond)
}

func BenchmarkWorkerManager_25worker_1msg_1000us(b *testing.B) {
	benchmarkWorkerManager(b, 25, 1, 1000*time.Microsecond)
}

func BenchmarkWorkerManager_25worker_10msg_10us(b *testing.B) {
	benchmarkWorkerManager(b, 25, 10, 10*time.Microsecond)
}

func BenchmarkWorkerManager_25worker_10msg_100us(b *testing.B) {
	benchmarkWorkerManager(b, 25, 10, 100*time.Microsecond)
}

func BenchmarkWorkerManager_25worker_10msg_1000us(b *testing.B) {
	benchmarkWorkerManager(b, 25, 10, 1000*time.Microsecond)
}

func BenchmarkWorkerManager_25worker_100msg_10us(b *testing.B) {
	benchmarkWorkerManager(b, 25, 10, 10*time.Microsecond)
}

func BenchmarkWorkerManager_25worker_100msg_100us(b *testing.B) {
	benchmarkWorkerManager(b, 25, 10, 100*time.Microsecond)
}

func BenchmarkWorkerManager_25worker_100msg_1000us(b *testing.B) {
	benchmarkWorkerManager(b, 25, 10, 1000*time.Microsecond)
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2017 Tigera, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package conntrack

import (
	"net"
	"os/exec"
	"strings"

	log "github.com/Sirupsen/logrus"
)

// For TCP/UDP, each conntrack entry holds two copies of the tuple
// (src addr, dst addr, src port, dst port).  One copy for the original direction and one copy for
// the reply direction.  This is how the kernel handles NAT: by looking up the tuple for a packet
// by its original tuple and mapping onto the corresponding reply direction tuple (or vice versa).
//
// When we delete conntrack entries by IP address, we need to specify which element of the tuple
// to look in.  This slice holds the flags corresponding to the fields we care about.  Since we're
// deleting entries for local workload endpoints, either the endpoint originated the traffic, or it
// received the traffic and replied to it.  In the originating case, the "original source" will be
// set to the endpoint's IP; in the other case, the "reply source". Hence, it's sufficient to only
// look in those two fields.
var deleteDirections = []string{
	"--orig-src",
	"--reply-src",
}

const numRetries = 3

type Conntrack struct {
	newCmd newCmd
}

func New() *Conntrack {
	return NewWithCmdShim(func(name string, arg ...string) CmdIface {
		return exec.Command(name, arg...)
	})
}

// NewWithCmdShim is a test constructor that allows for shimming exec.Command.
func NewWithCmdShim(newCmd newCmd) *Conntrack {
	return &Conntrack{
		newCmd: newCmd,
	}
}

type newCmd func(name string, arg ...string) CmdIface

type CmdIface interface {
	CombinedOutput() ([]byte, error)
}

func (c Conntrack) RemoveConntrackFlows(ipVersion uint8, ipAddr net.IP) {
	var family string
	switch ipVersion {
	case 4:
		family = "ipv4"
	case 6:
		family = "ipv6"
	default:
		log.WithField("version", ipVersion).Panic("Unknown IP version")
	}
	log.WithField("ip", ipAddr).Info("Removing conntrack flows")
	for _, direction := range deleteDirections {
		logCxt := log.WithFields(log.Fields{"ip": ipAddr, "direction": direction})
		// Retry a few times because the conntrack command seems to fail at random.
		for retry := 0; retry <= numRetries; retry += 1 {
			cmd := c.newCmd("conntrack",
				"--family", family,
				"--delete", direction,
				ipAddr.String())
			output, err := cmd.CombinedOutput()
			if err == nil {
				logCxt.Debug("Successfully removed conntrack flows.")
				break
			}
			if strings.Contains(string(output), "0 flow entries") {
				// Success, there were no flows.
				logCxt.Debug("IP wasn't in conntrack")
				break
			}
			if retry == numRetries {
				logCxt.WithError(err).Error("Failed to remove conntrack flows after retries.")
			} else {
				logCxt.WithError(err).Warn("Failed to remove conntrack flows, will retry...")
			}
		}
	}
}
<commit_msg>Code review markups.<commit_after>// Copyright (c) 2016-2017 Tigera, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package conntrack

import (
	"net"
	"os/exec"
	"strings"

	log "github.com/Sirupsen/logrus"
)

// For TCP/UDP, each conntrack entry holds two copies of the tuple
// (src addr, dst addr, src port, dst port).  One copy for the original direction and one copy for
// the reply direction.  This is how the kernel handles NAT: by looking up the tuple for a packet
// by its original tuple and mapping onto the corresponding reply direction tuple (or vice versa).
// The reply tuple is calculated when the original outgoing packet is processed (and possibly
// NATted).
//
// When we delete conntrack entries by IP address, we need to specify which element of the tuple
// to look in.  This slice holds the flags corresponding to the fields we care about.  Since we're
// deleting entries for local workload endpoints, either the endpoint originated the traffic, or it
// received the traffic and replied to it.  In the originating case, the "original source" will be
// set to the endpoint's IP; in the other case, the "reply source". Hence, it's sufficient to only
// look in those two fields.
var deleteDirections = []string{
	"--orig-src",
	"--reply-src",
}

const numRetries = 3

type Conntrack struct {
	newCmd newCmd
}

func New() *Conntrack {
	return NewWithCmdShim(func(name string, arg ...string) CmdIface {
		return exec.Command(name, arg...)
	})
}

// NewWithCmdShim is a test constructor that allows for shimming exec.Command.
func NewWithCmdShim(newCmd newCmd) *Conntrack {
	return &Conntrack{
		newCmd: newCmd,
	}
}

type newCmd func(name string, arg ...string) CmdIface

type CmdIface interface {
	CombinedOutput() ([]byte, error)
}

func (c Conntrack) RemoveConntrackFlows(ipVersion uint8, ipAddr net.IP) {
	var family string
	switch ipVersion {
	case 4:
		family = "ipv4"
	case 6:
		family = "ipv6"
	default:
		log.WithField("version", ipVersion).Panic("Unknown IP version")
	}
	log.WithField("ip", ipAddr).Info("Removing conntrack flows")
	for _, direction := range deleteDirections {
		logCxt := log.WithFields(log.Fields{"ip": ipAddr, "direction": direction})
		// Retry a few times because the conntrack command seems to fail at random.
		for retry := 0; retry <= numRetries; retry += 1 {
			cmd := c.newCmd("conntrack",
				"--family", family,
				"--delete", direction,
				ipAddr.String())
			output, err := cmd.CombinedOutput()
			if err == nil {
				logCxt.Debug("Successfully removed conntrack flows.")
				break
			}
			if strings.Contains(string(output), "0 flow entries") {
				// Success, there were no flows.
				logCxt.Debug("IP wasn't in conntrack")
				break
			}
			if retry == numRetries {
				logCxt.WithError(err).Error("Failed to remove conntrack flows after retries.")
			} else {
				logCxt.WithError(err).Warn("Failed to remove conntrack flows, will retry...")
			}
		}
	}
}
<|endoftext|>


<commit_before>package consensus

import (
	"crypto/ecdsa"
	"math"

	log "github.com/Sirupsen/logrus"
	"github.com/ubclaunchpad/cumulus/blockchain"
	c "github.com/ubclaunchpad/cumulus/common/constants"
)

// VerifyTransaction tests whether a transaction valid.
func VerifyTransaction(bc *blockchain.BlockChain,
	t *blockchain.Transaction) (bool, TransactionCode) {

	// Check if the transaction is equal to nil
	if t == nil {
		return false, NilTransaction
	}

	// Find the transaction input in the chain (by hash)
	var input *blockchain.Transaction
	input = bc.GetInputTransaction(t)
	if input == nil || blockchain.HashSum(input) != t.Input.Hash {
		return false, NoInputTransaction
	}

	// Check that output to sender in input is equal to outputs in t
	if !input.InputsEqualOutputs(t) {
		return false, Overspend
	}

	// Verify signature of t
	hash := blockchain.HashSum(t.TxBody)
	if !ecdsa.Verify(t.Sender.Key(), hash.Marshal(), t.Sig.R, t.Sig.S) {
		return false, BadSig
	}

	// Test if identical transaction already exists in chain.
	end := uint32(len(bc.Blocks))
	start := t.Input.BlockNumber
	if exists, _, _ := bc.ContainsTransaction(t, start, end); exists {
		return false, Respend
	}

	return true, ValidTransaction
}

// VerifyCloudBase returns true if a transaction is a valid CloudBase transaction
// and false otherwise
func VerifyCloudBase(bc *blockchain.BlockChain,
	t *blockchain.Transaction) (bool, CloudBaseTransactionCode) {

	// Check if the CloudBase transaction is equal to nil.
	if t == nil {
		return false, NilCloudBaseTransaction
	}

	// Check that the sender address is nil.
	if t.Sender != blockchain.NilAddr {
		return false, BadCloudBaseSender
	}

	// Check that the input is 0.
	if t.TxBody.Input.BlockNumber != 0 ||
		t.TxBody.Input.Hash != blockchain.NilHash ||
		t.Input.Index != 0 {
		return false, BadCloudBaseInput
	}

	// Search for the block associated with the CloudBase transaction. If the
	// transaction is not found, then it is to be added to the next block in the
	// blockchain.
	var i int
	for i = 0; i < len(bc.Blocks); i++ {
		if b, _ := bc.Blocks[i].ContainsTransaction(t); b {
			break
		}
	}

	// Determine the reward associated with that specific block.
	timesHalved := float64(((i + 1) / blockRewardHalvingRate))
	reward := StartingBlockReward / uint64(math.Pow(float64(2), timesHalved))

	// Check that the output is properly set.
	if len(t.Outputs) != 1 || t.Outputs[0].Recipient == blockchain.NilAddr {
		return false, BadCloudBaseOutput
	}

	// Check that the reward is properly set.
	if t.Outputs[0].Amount != reward {
		return false, BadCloudBaseReward
	}

	// Assert that the signature is equal to nil.
	if t.Sig != blockchain.NilSig {
		return false, BadCloudBaseSig
	}

	return true, ValidCloudBaseTransaction
}

// VerifyGenesisBlock checks whether a block is a valid genesis block.
func VerifyGenesisBlock(bc *blockchain.BlockChain,
	gb *blockchain.Block) (bool, GenesisBlockCode) {

	// Check if the genesis block is equal to nil.
	if gb == nil {
		return false, NilGenesisBlock
	}

	// Check if the genesis block's block number is equal to 0.
	if gb.BlockHeader.BlockNumber != 0 ||
		bc.Blocks[0] != gb {
		return false, BadGenesisBlockNumber
	}

	// Check if the genesis block's last block hash is equal to 0.
	if blockchain.HashToBigInt(gb.BlockHeader.LastBlock).Cmp(c.Big0) != 0 {
		return false, BadGenesisLastBlock
	}

	// Check if the size of the transaction list is equal to 1.
	if len(gb.Transactions) != 1 {
		return false, BadGenesisTransactions
	}

	// Check if the transaction is a valid cloud base transaction.
	if valid, code := VerifyCloudBase(bc, gb.Transactions[0]); !valid {
		log.Errorf("Invalid CloudBase, CloudBaseTransactionCode: %d", code)
		return false, BadGenesisCloudBaseTransaction
	}

	// Check that the target is within the min and max difficulty levels.
	// TODO: CurrentTarget() is used because the difficulty is static for v1 of
	// cumulus. Once the difficulty is dynamic, the target would need to be
	// compared to the target calculated for that specific block.
	target := blockchain.HashToBigInt(gb.Target)
	if target.Cmp(c.MaxTarget) == 1 ||
		target.Cmp(c.MinTarget) == -1 ||
		target.Cmp(blockchain.HashToBigInt(CurrentTarget())) != 0 {
		return false, BadGenesisTarget
	}

	// Check that time is not greater than current time or equal to 0.
	if uint32(gb.Time) == 0 {
		return false, BadGenesisTime
	}

	return true, ValidGenesisBlock
}

// VerifyBlock checks whether a block is valid.
func VerifyBlock(bc *blockchain.BlockChain,
	b *blockchain.Block) (bool, BlockCode) {

	// Check if the block is equal to nil.
	if b == nil {
		return false, NilBlock
	}

	// Check if the block is the genesis block.
	if b.BlockHeader.BlockNumber == 0 || bc.Blocks[0] == b {
		if valid, code := VerifyGenesisBlock(bc, b); !valid {
			log.Errorf("Invalid GenesisBlock, GenesisBlockCode: %d", code)
			return false, BadGenesisBlock
		}
		return true, ValidBlock
	}

	// Check that block number is between 0 and max blocks.
	ix := b.BlockNumber - 1
	if int(ix) > len(bc.Blocks)-1 || ix < 0 {
		return false, BadBlockNumber
	}

	// Check that block number is one greater than last block
	lastBlock := bc.Blocks[ix]
	if lastBlock.BlockNumber != ix {
		return false, BadBlockNumber
	}

	// Check that the first transaction is a CloudBase transaction
	if valid, code := VerifyCloudBase(bc, b.GetCloudBaseTransaction()); !valid {
		log.Errorf("Invalid CloudBase, CloudBaseTransactionCode: %d", code)
		return false, BadCloudBaseTransaction
	}

	// Verify every Transaction in the block.
	for _, t := range b.Transactions[1:] {
		if valid, code := VerifyTransaction(bc, t); !valid {
			log.Errorf("Invalid Transaction, TransactionCode: %d", code)
			return false, BadTransaction
		}
	}

	// Check that the target is within the min and max difficulty levels and
	// that the target is correct.
	// TODO: CurrentTarget() is used because the difficulty is static for v1 of
	// cumulus. Once the difficulty is dynamic, the target would need to be
	// compared to the target calculated for that specific block.
	target := blockchain.HashToBigInt(b.Target)
	if target.Cmp(c.MaxTarget) == 1 ||
		target.Cmp(c.MinTarget) == -1 ||
		target.Cmp(blockchain.HashToBigInt(CurrentTarget())) != 0 {
		return false, BadTarget
	}

	// Check that time is not greater than current time or equal to 0
	if uint32(b.Time) == 0 {
		return false, BadTime
	}

	// Check that hash of last block is correct
	if blockchain.HashSum(lastBlock) != b.LastBlock {
		return false, BadHash
	}

	// Verify proof of work
	if !blockchain.HashSum(b).LessThan(b.Target) {
		return false, BadNonce
	}

	// Check for multiple transactions referencing same input transaction.
	for i, trA := range b.Transactions {
		for j, trB := range b.Transactions {
			if (i != j) && (trA.Input.Hash == trB.Input.Hash) {
				inputTr := bc.GetInputTransaction(trA)
				if !inputTr.InputsEqualOutputs(trA, trB) {
					return false, DoubleSpend
				}
			}
		}
	}

	return true, ValidBlock
}
<commit_msg>PR fixes<commit_after>package consensus

import (
	"crypto/ecdsa"
	"math"

	log "github.com/Sirupsen/logrus"
	"github.com/ubclaunchpad/cumulus/blockchain"
	c "github.com/ubclaunchpad/cumulus/common/constants"
)

// VerifyTransaction tests whether a transaction valid.
func VerifyTransaction(bc *blockchain.BlockChain,
	t *blockchain.Transaction) (bool, TransactionCode) {

	// Check if the transaction is equal to nil
	if t == nil {
		return false, NilTransaction
	}

	// Find the transaction input in the chain (by hash)
	input := bc.GetInputTransaction(t)
	if input == nil || blockchain.HashSum(input) != t.Input.Hash {
		return false, NoInputTransaction
	}

	// Check that output to sender in input is equal to outputs in t
	if !input.InputsEqualOutputs(t) {
		return false, Overspend
	}

	// Verify signature of t
	hash := blockchain.HashSum(t.TxBody)
	if !ecdsa.Verify(t.Sender.Key(), hash.Marshal(), t.Sig.R, t.Sig.S) {
		return false, BadSig
	}

	// Test if identical transaction already exists in chain.
	end := uint32(len(bc.Blocks))
	start := t.Input.BlockNumber
	if exists, _, _ := bc.ContainsTransaction(t, start, end); exists {
		return false, Respend
	}

	return true, ValidTransaction
}

// VerifyCloudBase returns true if a transaction is a valid CloudBase transaction
// and false otherwise
func VerifyCloudBase(bc *blockchain.BlockChain,
	t *blockchain.Transaction) (bool, CloudBaseTransactionCode) {

	// Check if the CloudBase transaction is equal to nil.
	if t == nil {
		return false, NilCloudBaseTransaction
	}

	// Check that the sender address is nil.
	if t.Sender != blockchain.NilAddr {
		return false, BadCloudBaseSender
	}

	// Check that the input is 0.
	if t.TxBody.Input.BlockNumber != 0 ||
		t.TxBody.Input.Hash != blockchain.NilHash ||
		t.Input.Index != 0 {
		return false, BadCloudBaseInput
	}

	// Search for the block associated with the CloudBase transaction. If the
	// transaction is not found, then it is to be added to the next block in the
	// blockchain.
	var i int
	for i = 0; i < len(bc.Blocks); i++ {
		if b, _ := bc.Blocks[i].ContainsTransaction(t); b {
			break
		}
	}

	// Determine the reward associated with that specific block.
	timesHalved := float64(((i + 1) / blockRewardHalvingRate))
	reward := StartingBlockReward / uint64(math.Pow(float64(2), timesHalved))

	// Check that the output is properly set.
	if len(t.Outputs) != 1 || t.Outputs[0].Recipient == blockchain.NilAddr {
		return false, BadCloudBaseOutput
	}

	// Check that the reward is properly set.
	if t.Outputs[0].Amount != reward {
		return false, BadCloudBaseReward
	}

	// Assert that the signature is equal to nil.
	if t.Sig != blockchain.NilSig {
		return false, BadCloudBaseSig
	}

	return true, ValidCloudBaseTransaction
}

// VerifyGenesisBlock checks whether a block is a valid genesis block.
func VerifyGenesisBlock(bc *blockchain.BlockChain,
	gb *blockchain.Block) (bool, GenesisBlockCode) {

	// Check if the genesis block is equal to nil.
	if gb == nil {
		return false, NilGenesisBlock
	}

	// Check if the genesis block's block number is equal to 0.
	if gb.BlockHeader.BlockNumber != 0 ||
		bc.Blocks[0] != gb {
		return false, BadGenesisBlockNumber
	}

	// Check if the genesis block's last block hash is equal to 0.
	if blockchain.HashToBigInt(gb.BlockHeader.LastBlock).Cmp(c.Big0) != 0 {
		return false, BadGenesisLastBlock
	}

	// Check if the size of the transaction list is equal to 1.
	if len(gb.Transactions) != 1 {
		return false, BadGenesisTransactions
	}

	// Check if the transaction is a valid cloud base transaction.
	if valid, code := VerifyCloudBase(bc, gb.Transactions[0]); !valid {
		log.Errorf("Invalid CloudBase, CloudBaseTransactionCode: %d", code)
		return false, BadGenesisCloudBaseTransaction
	}

	// Check that the target is within the min and max difficulty levels.
	// TODO: CurrentTarget() is used because the difficulty is static for v1 of
	// cumulus. Once the difficulty is dynamic, the target would need to be
	// compared to the target calculated for that specific block.
	target := blockchain.HashToBigInt(gb.Target)
	if target.Cmp(c.MaxTarget) == 1 ||
		target.Cmp(c.MinTarget) == -1 ||
		target.Cmp(blockchain.HashToBigInt(CurrentTarget())) != 0 {
		return false, BadGenesisTarget
	}

	// Check that time is not greater than current time or equal to 0.
	// TODO: check if time is the current time
	if gb.Time == 0 {
		return false, BadGenesisTime
	}

	return true, ValidGenesisBlock
}

// VerifyBlock checks whether a block is valid.
func VerifyBlock(bc *blockchain.BlockChain,
	b *blockchain.Block) (bool, BlockCode) {

	// Check if the block is equal to nil.
	if b == nil {
		return false, NilBlock
	}

	// Check if the block is the genesis block.
	if b.BlockHeader.BlockNumber == 0 || bc.Blocks[0] == b {
		if valid, code := VerifyGenesisBlock(bc, b); !valid {
			log.Errorf("Invalid GenesisBlock, GenesisBlockCode: %d", code)
			return false, BadGenesisBlock
		}
		return true, ValidBlock
	}

	// Check that block number is valid.
	ix := b.BlockNumber - 1
	if int(ix) > len(bc.Blocks)-1 || ix < 0 {
		return false, BadBlockNumber
	}

	// Check that block number is one greater than last block
	lastBlock := bc.Blocks[ix]
	if lastBlock.BlockNumber != ix {
		return false, BadBlockNumber
	}

	// Check that the first transaction is a CloudBase transaction
	if valid, code := VerifyCloudBase(bc, b.GetCloudBaseTransaction()); !valid {
		log.Errorf("Invalid CloudBase, CloudBaseTransactionCode: %d", code)
		return false, BadCloudBaseTransaction
	}

	// Verify every Transaction in the block.
	for _, t := range b.Transactions[1:] {
		if valid, code := VerifyTransaction(bc, t); !valid {
			log.Errorf("Invalid Transaction, TransactionCode: %d", code)
			return false, BadTransaction
		}
	}

	// Check that the target is within the min and max difficulty levels and
	// that the target is correct.
	// TODO: CurrentTarget() is used because the difficulty is static for v1 of
	// cumulus. Once the difficulty is dynamic, the target would need to be
	// compared to the target calculated for that specific block.
	target := blockchain.HashToBigInt(b.Target)
	if target.Cmp(blockchain.HashToBigInt(CurrentTarget())) != 0 {
		return false, BadTarget
	}

	// Check that time is not greater than current time or equal to 0
	// TODO: check if time is the current time
	if b.Time == 0 {
		return false, BadTime
	}

	// Check that hash of last block is correct
	if blockchain.HashSum(lastBlock) != b.LastBlock {
		return false, BadHash
	}

	// Verify proof of work
	if !blockchain.HashSum(b).LessThan(b.Target) {
		return false, BadNonce
	}

	// Check for multiple transactions referencing same input transaction.
	for i, trA := range b.Transactions {
		for j, trB := range b.Transactions {
			if (i != j) && (trA.Input.Hash == trB.Input.Hash) {
				inputTr := bc.GetInputTransaction(trA)
				if !inputTr.InputsEqualOutputs(trA, trB) {
					return false, DoubleSpend
				}
			}
		}
	}

	return true, ValidBlock
}
<|endoftext|>