<commit_before>package handlers

import (
	"github.com/danryan/hal"
	"github.com/davecgh/go-spew/spew"
)

const okStr = "OK"

// GetHandler pulls a key out of the store, when addressed like so:
// foobar?
// bawt> get: foobar=baz
var GetHandler = hal.Hear(`^(\w+)\?$`, func(res *hal.Response) error {
	key := res.Match[1]
	val, err := res.Robot.Store.Get(key)
	if err != nil {
		res.Send(err.Error())
		return err
	}
	return res.Send(string(val))
})

// BUG(fujin): Add AppendHandler for 'is/are also' info-form.
// SetHandler does a simple match/set into the Store
var SetHandler = hal.Hear(`^(\w+)\s+(?:is|are)\s+(\w+)$`, func(res *hal.Response) error {
	key := res.Match[1]
	val := res.Match[2]
	err := res.Robot.Store.Set(key, []byte(val))
	if err != nil {
		res.Send(err.Error())
		return err
	}
	return res.Send(okStr)
})

// DeleteHandler nukes keys in the Store
var DeleteHandler = hal.Hear(`^!forget (\w+)$`, func(res *hal.Response) error {
	key := res.Match[1]

	if err := res.Robot.Store.Delete(key); err != nil {
		res.Send(err.Error())
		return err
	}
	return res.Send(okStr)
})

// UsersHandler provides insite into the Robots user storage
var UsersHandler = hal.Hear(`^!show users$`, func(res *hal.Response) error {
	lines := []string{}
	for _, user := range res.Robot.Users.All() {
		lines = append(lines, spew.Sdump(user))
	}
	return res.Send(lines...)
})

// UserHandler is the singular form of UsersHandler
var UserHandler = hal.Hear(`^!show user (.+)$`, func(res *hal.Response) error {
	id := res.Match[1]
	user, _ := res.Robot.Users.Get(id)
	line := spew.Sdump(user)
	return res.Send(line)
})
<commit_msg>make keyword response more obvious<commit_after>package handlers

import (
	"fmt"
	"github.com/danryan/hal"
	"github.com/davecgh/go-spew/spew"
)

const okStr = "OK"

// GetHandler pulls a key out of the store, when addressed like so:
// foobar?
// bawt> get: foobar=baz
var GetHandler = hal.Hear(`^(\w+)\?$`, func(res *hal.Response) error {
	key := res.Match[1]
	val, err := res.Robot.Store.Get(key)
	if err != nil {
		res.Send(err.Error())
		return err
	}
	return res.Send(fmt.Sprintf("%s -> %s", key, string(val)))
})

// BUG(fujin): Add AppendHandler for 'is/are also' info-form.
// SetHandler does a simple match/set into the Store
var SetHandler = hal.Hear(`^(\w+)\s+(?:is|are)\s+(\w+)$`, func(res *hal.Response) error {
	key := res.Match[1]
	val := res.Match[2]
	err := res.Robot.Store.Set(key, []byte(val))
	if err != nil {
		res.Send(err.Error())
		return err
	}
	return res.Send(okStr)
})

// DeleteHandler nukes keys in the Store
var DeleteHandler = hal.Hear(`^!forget (\w+)$`, func(res *hal.Response) error {
	key := res.Match[1]

	if err := res.Robot.Store.Delete(key); err != nil {
		res.Send(err.Error())
		return err
	}
	return res.Send(okStr)
})

// UsersHandler provides insite into the Robots user storage
var UsersHandler = hal.Hear(`^!show users$`, func(res *hal.Response) error {
	lines := []string{}
	for _, user := range res.Robot.Users.All() {
		lines = append(lines, spew.Sdump(user))
	}
	return res.Send(lines...)
})

// UserHandler is the singular form of UsersHandler
var UserHandler = hal.Hear(`^!show user (.+)$`, func(res *hal.Response) error {
	id := res.Match[1]
	user, _ := res.Robot.Users.Get(id)
	line := spew.Sdump(user)
	return res.Send(line)
})
<|endoftext|>


<commit_before>package common

import (
	"context"
	"fmt"
	"path/filepath"

	"github.com/hashicorp/packer/builder/vsphere/driver"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
)

type StepRemoteUpload struct {
	Datastore                  string
	Host                       string
	SetHostForDatastoreUploads bool
}

func (s *StepRemoteUpload) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
	ui := state.Get("ui").(packer.Ui)
	d := state.Get("driver").(driver.Driver)

	if path, ok := state.GetOk("iso_path"); ok {
		// user-supplied boot iso
		fullRemotePath, err := s.uploadFile(path.(string), d, ui)
		if err != nil {
			state.Put("error", err)
			return multistep.ActionHalt
		}
		state.Put("iso_remote_path", fullRemotePath)
	}
	if cdPath, ok := state.GetOk("cd_path"); ok {
		// Packer-created cd_files disk
		fullRemotePath, err := s.uploadFile(cdPath.(string), d, ui)
		if err != nil {
			state.Put("error", err)
			return multistep.ActionHalt
		}
		state.Put("cd_path", fullRemotePath)
	}

	return multistep.ActionContinue
}

func GetRemoteDirectoryAndPath(path string, ds driver.Datastore) (string, string, string, string) {
	filename := filepath.Base(path)
	remotePath := fmt.Sprintf("packer_cache/%s", filename)
	remoteDirectory := fmt.Sprintf("[%s] packer_cache/", ds.Name())
	fullRemotePath := fmt.Sprintf("%s/%s", remoteDirectory, filename)

	return filename, remotePath, remoteDirectory, fullRemotePath

}
func (s *StepRemoteUpload) uploadFile(path string, d driver.Driver, ui packer.Ui) (string, error) {
	ds, err := d.FindDatastore(s.Datastore, s.Host)
	if err != nil {
		return "", fmt.Errorf("datastore doesn't exist: %v", err)
	}

	filename, remotePath, remoteDirectory, fullRemotePath := GetRemoteDirectoryAndPath(path, ds)

	if exists := ds.FileExists(remotePath); exists == true {
		ui.Say(fmt.Sprintf("File %s already exists; skipping upload.", fullRemotePath))
		return fullRemotePath, nil
	}

	ui.Say(fmt.Sprintf("Uploading %s to %s", filename, remotePath))

	if err := ds.MakeDirectory(remoteDirectory); err != nil {
		return "", err
	}

	if err := ds.UploadFile(path, remotePath, s.Host, s.SetHostForDatastoreUploads); err != nil {
		return "", err
	}
	return fullRemotePath, nil
}

func (s *StepRemoteUpload) Cleanup(state multistep.StateBag) {
	_, cancelled := state.GetOk(multistep.StateCancelled)
	_, halted := state.GetOk(multistep.StateHalted)
	if !cancelled && !halted {
		return
	}

	ui := state.Get("ui").(packer.Ui)
	d := state.Get("driver").(*driver.VCenterDriver)

	if UploadedCDPath, ok := state.GetOk("cd_path"); ok {
		ui.Say("Deleting cd_files image from remote datastore ...")

		ds, err := d.FindDatastore(s.Datastore, s.Host)
		if err != nil {
			state.Put("error", err)
			return
		}

		err = ds.Delete(UploadedCDPath.(string))
		if err != nil {
			state.Put("error", err)
			return
		}

	}
}
<commit_msg>don't try to delete a cd file if the upload of the cd itself failed<commit_after>package common

import (
	"context"
	"fmt"
	"log"
	"path/filepath"

	"github.com/hashicorp/packer/builder/vsphere/driver"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
)

type StepRemoteUpload struct {
	Datastore                  string
	Host                       string
	SetHostForDatastoreUploads bool
	UploadedCustomCD           bool
}

func (s *StepRemoteUpload) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
	ui := state.Get("ui").(packer.Ui)
	d := state.Get("driver").(driver.Driver)

	if path, ok := state.GetOk("iso_path"); ok {
		// user-supplied boot iso
		fullRemotePath, err := s.uploadFile(path.(string), d, ui)
		if err != nil {
			state.Put("error", err)
			return multistep.ActionHalt
		}
		state.Put("iso_remote_path", fullRemotePath)
	}
	if cdPath, ok := state.GetOk("cd_path"); ok {
		// Packer-created cd_files disk
		fullRemotePath, err := s.uploadFile(cdPath.(string), d, ui)
		if err != nil {
			state.Put("error", err)
			return multistep.ActionHalt
		}
		s.UploadedCustomCD = true
		state.Put("cd_path", fullRemotePath)
	}

	return multistep.ActionContinue
}

func GetRemoteDirectoryAndPath(path string, ds driver.Datastore) (string, string, string, string) {
	filename := filepath.Base(path)
	remotePath := fmt.Sprintf("packer_cache/%s", filename)
	remoteDirectory := fmt.Sprintf("[%s] packer_cache/", ds.Name())
	fullRemotePath := fmt.Sprintf("%s/%s", remoteDirectory, filename)

	return filename, remotePath, remoteDirectory, fullRemotePath

}
func (s *StepRemoteUpload) uploadFile(path string, d driver.Driver, ui packer.Ui) (string, error) {
	ds, err := d.FindDatastore(s.Datastore, s.Host)
	if err != nil {
		return "", fmt.Errorf("datastore doesn't exist: %v", err)
	}

	filename, remotePath, remoteDirectory, fullRemotePath := GetRemoteDirectoryAndPath(path, ds)

	if exists := ds.FileExists(remotePath); exists == true {
		ui.Say(fmt.Sprintf("File %s already exists; skipping upload.", fullRemotePath))
		return fullRemotePath, nil
	}

	ui.Say(fmt.Sprintf("Uploading %s to %s", filename, remotePath))

	if err := ds.MakeDirectory(remoteDirectory); err != nil {
		return "", err
	}

	if err := ds.UploadFile(path, remotePath, s.Host, s.SetHostForDatastoreUploads); err != nil {
		return "", err
	}
	return fullRemotePath, nil
}

func (s *StepRemoteUpload) Cleanup(state multistep.StateBag) {
	_, cancelled := state.GetOk(multistep.StateCancelled)
	_, halted := state.GetOk(multistep.StateHalted)
	if !cancelled && !halted {
		return
	}

	ui := state.Get("ui").(packer.Ui)
	d := state.Get("driver").(*driver.VCenterDriver)

	if s.UploadedCustomCD {
		if UploadedCDPath, ok := state.GetOk("cd_path"); ok {
			ui.Say("Deleting cd_files image from remote datastore ...")

			ds, err := d.FindDatastore(s.Datastore, s.Host)
			if err != nil {
				log.Printf("Error finding datastore to delete custom CD; please delete manually: %s", err)
				return
			}

			err = ds.Delete(UploadedCDPath.(string))
			if err != nil {
				log.Printf("Error deleting custom CD from remote datastore; please delete manually: %s", err)
				return
			}
		}
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsSubnet() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSubnetCreate,
		Read:   resourceAwsSubnetRead,
		Update: resourceAwsSubnetUpdate,
		Delete: resourceAwsSubnetDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		SchemaVersion: 1,
		MigrateState:  resourceAwsSubnetMigrateState,

		Schema: map[string]*schema.Schema{
			"vpc_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"cidr_block": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"ipv6_cidr_block": {
				Type:     schema.TypeString,
				Optional: true,
			},

			"availability_zone": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"map_public_ip_on_launch": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},

			"assign_ipv6_address_on_creation": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},

			"ipv6_cidr_block_association_id": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceAwsSubnetCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	createOpts := &ec2.CreateSubnetInput{
		AvailabilityZone: aws.String(d.Get("availability_zone").(string)),
		CidrBlock:        aws.String(d.Get("cidr_block").(string)),
		VpcId:            aws.String(d.Get("vpc_id").(string)),
	}

	if v, ok := d.GetOk("ipv6_cidr_block"); ok {
		createOpts.Ipv6CidrBlock = aws.String(v.(string))
	}

	var err error
	resp, err := conn.CreateSubnet(createOpts)

	if err != nil {
		return fmt.Errorf("Error creating subnet: %s", err)
	}

	// Get the ID and store it
	subnet := resp.Subnet
	d.SetId(*subnet.SubnetId)
	log.Printf("[INFO] Subnet ID: %s", *subnet.SubnetId)

	// Wait for the Subnet to become available
	log.Printf("[DEBUG] Waiting for subnet (%s) to become available", *subnet.SubnetId)
	stateConf := &resource.StateChangeConf{
		Pending: []string{"pending"},
		Target:  []string{"available"},
		Refresh: SubnetStateRefreshFunc(conn, *subnet.SubnetId),
		Timeout: 10 * time.Minute,
	}

	_, err = stateConf.WaitForState()

	if err != nil {
		return fmt.Errorf(
			"Error waiting for subnet (%s) to become ready: %s",
			d.Id(), err)
	}

	return resourceAwsSubnetUpdate(d, meta)
}

func resourceAwsSubnetRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	resp, err := conn.DescribeSubnets(&ec2.DescribeSubnetsInput{
		SubnetIds: []*string{aws.String(d.Id())},
	})

	if err != nil {
		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidSubnetID.NotFound" {
			// Update state to indicate the subnet no longer exists.
			d.SetId("")
			return nil
		}
		return err
	}
	if resp == nil {
		return nil
	}

	subnet := resp.Subnets[0]

	d.Set("vpc_id", subnet.VpcId)
	d.Set("availability_zone", subnet.AvailabilityZone)
	d.Set("cidr_block", subnet.CidrBlock)
	d.Set("map_public_ip_on_launch", subnet.MapPublicIpOnLaunch)
	d.Set("assign_ipv6_address_on_creation", subnet.AssignIpv6AddressOnCreation)
	for _, a := range subnet.Ipv6CidrBlockAssociationSet {
		if *a.Ipv6CidrBlockState.State == "associated" { //we can only ever have 1 IPv6 block associated at once
			d.Set("ipv6_cidr_block_association_id", a.AssociationId)
			d.Set("ipv6_cidr_block", a.Ipv6CidrBlock)
			break
		} else {
			d.Set("ipv6_cidr_block_association_id", "") // we blank these out to remove old entries
			d.Set("ipv6_cidr_block", "")
		}
	}
	d.Set("tags", tagsToMap(subnet.Tags))

	return nil
}

func resourceAwsSubnetUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	d.Partial(true)

	if err := setTags(conn, d); err != nil {
		return err
	} else {
		d.SetPartial("tags")
	}

	if d.HasChange("assign_ipv6_address_on_creation") {
		modifyOpts := &ec2.ModifySubnetAttributeInput{
			SubnetId: aws.String(d.Id()),
			AssignIpv6AddressOnCreation: &ec2.AttributeBooleanValue{
				Value: aws.Bool(d.Get("assign_ipv6_address_on_creation").(bool)),
			},
		}

		log.Printf("[DEBUG] Subnet modify attributes: %#v", modifyOpts)

		_, err := conn.ModifySubnetAttribute(modifyOpts)

		if err != nil {
			return err
		} else {
			d.SetPartial("assign_ipv6_address_on_creation")
		}
	}

	if d.HasChange("map_public_ip_on_launch") {
		modifyOpts := &ec2.ModifySubnetAttributeInput{
			SubnetId: aws.String(d.Id()),
			MapPublicIpOnLaunch: &ec2.AttributeBooleanValue{
				Value: aws.Bool(d.Get("map_public_ip_on_launch").(bool)),
			},
		}

		log.Printf("[DEBUG] Subnet modify attributes: %#v", modifyOpts)

		_, err := conn.ModifySubnetAttribute(modifyOpts)

		if err != nil {
			return err
		} else {
			d.SetPartial("map_public_ip_on_launch")
		}
	}

	// We have to be careful here to not go through a change of association if this is a new resource
	// A New resource here would denote that the Update func is called by the Create func
	if d.HasChange("ipv6_cidr_block") && !d.IsNewResource() {
		// We need to handle that we disassociate the IPv6 CIDR block before we try and associate the new one
		// This could be an issue as, we could error out when we try and add the new one
		// We may need to roll back the state and reattach the old one if this is the case

		_, new := d.GetChange("ipv6_cidr_block")

		//Firstly we have to disassociate the old IPv6 CIDR Block
		disassociateOps := &ec2.DisassociateSubnetCidrBlockInput{
			AssociationId: aws.String(d.Get("ipv6_cidr_block_association_id").(string)),
		}

		_, err := conn.DisassociateSubnetCidrBlock(disassociateOps)
		if err != nil {
			return err
		}

		// Wait for the CIDR to become disassociated
		log.Printf(
			"[DEBUG] Waiting for IPv6 CIDR (%s) to become disassociated",
			d.Id())
		stateConf := &resource.StateChangeConf{
			Pending: []string{"disassociating", "associated"},
			Target:  []string{"disassociated"},
			Refresh: SubnetIpv6CidrStateRefreshFunc(conn, d.Id(), d.Get("ipv6_cidr_block_association_id").(string)),
			Timeout: 1 * time.Minute,
		}
		if _, err := stateConf.WaitForState(); err != nil {
			return fmt.Errorf(
				"Error waiting for IPv6 CIDR (%s) to become disassociated: %s",
				d.Id(), err)
		}

		//Now we need to try and associate the new CIDR block
		associatesOpts := &ec2.AssociateSubnetCidrBlockInput{
			SubnetId:      aws.String(d.Id()),
			Ipv6CidrBlock: aws.String(new.(string)),
		}

		resp, err := conn.AssociateSubnetCidrBlock(associatesOpts)
		if err != nil {
			//The big question here is, do we want to try and reassociate the old one??
			//If we have a failure here, then we may be in a situation that we have nothing associated
			return err
		}

		// Wait for the CIDR to become associated
		log.Printf(
			"[DEBUG] Waiting for IPv6 CIDR (%s) to become associated",
			d.Id())
		stateConf = &resource.StateChangeConf{
			Pending: []string{"associating", "disassociated"},
			Target:  []string{"associated"},
			Refresh: SubnetIpv6CidrStateRefreshFunc(conn, d.Id(), *resp.Ipv6CidrBlockAssociation.AssociationId),
			Timeout: 1 * time.Minute,
		}
		if _, err := stateConf.WaitForState(); err != nil {
			return fmt.Errorf(
				"Error waiting for IPv6 CIDR (%s) to become associated: %s",
				d.Id(), err)
		}

		d.SetPartial("ipv6_cidr_block")
	}

	d.Partial(false)

	return resourceAwsSubnetRead(d, meta)
}

func resourceAwsSubnetDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	log.Printf("[INFO] Deleting subnet: %s", d.Id())
	req := &ec2.DeleteSubnetInput{
		SubnetId: aws.String(d.Id()),
	}

	wait := resource.StateChangeConf{
		Pending:    []string{"pending"},
		Target:     []string{"destroyed"},
		Timeout:    10 * time.Minute,
		MinTimeout: 1 * time.Second,
		Refresh: func() (interface{}, string, error) {
			_, err := conn.DeleteSubnet(req)
			if err != nil {
				if apiErr, ok := err.(awserr.Error); ok {
					if apiErr.Code() == "DependencyViolation" {
						// There is some pending operation, so just retry
						// in a bit.
						return 42, "pending", nil
					}

					if apiErr.Code() == "InvalidSubnetID.NotFound" {
						return 42, "destroyed", nil
					}
				}

				return 42, "failure", err
			}

			return 42, "destroyed", nil
		},
	}

	if _, err := wait.WaitForState(); err != nil {
		return fmt.Errorf("Error deleting subnet: %s", err)
	}

	return nil
}

// SubnetStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch a Subnet.
func SubnetStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		resp, err := conn.DescribeSubnets(&ec2.DescribeSubnetsInput{
			SubnetIds: []*string{aws.String(id)},
		})
		if err != nil {
			if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidSubnetID.NotFound" {
				resp = nil
			} else {
				log.Printf("Error on SubnetStateRefresh: %s", err)
				return nil, "", err
			}
		}

		if resp == nil {
			// Sometimes AWS just has consistency issues and doesn't see
			// our instance yet. Return an empty state.
			return nil, "", nil
		}

		subnet := resp.Subnets[0]
		return subnet, *subnet.State, nil
	}
}

func SubnetIpv6CidrStateRefreshFunc(conn *ec2.EC2, id string, associationId string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		opts := &ec2.DescribeSubnetsInput{
			SubnetIds: []*string{aws.String(id)},
		}
		resp, err := conn.DescribeSubnets(opts)
		if err != nil {
			if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidSubnetID.NotFound" {
				resp = nil
			} else {
				log.Printf("Error on SubnetIpv6CidrStateRefreshFunc: %s", err)
				return nil, "", err
			}
		}

		if resp == nil {
			// Sometimes AWS just has consistency issues and doesn't see
			// our instance yet. Return an empty state.
			return nil, "", nil
		}

		if resp.Subnets[0].Ipv6CidrBlockAssociationSet == nil {
			return nil, "", nil
		}

		for _, association := range resp.Subnets[0].Ipv6CidrBlockAssociationSet {
			if *association.AssociationId == associationId {
				return association, *association.Ipv6CidrBlockState.State, nil
			}
		}

		return nil, "", nil
	}
}
<commit_msg>provider/aws: Increase timeout for (dis)associating IPv6 addr to a subnet (#14401)<commit_after>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsSubnet() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSubnetCreate,
		Read:   resourceAwsSubnetRead,
		Update: resourceAwsSubnetUpdate,
		Delete: resourceAwsSubnetDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		SchemaVersion: 1,
		MigrateState:  resourceAwsSubnetMigrateState,

		Schema: map[string]*schema.Schema{
			"vpc_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"cidr_block": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"ipv6_cidr_block": {
				Type:     schema.TypeString,
				Optional: true,
			},

			"availability_zone": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"map_public_ip_on_launch": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},

			"assign_ipv6_address_on_creation": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},

			"ipv6_cidr_block_association_id": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceAwsSubnetCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	createOpts := &ec2.CreateSubnetInput{
		AvailabilityZone: aws.String(d.Get("availability_zone").(string)),
		CidrBlock:        aws.String(d.Get("cidr_block").(string)),
		VpcId:            aws.String(d.Get("vpc_id").(string)),
	}

	if v, ok := d.GetOk("ipv6_cidr_block"); ok {
		createOpts.Ipv6CidrBlock = aws.String(v.(string))
	}

	var err error
	resp, err := conn.CreateSubnet(createOpts)

	if err != nil {
		return fmt.Errorf("Error creating subnet: %s", err)
	}

	// Get the ID and store it
	subnet := resp.Subnet
	d.SetId(*subnet.SubnetId)
	log.Printf("[INFO] Subnet ID: %s", *subnet.SubnetId)

	// Wait for the Subnet to become available
	log.Printf("[DEBUG] Waiting for subnet (%s) to become available", *subnet.SubnetId)
	stateConf := &resource.StateChangeConf{
		Pending: []string{"pending"},
		Target:  []string{"available"},
		Refresh: SubnetStateRefreshFunc(conn, *subnet.SubnetId),
		Timeout: 10 * time.Minute,
	}

	_, err = stateConf.WaitForState()

	if err != nil {
		return fmt.Errorf(
			"Error waiting for subnet (%s) to become ready: %s",
			d.Id(), err)
	}

	return resourceAwsSubnetUpdate(d, meta)
}

func resourceAwsSubnetRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	resp, err := conn.DescribeSubnets(&ec2.DescribeSubnetsInput{
		SubnetIds: []*string{aws.String(d.Id())},
	})

	if err != nil {
		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidSubnetID.NotFound" {
			// Update state to indicate the subnet no longer exists.
			d.SetId("")
			return nil
		}
		return err
	}
	if resp == nil {
		return nil
	}

	subnet := resp.Subnets[0]

	d.Set("vpc_id", subnet.VpcId)
	d.Set("availability_zone", subnet.AvailabilityZone)
	d.Set("cidr_block", subnet.CidrBlock)
	d.Set("map_public_ip_on_launch", subnet.MapPublicIpOnLaunch)
	d.Set("assign_ipv6_address_on_creation", subnet.AssignIpv6AddressOnCreation)
	for _, a := range subnet.Ipv6CidrBlockAssociationSet {
		if *a.Ipv6CidrBlockState.State == "associated" { //we can only ever have 1 IPv6 block associated at once
			d.Set("ipv6_cidr_block_association_id", a.AssociationId)
			d.Set("ipv6_cidr_block", a.Ipv6CidrBlock)
			break
		} else {
			d.Set("ipv6_cidr_block_association_id", "") // we blank these out to remove old entries
			d.Set("ipv6_cidr_block", "")
		}
	}
	d.Set("tags", tagsToMap(subnet.Tags))

	return nil
}

func resourceAwsSubnetUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	d.Partial(true)

	if err := setTags(conn, d); err != nil {
		return err
	} else {
		d.SetPartial("tags")
	}

	if d.HasChange("assign_ipv6_address_on_creation") {
		modifyOpts := &ec2.ModifySubnetAttributeInput{
			SubnetId: aws.String(d.Id()),
			AssignIpv6AddressOnCreation: &ec2.AttributeBooleanValue{
				Value: aws.Bool(d.Get("assign_ipv6_address_on_creation").(bool)),
			},
		}

		log.Printf("[DEBUG] Subnet modify attributes: %#v", modifyOpts)

		_, err := conn.ModifySubnetAttribute(modifyOpts)

		if err != nil {
			return err
		} else {
			d.SetPartial("assign_ipv6_address_on_creation")
		}
	}

	if d.HasChange("map_public_ip_on_launch") {
		modifyOpts := &ec2.ModifySubnetAttributeInput{
			SubnetId: aws.String(d.Id()),
			MapPublicIpOnLaunch: &ec2.AttributeBooleanValue{
				Value: aws.Bool(d.Get("map_public_ip_on_launch").(bool)),
			},
		}

		log.Printf("[DEBUG] Subnet modify attributes: %#v", modifyOpts)

		_, err := conn.ModifySubnetAttribute(modifyOpts)

		if err != nil {
			return err
		} else {
			d.SetPartial("map_public_ip_on_launch")
		}
	}

	// We have to be careful here to not go through a change of association if this is a new resource
	// A New resource here would denote that the Update func is called by the Create func
	if d.HasChange("ipv6_cidr_block") && !d.IsNewResource() {
		// We need to handle that we disassociate the IPv6 CIDR block before we try and associate the new one
		// This could be an issue as, we could error out when we try and add the new one
		// We may need to roll back the state and reattach the old one if this is the case

		_, new := d.GetChange("ipv6_cidr_block")

		//Firstly we have to disassociate the old IPv6 CIDR Block
		disassociateOps := &ec2.DisassociateSubnetCidrBlockInput{
			AssociationId: aws.String(d.Get("ipv6_cidr_block_association_id").(string)),
		}

		_, err := conn.DisassociateSubnetCidrBlock(disassociateOps)
		if err != nil {
			return err
		}

		// Wait for the CIDR to become disassociated
		log.Printf(
			"[DEBUG] Waiting for IPv6 CIDR (%s) to become disassociated",
			d.Id())
		stateConf := &resource.StateChangeConf{
			Pending: []string{"disassociating", "associated"},
			Target:  []string{"disassociated"},
			Refresh: SubnetIpv6CidrStateRefreshFunc(conn, d.Id(), d.Get("ipv6_cidr_block_association_id").(string)),
			Timeout: 3 * time.Minute,
		}
		if _, err := stateConf.WaitForState(); err != nil {
			return fmt.Errorf(
				"Error waiting for IPv6 CIDR (%s) to become disassociated: %s",
				d.Id(), err)
		}

		//Now we need to try and associate the new CIDR block
		associatesOpts := &ec2.AssociateSubnetCidrBlockInput{
			SubnetId:      aws.String(d.Id()),
			Ipv6CidrBlock: aws.String(new.(string)),
		}

		resp, err := conn.AssociateSubnetCidrBlock(associatesOpts)
		if err != nil {
			//The big question here is, do we want to try and reassociate the old one??
			//If we have a failure here, then we may be in a situation that we have nothing associated
			return err
		}

		// Wait for the CIDR to become associated
		log.Printf(
			"[DEBUG] Waiting for IPv6 CIDR (%s) to become associated",
			d.Id())
		stateConf = &resource.StateChangeConf{
			Pending: []string{"associating", "disassociated"},
			Target:  []string{"associated"},
			Refresh: SubnetIpv6CidrStateRefreshFunc(conn, d.Id(), *resp.Ipv6CidrBlockAssociation.AssociationId),
			Timeout: 3 * time.Minute,
		}
		if _, err := stateConf.WaitForState(); err != nil {
			return fmt.Errorf(
				"Error waiting for IPv6 CIDR (%s) to become associated: %s",
				d.Id(), err)
		}

		d.SetPartial("ipv6_cidr_block")
	}

	d.Partial(false)

	return resourceAwsSubnetRead(d, meta)
}

func resourceAwsSubnetDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	log.Printf("[INFO] Deleting subnet: %s", d.Id())
	req := &ec2.DeleteSubnetInput{
		SubnetId: aws.String(d.Id()),
	}

	wait := resource.StateChangeConf{
		Pending:    []string{"pending"},
		Target:     []string{"destroyed"},
		Timeout:    10 * time.Minute,
		MinTimeout: 1 * time.Second,
		Refresh: func() (interface{}, string, error) {
			_, err := conn.DeleteSubnet(req)
			if err != nil {
				if apiErr, ok := err.(awserr.Error); ok {
					if apiErr.Code() == "DependencyViolation" {
						// There is some pending operation, so just retry
						// in a bit.
						return 42, "pending", nil
					}

					if apiErr.Code() == "InvalidSubnetID.NotFound" {
						return 42, "destroyed", nil
					}
				}

				return 42, "failure", err
			}

			return 42, "destroyed", nil
		},
	}

	if _, err := wait.WaitForState(); err != nil {
		return fmt.Errorf("Error deleting subnet: %s", err)
	}

	return nil
}

// SubnetStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch a Subnet.
func SubnetStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		resp, err := conn.DescribeSubnets(&ec2.DescribeSubnetsInput{
			SubnetIds: []*string{aws.String(id)},
		})
		if err != nil {
			if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidSubnetID.NotFound" {
				resp = nil
			} else {
				log.Printf("Error on SubnetStateRefresh: %s", err)
				return nil, "", err
			}
		}

		if resp == nil {
			// Sometimes AWS just has consistency issues and doesn't see
			// our instance yet. Return an empty state.
			return nil, "", nil
		}

		subnet := resp.Subnets[0]
		return subnet, *subnet.State, nil
	}
}

func SubnetIpv6CidrStateRefreshFunc(conn *ec2.EC2, id string, associationId string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		opts := &ec2.DescribeSubnetsInput{
			SubnetIds: []*string{aws.String(id)},
		}
		resp, err := conn.DescribeSubnets(opts)
		if err != nil {
			if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidSubnetID.NotFound" {
				resp = nil
			} else {
				log.Printf("Error on SubnetIpv6CidrStateRefreshFunc: %s", err)
				return nil, "", err
			}
		}

		if resp == nil {
			// Sometimes AWS just has consistency issues and doesn't see
			// our instance yet. Return an empty state.
			return nil, "", nil
		}

		if resp.Subnets[0].Ipv6CidrBlockAssociationSet == nil {
			return nil, "", nil
		}

		for _, association := range resp.Subnets[0].Ipv6CidrBlockAssociationSet {
			if *association.AssociationId == associationId {
				return association, *association.Ipv6CidrBlockState.State, nil
			}
		}

		return nil, "", nil
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"strconv"
	"unsafe"

	log "github.com/cihub/seelog"
	"github.com/stampzilla/stampzilla-go/nodes/basenode"
	"github.com/stampzilla/stampzilla-go/protocol"
)

/*
#cgo LDFLAGS: -ltelldus-core

#include <telldus-core.h>

extern void registerCallbacks();
extern void unregisterCallbacks();
extern int updateDevices();

*/
import "C"

var node *protocol.Node
var state *State = &State{[]*Device{}, make(map[string]*Sensor, 0)}

func main() {
	// Load logger
	//logger, err := log.LoggerFromConfigAsFile("../logconfig.xml")
	//if err != nil {
	//panic(err)
	//}
	//log.ReplaceLogger(logger)

	//Get a config with the correct parameters
	config := basenode.NewConfig()
	basenode.SetConfig(config)

	// Load flags
	//var host string
	//var port string
	//flag.StringVar(&host, "host", "localhost", "Stampzilla server hostname")
	//flag.StringVar(&port, "port", "8282", "Stampzilla server port")
	flag.Parse()

	log.Info("Starting TELLDUS-events node")

	C.registerCallbacks()
	defer C.unregisterCallbacks()

	// Create new node description
	node = protocol.NewNode("telldus-events")
	node.SetState(state)

	// Describe available actions
	node.AddAction("set", "Set", []string{"Devices.Id"})
	node.AddAction("toggle", "Toggle", []string{"Devices.Id"})
	node.AddAction("dim", "Dim", []string{"Devices.Id", "value"})

	// Describe available layouts
	//node.AddLayout("1", "switch", "toggle", "Devices", []string{"on"}, "Switches")
	//node.AddLayout("2", "slider", "dim", "Devices", []string{"dim"}, "Dimmers")
	//node.AddLayout("3", "slider", "dim", "Devices", []string{"dim"}, "Specials")

	// Add devices
	cnt := C.updateDevices()
	log.Info("Updated devices (", cnt, " in total)")

	for _, dev := range state.Devices {
		node.AddElement(&protocol.Element{
			Type: protocol.ElementTypeToggle,
			Name: dev.Name,
			Command: &protocol.Command{
				Cmd:  "toggle",
				Args: []string{dev.Id},
			},
			Feedback: `Devices[` + dev.Id + `].On`,
		})
	}

	// Start the connection
	//go connection(host, port, node)

	//Create channels so we can communicate with the stampzilla-go server
	serverSendChannel := make(chan interface{})
	serverRecvChannel := make(chan protocol.Command)
	connectionState := basenode.Connect(serverSendChannel, serverRecvChannel)
	go monitorState(connectionState, serverSendChannel)

	// This worker recives all incomming commands
	go serverRecv(serverRecvChannel)

	select {}
}

// WORKER that monitors the current connection state
func monitorState(connectionState chan int, send chan interface{}) {
	for s := range connectionState {
		switch s {
		case basenode.ConnectionStateConnected:
			send <- node.Node()
		case basenode.ConnectionStateDisconnected:
		}
	}
}

// WORKER that recives all incomming commands
func serverRecv(recv chan protocol.Command) {
	for d := range recv {
		processCommand(d)
	}
}

func processCommand(cmd protocol.Command) {
	var result C.int = C.TELLSTICK_ERROR_UNKNOWN
	var id C.int = 0

	i, err := strconv.Atoi(cmd.Args[0])
	if err != nil {
		log.Error(err)
		return
	}

	id = C.int(i)

	switch cmd.Cmd {
	case "on":
		result = C.tdTurnOn(id)
	case "off":
		result = C.tdTurnOff(id)
	case "toggle":
		s := C.tdLastSentCommand(id, C.TELLSTICK_TURNON|C.TELLSTICK_TURNOFF|C.TELLSTICK_DIM)
		switch {
		case s&C.TELLSTICK_DIM != 0:
			var state *C.char = C.tdLastSentValue(id)
			log.Info("DIM: ", C.GoString(state))
			if C.GoString(state) == "0" {
				result = C.tdTurnOn(id)
			} else {
				result = C.tdTurnOff(id)
			}
			C.tdReleaseString(state)
		case s&C.TELLSTICK_TURNON != 0:
			result = C.tdTurnOff(id)
		case s&C.TELLSTICK_TURNOFF != 0:
			result = C.tdTurnOn(id)
		}
	}

	if result != C.TELLSTICK_SUCCESS {
		var errorString *C.char = C.tdGetErrorString(result)
		log.Error(C.GoString(errorString))
		C.tdReleaseString(errorString)
	}
}

//export newDevice
func newDevice(id int, name *C.char, methods, s int, value *C.char) {
	//log.Info(id, C.GoString(name))

	features := []string{}
	if methods&C.TELLSTICK_TURNON != 0 {
		features = append(features, "on")
	}
	if methods&C.TELLSTICK_TURNOFF != 0 {
		features = append(features, "off")
	}
	if methods&C.TELLSTICK_BELL != 0 {
		features = append(features, "bell")
	}
	if methods&C.TELLSTICK_TOGGLE != 0 {
		features = append(features, "toggle")
	}
	if methods&C.TELLSTICK_DIM != 0 {
		features = append(features, "dim")
	}
	if methods&C.TELLSTICK_EXECUTE != 0 {
		features = append(features, "execute")
	}
	if methods&C.TELLSTICK_UP != 0 {
		features = append(features, "up")
	}
	if methods&C.TELLSTICK_DOWN != 0 {
		features = append(features, "down")
	}
	if methods&C.TELLSTICK_STOP != 0 {
		features = append(features, "stop")
	}

	if s&C.TELLSTICK_TURNON != 0 {
		state.AddDevice(strconv.Itoa(id), C.GoString(name), features, DeviceState{On: true, Dim: 100})
	}
	if s&C.TELLSTICK_TURNOFF != 0 {
		state.AddDevice(strconv.Itoa(id), C.GoString(name), features, DeviceState{On: false})
	}
	if s&C.TELLSTICK_DIM != 0 {
		var currentState = C.GoString(value)
		level, _ := strconv.ParseUint(currentState, 10, 16)
		state.AddDevice(strconv.Itoa(id), C.GoString(name), features, DeviceState{On: level > 0, Dim: int(level)})
	}

}

//export sensorEvent
func sensorEvent(protocol, model *C.char, sensorId, dataType int, value *C.char) {
	log.Debugf("SensorEVENT %s,\t%s,\t%d -> ", C.GoString(protocol), C.GoString(model), sensorId)

	if dataType == C.TELLSTICK_TEMPERATURE {
		log.Debugf("Temperature:\t%s\n", C.GoString(value))
	} else if dataType == C.TELLSTICK_HUMIDITY {
		log.Debugf("Humidity:\t%s%%\n", C.GoString(value))
	}
}

//export deviceEvent
func deviceEvent(deviceId, method int, data *C.char, callbackId int, context unsafe.Pointer) {
	log.Debugf("DeviceEVENT %d\t%d\t:%s\n", deviceId, method, C.GoString(data))
}

//export deviceChangeEvent
func deviceChangeEvent(deviceId, changeEvent, changeType, callbackId int, context unsafe.Pointer) {
	log.Debugf("DeviceChangeEVENT %d\t%d\t%d\n", deviceId, changeEvent, changeType)
}

//export rawDeviceEvent
func rawDeviceEvent(data *C.char, controllerId, callbackId int, context unsafe.Pointer) {
	log.Debugf("rawDeviceEVENT (%d):%s\n", controllerId, C.GoString(data))
}
<commit_msg>Added a better error message in the stampzilla-telldus-events<commit_after>package main

import (
	"flag"
	"strconv"
	"unsafe"

	log "github.com/cihub/seelog"
	"github.com/stampzilla/stampzilla-go/nodes/basenode"
	"github.com/stampzilla/stampzilla-go/protocol"
)

/*
#cgo LDFLAGS: -ltelldus-core

#include <telldus-core.h>

extern void registerCallbacks();
extern void unregisterCallbacks();
extern int updateDevices();

*/
import "C"

var node *protocol.Node
var state *State = &State{[]*Device{}, make(map[string]*Sensor, 0)}

func main() {
	// Load logger
	//logger, err := log.LoggerFromConfigAsFile("../logconfig.xml")
	//if err != nil {
	//panic(err)
	//}
	//log.ReplaceLogger(logger)

	//Get a config with the correct parameters
	config := basenode.NewConfig()
	basenode.SetConfig(config)

	// Load flags
	//var host string
	//var port string
	//flag.StringVar(&host, "host", "localhost", "Stampzilla server hostname")
	//flag.StringVar(&port, "port", "8282", "Stampzilla server port")
	flag.Parse()

	log.Info("Starting TELLDUS-events node")

	C.registerCallbacks()
	defer C.unregisterCallbacks()

	// Create new node description
	node = protocol.NewNode("telldus-events")
	node.SetState(state)

	// Describe available actions
	node.AddAction("set", "Set", []string{"Devices.Id"})
	node.AddAction("toggle", "Toggle", []string{"Devices.Id"})
	node.AddAction("dim", "Dim", []string{"Devices.Id", "value"})

	// Describe available layouts
	//node.AddLayout("1", "switch", "toggle", "Devices", []string{"on"}, "Switches")
	//node.AddLayout("2", "slider", "dim", "Devices", []string{"dim"}, "Dimmers")
	//node.AddLayout("3", "slider", "dim", "Devices", []string{"dim"}, "Specials")

	// Add devices
	cnt := C.updateDevices()
	log.Info("Updated devices (", cnt, " in total)")

	for _, dev := range state.Devices {
		node.AddElement(&protocol.Element{
			Type: protocol.ElementTypeToggle,
			Name: dev.Name,
			Command: &protocol.Command{
				Cmd:  "toggle",
				Args: []string{dev.Id},
			},
			Feedback: `Devices[` + dev.Id + `].On`,
		})
	}

	// Start the connection
	//go connection(host, port, node)

	//Create channels so we can communicate with the stampzilla-go server
	serverSendChannel := make(chan interface{})
	serverRecvChannel := make(chan protocol.Command)
	connectionState := basenode.Connect(serverSendChannel, serverRecvChannel)
	go monitorState(connectionState, serverSendChannel)

	// This worker recives all incomming commands
	go serverRecv(serverRecvChannel)

	select {}
}

// WORKER that monitors the current connection state
func monitorState(connectionState chan int, send chan interface{}) {
	for s := range connectionState {
		switch s {
		case basenode.ConnectionStateConnected:
			send <- node.Node()
		case basenode.ConnectionStateDisconnected:
		}
	}
}

// WORKER that recives all incomming commands
func serverRecv(recv chan protocol.Command) {
	for d := range recv {
		processCommand(d)
	}
}

func processCommand(cmd protocol.Command) {
	var result C.int = C.TELLSTICK_ERROR_UNKNOWN
	var id C.int = 0

	i, err := strconv.Atoi(cmd.Args[0])
	if err != nil {
		log.Error("Failed to decode arg[0] to int", err, cmd.Args[0])
		return
	}

	id = C.int(i)

	switch cmd.Cmd {
	case "on":
		result = C.tdTurnOn(id)
	case "off":
		result = C.tdTurnOff(id)
	case "toggle":
		s := C.tdLastSentCommand(id, C.TELLSTICK_TURNON|C.TELLSTICK_TURNOFF|C.TELLSTICK_DIM)
		switch {
		case s&C.TELLSTICK_DIM != 0:
			var state *C.char = C.tdLastSentValue(id)
			log.Info("DIM: ", C.GoString(state))
			if C.GoString(state) == "0" {
				result = C.tdTurnOn(id)
			} else {
				result = C.tdTurnOff(id)
			}
			C.tdReleaseString(state)
		case s&C.TELLSTICK_TURNON != 0:
			result = C.tdTurnOff(id)
		case s&C.TELLSTICK_TURNOFF != 0:
			result = C.tdTurnOn(id)
		}
	}

	if result != C.TELLSTICK_SUCCESS {
		var errorString *C.char = C.tdGetErrorString(result)
		log.Error(C.GoString(errorString))
		C.tdReleaseString(errorString)
	}
}

//export newDevice
func newDevice(id int, name *C.char, methods, s int, value *C.char) {
	//log.Info(id, C.GoString(name))

	features := []string{}
	if methods&C.TELLSTICK_TURNON != 0 {
		features = append(features, "on")
	}
	if methods&C.TELLSTICK_TURNOFF != 0 {
		features = append(features, "off")
	}
	if methods&C.TELLSTICK_BELL != 0 {
		features = append(features, "bell")
	}
	if methods&C.TELLSTICK_TOGGLE != 0 {
		features = append(features, "toggle")
	}
	if methods&C.TELLSTICK_DIM != 0 {
		features = append(features, "dim")
	}
	if methods&C.TELLSTICK_EXECUTE != 0 {
		features = append(features, "execute")
	}
	if methods&C.TELLSTICK_UP != 0 {
		features = append(features, "up")
	}
	if methods&C.TELLSTICK_DOWN != 0 {
		features = append(features, "down")
	}
	if methods&C.TELLSTICK_STOP != 0 {
		features = append(features, "stop")
	}

	if s&C.TELLSTICK_TURNON != 0 {
		state.AddDevice(strconv.Itoa(id), C.GoString(name), features, DeviceState{On: true, Dim: 100})
	}
	if s&C.TELLSTICK_TURNOFF != 0 {
		state.AddDevice(strconv.Itoa(id), C.GoString(name), features, DeviceState{On: false})
	}
	if s&C.TELLSTICK_DIM != 0 {
		var currentState = C.GoString(value)
		level, _ := strconv.ParseUint(currentState, 10, 16)
		state.AddDevice(strconv.Itoa(id), C.GoString(name), features, DeviceState{On: level > 0, Dim: int(level)})
	}

}

//export sensorEvent
func sensorEvent(protocol, model *C.char, sensorId, dataType int, value *C.char) {
	log.Debugf("SensorEVENT %s,\t%s,\t%d -> ", C.GoString(protocol), C.GoString(model), sensorId)

	if dataType == C.TELLSTICK_TEMPERATURE {
		log.Debugf("Temperature:\t%s\n", C.GoString(value))
	} else if dataType == C.TELLSTICK_HUMIDITY {
		log.Debugf("Humidity:\t%s%%\n", C.GoString(value))
	}
}

//export deviceEvent
func deviceEvent(deviceId, method int, data *C.char, callbackId int, context unsafe.Pointer) {
	log.Debugf("DeviceEVENT %d\t%d\t:%s\n", deviceId, method, C.GoString(data))
}

//export deviceChangeEvent
func deviceChangeEvent(deviceId, changeEvent, changeType, callbackId int, context unsafe.Pointer) {
	log.Debugf("DeviceChangeEVENT %d\t%d\t%d\n", deviceId, changeEvent, changeType)
}

//export rawDeviceEvent
func rawDeviceEvent(data *C.char, controllerId, callbackId int, context unsafe.Pointer) {
	log.Debugf("rawDeviceEVENT (%d):%s\n", controllerId, C.GoString(data))
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"strconv"
	"unsafe"

	log "github.com/cihub/seelog"
	"github.com/stampzilla/stampzilla-go/nodes/basenode"
	"github.com/stampzilla/stampzilla-go/protocol"
)

/*
#cgo LDFLAGS: -ltelldus-core

#include <telldus-core.h>

extern void registerCallbacks();
extern void unregisterCallbacks();
extern int updateDevices();

*/
import "C"

var node *protocol.Node
var state *State = &State{make(map[string]*Device), make(map[string]*Sensor, 0)}
var serverConnection *basenode.Connection

func main() {
	// Load logger
	//logger, err := log.LoggerFromConfigAsFile("../logconfig.xml")
	//if err != nil {
	//panic(err)
	//}
	//log.ReplaceLogger(logger)

	//Get a config with the correct parameters
	config := basenode.NewConfig()
	basenode.SetConfig(config)

	// Load flags
	//var host string
	//var port string
	//flag.StringVar(&host, "host", "localhost", "Stampzilla server hostname")
	//flag.StringVar(&port, "port", "8282", "Stampzilla server port")
	flag.Parse()

	log.Info("Starting TELLDUS-events node")

	C.registerCallbacks()
	defer C.unregisterCallbacks()

	// Create new node description
	node = protocol.NewNode("telldus-events")
	node.SetState(state)

	// Describe available actions
	node.AddAction("set", "Set", []string{"Devices.Id"})
	node.AddAction("toggle", "Toggle", []string{"Devices.Id"})
	node.AddAction("dim", "Dim", []string{"Devices.Id", "value"})

	// Describe available layouts
	//node.AddLayout("1", "switch", "toggle", "Devices", []string{"on"}, "Switches")
	//node.AddLayout("2", "slider", "dim", "Devices", []string{"dim"}, "Dimmers")
	//node.AddLayout("3", "slider", "dim", "Devices", []string{"dim"}, "Specials")

	// Add devices
	cnt := C.updateDevices()
	log.Info("Updated devices (", cnt, " in total)")

	for _, dev := range state.Devices {
		node.AddElement(&protocol.Element{
			Type: protocol.ElementTypeToggle,
			Name: dev.Name,
			Command: &protocol.Command{
				Cmd:  "toggle",
				Args: []string{dev.Id},
			},
			Feedback: `Devices[` + dev.Id + `].State.On`,
		})
	}

	// Start the connection
	//go connection(host, port, node)

	serverConnection = basenode.Connect()
	go monitorState(serverConnection)

	// This worker recives all incomming commands
	go serverRecv(serverConnection)

	select {}
}

// WORKER that monitors the current connection state
func monitorState(connection *basenode.Connection) {
	for s := range connection.State {
		switch s {
		case basenode.ConnectionStateConnected:
			connection.Send <- node.Node()
		case basenode.ConnectionStateDisconnected:
		}
	}
}

// WORKER that recives all incomming commands
func serverRecv(connection *basenode.Connection) {
	for d := range connection.Receive {
		if err := processCommand(d); err != nil {
			log.Error(err)
		}
	}
}

func processCommand(cmd protocol.Command) error {
	var result C.int = C.TELLSTICK_ERROR_UNKNOWN
	var id C.int = 0

	i, err := strconv.Atoi(cmd.Args[0])
	if err != nil {
		return fmt.Errorf("Failed to decode arg[0] to int %s %s", err, cmd.Args[0])
	}

	id = C.int(i)

	switch cmd.Cmd {
	case "on":
		result = C.tdTurnOn(id)
	case "off":
		result = C.tdTurnOff(id)
	case "toggle":
		s := C.tdLastSentCommand(id, C.TELLSTICK_TURNON|C.TELLSTICK_TURNOFF|C.TELLSTICK_DIM)
		switch {
		case s&C.TELLSTICK_DIM != 0:
			var state *C.char = C.tdLastSentValue(id)
			log.Info("DIM: ", C.GoString(state))
			if C.GoString(state) == "0" {
				result = C.tdTurnOn(id)
			} else {
				result = C.tdTurnOff(id)
			}
			C.tdReleaseString(state)
		case s&C.TELLSTICK_TURNON != 0:
			result = C.tdTurnOff(id)
		case s&C.TELLSTICK_TURNOFF != 0:
			result = C.tdTurnOn(id)
		}
	}

	if result != C.TELLSTICK_SUCCESS {
		var errorString *C.char = C.tdGetErrorString(result)
		C.tdReleaseString(errorString)
		return errors.New(C.GoString(errorString))
	}

	return nil
}

//export newDevice
func newDevice(id int, name *C.char, methods, s int, value *C.char) {
	//log.Info(id, C.GoString(name))

	features := []string{}
	if methods&C.TELLSTICK_TURNON != 0 {
		features = append(features, "on")
	}
	if methods&C.TELLSTICK_TURNOFF != 0 {
		features = append(features, "off")
	}
	if methods&C.TELLSTICK_BELL != 0 {
		features = append(features, "bell")
	}
	if methods&C.TELLSTICK_TOGGLE != 0 {
		features = append(features, "toggle")
	}
	if methods&C.TELLSTICK_DIM != 0 {
		features = append(features, "dim")
	}
	if methods&C.TELLSTICK_EXECUTE != 0 {
		features = append(features, "execute")
	}
	if methods&C.TELLSTICK_UP != 0 {
		features = append(features, "up")
	}
	if methods&C.TELLSTICK_DOWN != 0 {
		features = append(features, "down")
	}
	if methods&C.TELLSTICK_STOP != 0 {
		features = append(features, "stop")
	}

	if s&C.TELLSTICK_TURNON != 0 {
		state.AddDevice(strconv.Itoa(id), C.GoString(name), features, DeviceState{On: true, Dim: 100})
	}
	if s&C.TELLSTICK_TURNOFF != 0 {
		state.AddDevice(strconv.Itoa(id), C.GoString(name), features, DeviceState{On: false})
	}
	if s&C.TELLSTICK_DIM != 0 {
		var currentState = C.GoString(value)
		level, _ := strconv.ParseUint(currentState, 10, 16)
		state.AddDevice(strconv.Itoa(id), C.GoString(name), features, DeviceState{On: level > 0, Dim: int(level)})
	}

}

//export sensorEvent
func sensorEvent(protocol, model *C.char, sensorId, dataType int, value *C.char) {
	log.Debugf("SensorEVENT %s,\t%s,\t%d -> ", C.GoString(protocol), C.GoString(model), sensorId)

	if dataType == C.TELLSTICK_TEMPERATURE {
		log.Debugf("Temperature:\t%s\n", C.GoString(value))
	} else if dataType == C.TELLSTICK_HUMIDITY {
		log.Debugf("Humidity:\t%s%%\n", C.GoString(value))
	}
	C.tdReleaseString(model)
}

//export deviceEvent
func deviceEvent(deviceId, method int, data *C.char, callbackId int, context unsafe.Pointer) {
	log.Debugf("DeviceEVENT %d\t%d\t:%s\n", deviceId, method, C.GoString(data))
	device := state.GetDevice(strconv.Itoa(deviceId))
	if method&C.TELLSTICK_TURNON != 0 {
		device.State.On = true
		serverConnection.Send <- node.Node()
	}
	if method&C.TELLSTICK_TURNOFF != 0 {
		device.State.On = false
		serverConnection.Send <- node.Node()
	}
	if method&C.TELLSTICK_DIM != 0 {
		level, err := strconv.ParseUint(C.GoString(data), 10, 16)
		if err != nil {
			log.Error(err)
			return
		}
		if level == 0 {
			device.State.On = false
		}
		if level > 0 {
			device.State.On = true
		}
		device.State.Dim = int(level)
		serverConnection.Send <- node.Node()
	}
	C.tdReleaseString(data)
}

//export deviceChangeEvent
func deviceChangeEvent(deviceId, changeEvent, changeType, callbackId int, context unsafe.Pointer) {
	log.Debugf("DeviceChangeEVENT %d\t%d\t%d\n", deviceId, changeEvent, changeType)
}

//export rawDeviceEvent
func rawDeviceEvent(data *C.char, controllerId, callbackId int, context unsafe.Pointer) {
	log.Debugf("rawDeviceEVENT (%d):%s\n", controllerId, C.GoString(data))
	C.tdReleaseString(data)
}
<commit_msg>Revert "try to fix threadleak"<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"strconv"
	"unsafe"

	log "github.com/cihub/seelog"
	"github.com/stampzilla/stampzilla-go/nodes/basenode"
	"github.com/stampzilla/stampzilla-go/protocol"
)

/*
#cgo LDFLAGS: -ltelldus-core

#include <telldus-core.h>

extern void registerCallbacks();
extern void unregisterCallbacks();
extern int updateDevices();

*/
import "C"

var node *protocol.Node
var state *State = &State{make(map[string]*Device), make(map[string]*Sensor, 0)}
var serverConnection *basenode.Connection

func main() {
	// Load logger
	//logger, err := log.LoggerFromConfigAsFile("../logconfig.xml")
	//if err != nil {
	//panic(err)
	//}
	//log.ReplaceLogger(logger)

	//Get a config with the correct parameters
	config := basenode.NewConfig()
	basenode.SetConfig(config)

	// Load flags
	//var host string
	//var port string
	//flag.StringVar(&host, "host", "localhost", "Stampzilla server hostname")
	//flag.StringVar(&port, "port", "8282", "Stampzilla server port")
	flag.Parse()

	log.Info("Starting TELLDUS-events node")

	C.registerCallbacks()
	defer C.unregisterCallbacks()

	// Create new node description
	node = protocol.NewNode("telldus-events")
	node.SetState(state)

	// Describe available actions
	node.AddAction("set", "Set", []string{"Devices.Id"})
	node.AddAction("toggle", "Toggle", []string{"Devices.Id"})
	node.AddAction("dim", "Dim", []string{"Devices.Id", "value"})

	// Describe available layouts
	//node.AddLayout("1", "switch", "toggle", "Devices", []string{"on"}, "Switches")
	//node.AddLayout("2", "slider", "dim", "Devices", []string{"dim"}, "Dimmers")
	//node.AddLayout("3", "slider", "dim", "Devices", []string{"dim"}, "Specials")

	// Add devices
	cnt := C.updateDevices()
	log.Info("Updated devices (", cnt, " in total)")

	for _, dev := range state.Devices {
		node.AddElement(&protocol.Element{
			Type: protocol.ElementTypeToggle,
			Name: dev.Name,
			Command: &protocol.Command{
				Cmd:  "toggle",
				Args: []string{dev.Id},
			},
			Feedback: `Devices[` + dev.Id + `].State.On`,
		})
	}

	// Start the connection
	//go connection(host, port, node)

	serverConnection = basenode.Connect()
	go monitorState(serverConnection)

	// This worker recives all incomming commands
	go serverRecv(serverConnection)

	select {}
}

// WORKER that monitors the current connection state
func monitorState(connection *basenode.Connection) {
	for s := range connection.State {
		switch s {
		case basenode.ConnectionStateConnected:
			connection.Send <- node.Node()
		case basenode.ConnectionStateDisconnected:
		}
	}
}

// WORKER that recives all incomming commands
func serverRecv(connection *basenode.Connection) {
	for d := range connection.Receive {
		if err := processCommand(d); err != nil {
			log.Error(err)
		}
	}
}

func processCommand(cmd protocol.Command) error {
	var result C.int = C.TELLSTICK_ERROR_UNKNOWN
	var id C.int = 0

	i, err := strconv.Atoi(cmd.Args[0])
	if err != nil {
		return fmt.Errorf("Failed to decode arg[0] to int %s %s", err, cmd.Args[0])
	}

	id = C.int(i)

	switch cmd.Cmd {
	case "on":
		result = C.tdTurnOn(id)
	case "off":
		result = C.tdTurnOff(id)
	case "toggle":
		s := C.tdLastSentCommand(id, C.TELLSTICK_TURNON|C.TELLSTICK_TURNOFF|C.TELLSTICK_DIM)
		switch {
		case s&C.TELLSTICK_DIM != 0:
			var state *C.char = C.tdLastSentValue(id)
			log.Info("DIM: ", C.GoString(state))
			if C.GoString(state) == "0" {
				result = C.tdTurnOn(id)
			} else {
				result = C.tdTurnOff(id)
			}
			C.tdReleaseString(state)
		case s&C.TELLSTICK_TURNON != 0:
			result = C.tdTurnOff(id)
		case s&C.TELLSTICK_TURNOFF != 0:
			result = C.tdTurnOn(id)
		}
	}

	if result != C.TELLSTICK_SUCCESS {
		var errorString *C.char = C.tdGetErrorString(result)
		C.tdReleaseString(errorString)
		return errors.New(C.GoString(errorString))
	}

	return nil
}

//export newDevice
func newDevice(id int, name *C.char, methods, s int, value *C.char) {
	//log.Info(id, C.GoString(name))

	features := []string{}
	if methods&C.TELLSTICK_TURNON != 0 {
		features = append(features, "on")
	}
	if methods&C.TELLSTICK_TURNOFF != 0 {
		features = append(features, "off")
	}
	if methods&C.TELLSTICK_BELL != 0 {
		features = append(features, "bell")
	}
	if methods&C.TELLSTICK_TOGGLE != 0 {
		features = append(features, "toggle")
	}
	if methods&C.TELLSTICK_DIM != 0 {
		features = append(features, "dim")
	}
	if methods&C.TELLSTICK_EXECUTE != 0 {
		features = append(features, "execute")
	}
	if methods&C.TELLSTICK_UP != 0 {
		features = append(features, "up")
	}
	if methods&C.TELLSTICK_DOWN != 0 {
		features = append(features, "down")
	}
	if methods&C.TELLSTICK_STOP != 0 {
		features = append(features, "stop")
	}

	if s&C.TELLSTICK_TURNON != 0 {
		state.AddDevice(strconv.Itoa(id), C.GoString(name), features, DeviceState{On: true, Dim: 100})
	}
	if s&C.TELLSTICK_TURNOFF != 0 {
		state.AddDevice(strconv.Itoa(id), C.GoString(name), features, DeviceState{On: false})
	}
	if s&C.TELLSTICK_DIM != 0 {
		var currentState = C.GoString(value)
		level, _ := strconv.ParseUint(currentState, 10, 16)
		state.AddDevice(strconv.Itoa(id), C.GoString(name), features, DeviceState{On: level > 0, Dim: int(level)})
	}

}

//export sensorEvent
func sensorEvent(protocol, model *C.char, sensorId, dataType int, value *C.char) {
	log.Debugf("SensorEVENT %s,\t%s,\t%d -> ", C.GoString(protocol), C.GoString(model), sensorId)

	if dataType == C.TELLSTICK_TEMPERATURE {
		log.Debugf("Temperature:\t%s\n", C.GoString(value))
	} else if dataType == C.TELLSTICK_HUMIDITY {
		log.Debugf("Humidity:\t%s%%\n", C.GoString(value))
	}
}

//export deviceEvent
func deviceEvent(deviceId, method int, data *C.char, callbackId int, context unsafe.Pointer) {
	log.Debugf("DeviceEVENT %d\t%d\t:%s\n", deviceId, method, C.GoString(data))
	device := state.GetDevice(strconv.Itoa(deviceId))
	if method&C.TELLSTICK_TURNON != 0 {
		device.State.On = true
		serverConnection.Send <- node.Node()
	}
	if method&C.TELLSTICK_TURNOFF != 0 {
		device.State.On = false
		serverConnection.Send <- node.Node()
	}
	if method&C.TELLSTICK_DIM != 0 {
		level, err := strconv.ParseUint(C.GoString(data), 10, 16)
		if err != nil {
			log.Error(err)
			return
		}
		if level == 0 {
			device.State.On = false
		}
		if level > 0 {
			device.State.On = true
		}
		device.State.Dim = int(level)
		serverConnection.Send <- node.Node()
	}
}

//export deviceChangeEvent
func deviceChangeEvent(deviceId, changeEvent, changeType, callbackId int, context unsafe.Pointer) {
	log.Debugf("DeviceChangeEVENT %d\t%d\t%d\n", deviceId, changeEvent, changeType)
}

//export rawDeviceEvent
func rawDeviceEvent(data *C.char, controllerId, callbackId int, context unsafe.Pointer) {
	log.Debugf("rawDeviceEVENT (%d):%s\n", controllerId, C.GoString(data))
}
<|endoftext|>


<commit_before>package vm

import (
	"sync"
)

type callFrameStack struct {
	callFrames []callFrame
	pointer    int
}

type baseFrame struct {
	// environment pointer, points to the call frame we want to get locals from
	ep     *normalCallFrame
	self   Object
	locals []*Pointer
	// local pointer
	lPr           int
	isBlock       bool
	isSourceBlock bool
	// for helping stop the frame execution
	isRemoved  bool
	blockFrame *normalCallFrame
	sync.RWMutex
	sourceLine int
	fileName   string
}

type callFrame interface {
	// Getters
	Self() Object
	BlockFrame() *normalCallFrame
	IsBlock() bool
	IsSourceBlock() bool
	IsRemoved() bool
	setAsRemoved()
	EP() *normalCallFrame
	Locals() []*Pointer
	LocalPtr() int
	SourceLine() int
	FileName() string

	getLCL(index, depth int) *Pointer
	insertLCL(index, depth int, value Object)
	storeConstant(constName string, constant interface{}) *Pointer
	lookupConstantUnderAllScope(constName string) *Pointer
	lookupConstantUnderCurrentScope(constName string) *Pointer
	lookupConstantInCurrentScope(constName string) *Pointer
	inspect() string
	stopExecution()
}

type goMethodCallFrame struct {
	*baseFrame
	method   builtinMethodBody
	argPtr   int
	argCount int
	receiver Object
	name     string
}

func (cf *goMethodCallFrame) stopExecution() {}

type normalCallFrame struct {
	*baseFrame
	instructionSet *instructionSet
	// program counter
	pc int
}

func (n *normalCallFrame) instructionsCount() int {
	return len(n.instructionSet.instructions)
}

func (n *normalCallFrame) stopExecution() {
	n.pc = n.instructionsCount()
}

func (b *baseFrame) Self() Object {
	return b.self
}

func (b *baseFrame) BlockFrame() *normalCallFrame {
	return b.blockFrame
}

func (b *baseFrame) IsBlock() bool {
	return b.isBlock
}

func (b *baseFrame) IsRemoved() bool {
	return b.isRemoved
}

func (b *baseFrame) setAsRemoved() {
	b.isRemoved = true
}

func (b *baseFrame) IsSourceBlock() bool {
	return b.isSourceBlock
}

func (b *baseFrame) EP() *normalCallFrame {
	return b.ep
}

func (b *baseFrame) Locals() []*Pointer {
	return b.locals
}

func (b *baseFrame) LocalPtr() int {
	return b.lPr
}

func (b *baseFrame) SourceLine() int {
	return b.sourceLine
}

func (b *baseFrame) FileName() string {
	return b.fileName
}

// We use lock on every local variable retrieval and insertion.
// The main scenario is when multiple threads want to access local variables outside it's block
// Since they share same block frame, they will all access to that frame's locals.
// TODO: Find a better way to fix this, or prevent thread from accessing outside locals.
func (b *baseFrame) getLCL(index, depth int) (p *Pointer) {

	if depth == 0 {
		b.RLock()
		if index < len(b.locals) {
			p = b.locals[index]
		}
		b.RUnlock()
		return
	}

	return b.blockFrame.ep.getLCL(index, depth-1)
}

func (b *baseFrame) insertLCL(index, depth int, value Object) {
	existedLCL := b.getLCL(index, depth)

	if existedLCL != nil {
		existedLCL.Target = value
		return
	}

	b.Lock()

	if index >= len(b.locals) {
		b.locals = append(b.locals, nil)
		copy(b.locals[index:], b.locals[index:])
	}

	b.locals[index] = &Pointer{Target: value}

	if index >= b.lPr {
		b.lPr = index + 1
	}
	b.Unlock()
}

func (b *baseFrame) storeConstant(constName string, constant interface{}) *Pointer {
	var ptr *Pointer

	switch c := constant.(type) {
	case *Pointer:
		ptr = c
	case Object:
		ptr = &Pointer{Target: c}
	}

	switch scope := b.self.(type) {
	case *RClass:
		scope.constants[constName] = ptr

		if class, ok := ptr.Target.(*RClass); ok {
			class.scope = scope
		}
	default:
		c := b.self.Class()
		c.constants[constName] = ptr
	}

	return ptr
}

func (b *baseFrame) lookupConstantUnderAllScope(constName string) *Pointer {
	var c *Pointer

	switch scope := b.self.(type) {
	case *RClass:
		c = scope.lookupConstantUnderAllScope(constName)
	default:
		scopeClass := scope.Class()
		c = scopeClass.lookupConstantUnderAllScope(constName)
	}

	return c
}

func (b *baseFrame) lookupConstantUnderCurrentScope(constName string) *Pointer {
	var c *Pointer

	switch scope := b.self.(type) {
	case *RClass:
		c = scope.lookupConstantUnderCurrentScope(constName)
	default:
		scopeClass := scope.Class()
		c = scopeClass.lookupConstantUnderCurrentScope(constName)
	}

	return c
}

func (b *baseFrame) lookupConstantInCurrentScope(constName string) *Pointer {
	var c *Pointer

	switch scope := b.self.(type) {
	case *RClass:
		c = scope.lookupConstantInCurrentScope(constName)
	default:
		scopeClass := scope.Class()
		c = scopeClass.lookupConstantInCurrentScope(constName)
	}

	return c
}

func (cfs *callFrameStack) push(cf callFrame) {
	if cf == nil {
		panic("Callframe can't be nil!")
	}

	if len(cfs.callFrames) <= cfs.pointer {
		cfs.callFrames = append(cfs.callFrames, cf)
	} else {
		cfs.callFrames[cfs.pointer] = cf
	}

	cfs.pointer++
}

func (cfs *callFrameStack) pop() callFrame {
	var cf callFrame

	if len(cfs.callFrames) < 1 {
		panic("Nothing to pop!")
	}

	if cfs.pointer > 0 {
		cfs.pointer--
	}

	cf = cfs.callFrames[cfs.pointer]
	cfs.callFrames[cfs.pointer] = nil

	return cf
}

func (cfs *callFrameStack) top() callFrame {
	if cfs.pointer > 0 {
		return cfs.callFrames[cfs.pointer-1]
	}

	return nil
}

func newNormalCallFrame(is *instructionSet, filename string, sourceLine int) *normalCallFrame {
	return &normalCallFrame{baseFrame: &baseFrame{locals: make([]*Pointer, 5), lPr: 0, fileName: filename, sourceLine: sourceLine}, instructionSet: is, pc: 0}
}

func newGoMethodCallFrame(m builtinMethodBody, receiver Object, argCount, argPtr int, n, filename string, sourceLine int, blockFrame *normalCallFrame) *goMethodCallFrame {
	return &goMethodCallFrame{
		baseFrame: &baseFrame{
			locals:     make([]*Pointer, 5),
			lPr:        0,
			fileName:   filename,
			sourceLine: sourceLine,
			blockFrame: blockFrame,
		},
		method:   m,
		name:     n,
		receiver: receiver,
		argCount: argCount,
		argPtr:   argPtr,
	}
}
<commit_msg>refactor for readability<commit_after>package vm

import (
	"sync"
)

type callFrameStack struct {
	callFrames []callFrame
	pointer    int
}

type baseFrame struct {
	// environment pointer, points to the call frame we want to get locals from
	ep     *normalCallFrame
	self   Object
	locals []*Pointer
	// local pointer
	lPr           int
	isBlock       bool
	isSourceBlock bool
	// for helping stop the frame execution
	isRemoved  bool
	blockFrame *normalCallFrame
	sync.RWMutex
	sourceLine int
	fileName   string
}

type callFrame interface {
	// Getters
	Self() Object
	BlockFrame() *normalCallFrame
	IsBlock() bool
	IsSourceBlock() bool
	IsRemoved() bool
	setAsRemoved()
	EP() *normalCallFrame
	Locals() []*Pointer
	LocalPtr() int
	SourceLine() int
	FileName() string

	getLCL(index, depth int) *Pointer
	insertLCL(index, depth int, value Object)
	storeConstant(constName string, constant interface{}) *Pointer
	lookupConstantUnderAllScope(constName string) *Pointer
	lookupConstantUnderCurrentScope(constName string) *Pointer
	lookupConstantInCurrentScope(constName string) *Pointer
	inspect() string
	stopExecution()
}

type goMethodCallFrame struct {
	*baseFrame
	method   builtinMethodBody
	argPtr   int
	argCount int
	receiver Object
	name     string
}

func (cf *goMethodCallFrame) stopExecution() {}

type normalCallFrame struct {
	*baseFrame
	instructionSet *instructionSet
	// program counter
	pc int
}

func (n *normalCallFrame) instructionsCount() int {
	return len(n.instructionSet.instructions)
}

func (n *normalCallFrame) stopExecution() {
	n.pc = n.instructionsCount()
}

func (b *baseFrame) Self() Object {
	return b.self
}

func (b *baseFrame) BlockFrame() *normalCallFrame {
	return b.blockFrame
}

func (b *baseFrame) IsBlock() bool {
	return b.isBlock
}

func (b *baseFrame) IsRemoved() bool {
	return b.isRemoved
}

func (b *baseFrame) setAsRemoved() {
	b.isRemoved = true
}

func (b *baseFrame) IsSourceBlock() bool {
	return b.isSourceBlock
}

func (b *baseFrame) EP() *normalCallFrame {
	return b.ep
}

func (b *baseFrame) Locals() []*Pointer {
	return b.locals
}

func (b *baseFrame) LocalPtr() int {
	return b.lPr
}

func (b *baseFrame) SourceLine() int {
	return b.sourceLine
}

func (b *baseFrame) FileName() string {
	return b.fileName
}

// We use lock on every local variable retrieval and insertion.
// The main scenario is when multiple threads want to access local variables outside it's block
// Since they share same block frame, they will all access to that frame's locals.
// TODO: Find a better way to fix this, or prevent thread from accessing outside locals.
func (b *baseFrame) getLCL(index, depth int) (p *Pointer) {

	if depth == 0 {
		b.RLock()

		if index >= len(b.locals) {
			b.RUnlock()
			return
		}
		p = b.locals[index]

		b.RUnlock()
		return
	}

	return b.blockFrame.ep.getLCL(index, depth-1)
}

func (b *baseFrame) insertLCL(index, depth int, value Object) {
	existedLCL := b.getLCL(index, depth)

	if existedLCL != nil {
		existedLCL.Target = value
		return
	}

	b.Lock()

	if index >= len(b.locals) {
		b.locals = append(b.locals, nil)
		copy(b.locals[index:], b.locals[index:])
	}

	b.locals[index] = &Pointer{Target: value}

	if index >= b.lPr {
		b.lPr = index + 1
	}
	b.Unlock()
}

func (b *baseFrame) storeConstant(constName string, constant interface{}) *Pointer {
	var ptr *Pointer

	switch c := constant.(type) {
	case *Pointer:
		ptr = c
	case Object:
		ptr = &Pointer{Target: c}
	}

	switch scope := b.self.(type) {
	case *RClass:
		scope.constants[constName] = ptr

		if class, ok := ptr.Target.(*RClass); ok {
			class.scope = scope
		}
	default:
		c := b.self.Class()
		c.constants[constName] = ptr
	}

	return ptr
}

func (b *baseFrame) lookupConstantUnderAllScope(constName string) *Pointer {
	var c *Pointer

	switch scope := b.self.(type) {
	case *RClass:
		c = scope.lookupConstantUnderAllScope(constName)
	default:
		scopeClass := scope.Class()
		c = scopeClass.lookupConstantUnderAllScope(constName)
	}

	return c
}

func (b *baseFrame) lookupConstantUnderCurrentScope(constName string) *Pointer {
	var c *Pointer

	switch scope := b.self.(type) {
	case *RClass:
		c = scope.lookupConstantUnderCurrentScope(constName)
	default:
		scopeClass := scope.Class()
		c = scopeClass.lookupConstantUnderCurrentScope(constName)
	}

	return c
}

func (b *baseFrame) lookupConstantInCurrentScope(constName string) *Pointer {
	var c *Pointer

	switch scope := b.self.(type) {
	case *RClass:
		c = scope.lookupConstantInCurrentScope(constName)
	default:
		scopeClass := scope.Class()
		c = scopeClass.lookupConstantInCurrentScope(constName)
	}

	return c
}

func (cfs *callFrameStack) push(cf callFrame) {
	if cf == nil {
		panic("Callframe can't be nil!")
	}

	if len(cfs.callFrames) <= cfs.pointer {
		cfs.callFrames = append(cfs.callFrames, cf)
	} else {
		cfs.callFrames[cfs.pointer] = cf
	}

	cfs.pointer++
}

func (cfs *callFrameStack) pop() callFrame {
	var cf callFrame

	if len(cfs.callFrames) < 1 {
		panic("Nothing to pop!")
	}

	if cfs.pointer > 0 {
		cfs.pointer--
	}

	cf = cfs.callFrames[cfs.pointer]
	cfs.callFrames[cfs.pointer] = nil

	return cf
}

func (cfs *callFrameStack) top() callFrame {
	if cfs.pointer > 0 {
		return cfs.callFrames[cfs.pointer-1]
	}

	return nil
}

func newNormalCallFrame(is *instructionSet, filename string, sourceLine int) *normalCallFrame {
	return &normalCallFrame{baseFrame: &baseFrame{locals: make([]*Pointer, 5), lPr: 0, fileName: filename, sourceLine: sourceLine}, instructionSet: is, pc: 0}
}

func newGoMethodCallFrame(m builtinMethodBody, receiver Object, argCount, argPtr int, n, filename string, sourceLine int, blockFrame *normalCallFrame) *goMethodCallFrame {
	return &goMethodCallFrame{
		baseFrame: &baseFrame{
			locals:     make([]*Pointer, 5),
			lPr:        0,
			fileName:   filename,
			sourceLine: sourceLine,
			blockFrame: blockFrame,
		},
		method:   m,
		name:     n,
		receiver: receiver,
		argCount: argCount,
		argPtr:   argPtr,
	}
}
<|endoftext|>


<commit_before>// Package encoding converts arbitrary objects into byte slices, and vis
// versa. It also contains helper functions for reading and writing length-
// prefixed data. The encoding rules are as follows:
//
// Objects are encoded as binary data, without type information. The receiver
// uses context to determine the type to decode into.
//
// Integers are little-endian, and are always encoded as 8 bytes, i.e. their
// int64 or uint64 equivalent.
//
// Booleans are encoded as one byte, either zero (false) or one (true). No
// other values may be used.
//
// Nil pointers are equivalent to "false," i.e. a single zero byte. Valid
// pointers are represented by a "true" byte (0x01) followed by the encoding
// of the dereferenced value.
//
// Variable-length types, such as strings and slices, are represented by an 8-byte
// length-prefix followed by the encoded value.
//
// Slices and structs are simply the concatenation of their encoded elements.
// Byte slices are not subject to the 8-byte integer rule; they are encoded as
// their literal representation, one byte per byte.
//
// The ordering of struct fields is determined by their type definition. For
// example:
//
//   type foo struct { S string; I int }
//
//   Marshal(foo{"bar", 3}) == append(Marshal("bar"), Marshal(3)...)
//
// Finally, if a type implements the SiaMarshaler interface, its MarshalSia
// method will be used to encode the type. The resulting byte slice will be
// length-prefixed like any other variable-length type. During decoding, the
// type is decoded as a byte slice, and then passed to UnmarshalSia.
package encoding

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"reflect"
)

// A SiaMarshaler can encode itself as a byte slice.
type SiaMarshaler interface {
	MarshalSia() []byte
}

// A SiaUnmarshaler can decode itself from a byte slice. If a decoding error
// occurs, UnmarshalSia should panic.
type SiaUnmarshaler interface {
	UnmarshalSia([]byte)
}

// An Encoder writes objects to an output stream.
type Encoder struct {
	w io.Writer
}

const (
	maxDecodeLen = 10 * 1024 * 1024 // 10 MB
	maxSliceLen  = 4 * 1024 * 1024  // 4 MB
)

var (
	errBadPointer = errors.New("cannot decode into invalid pointer")
)

// Encode writes the encoding of v to the stream. For encoding details, see
// the package docstring.
func (e *Encoder) Encode(v interface{}) error {
	return e.encode(reflect.ValueOf(v))
}

// write catches instances where short writes do not return an error.
func (e *Encoder) write(p []byte) error {
	n, err := e.w.Write(p)
	if n != len(p) {
		return io.ErrShortWrite
	}
	return err
}

// Encode writes the encoding of val to the stream. For encoding details, see
// the package docstring.
func (e *Encoder) encode(val reflect.Value) error {
	// check for MarshalSia interface first
	if m, ok := val.Interface().(SiaMarshaler); ok {
		return WritePrefix(e.w, m.MarshalSia())
	}

	switch val.Kind() {
	case reflect.Ptr:
		// write either a 1 or 0
		if err := e.Encode(!val.IsNil()); err != nil {
			return err
		}
		if !val.IsNil() {
			return e.encode(val.Elem())
		}
	case reflect.Bool:
		if val.Bool() {
			return e.write([]byte{1})
		} else {
			return e.write([]byte{0})
		}
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return e.write(EncInt64(val.Int()))
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return e.write(EncUint64(val.Uint()))
	case reflect.String:
		return WritePrefix(e.w, []byte(val.String()))
	case reflect.Slice:
		// slices are variable length, so prepend the length and then fallthrough to array logic
		if err := e.write(EncUint64(uint64(val.Len()))); err != nil {
			return err
		}
		fallthrough
	case reflect.Array:
		// special case for byte arrays
		if val.Type().Elem().Kind() == reflect.Uint8 {
			// convert array to slice so we can use Bytes()
			// can't just use Slice() because array may be unaddressable
			slice := reflect.MakeSlice(reflect.SliceOf(val.Type().Elem()), val.Len(), val.Len())
			reflect.Copy(slice, val)
			return e.write(slice.Bytes())
		}
		// normal slices/arrays are encoded by sequentially encoding their elements
		for i := 0; i < val.Len(); i++ {
			if err := e.encode(val.Index(i)); err != nil {
				return err
			}
		}
	case reflect.Struct:
		for i := 0; i < val.NumField(); i++ {
			if err := e.encode(val.Field(i)); err != nil {
				return err
			}
		}
	default:
		// Marshalling should never fail. If it panics, you're doing something wrong,
		// like trying to encode a map or an unexported struct field.
		panic("could not marshal type " + val.Type().String())
	}
	return nil
}

// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
	return &Encoder{w}
}

// Marshal returns the encoding of v. For encoding details, see the package
// docstring.
func Marshal(v interface{}) []byte {
	b := new(bytes.Buffer)
	NewEncoder(b).Encode(v) // no error possible when using a bytes.Buffer
	return b.Bytes()
}

// MarshalAll encodes all of its inputs and returns their concatenation.
func MarshalAll(v ...interface{}) []byte {
	b := new(bytes.Buffer)
	enc := NewEncoder(b)
	for i := range v {
		enc.Encode(v[i]) // no error possible when using a bytes.Buffer
	}
	return b.Bytes()
}

// WriteFile writes v to a file. The file will be created if it does not exist.
func WriteFile(filename string, v interface{}) error {
	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()
	return NewEncoder(file).Encode(v)
}

// A Decoder reads and decodes values from an input stream.
type Decoder struct {
	r io.Reader
	n int
}

// Read implements the io.Reader interface. It also keeps track of the total
// number of bytes decoded, and panics if that number exceeds a global
// maximum.
func (d *Decoder) Read(p []byte) (int, error) {
	n, err := d.r.Read(p)
	// enforce an absolute maximum size limit
	if d.n += n; d.n > maxDecodeLen {
		panic("encoded type exceeds size limit")
	}
	return n, err
}

// Decode reads the next encoded value from its input stream and stores it in
// v, which must be a pointer. The decoding rules are the inverse of those
// specified in the package docstring.
func (d *Decoder) Decode(v interface{}) (err error) {
	// v must be a pointer
	pval := reflect.ValueOf(v)
	if pval.Kind() != reflect.Ptr || pval.IsNil() {
		return errBadPointer
	}

	// catch decoding panics and convert them to errors
	// note that this allows us to skip boundary checks during decoding
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("could not decode type %s: %v", pval.Elem().Type().String(), r)
		}
	}()

	// reset the read count
	d.n = 0

	d.decode(pval.Elem())
	return
}

// readN reads n bytes and panics if the read fails.
func (d *Decoder) readN(n int) []byte {
	b := make([]byte, n)
	_, err := io.ReadFull(d, b)
	if err != nil {
		panic(err)
	}
	return b
}

// readPrefix reads a length-prefixed byte slice and panics if the read fails.
func (d *Decoder) readPrefix() []byte {
	b, err := ReadPrefix(d, maxSliceLen)
	if err != nil {
		panic(err)
	}
	return b
}

// decode reads the next encoded value from its input stream and stores it in
// val. The decoding rules are the inverse of those specified in the package
// docstring.
func (d *Decoder) decode(val reflect.Value) {
	// check for UnmarshalSia interface first
	if val.CanAddr() {
		if u, ok := val.Addr().Interface().(SiaUnmarshaler); ok {
			u.UnmarshalSia(d.readPrefix())
			return
		}
	}

	switch val.Kind() {
	case reflect.Ptr:
		var valid bool
		d.decode(reflect.ValueOf(&valid).Elem())
		// nil pointer, nothing to decode
		if !valid {
			return
		}
		// make sure we aren't decoding into nil
		if val.IsNil() {
			val.Set(reflect.New(val.Type().Elem()))
		}
		d.decode(val.Elem())
	case reflect.Bool:
		b := d.readN(1)
		if b[0] > 1 {
			panic("boolean value was not 0 or 1")
		}
		val.SetBool(b[0] == 1)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		val.SetInt(DecInt64(d.readN(8)))
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		val.SetUint(DecUint64(d.readN(8)))
	case reflect.String:
		val.SetString(string(d.readPrefix()))
	case reflect.Slice:
		// slices are variable length, but otherwise the same as arrays.
		// just have to allocate them first, then we can fallthrough to the array logic.
		sliceLen := DecUint64(d.readN(8))
		// sanity-check the sliceLen, otherwise you can crash a peer by making
		// them allocate a massive slice
		if sliceLen > 1<<31-1 || sliceLen*uint64(val.Type().Elem().Size()) > maxSliceLen {
			panic("slice is too large")
		}
		val.Set(reflect.MakeSlice(val.Type(), int(sliceLen), int(sliceLen)))
		fallthrough
	case reflect.Array:
		// special case for byte arrays (e.g. hashes)
		if val.Type().Elem().Kind() == reflect.Uint8 {
			// convert val to a slice and read into it directly
			b := val.Slice(0, val.Len())
			_, err := io.ReadFull(d, b.Bytes())
			if err != nil {
				panic(err)
			}
			return
		}
		// arrays are unmarshalled by sequentially unmarshalling their elements
		for i := 0; i < val.Len(); i++ {
			d.decode(val.Index(i))
		}
		return
	case reflect.Struct:
		for i := 0; i < val.NumField(); i++ {
			d.decode(val.Field(i))
		}
		return
	default:
		panic("unknown type")
	}
}

// NewDecoder returns a new decoder that reads from r.
func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{r, 0}
}

// Unmarshal decodes the encoded value b and stores it in v, which must be a
// pointer. The decoding rules are the inverse of those specified in the
// package docstring.
func Unmarshal(b []byte, v interface{}) error {
	r := bytes.NewReader(b)
	return NewDecoder(r).Decode(v)
}

// ReadFile reads the contents of a file and decodes them into v.
func ReadFile(filename string, v interface{}) error {
	file, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer file.Close()
	return NewDecoder(file).Decode(v)
}
<commit_msg>optimize some array encoding<commit_after>// Package encoding converts arbitrary objects into byte slices, and vis
// versa. It also contains helper functions for reading and writing length-
// prefixed data. The encoding rules are as follows:
//
// Objects are encoded as binary data, without type information. The receiver
// uses context to determine the type to decode into.
//
// Integers are little-endian, and are always encoded as 8 bytes, i.e. their
// int64 or uint64 equivalent.
//
// Booleans are encoded as one byte, either zero (false) or one (true). No
// other values may be used.
//
// Nil pointers are equivalent to "false," i.e. a single zero byte. Valid
// pointers are represented by a "true" byte (0x01) followed by the encoding
// of the dereferenced value.
//
// Variable-length types, such as strings and slices, are represented by an 8-byte
// length-prefix followed by the encoded value.
//
// Slices and structs are simply the concatenation of their encoded elements.
// Byte slices are not subject to the 8-byte integer rule; they are encoded as
// their literal representation, one byte per byte.
//
// The ordering of struct fields is determined by their type definition. For
// example:
//
//   type foo struct { S string; I int }
//
//   Marshal(foo{"bar", 3}) == append(Marshal("bar"), Marshal(3)...)
//
// Finally, if a type implements the SiaMarshaler interface, its MarshalSia
// method will be used to encode the type. The resulting byte slice will be
// length-prefixed like any other variable-length type. During decoding, the
// type is decoded as a byte slice, and then passed to UnmarshalSia.
package encoding

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"reflect"
)

// A SiaMarshaler can encode itself as a byte slice.
type SiaMarshaler interface {
	MarshalSia() []byte
}

// A SiaUnmarshaler can decode itself from a byte slice. If a decoding error
// occurs, UnmarshalSia should panic.
type SiaUnmarshaler interface {
	UnmarshalSia([]byte)
}

// An Encoder writes objects to an output stream.
type Encoder struct {
	w io.Writer
}

const (
	maxDecodeLen = 10 * 1024 * 1024 // 10 MB
	maxSliceLen  = 4 * 1024 * 1024  // 4 MB
)

var (
	errBadPointer = errors.New("cannot decode into invalid pointer")
)

// Encode writes the encoding of v to the stream. For encoding details, see
// the package docstring.
func (e *Encoder) Encode(v interface{}) error {
	return e.encode(reflect.ValueOf(v))
}

// write catches instances where short writes do not return an error.
func (e *Encoder) write(p []byte) error {
	n, err := e.w.Write(p)
	if n != len(p) {
		return io.ErrShortWrite
	}
	return err
}

// Encode writes the encoding of val to the stream. For encoding details, see
// the package docstring.
func (e *Encoder) encode(val reflect.Value) error {
	// check for MarshalSia interface first
	if m, ok := val.Interface().(SiaMarshaler); ok {
		return WritePrefix(e.w, m.MarshalSia())
	}

	switch val.Kind() {
	case reflect.Ptr:
		// write either a 1 or 0
		if err := e.Encode(!val.IsNil()); err != nil {
			return err
		}
		if !val.IsNil() {
			return e.encode(val.Elem())
		}
	case reflect.Bool:
		if val.Bool() {
			return e.write([]byte{1})
		} else {
			return e.write([]byte{0})
		}
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return e.write(EncInt64(val.Int()))
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return e.write(EncUint64(val.Uint()))
	case reflect.String:
		return WritePrefix(e.w, []byte(val.String()))
	case reflect.Slice:
		// slices are variable length, so prepend the length and then fallthrough to array logic
		if err := e.write(EncUint64(uint64(val.Len()))); err != nil {
			return err
		}
		fallthrough
	case reflect.Array:
		// special case for byte arrays
		if val.Type().Elem().Kind() == reflect.Uint8 {
			// if the array is addressable, we can optimize a bit here
			if val.CanAddr() {
				return e.write(val.Slice(0, val.Len()).Bytes())
			}
			// otherwise we have to copy into a newly allocated slice
			slice := reflect.MakeSlice(reflect.SliceOf(val.Type().Elem()), val.Len(), val.Len())
			reflect.Copy(slice, val)
			return e.write(slice.Bytes())
		}
		// normal slices/arrays are encoded by sequentially encoding their elements
		for i := 0; i < val.Len(); i++ {
			if err := e.encode(val.Index(i)); err != nil {
				return err
			}
		}
	case reflect.Struct:
		for i := 0; i < val.NumField(); i++ {
			if err := e.encode(val.Field(i)); err != nil {
				return err
			}
		}
	default:
		// Marshalling should never fail. If it panics, you're doing something wrong,
		// like trying to encode a map or an unexported struct field.
		panic("could not marshal type " + val.Type().String())
	}
	return nil
}

// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
	return &Encoder{w}
}

// Marshal returns the encoding of v. For encoding details, see the package
// docstring.
func Marshal(v interface{}) []byte {
	b := new(bytes.Buffer)
	NewEncoder(b).Encode(v) // no error possible when using a bytes.Buffer
	return b.Bytes()
}

// MarshalAll encodes all of its inputs and returns their concatenation.
func MarshalAll(v ...interface{}) []byte {
	b := new(bytes.Buffer)
	enc := NewEncoder(b)
	for i := range v {
		enc.Encode(v[i]) // no error possible when using a bytes.Buffer
	}
	return b.Bytes()
}

// WriteFile writes v to a file. The file will be created if it does not exist.
func WriteFile(filename string, v interface{}) error {
	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()
	return NewEncoder(file).Encode(v)
}

// A Decoder reads and decodes values from an input stream.
type Decoder struct {
	r io.Reader
	n int
}

// Read implements the io.Reader interface. It also keeps track of the total
// number of bytes decoded, and panics if that number exceeds a global
// maximum.
func (d *Decoder) Read(p []byte) (int, error) {
	n, err := d.r.Read(p)
	// enforce an absolute maximum size limit
	if d.n += n; d.n > maxDecodeLen {
		panic("encoded type exceeds size limit")
	}
	return n, err
}

// Decode reads the next encoded value from its input stream and stores it in
// v, which must be a pointer. The decoding rules are the inverse of those
// specified in the package docstring.
func (d *Decoder) Decode(v interface{}) (err error) {
	// v must be a pointer
	pval := reflect.ValueOf(v)
	if pval.Kind() != reflect.Ptr || pval.IsNil() {
		return errBadPointer
	}

	// catch decoding panics and convert them to errors
	// note that this allows us to skip boundary checks during decoding
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("could not decode type %s: %v", pval.Elem().Type().String(), r)
		}
	}()

	// reset the read count
	d.n = 0

	d.decode(pval.Elem())
	return
}

// readN reads n bytes and panics if the read fails.
func (d *Decoder) readN(n int) []byte {
	b := make([]byte, n)
	_, err := io.ReadFull(d, b)
	if err != nil {
		panic(err)
	}
	return b
}

// readPrefix reads a length-prefixed byte slice and panics if the read fails.
func (d *Decoder) readPrefix() []byte {
	b, err := ReadPrefix(d, maxSliceLen)
	if err != nil {
		panic(err)
	}
	return b
}

// decode reads the next encoded value from its input stream and stores it in
// val. The decoding rules are the inverse of those specified in the package
// docstring.
func (d *Decoder) decode(val reflect.Value) {
	// check for UnmarshalSia interface first
	if val.CanAddr() {
		if u, ok := val.Addr().Interface().(SiaUnmarshaler); ok {
			u.UnmarshalSia(d.readPrefix())
			return
		}
	}

	switch val.Kind() {
	case reflect.Ptr:
		var valid bool
		d.decode(reflect.ValueOf(&valid).Elem())
		// nil pointer, nothing to decode
		if !valid {
			return
		}
		// make sure we aren't decoding into nil
		if val.IsNil() {
			val.Set(reflect.New(val.Type().Elem()))
		}
		d.decode(val.Elem())
	case reflect.Bool:
		b := d.readN(1)
		if b[0] > 1 {
			panic("boolean value was not 0 or 1")
		}
		val.SetBool(b[0] == 1)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		val.SetInt(DecInt64(d.readN(8)))
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		val.SetUint(DecUint64(d.readN(8)))
	case reflect.String:
		val.SetString(string(d.readPrefix()))
	case reflect.Slice:
		// slices are variable length, but otherwise the same as arrays.
		// just have to allocate them first, then we can fallthrough to the array logic.
		sliceLen := DecUint64(d.readN(8))
		// sanity-check the sliceLen, otherwise you can crash a peer by making
		// them allocate a massive slice
		if sliceLen > 1<<31-1 || sliceLen*uint64(val.Type().Elem().Size()) > maxSliceLen {
			panic("slice is too large")
		}
		val.Set(reflect.MakeSlice(val.Type(), int(sliceLen), int(sliceLen)))
		fallthrough
	case reflect.Array:
		// special case for byte arrays (e.g. hashes)
		if val.Type().Elem().Kind() == reflect.Uint8 {
			// convert val to a slice and read into it directly
			b := val.Slice(0, val.Len())
			_, err := io.ReadFull(d, b.Bytes())
			if err != nil {
				panic(err)
			}
			return
		}
		// arrays are unmarshalled by sequentially unmarshalling their elements
		for i := 0; i < val.Len(); i++ {
			d.decode(val.Index(i))
		}
		return
	case reflect.Struct:
		for i := 0; i < val.NumField(); i++ {
			d.decode(val.Field(i))
		}
		return
	default:
		panic("unknown type")
	}
}

// NewDecoder returns a new decoder that reads from r.
func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{r, 0}
}

// Unmarshal decodes the encoded value b and stores it in v, which must be a
// pointer. The decoding rules are the inverse of those specified in the
// package docstring.
func Unmarshal(b []byte, v interface{}) error {
	r := bytes.NewReader(b)
	return NewDecoder(r).Decode(v)
}

// ReadFile reads the contents of a file and decodes them into v.
func ReadFile(filename string, v interface{}) error {
	file, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer file.Close()
	return NewDecoder(file).Decode(v)
}
<|endoftext|>


<commit_before>package main

import (
	"time"
	"flag"

	"github.com/mitchellh/cli"
	"github.com/aws/aws-sdk-go/service/ec2"
	//"os"
	"github.com/aws/aws-sdk-go/aws"
	"strings"
)

type CreateCommand struct {
	Ui 			cli.Ui
	InstanceId 	string
	Name 		string
	DryRun		bool
	NoReboot	bool
}

// descriptions for args
var createDscrInstanceId = "The instance from which to create the AMI"
var createDscrName = "The name of the AMI; the current timestamp will be automatically appended"
var createDscrDryRun = "Execute a simulated run"
var createDscrNoReboot = "If true, do not reboot the instance before creating the AMI. It is preferable to reboot the instance to guarantee a consistent filesystem when taking the snapshot, but the likelihood of an inconsistent snapshot is very low."

func (c *CreateCommand) Help() string {
	return `ec2-snapper create <args> [--help]

Create an AMI of the given EC2 instance.

Available args are:
--instance      ` + createDscrInstanceId + `
--name          ` + createDscrName + `
--dry-run       ` + createDscrDryRun + `
--no-reboot     ` + createDscrNoReboot
}

func (c *CreateCommand) Synopsis() string {
	return "Create an AMI of the given EC2 instance"
}

func (c *CreateCommand) Run(args []string) int {

	// Handle the command-line args
	cmdFlags := flag.NewFlagSet("create", flag.ExitOnError)
	cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }

	cmdFlags.StringVar(&c.InstanceId, "instance", "", createDscrInstanceId)
	cmdFlags.StringVar(&c.Name, "name", "", createDscrName)
	cmdFlags.BoolVar(&c.DryRun, "dry-run", false, createDscrDryRun)
	cmdFlags.BoolVar(&c.NoReboot, "no-reboot", true, createDscrNoReboot)

	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	// Check for required command-line args
	if c.InstanceId == "" {
		c.Ui.Error("ERROR: The argument '--instance' is required.")
		return 1
	}

	if c.Name == "" {
		c.Ui.Error("ERROR: The argument '--name' is required.")
		return 1
	}

	// Create an EC2 service object; AWS region is picked up from the "AWS_REGION" env var.
	svc := ec2.New(nil)

	// Generate a nicely formatted timestamp for right now
	const dateLayoutForAmiName = "2006-01-02 at 15_04_05 (MST)"
	t := time.Now()

	// Create the AMI Snapshot
	name := c.Name + " - " + t.Format(dateLayoutForAmiName)

	c.Ui.Output("==> Creating AMI for " + c.InstanceId + "...")

	resp, err := svc.CreateImage(&ec2.CreateImageInput{
		Name: &name,
		InstanceID: &c.InstanceId,
		DryRun: &c.DryRun,
		NoReboot: &c.NoReboot })
	if err != nil && strings.Contains(err.Error(), "NoCredentialProviders") {
		c.Ui.Error("ERROR: No AWS credentials were found.  Either set the environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, or run this program on an EC2 instance that has an IAM Role with the appropriate permissions.")
		return 1
	} else if err != nil {
		panic(err)
	}

	// Assign tags to this AMI.  We'll use these when it comes time to delete the AMI
	c.Ui.Output("==> Adding tags to AMI " + *resp.ImageID + "...")

	svc.CreateTags(&ec2.CreateTagsInput{
		Resources: []*string{resp.ImageID},
		Tags: []*ec2.Tag{
			&ec2.Tag{ Key: aws.String("ec2-snapper-instance-id"), Value: &c.InstanceId },
		},
	})

	c.Ui.Info("==> Success! Created " + *resp.ImageID + " named \"" + name + "\"")
	return 0
}

<commit_msg>Re-ordered imports<commit_after>package main

import (
	"flag"
	"time"
	"strings"

	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/mitchellh/cli"
)

type CreateCommand struct {
	Ui 			cli.Ui
	InstanceId 	string
	Name 		string
	DryRun		bool
	NoReboot	bool
}

// descriptions for args
var createDscrInstanceId = "The instance from which to create the AMI"
var createDscrName = "The name of the AMI; the current timestamp will be automatically appended"
var createDscrDryRun = "Execute a simulated run"
var createDscrNoReboot = "If true, do not reboot the instance before creating the AMI. It is preferable to reboot the instance to guarantee a consistent filesystem when taking the snapshot, but the likelihood of an inconsistent snapshot is very low."

func (c *CreateCommand) Help() string {
	return `ec2-snapper create <args> [--help]

Create an AMI of the given EC2 instance.

Available args are:
--instance      ` + createDscrInstanceId + `
--name          ` + createDscrName + `
--dry-run       ` + createDscrDryRun + `
--no-reboot     ` + createDscrNoReboot
}

func (c *CreateCommand) Synopsis() string {
	return "Create an AMI of the given EC2 instance"
}

func (c *CreateCommand) Run(args []string) int {

	// Handle the command-line args
	cmdFlags := flag.NewFlagSet("create", flag.ExitOnError)
	cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }

	cmdFlags.StringVar(&c.InstanceId, "instance", "", createDscrInstanceId)
	cmdFlags.StringVar(&c.Name, "name", "", createDscrName)
	cmdFlags.BoolVar(&c.DryRun, "dry-run", false, createDscrDryRun)
	cmdFlags.BoolVar(&c.NoReboot, "no-reboot", true, createDscrNoReboot)

	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	// Check for required command-line args
	if c.InstanceId == "" {
		c.Ui.Error("ERROR: The argument '--instance' is required.")
		return 1
	}

	if c.Name == "" {
		c.Ui.Error("ERROR: The argument '--name' is required.")
		return 1
	}

	// Create an EC2 service object; AWS region is picked up from the "AWS_REGION" env var.
	svc := ec2.New(nil)

	// Generate a nicely formatted timestamp for right now
	const dateLayoutForAmiName = "2006-01-02 at 15_04_05 (MST)"
	t := time.Now()

	// Create the AMI Snapshot
	name := c.Name + " - " + t.Format(dateLayoutForAmiName)

	c.Ui.Output("==> Creating AMI for " + c.InstanceId + "...")

	resp, err := svc.CreateImage(&ec2.CreateImageInput{
		Name: &name,
		InstanceID: &c.InstanceId,
		DryRun: &c.DryRun,
		NoReboot: &c.NoReboot })
	if err != nil && strings.Contains(err.Error(), "NoCredentialProviders") {
		c.Ui.Error("ERROR: No AWS credentials were found.  Either set the environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, or run this program on an EC2 instance that has an IAM Role with the appropriate permissions.")
		return 1
	} else if err != nil {
		panic(err)
	}

	// Assign tags to this AMI.  We'll use these when it comes time to delete the AMI
	c.Ui.Output("==> Adding tags to AMI " + *resp.ImageID + "...")

	svc.CreateTags(&ec2.CreateTagsInput{
		Resources: []*string{resp.ImageID},
		Tags: []*ec2.Tag{
			&ec2.Tag{ Key: aws.String("ec2-snapper-instance-id"), Value: &c.InstanceId },
		},
	})

	c.Ui.Info("==> Success! Created " + *resp.ImageID + " named \"" + name + "\"")
	return 0
}

<|endoftext|>