<commit_before>package ecs

import (
	"encoding/base64"
	"encoding/json"
	"strconv"
	"time"

	"github.com/denverdino/aliyungo/common"
	"github.com/denverdino/aliyungo/util"
)

// InstanceStatus represents instance status
type InstanceStatus string

// Constants of InstanceStatus
const (
	Creating = InstanceStatus("Creating")
	Running  = InstanceStatus("Running")
	Starting = InstanceStatus("Starting")

	Stopped  = InstanceStatus("Stopped")
	Stopping = InstanceStatus("Stopping")
)

type LockReason string

const (
	LockReasonFinancial = LockReason("financial")
	LockReasonSecurity  = LockReason("security")
)

type LockReasonType struct {
	LockReason LockReason
}

type DescribeInstanceStatusArgs struct {
	RegionId common.Region
	ZoneId   string
	common.Pagination
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancestatusitemtype
type InstanceStatusItemType struct {
	InstanceId string
	Status     InstanceStatus
}

type DescribeInstanceStatusResponse struct {
	common.Response
	common.PaginationResult
	InstanceStatuses struct {
		InstanceStatus []InstanceStatusItemType
	}
}

// DescribeInstanceStatus describes instance status
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstancestatus
func (client *Client) DescribeInstanceStatus(args *DescribeInstanceStatusArgs) (instanceStatuses []InstanceStatusItemType, pagination *common.PaginationResult, err error) {
	args.Validate()
	response := DescribeInstanceStatusResponse{}

	err = client.Invoke("DescribeInstanceStatus", args, &response)

	if err == nil {
		return response.InstanceStatuses.InstanceStatus, &response.PaginationResult, nil
	}

	return nil, nil, err
}

type StopInstanceArgs struct {
	InstanceId string
	ForceStop  bool
}

type StopInstanceResponse struct {
	common.Response
}

// StopInstance stops instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&stopinstance
func (client *Client) StopInstance(instanceId string, forceStop bool) error {
	args := StopInstanceArgs{
		InstanceId: instanceId,
		ForceStop:  forceStop,
	}
	response := StopInstanceResponse{}
	err := client.Invoke("StopInstance", &args, &response)
	return err
}

type StartInstanceArgs struct {
	InstanceId string
}

type StartInstanceResponse struct {
	common.Response
}

// StartInstance starts instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&startinstance
func (client *Client) StartInstance(instanceId string) error {
	args := StartInstanceArgs{InstanceId: instanceId}
	response := StartInstanceResponse{}
	err := client.Invoke("StartInstance", &args, &response)
	return err
}

type RebootInstanceArgs struct {
	InstanceId string
	ForceStop  bool
}

type RebootInstanceResponse struct {
	common.Response
}

// RebootInstance reboot instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&rebootinstance
func (client *Client) RebootInstance(instanceId string, forceStop bool) error {
	request := RebootInstanceArgs{
		InstanceId: instanceId,
		ForceStop:  forceStop,
	}
	response := RebootInstanceResponse{}
	err := client.Invoke("RebootInstance", &request, &response)
	return err
}

type DescribeInstanceAttributeArgs struct {
	InstanceId string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&operationlockstype
type OperationLocksType struct {
	LockReason []LockReasonType //enum for financial, security
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&securitygroupidsettype
type SecurityGroupIdSetType struct {
	SecurityGroupId string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&ipaddresssettype
type IpAddressSetType struct {
	IpAddress []string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&vpcattributestype
type VpcAttributesType struct {
	VpcId            string
	VSwitchId        string
	PrivateIpAddress IpAddressSetType
	NatIpAddress     string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&eipaddressassociatetype
type EipAddressAssociateType struct {
	AllocationId       string
	IpAddress          string
	Bandwidth          int
	InternetChargeType common.InternetChargeType
}

// Experimental feature
type SpotStrategyType string

// Constants of SpotStrategyType
const (
	NoSpot             = SpotStrategyType("NoSpot")
	SpotWithPriceLimit = SpotStrategyType("SpotWithPriceLimit")
)

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instanceattributestype
type InstanceAttributesType struct {
	InstanceId         string
	InstanceName       string
	Description        string
	ImageId            string
	RegionId           common.Region
	ZoneId             string
	CPU                int
	Memory             int
	ClusterId          string
	InstanceType       string
	InstanceTypeFamily string
	HostName           string
	SerialNumber       string
	Status             InstanceStatus
	OperationLocks     OperationLocksType
	SecurityGroupIds   struct {
		SecurityGroupId []string
	}
	PublicIpAddress         IpAddressSetType
	InnerIpAddress          IpAddressSetType
	InstanceNetworkType     string //enum Classic | Vpc
	InternetMaxBandwidthIn  int
	InternetMaxBandwidthOut int
	InternetChargeType      common.InternetChargeType
	CreationTime            util.ISO6801Time //time.Time
	VpcAttributes           VpcAttributesType
	EipAddress              EipAddressAssociateType
	IoOptimized             StringOrBool
	InstanceChargeType      common.InstanceChargeType
	ExpiredTime             util.ISO6801Time
	Tags                    struct {
		Tag []TagItemType
	}
	SpotStrategy SpotStrategyType
}

type DescribeInstanceAttributeResponse struct {
	common.Response
	InstanceAttributesType
}

// DescribeInstanceAttribute describes instance attribute
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstanceattribute
func (client *Client) DescribeInstanceAttribute(instanceId string) (instance *InstanceAttributesType, err error) {
	args := DescribeInstanceAttributeArgs{InstanceId: instanceId}

	response := DescribeInstanceAttributeResponse{}
	err = client.Invoke("DescribeInstanceAttribute", &args, &response)
	if err != nil {
		return nil, err
	}
	return &response.InstanceAttributesType, err
}

type ModifyInstanceAttributeArgs struct {
	InstanceId   string
	InstanceName string
	Description  string
	Password     string
	HostName     string
}

type ModifyInstanceAttributeResponse struct {
	common.Response
}

//ModifyInstanceAttribute  modify instance attrbute
//
// You can read doc at https://help.aliyun.com/document_detail/ecs/open-api/instance/modifyinstanceattribute.html
func (client *Client) ModifyInstanceAttribute(args *ModifyInstanceAttributeArgs) error {
	response := ModifyInstanceAttributeResponse{}
	err := client.Invoke("ModifyInstanceAttribute", args, &response)
	return err
}

// Default timeout value for WaitForInstance method
const InstanceDefaultTimeout = 120

// WaitForInstance waits for instance to given status
func (client *Client) WaitForInstance(instanceId string, status InstanceStatus, timeout int) error {
	if timeout <= 0 {
		timeout = InstanceDefaultTimeout
	}
	for {
		instance, err := client.DescribeInstanceAttribute(instanceId)
		if err != nil {
			return err
		}
		if instance.Status == status {
			//TODO
			//Sleep one more time for timing issues
			time.Sleep(DefaultWaitForInterval * time.Second)
			break
		}
		timeout = timeout - DefaultWaitForInterval
		if timeout <= 0 {
			return common.GetClientErrorFromString("Timeout")
		}
		time.Sleep(DefaultWaitForInterval * time.Second)

	}
	return nil
}

type DescribeInstanceVncUrlArgs struct {
	RegionId   common.Region
	InstanceId string
}

type DescribeInstanceVncUrlResponse struct {
	common.Response
	VncUrl string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstancevncurl
func (client *Client) DescribeInstanceVncUrl(args *DescribeInstanceVncUrlArgs) (string, error) {
	response := DescribeInstanceVncUrlResponse{}

	err := client.Invoke("DescribeInstanceVncUrl", args, &response)

	if err == nil {
		return response.VncUrl, nil
	}

	return "", err
}

type DescribeInstancesArgs struct {
	RegionId            common.Region
	VpcId               string
	VSwitchId           string
	ZoneId              string
	InstanceIds         string
	InstanceNetworkType string
	InstanceName        string
	Status              InstanceStatus
	PrivateIpAddresses  string
	InnerIpAddresses    string
	PublicIpAddresses   string
	SecurityGroupId     string
	Tag                 map[string]string
	InstanceType        string
	SpotStrategy        SpotStrategyType
	common.Pagination
}

type DescribeInstancesResponse struct {
	common.Response
	common.PaginationResult
	Instances struct {
		Instance []InstanceAttributesType
	}
}

// DescribeInstances describes instances
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstances
func (client *Client) DescribeInstances(args *DescribeInstancesArgs) (instances []InstanceAttributesType, pagination *common.PaginationResult, err error) {
	args.Validate()
	response := DescribeInstancesResponse{}

	err = client.Invoke("DescribeInstances", args, &response)

	if err == nil {
		return response.Instances.Instance, &response.PaginationResult, nil
	}

	return nil, nil, err
}

type DeleteInstanceArgs struct {
	InstanceId string
}

type DeleteInstanceResponse struct {
	common.Response
}

// DeleteInstance deletes instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&deleteinstance
func (client *Client) DeleteInstance(instanceId string) error {
	args := DeleteInstanceArgs{InstanceId: instanceId}
	response := DeleteInstanceResponse{}
	err := client.Invoke("DeleteInstance", &args, &response)
	return err
}

type DataDiskType struct {
	Size               int
	Category           DiskCategory //Enum cloud, ephemeral, ephemeral_ssd
	SnapshotId         string
	DiskName           string
	Description        string
	Device             string
	DeleteWithInstance bool
}

type SystemDiskType struct {
	Size        int
	Category    DiskCategory //Enum cloud, ephemeral, ephemeral_ssd
	DiskName    string
	Description string
}

type IoOptimized string

type StringOrBool struct {
	Value bool
}

// UnmarshalJSON implements the json.Unmarshaller interface.
func (io *StringOrBool) UnmarshalJSON(value []byte) error {
	if value[0] == '"' {
		var str string
		err := json.Unmarshal(value, &str)
		if err == nil {
			io.Value = (str == "true" || str == "optimized")
		}
		return err
	}
	var boolVal bool
	err := json.Unmarshal(value, &boolVal)
	if err == nil {
		io.Value = boolVal
	}
	return err
}

func (io StringOrBool) Bool() bool {
	return io.Value
}

func (io StringOrBool) String() string {
	return strconv.FormatBool(io.Value)
}

var (
	IoOptimizedNone      = IoOptimized("none")
	IoOptimizedOptimized = IoOptimized("optimized")
)

type CreateInstanceArgs struct {
	RegionId                common.Region
	ZoneId                  string
	ImageId                 string
	InstanceType            string
	SecurityGroupId         string
	InstanceName            string
	Description             string
	InternetChargeType      common.InternetChargeType
	InternetMaxBandwidthIn  int
	InternetMaxBandwidthOut int
	HostName                string
	Password                string
	IoOptimized             IoOptimized
	SystemDisk              SystemDiskType
	DataDisk                []DataDiskType
	VSwitchId               string
	PrivateIpAddress        string
	ClientToken             string
	InstanceChargeType      common.InstanceChargeType
	Period                  int
	UserData                string
	AutoRenew               bool
	AutoRenewPeriod         int
	SpotStrategy            SpotStrategyType
}

type CreateInstanceResponse struct {
	common.Response
	InstanceId string
}

// CreateInstance creates instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&createinstance
func (client *Client) CreateInstance(args *CreateInstanceArgs) (instanceId string, err error) {
	if args.UserData != "" {
		// Encode to base64 string
		args.UserData = base64.StdEncoding.EncodeToString([]byte(args.UserData))
	}
	response := CreateInstanceResponse{}
	err = client.Invoke("CreateInstance", args, &response)
	if err != nil {
		return "", err
	}
	return response.InstanceId, err
}

type SecurityGroupArgs struct {
	InstanceId      string
	SecurityGroupId string
}

type SecurityGroupResponse struct {
	common.Response
}

//JoinSecurityGroup
//
//You can read doc at https://help.aliyun.com/document_detail/ecs/open-api/instance/joinsecuritygroup.html
func (client *Client) JoinSecurityGroup(instanceId string, securityGroupId string) error {
	args := SecurityGroupArgs{InstanceId: instanceId, SecurityGroupId: securityGroupId}
	response := SecurityGroupResponse{}
	err := client.Invoke("JoinSecurityGroup", &args, &response)
	return err
}

//LeaveSecurityGroup
//
//You can read doc at https://help.aliyun.com/document_detail/ecs/open-api/instance/leavesecuritygroup.html
func (client *Client) LeaveSecurityGroup(instanceId string, securityGroupId string) error {
	args := SecurityGroupArgs{InstanceId: instanceId, SecurityGroupId: securityGroupId}
	response := SecurityGroupResponse{}
	err := client.Invoke("LeaveSecurityGroup", &args, &response)
	return err
}
<commit_msg>describe userdata<commit_after>package ecs

import (
	"encoding/base64"
	"encoding/json"
	"strconv"
	"time"

	"github.com/denverdino/aliyungo/common"
	"github.com/denverdino/aliyungo/util"
)

// InstanceStatus represents instance status
type InstanceStatus string

// Constants of InstanceStatus
const (
	Creating = InstanceStatus("Creating")
	Running  = InstanceStatus("Running")
	Starting = InstanceStatus("Starting")

	Stopped  = InstanceStatus("Stopped")
	Stopping = InstanceStatus("Stopping")
)

type LockReason string

const (
	LockReasonFinancial = LockReason("financial")
	LockReasonSecurity  = LockReason("security")
)

type LockReasonType struct {
	LockReason LockReason
}

type DescribeUserdataArgs struct {
	RegionId   common.Region
	InstanceId string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancestatusitemtype
type DescribeUserdataItemType struct {
	UserData   string
	InstanceId string
	RegionId   string
}

type DescribeUserdataResponse struct {
	common.Response
	DescribeUserdataItemType
}

// DescribeInstanceStatus describes instance status
//
// You can read doc at https://intl.aliyun.com/help/doc-detail/49227.htm
func (client *Client) DescribeUserdata(args *DescribeUserdataArgs) (userData *DescribeUserdataItemType, err error) {
	response := DescribeUserdataResponse{}

	err = client.Invoke("DescribeUserdata", args, &response)

	if err == nil {
		return &response.DescribeUserdataItemType, nil
	}

	return nil, err
}

type DescribeInstanceStatusArgs struct {
	RegionId common.Region
	ZoneId   string
	common.Pagination
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancestatusitemtype
type InstanceStatusItemType struct {
	InstanceId string
	Status     InstanceStatus
}

type DescribeInstanceStatusResponse struct {
	common.Response
	common.PaginationResult
	InstanceStatuses struct {
		InstanceStatus []InstanceStatusItemType
	}
}

// DescribeInstanceStatus describes instance status
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstancestatus
func (client *Client) DescribeInstanceStatus(args *DescribeInstanceStatusArgs) (instanceStatuses []InstanceStatusItemType, pagination *common.PaginationResult, err error) {
	args.Validate()
	response := DescribeInstanceStatusResponse{}

	err = client.Invoke("DescribeInstanceStatus", args, &response)

	if err == nil {
		return response.InstanceStatuses.InstanceStatus, &response.PaginationResult, nil
	}

	return nil, nil, err
}

type StopInstanceArgs struct {
	InstanceId string
	ForceStop  bool
}

type StopInstanceResponse struct {
	common.Response
}

// StopInstance stops instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&stopinstance
func (client *Client) StopInstance(instanceId string, forceStop bool) error {
	args := StopInstanceArgs{
		InstanceId: instanceId,
		ForceStop:  forceStop,
	}
	response := StopInstanceResponse{}
	err := client.Invoke("StopInstance", &args, &response)
	return err
}

type StartInstanceArgs struct {
	InstanceId string
}

type StartInstanceResponse struct {
	common.Response
}

// StartInstance starts instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&startinstance
func (client *Client) StartInstance(instanceId string) error {
	args := StartInstanceArgs{InstanceId: instanceId}
	response := StartInstanceResponse{}
	err := client.Invoke("StartInstance", &args, &response)
	return err
}

type RebootInstanceArgs struct {
	InstanceId string
	ForceStop  bool
}

type RebootInstanceResponse struct {
	common.Response
}

// RebootInstance reboot instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&rebootinstance
func (client *Client) RebootInstance(instanceId string, forceStop bool) error {
	request := RebootInstanceArgs{
		InstanceId: instanceId,
		ForceStop:  forceStop,
	}
	response := RebootInstanceResponse{}
	err := client.Invoke("RebootInstance", &request, &response)
	return err
}

type DescribeInstanceAttributeArgs struct {
	InstanceId string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&operationlockstype
type OperationLocksType struct {
	LockReason []LockReasonType //enum for financial, security
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&securitygroupidsettype
type SecurityGroupIdSetType struct {
	SecurityGroupId string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&ipaddresssettype
type IpAddressSetType struct {
	IpAddress []string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&vpcattributestype
type VpcAttributesType struct {
	VpcId            string
	VSwitchId        string
	PrivateIpAddress IpAddressSetType
	NatIpAddress     string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&eipaddressassociatetype
type EipAddressAssociateType struct {
	AllocationId       string
	IpAddress          string
	Bandwidth          int
	InternetChargeType common.InternetChargeType
}

// Experimental feature
type SpotStrategyType string

// Constants of SpotStrategyType
const (
	NoSpot             = SpotStrategyType("NoSpot")
	SpotWithPriceLimit = SpotStrategyType("SpotWithPriceLimit")
)

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instanceattributestype
type InstanceAttributesType struct {
	InstanceId         string
	InstanceName       string
	Description        string
	ImageId            string
	RegionId           common.Region
	ZoneId             string
	CPU                int
	Memory             int
	ClusterId          string
	InstanceType       string
	InstanceTypeFamily string
	HostName           string
	SerialNumber       string
	Status             InstanceStatus
	OperationLocks     OperationLocksType
	SecurityGroupIds   struct {
		SecurityGroupId []string
	}
	PublicIpAddress         IpAddressSetType
	InnerIpAddress          IpAddressSetType
	InstanceNetworkType     string //enum Classic | Vpc
	InternetMaxBandwidthIn  int
	InternetMaxBandwidthOut int
	InternetChargeType      common.InternetChargeType
	CreationTime            util.ISO6801Time //time.Time
	VpcAttributes           VpcAttributesType
	EipAddress              EipAddressAssociateType
	IoOptimized             StringOrBool
	InstanceChargeType      common.InstanceChargeType
	ExpiredTime             util.ISO6801Time
	Tags                    struct {
		Tag []TagItemType
	}
	SpotStrategy SpotStrategyType
}

type DescribeInstanceAttributeResponse struct {
	common.Response
	InstanceAttributesType
}

// DescribeInstanceAttribute describes instance attribute
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstanceattribute
func (client *Client) DescribeInstanceAttribute(instanceId string) (instance *InstanceAttributesType, err error) {
	args := DescribeInstanceAttributeArgs{InstanceId: instanceId}

	response := DescribeInstanceAttributeResponse{}
	err = client.Invoke("DescribeInstanceAttribute", &args, &response)
	if err != nil {
		return nil, err
	}
	return &response.InstanceAttributesType, err
}

type ModifyInstanceAttributeArgs struct {
	InstanceId   string
	InstanceName string
	Description  string
	Password     string
	HostName     string
}

type ModifyInstanceAttributeResponse struct {
	common.Response
}

//ModifyInstanceAttribute  modify instance attrbute
//
// You can read doc at https://help.aliyun.com/document_detail/ecs/open-api/instance/modifyinstanceattribute.html
func (client *Client) ModifyInstanceAttribute(args *ModifyInstanceAttributeArgs) error {
	response := ModifyInstanceAttributeResponse{}
	err := client.Invoke("ModifyInstanceAttribute", args, &response)
	return err
}

// Default timeout value for WaitForInstance method
const InstanceDefaultTimeout = 120

// WaitForInstance waits for instance to given status
func (client *Client) WaitForInstance(instanceId string, status InstanceStatus, timeout int) error {
	if timeout <= 0 {
		timeout = InstanceDefaultTimeout
	}
	for {
		instance, err := client.DescribeInstanceAttribute(instanceId)
		if err != nil {
			return err
		}
		if instance.Status == status {
			//TODO
			//Sleep one more time for timing issues
			time.Sleep(DefaultWaitForInterval * time.Second)
			break
		}
		timeout = timeout - DefaultWaitForInterval
		if timeout <= 0 {
			return common.GetClientErrorFromString("Timeout")
		}
		time.Sleep(DefaultWaitForInterval * time.Second)

	}
	return nil
}

type DescribeInstanceVncUrlArgs struct {
	RegionId   common.Region
	InstanceId string
}

type DescribeInstanceVncUrlResponse struct {
	common.Response
	VncUrl string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstancevncurl
func (client *Client) DescribeInstanceVncUrl(args *DescribeInstanceVncUrlArgs) (string, error) {
	response := DescribeInstanceVncUrlResponse{}

	err := client.Invoke("DescribeInstanceVncUrl", args, &response)

	if err == nil {
		return response.VncUrl, nil
	}

	return "", err
}

type DescribeInstancesArgs struct {
	RegionId            common.Region
	VpcId               string
	VSwitchId           string
	ZoneId              string
	InstanceIds         string
	InstanceNetworkType string
	InstanceName        string
	Status              InstanceStatus
	PrivateIpAddresses  string
	InnerIpAddresses    string
	PublicIpAddresses   string
	SecurityGroupId     string
	Tag                 map[string]string
	InstanceType        string
	SpotStrategy        SpotStrategyType
	common.Pagination
}

type DescribeInstancesResponse struct {
	common.Response
	common.PaginationResult
	Instances struct {
		Instance []InstanceAttributesType
	}
}

// DescribeInstances describes instances
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstances
func (client *Client) DescribeInstances(args *DescribeInstancesArgs) (instances []InstanceAttributesType, pagination *common.PaginationResult, err error) {
	args.Validate()
	response := DescribeInstancesResponse{}

	err = client.Invoke("DescribeInstances", args, &response)

	if err == nil {
		return response.Instances.Instance, &response.PaginationResult, nil
	}

	return nil, nil, err
}

type DeleteInstanceArgs struct {
	InstanceId string
}

type DeleteInstanceResponse struct {
	common.Response
}

// DeleteInstance deletes instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&deleteinstance
func (client *Client) DeleteInstance(instanceId string) error {
	args := DeleteInstanceArgs{InstanceId: instanceId}
	response := DeleteInstanceResponse{}
	err := client.Invoke("DeleteInstance", &args, &response)
	return err
}

type DataDiskType struct {
	Size               int
	Category           DiskCategory //Enum cloud, ephemeral, ephemeral_ssd
	SnapshotId         string
	DiskName           string
	Description        string
	Device             string
	DeleteWithInstance bool
}

type SystemDiskType struct {
	Size        int
	Category    DiskCategory //Enum cloud, ephemeral, ephemeral_ssd
	DiskName    string
	Description string
}

type IoOptimized string

type StringOrBool struct {
	Value bool
}

// UnmarshalJSON implements the json.Unmarshaller interface.
func (io *StringOrBool) UnmarshalJSON(value []byte) error {
	if value[0] == '"' {
		var str string
		err := json.Unmarshal(value, &str)
		if err == nil {
			io.Value = (str == "true" || str == "optimized")
		}
		return err
	}
	var boolVal bool
	err := json.Unmarshal(value, &boolVal)
	if err == nil {
		io.Value = boolVal
	}
	return err
}

func (io StringOrBool) Bool() bool {
	return io.Value
}

func (io StringOrBool) String() string {
	return strconv.FormatBool(io.Value)
}

var (
	IoOptimizedNone      = IoOptimized("none")
	IoOptimizedOptimized = IoOptimized("optimized")
)

type CreateInstanceArgs struct {
	RegionId                common.Region
	ZoneId                  string
	ImageId                 string
	InstanceType            string
	SecurityGroupId         string
	InstanceName            string
	Description             string
	InternetChargeType      common.InternetChargeType
	InternetMaxBandwidthIn  int
	InternetMaxBandwidthOut int
	HostName                string
	Password                string
	IoOptimized             IoOptimized
	SystemDisk              SystemDiskType
	DataDisk                []DataDiskType
	VSwitchId               string
	PrivateIpAddress        string
	ClientToken             string
	InstanceChargeType      common.InstanceChargeType
	Period                  int
	UserData                string
	AutoRenew               bool
	AutoRenewPeriod         int
	SpotStrategy            SpotStrategyType
}

type CreateInstanceResponse struct {
	common.Response
	InstanceId string
}

// CreateInstance creates instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&createinstance
func (client *Client) CreateInstance(args *CreateInstanceArgs) (instanceId string, err error) {
	if args.UserData != "" {
		// Encode to base64 string
		args.UserData = base64.StdEncoding.EncodeToString([]byte(args.UserData))
	}
	response := CreateInstanceResponse{}
	err = client.Invoke("CreateInstance", args, &response)
	if err != nil {
		return "", err
	}
	return response.InstanceId, err
}

type SecurityGroupArgs struct {
	InstanceId      string
	SecurityGroupId string
}

type SecurityGroupResponse struct {
	common.Response
}

//JoinSecurityGroup
//
//You can read doc at https://help.aliyun.com/document_detail/ecs/open-api/instance/joinsecuritygroup.html
func (client *Client) JoinSecurityGroup(instanceId string, securityGroupId string) error {
	args := SecurityGroupArgs{InstanceId: instanceId, SecurityGroupId: securityGroupId}
	response := SecurityGroupResponse{}
	err := client.Invoke("JoinSecurityGroup", &args, &response)
	return err
}

//LeaveSecurityGroup
//
//You can read doc at https://help.aliyun.com/document_detail/ecs/open-api/instance/leavesecuritygroup.html
func (client *Client) LeaveSecurityGroup(instanceId string, securityGroupId string) error {
	args := SecurityGroupArgs{InstanceId: instanceId, SecurityGroupId: securityGroupId}
	response := SecurityGroupResponse{}
	err := client.Invoke("LeaveSecurityGroup", &args, &response)
	return err
}
<|endoftext|>


<commit_before>package ecs

import (
	"time"

	"github.com/denverdino/aliyungo/common"
	"github.com/denverdino/aliyungo/util"
)

// InstanceStatus represents instance status
type InstanceStatus string

// Constants of InstanceStatus
const (
	Creating = InstanceStatus("Creating")
	Running  = InstanceStatus("Running")
	Starting = InstanceStatus("Starting")

	Stopped  = InstanceStatus("Stopped")
	Stopping = InstanceStatus("Stopping")
)

type LockReason string

const (
	LockReasonFinancial = LockReason("financial")
	LockReasonSecurity  = LockReason("security")
)

type DescribeInstanceStatusArgs struct {
	RegionId common.Region
	ZoneId   string
	common.Pagination
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancestatusitemtype
type InstanceStatusItemType struct {
	InstanceId string
	Status     InstanceStatus
}

type DescribeInstanceStatusResponse struct {
	common.Response
	common.PaginationResult
	InstanceStatuses struct {
		InstanceStatus []InstanceStatusItemType
	}
}

// DescribeInstanceStatus describes instance status
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstancestatus
func (client *Client) DescribeInstanceStatus(args *DescribeInstanceStatusArgs) (instanceStatuses []InstanceStatusItemType, pagination *common.PaginationResult, err error) {
	args.Validate()
	response := DescribeInstanceStatusResponse{}

	err = client.Invoke("DescribeInstanceStatus", args, &response)

	if err == nil {
		return response.InstanceStatuses.InstanceStatus, &response.PaginationResult, nil
	}

	return nil, nil, err
}

type StopInstanceArgs struct {
	InstanceId string
	ForceStop  bool
}

type StopInstanceResponse struct {
	common.Response
}

// StopInstance stops instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&stopinstance
func (client *Client) StopInstance(instanceId string, forceStop bool) error {
	args := StopInstanceArgs{
		InstanceId: instanceId,
		ForceStop:  forceStop,
	}
	response := StopInstanceResponse{}
	err := client.Invoke("StopInstance", &args, &response)
	return err
}

type StartInstanceArgs struct {
	InstanceId string
}

type StartInstanceResponse struct {
	common.Response
}

// StartInstance starts instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&startinstance
func (client *Client) StartInstance(instanceId string) error {
	args := StartInstanceArgs{InstanceId: instanceId}
	response := StartInstanceResponse{}
	err := client.Invoke("StartInstance", &args, &response)
	return err
}

type RebootInstanceArgs struct {
	InstanceId string
	ForceStop  bool
}

type RebootInstanceResponse struct {
	common.Response
}

// RebootInstance reboot instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&rebootinstance
func (client *Client) RebootInstance(instanceId string, forceStop bool) error {
	request := RebootInstanceArgs{
		InstanceId: instanceId,
		ForceStop:  forceStop,
	}
	response := RebootInstanceResponse{}
	err := client.Invoke("RebootInstance", &request, &response)
	return err
}

type DescribeInstanceAttributeArgs struct {
	InstanceId string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&operationlockstype
type OperationLocksType struct {
	LockReason []LockReason //enum for financial, security
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&securitygroupidsettype
type SecurityGroupIdSetType struct {
	SecurityGroupId string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&ipaddresssettype
type IpAddressSetType struct {
	IpAddress []string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&vpcattributestype
type VpcAttributesType struct {
	VpcId            string
	VSwitchId        string
	PrivateIpAddress IpAddressSetType
	NatIpAddress     string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&eipaddressassociatetype
type EipAddressAssociateType struct {
	AllocationId       string
	IpAddress          string
	Bandwidth          int
	InternetChargeType common.InternetChargeType
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instanceattributestype
type InstanceAttributesType struct {
	InstanceId       string
	InstanceName     string
	Description      string
	ImageId          string
	RegionId         common.Region
	ZoneId           string
	ClusterId        string
	InstanceType     string
	HostName         string
	Status           InstanceStatus
	OperationLocks   OperationLocksType
	SecurityGroupIds struct {
		SecurityGroupId []string
	}
	PublicIpAddress         IpAddressSetType
	InnerIpAddress          IpAddressSetType
	InstanceNetworkType     string //enum Classic | Vpc
	InternetMaxBandwidthIn  int
	InternetMaxBandwidthOut int
	InternetChargeType      common.InternetChargeType
	CreationTime            util.ISO6801Time //time.Time
	VpcAttributes           VpcAttributesType
	EipAddress              EipAddressAssociateType
}

type DescribeInstanceAttributeResponse struct {
	common.Response
	InstanceAttributesType
}

// DescribeInstanceAttribute describes instance attribute
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstanceattribute
func (client *Client) DescribeInstanceAttribute(instanceId string) (instance *InstanceAttributesType, err error) {
	args := DescribeInstanceAttributeArgs{InstanceId: instanceId}

	response := DescribeInstanceAttributeResponse{}
	err = client.Invoke("DescribeInstanceAttribute", &args, &response)
	if err != nil {
		return nil, err
	}
	return &response.InstanceAttributesType, err
}

type ModifyInstanceAttributeArgs struct {
	InstanceId   string
	InstanceName string
	Description  string
	Password     string
	HostName     string
}

type ModifyInstanceAttributeResponse struct {
	common.Response
}

//ModifyInstanceAttribute  modify instance attrbute
//
// You can read doc at https://help.aliyun.com/document_detail/ecs/open-api/instance/modifyinstanceattribute.html
func (client *Client) ModifyInstanceAttribute(args *ModifyInstanceAttributeArgs) error {
	response := ModifyInstanceAttributeResponse{}
	err := client.Invoke("ModifyInstanceAttribute", args, &response)
	return err
}

// Default timeout value for WaitForInstance method
const InstanceDefaultTimeout = 120

// WaitForInstance waits for instance to given status
func (client *Client) WaitForInstance(instanceId string, status InstanceStatus, timeout int) error {
	if timeout <= 0 {
		timeout = InstanceDefaultTimeout
	}
	for {
		instance, err := client.DescribeInstanceAttribute(instanceId)
		if err != nil {
			return err
		}
		if instance.Status == status {
			//TODO
			//Sleep one more time for timing issues
			time.Sleep(DefaultWaitForInterval * time.Second)
			break
		}
		timeout = timeout - DefaultWaitForInterval
		if timeout <= 0 {
			return common.GetClientErrorFromString("Timeout")
		}
		time.Sleep(DefaultWaitForInterval * time.Second)

	}
	return nil
}

type DescribeInstanceVncUrlArgs struct {
	RegionId   common.Region
	InstanceId string
}

type DescribeInstanceVncUrlResponse struct {
	common.Response
	VncUrl string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstancevncurl
func (client *Client) DescribeInstanceVncUrl(args *DescribeInstanceVncUrlArgs) (string, error) {
	response := DescribeInstanceVncUrlResponse{}

	err := client.Invoke("DescribeInstanceVncUrl", args, &response)

	if err == nil {
		return response.VncUrl, nil
	}

	return "", err
}

type DescribeInstancesArgs struct {
	RegionId            common.Region
	VpcId               string
	VSwitchId           string
	ZoneId              string
	InstanceIds         string
	InstanceNetworkType string
	PrivateIpAddresses  string
	InnerIpAddresses    string
	PublicIpAddresses   string
	SecurityGroupId     string
	common.Pagination
}

type DescribeInstancesResponse struct {
	common.Response
	common.PaginationResult
	Instances struct {
		Instance []InstanceAttributesType
	}
}

// DescribeInstances describes instances
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstances
func (client *Client) DescribeInstances(args *DescribeInstancesArgs) (instances []InstanceAttributesType, pagination *common.PaginationResult, err error) {
	args.Validate()
	response := DescribeInstancesResponse{}

	err = client.Invoke("DescribeInstances", args, &response)

	if err == nil {
		return response.Instances.Instance, &response.PaginationResult, nil
	}

	return nil, nil, err
}

type DeleteInstanceArgs struct {
	InstanceId string
}

type DeleteInstanceResponse struct {
	common.Response
}

// DeleteInstance deletes instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&deleteinstance
func (client *Client) DeleteInstance(instanceId string) error {
	args := DeleteInstanceArgs{InstanceId: instanceId}
	response := DeleteInstanceResponse{}
	err := client.Invoke("DeleteInstance", &args, &response)
	return err
}

type DataDiskType struct {
	Size               int
	Category           DiskCategory //Enum cloud, ephemeral, ephemeral_ssd
	SnapshotId         string
	DiskName           string
	Description        string
	Device             string
	DeleteWithInstance bool
}

type SystemDiskType struct {
	Category    DiskCategory //Enum cloud, ephemeral, ephemeral_ssd
	DiskName    string
	Description string
}

type IoOptimized string

var (
	IoOptimizedNone      = IoOptimized("none")
	IoOptimizedOptimized = IoOptimized("optimized")
)

type CreateInstanceArgs struct {
	RegionId                common.Region
	ZoneId                  string
	ImageId                 string
	InstanceType            string
	SecurityGroupId         string
	InstanceName            string
	Description             string
	InternetChargeType      common.InternetChargeType
	InternetMaxBandwidthIn  int
	InternetMaxBandwidthOut int
	HostName                string
	Password                string
	IoOptimized             IoOptimized
	SystemDisk              SystemDiskType
	DataDisk                []DataDiskType
	VSwitchId               string
	PrivateIpAddress        string
	ClientToken             string
}

type CreateInstanceResponse struct {
	common.Response
	InstanceId string
}

// CreateInstance creates instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&createinstance
func (client *Client) CreateInstance(args *CreateInstanceArgs) (instanceId string, err error) {
	response := CreateInstanceResponse{}
	err = client.Invoke("CreateInstance", args, &response)
	if err != nil {
		return "", err
	}
	return response.InstanceId, err
}
<commit_msg>add_io_optimized_option<commit_after>package ecs

import (
	"time"

	"github.com/denverdino/aliyungo/common"
	"github.com/denverdino/aliyungo/util"
)

// InstanceStatus represents instance status
type InstanceStatus string

// Constants of InstanceStatus
const (
	Creating = InstanceStatus("Creating")
	Running  = InstanceStatus("Running")
	Starting = InstanceStatus("Starting")

	Stopped  = InstanceStatus("Stopped")
	Stopping = InstanceStatus("Stopping")
)

type LockReason string

const (
	LockReasonFinancial = LockReason("financial")
	LockReasonSecurity  = LockReason("security")
)

type DescribeInstanceStatusArgs struct {
	RegionId common.Region
	ZoneId   string
	common.Pagination
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancestatusitemtype
type InstanceStatusItemType struct {
	InstanceId string
	Status     InstanceStatus
}

type DescribeInstanceStatusResponse struct {
	common.Response
	common.PaginationResult
	InstanceStatuses struct {
		InstanceStatus []InstanceStatusItemType
	}
}

// DescribeInstanceStatus describes instance status
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstancestatus
func (client *Client) DescribeInstanceStatus(args *DescribeInstanceStatusArgs) (instanceStatuses []InstanceStatusItemType, pagination *common.PaginationResult, err error) {
	args.Validate()
	response := DescribeInstanceStatusResponse{}

	err = client.Invoke("DescribeInstanceStatus", args, &response)

	if err == nil {
		return response.InstanceStatuses.InstanceStatus, &response.PaginationResult, nil
	}

	return nil, nil, err
}

type StopInstanceArgs struct {
	InstanceId string
	ForceStop  bool
}

type StopInstanceResponse struct {
	common.Response
}

// StopInstance stops instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&stopinstance
func (client *Client) StopInstance(instanceId string, forceStop bool) error {
	args := StopInstanceArgs{
		InstanceId: instanceId,
		ForceStop:  forceStop,
	}
	response := StopInstanceResponse{}
	err := client.Invoke("StopInstance", &args, &response)
	return err
}

type StartInstanceArgs struct {
	InstanceId string
}

type StartInstanceResponse struct {
	common.Response
}

// StartInstance starts instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&startinstance
func (client *Client) StartInstance(instanceId string) error {
	args := StartInstanceArgs{InstanceId: instanceId}
	response := StartInstanceResponse{}
	err := client.Invoke("StartInstance", &args, &response)
	return err
}

type RebootInstanceArgs struct {
	InstanceId string
	ForceStop  bool
}

type RebootInstanceResponse struct {
	common.Response
}

// RebootInstance reboot instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&rebootinstance
func (client *Client) RebootInstance(instanceId string, forceStop bool) error {
	request := RebootInstanceArgs{
		InstanceId: instanceId,
		ForceStop:  forceStop,
	}
	response := RebootInstanceResponse{}
	err := client.Invoke("RebootInstance", &request, &response)
	return err
}

type DescribeInstanceAttributeArgs struct {
	InstanceId string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&operationlockstype
type OperationLocksType struct {
	LockReason []LockReason //enum for financial, security
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&securitygroupidsettype
type SecurityGroupIdSetType struct {
	SecurityGroupId string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&ipaddresssettype
type IpAddressSetType struct {
	IpAddress []string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&vpcattributestype
type VpcAttributesType struct {
	VpcId            string
	VSwitchId        string
	PrivateIpAddress IpAddressSetType
	NatIpAddress     string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&eipaddressassociatetype
type EipAddressAssociateType struct {
	AllocationId       string
	IpAddress          string
	Bandwidth          int
	InternetChargeType common.InternetChargeType
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instanceattributestype
type InstanceAttributesType struct {
	InstanceId       string
	InstanceName     string
	Description      string
	ImageId          string
	RegionId         common.Region
	ZoneId           string
	ClusterId        string
	InstanceType     string
	HostName         string
	Status           InstanceStatus
	OperationLocks   OperationLocksType
	SecurityGroupIds struct {
		SecurityGroupId []string
	}
	PublicIpAddress         IpAddressSetType
	InnerIpAddress          IpAddressSetType
	InstanceNetworkType     string //enum Classic | Vpc
	InternetMaxBandwidthIn  int
	InternetMaxBandwidthOut int
	InternetChargeType      common.InternetChargeType
	CreationTime            util.ISO6801Time //time.Time
	VpcAttributes           VpcAttributesType
	EipAddress              EipAddressAssociateType
	IoOptimized             bool
}

type DescribeInstanceAttributeResponse struct {
	common.Response
	InstanceAttributesType
}

// DescribeInstanceAttribute describes instance attribute
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstanceattribute
func (client *Client) DescribeInstanceAttribute(instanceId string) (instance *InstanceAttributesType, err error) {
	args := DescribeInstanceAttributeArgs{InstanceId: instanceId}

	response := DescribeInstanceAttributeResponse{}
	err = client.Invoke("DescribeInstanceAttribute", &args, &response)
	if err != nil {
		return nil, err
	}
	return &response.InstanceAttributesType, err
}

type ModifyInstanceAttributeArgs struct {
	InstanceId   string
	InstanceName string
	Description  string
	Password     string
	HostName     string
}

type ModifyInstanceAttributeResponse struct {
	common.Response
}

//ModifyInstanceAttribute  modify instance attrbute
//
// You can read doc at https://help.aliyun.com/document_detail/ecs/open-api/instance/modifyinstanceattribute.html
func (client *Client) ModifyInstanceAttribute(args *ModifyInstanceAttributeArgs) error {
	response := ModifyInstanceAttributeResponse{}
	err := client.Invoke("ModifyInstanceAttribute", args, &response)
	return err
}

// Default timeout value for WaitForInstance method
const InstanceDefaultTimeout = 120

// WaitForInstance waits for instance to given status
func (client *Client) WaitForInstance(instanceId string, status InstanceStatus, timeout int) error {
	if timeout <= 0 {
		timeout = InstanceDefaultTimeout
	}
	for {
		instance, err := client.DescribeInstanceAttribute(instanceId)
		if err != nil {
			return err
		}
		if instance.Status == status {
			//TODO
			//Sleep one more time for timing issues
			time.Sleep(DefaultWaitForInterval * time.Second)
			break
		}
		timeout = timeout - DefaultWaitForInterval
		if timeout <= 0 {
			return common.GetClientErrorFromString("Timeout")
		}
		time.Sleep(DefaultWaitForInterval * time.Second)

	}
	return nil
}

type DescribeInstanceVncUrlArgs struct {
	RegionId   common.Region
	InstanceId string
}

type DescribeInstanceVncUrlResponse struct {
	common.Response
	VncUrl string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstancevncurl
func (client *Client) DescribeInstanceVncUrl(args *DescribeInstanceVncUrlArgs) (string, error) {
	response := DescribeInstanceVncUrlResponse{}

	err := client.Invoke("DescribeInstanceVncUrl", args, &response)

	if err == nil {
		return response.VncUrl, nil
	}

	return "", err
}

type DescribeInstancesArgs struct {
	RegionId            common.Region
	VpcId               string
	VSwitchId           string
	ZoneId              string
	InstanceIds         string
	InstanceNetworkType string
	PrivateIpAddresses  string
	InnerIpAddresses    string
	PublicIpAddresses   string
	SecurityGroupId     string
	common.Pagination
}

type DescribeInstancesResponse struct {
	common.Response
	common.PaginationResult
	Instances struct {
		Instance []InstanceAttributesType
	}
}

// DescribeInstances describes instances
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstances
func (client *Client) DescribeInstances(args *DescribeInstancesArgs) (instances []InstanceAttributesType, pagination *common.PaginationResult, err error) {
	args.Validate()
	response := DescribeInstancesResponse{}

	err = client.Invoke("DescribeInstances", args, &response)

	if err == nil {
		return response.Instances.Instance, &response.PaginationResult, nil
	}

	return nil, nil, err
}

type DeleteInstanceArgs struct {
	InstanceId string
}

type DeleteInstanceResponse struct {
	common.Response
}

// DeleteInstance deletes instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&deleteinstance
func (client *Client) DeleteInstance(instanceId string) error {
	args := DeleteInstanceArgs{InstanceId: instanceId}
	response := DeleteInstanceResponse{}
	err := client.Invoke("DeleteInstance", &args, &response)
	return err
}

type DataDiskType struct {
	Size               int
	Category           DiskCategory //Enum cloud, ephemeral, ephemeral_ssd
	SnapshotId         string
	DiskName           string
	Description        string
	Device             string
	DeleteWithInstance bool
}

type SystemDiskType struct {
	Category    DiskCategory //Enum cloud, ephemeral, ephemeral_ssd
	DiskName    string
	Description string
}

type IoOptimized string

var (
	IoOptimizedNone      = IoOptimized("none")
	IoOptimizedOptimized = IoOptimized("optimized")
)

type CreateInstanceArgs struct {
	RegionId                common.Region
	ZoneId                  string
	ImageId                 string
	InstanceType            string
	SecurityGroupId         string
	InstanceName            string
	Description             string
	InternetChargeType      common.InternetChargeType
	InternetMaxBandwidthIn  int
	InternetMaxBandwidthOut int
	HostName                string
	Password                string
	IoOptimized             IoOptimized
	SystemDisk              SystemDiskType
	DataDisk                []DataDiskType
	VSwitchId               string
	PrivateIpAddress        string
	ClientToken             string
}

type CreateInstanceResponse struct {
	common.Response
	InstanceId string
}

// CreateInstance creates instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&createinstance
func (client *Client) CreateInstance(args *CreateInstanceArgs) (instanceId string, err error) {
	response := CreateInstanceResponse{}
	err = client.Invoke("CreateInstance", args, &response)
	if err != nil {
		return "", err
	}
	return response.InstanceId, err
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package main

import (
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"strings"

	"github.com/pkg/errors"
)

func main() {
	if strings.Contains(os.Args[0], "run_e2e.sh") || strings.Contains(os.Args[0], "gorunner") {
		log.Print("warn: calling test with e2e.test is deprecated, please rely on container manifest to invoke executable")
	}
	env := envWithDefaults(map[string]string{
		resultsDirEnvKey: defaultResultsDir,
		skipEnvKey:       defaultSkip,
		focusEnvKey:      defaultFocus,
		providerEnvKey:   defaultProvider,
		parallelEnvKey:   defaultParallel,
		ginkgoEnvKey:     defaultGinkgoBinary,
		testBinEnvKey:    defaultTestBinary,
	})

	if err := configureAndRunWithEnv(env); err != nil {
		log.Fatal(err)
	}
}

// configureAndRunWithEnv uses the given environment to configure and then start the test run.
// It will handle TERM signals gracefully and kill the test process and will
// save the logs/results to the location specified via the RESULTS_DIR environment
// variable.
func configureAndRunWithEnv(env Getenver) error {
	// Ensure we save results regardless of other errors. This helps any
	// consumer who may be polling for the results.
	resultsDir := env.Getenv(resultsDirEnvKey)
	defer saveResults(resultsDir)

	// Print the output to stdout and a logfile which will be returned
	// as part of the results tarball.
	logFilePath := filepath.Join(resultsDir, logFileName)
	logFile, err := os.Create(logFilePath)
	if err != nil {
		return errors.Wrapf(err, "failed to create log file %v", logFilePath)
	}
	mw := io.MultiWriter(os.Stdout, logFile)
	cmd := getCmd(env, mw)

	log.Printf("Running command:\n%v\n", cmdInfo(cmd))
	err = cmd.Start()
	if err != nil {
		return errors.Wrap(err, "starting command")
	}

	// Handle signals and shutdown process gracefully.
	go setupSigHandler(cmd.Process.Pid)
	return errors.Wrap(cmd.Wait(), "running command")
}

// setupSigHandler will kill the process identified by the given PID if it
// gets a TERM signal.
func setupSigHandler(pid int) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	// Block until a signal is received.
	log.Println("Now listening for interrupts")
	s := <-c
	log.Printf("Got signal: %v. Shutting down test process (PID: %v)\n", s, pid)
	p, err := os.FindProcess(pid)
	if err != nil {
		log.Printf("Could not find process %v to shut down.\n", pid)
		return
	}
	if err := p.Signal(s); err != nil {
		log.Printf("Failed to signal test process to terminate: %v\n", err)
		return
	}
	log.Printf("Signalled process %v to terminate successfully.\n", pid)
}

// saveResults will tar the results directory and write the resulting tarball path
// into the donefile.
func saveResults(resultsDir string) error {
	log.Printf("Saving results at %v\n", resultsDir)

	err := tarDir(resultsDir, filepath.Join(resultsDir, resultsTarballName))
	if err != nil {
		return errors.Wrapf(err, "tar directory %v", resultsDir)
	}

	doneFile := filepath.Join(resultsDir, doneFileName)

	resultsTarball := filepath.Join(resultsDir, resultsTarballName)
	resultsTarball, err = filepath.Abs(resultsTarball)
	if err != nil {
		return errors.Wrapf(err, "failed to find absolute path for %v", resultsTarball)
	}

	return errors.Wrap(
		ioutil.WriteFile(doneFile, []byte(resultsTarball), os.FileMode(0777)),
		"writing donefile",
	)
}
<commit_msg>Add deadline for deprecation<commit_after>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package main

import (
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"strings"

	"github.com/pkg/errors"
)

func main() {
	if strings.Contains(os.Args[0], "run_e2e.sh") || strings.Contains(os.Args[0], "gorunner") {
		log.Print("warn: calling test with e2e.test is deprecated and will be removed in 1.25, please rely on container manifest to invoke executable")
	}
	env := envWithDefaults(map[string]string{
		resultsDirEnvKey: defaultResultsDir,
		skipEnvKey:       defaultSkip,
		focusEnvKey:      defaultFocus,
		providerEnvKey:   defaultProvider,
		parallelEnvKey:   defaultParallel,
		ginkgoEnvKey:     defaultGinkgoBinary,
		testBinEnvKey:    defaultTestBinary,
	})

	if err := configureAndRunWithEnv(env); err != nil {
		log.Fatal(err)
	}
}

// configureAndRunWithEnv uses the given environment to configure and then start the test run.
// It will handle TERM signals gracefully and kill the test process and will
// save the logs/results to the location specified via the RESULTS_DIR environment
// variable.
func configureAndRunWithEnv(env Getenver) error {
	// Ensure we save results regardless of other errors. This helps any
	// consumer who may be polling for the results.
	resultsDir := env.Getenv(resultsDirEnvKey)
	defer saveResults(resultsDir)

	// Print the output to stdout and a logfile which will be returned
	// as part of the results tarball.
	logFilePath := filepath.Join(resultsDir, logFileName)
	logFile, err := os.Create(logFilePath)
	if err != nil {
		return errors.Wrapf(err, "failed to create log file %v", logFilePath)
	}
	mw := io.MultiWriter(os.Stdout, logFile)
	cmd := getCmd(env, mw)

	log.Printf("Running command:\n%v\n", cmdInfo(cmd))
	err = cmd.Start()
	if err != nil {
		return errors.Wrap(err, "starting command")
	}

	// Handle signals and shutdown process gracefully.
	go setupSigHandler(cmd.Process.Pid)
	return errors.Wrap(cmd.Wait(), "running command")
}

// setupSigHandler will kill the process identified by the given PID if it
// gets a TERM signal.
func setupSigHandler(pid int) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	// Block until a signal is received.
	log.Println("Now listening for interrupts")
	s := <-c
	log.Printf("Got signal: %v. Shutting down test process (PID: %v)\n", s, pid)
	p, err := os.FindProcess(pid)
	if err != nil {
		log.Printf("Could not find process %v to shut down.\n", pid)
		return
	}
	if err := p.Signal(s); err != nil {
		log.Printf("Failed to signal test process to terminate: %v\n", err)
		return
	}
	log.Printf("Signalled process %v to terminate successfully.\n", pid)
}

// saveResults will tar the results directory and write the resulting tarball path
// into the donefile.
func saveResults(resultsDir string) error {
	log.Printf("Saving results at %v\n", resultsDir)

	err := tarDir(resultsDir, filepath.Join(resultsDir, resultsTarballName))
	if err != nil {
		return errors.Wrapf(err, "tar directory %v", resultsDir)
	}

	doneFile := filepath.Join(resultsDir, doneFileName)

	resultsTarball := filepath.Join(resultsDir, resultsTarballName)
	resultsTarball, err = filepath.Abs(resultsTarball)
	if err != nil {
		return errors.Wrapf(err, "failed to find absolute path for %v", resultsTarball)
	}

	return errors.Wrap(
		ioutil.WriteFile(doneFile, []byte(resultsTarball), os.FileMode(0777)),
		"writing donefile",
	)
}
<|endoftext|>


<commit_before>package editor

import (
	"honnef.co/go/js/dom"
	"kego.io/editor/client/mdl"
	"kego.io/kerr"
)

type ObjectEditor struct {
	*Node
	*Editor
}

var _ EditorInterface = (*ObjectEditor)(nil)

func NewObjectEditor(n *Node) *ObjectEditor {
	return &ObjectEditor{Node: n, Editor: &Editor{}}
}

func (e *ObjectEditor) Layout() Layout {
	return Page
}

func (e *ObjectEditor) Initialize(holder BranchInterface, layout Layout, path string, aliases map[string]string) error {

	e.Editor.Initialize(holder, layout, path, aliases)

	e.initializeBlockEditors()

	if err := e.initializeTable(); err != nil {
		return kerr.New("KAVTMDDFYW", err, "initializeTable")
	}

	return nil
}

func (e *ObjectEditor) initializeBlockEditors() {
	for _, node := range e.Map {
		if node.Missing || node.Null {
			continue
		}
		ed := node.Editor()
		if ed == nil || ed.Layout() == Page {
			continue
		}
		e.Editors = append(e.Editors, ed)
		ed.Initialize(e.branch, Block, e.Path, e.Aliases)
		e.branch.ListenForEditorChanges(ed.Listen().Ch)
		e.AppendChild(ed)
	}
}

func (e *ObjectEditor) initializeTable() error {

	table, err := NewObjectSummary(e.Node, e.Path, e.Aliases)
	if err != nil {
		return kerr.New("VYJPBBMEBS", err, "NewObjectSummary")
	}
	e.AppendChild(table)

	return nil
}

func (e *ObjectEditor) AddChildTreeEntry(child EditorInterface) bool {
	return child.Layout() == Page
}

func (e *ObjectEditor) Value() interface{} {
	return e.Node.Value
}

func (e *ObjectEditor) AddField() {
	e.Missing = false
	e.Null = false
	// TODO: update e.Node.Value and other Node things
	// TODO: add block editor, or somehow get the branch to add a child :/
	e.Node.changes.Send(e.Node)
}

type objectSummary struct {
	*summary
}

type objectSummaryRow struct {
	*summaryRow
	name    *summaryCell
	origin  *summaryCell
	holds   *summaryCell
	value   *summaryCell
	options *summaryCell
}

func NewObjectSummary(node *Node, path string, aliases map[string]string) (*objectSummary, error) {
	s := &objectSummary{summary: &summary{TableStruct: mdl.Table(), path: path, aliases: aliases}}
	s.Head("name", "origin", "holds", "value", "options")
	for _, child := range node.Map {
		r := s.newRow(child)
		if err := r.update(); err != nil {
			return nil, kerr.New("UNVCXHWGRF", err, "update")
		}
	}
	s.Upgrade()
	return s, nil
}

func (s *objectSummary) newRow(node *Node) *objectSummaryRow {
	r := &objectSummaryRow{summaryRow: &summaryRow{TableRowStruct: s.Row(), node: node, table: s.summary}}
	r.name = &summaryCell{TableCellStruct: r.Cell()}
	r.origin = &summaryCell{TableCellStruct: r.Cell()}
	r.holds = &summaryCell{TableCellStruct: r.Cell()}
	r.value = &summaryCell{TableCellStruct: r.Cell()}
	r.options = &summaryCell{TableCellStruct: r.Cell()}

	changes := node.changes.Listen().Ch
	go func() {
		for e := range changes {
			_ = e.(*Node)
			r.update()
		}
	}()

	return r
}

func (r *objectSummaryRow) update() error {
	node := r.node

	if !node.Missing && !node.Null {
		ed := node.Editor()
		r.Click(func(e dom.Event) {
			e.(*dom.MouseEvent).PreventDefault()
			ed.Select()
			ed.Focus()
		})
	}
	r.name.Text(node.Key)

	origin, err := node.Origin.ValueContext(r.table.path, r.table.aliases)
	if err != nil {
		return kerr.New("ACQLJXWYQX", err, "ValueContext")
	}
	r.origin.Text(origin)

	hold, err := node.Rule.HoldsDisplayType(r.table.path, r.table.aliases)
	if err != nil {
		return kerr.New("OYMARPFDGA", err, "ValueContext")
	}
	r.holds.Text(hold)

	if node.Missing || node.Null {
		r.value.Text("")
	} else {
		value, err := node.Type.Id.ValueContext(r.table.path, r.table.aliases)
		if err != nil {
			return kerr.New("LDAMPELUCM", err, "ValueContext")
		}
		r.value.Text(value)
	}

	// TODO: optimize this with state
	for _, child := range r.options.ChildNodes() {
		r.options.RemoveChild(child)
	}
	if node.Missing || node.Null {
		a := mdl.Anchor().Text("add").Click(func(e dom.Event) {
			e.(*dom.MouseEvent).PreventDefault()
			node.Parent.editor.(*ObjectEditor).AddField()
		})
		r.options.AppendChild(a)
	}
	return nil
}
<commit_msg>Fixed add button<commit_after>package editor

import (
	"honnef.co/go/js/dom"
	"kego.io/editor/client/mdl"
	"kego.io/kerr"
)

type ObjectEditor struct {
	*Node
	*Editor
}

var _ EditorInterface = (*ObjectEditor)(nil)

func NewObjectEditor(n *Node) *ObjectEditor {
	return &ObjectEditor{Node: n, Editor: &Editor{}}
}

func (e *ObjectEditor) Layout() Layout {
	return Page
}

func (e *ObjectEditor) Initialize(holder BranchInterface, layout Layout, path string, aliases map[string]string) error {

	e.Editor.Initialize(holder, layout, path, aliases)

	e.initializeBlockEditors()

	if err := e.initializeTable(); err != nil {
		return kerr.New("KAVTMDDFYW", err, "initializeTable")
	}

	return nil
}

func (e *ObjectEditor) initializeBlockEditors() {
	for _, node := range e.Map {
		if node.Missing || node.Null {
			continue
		}
		ed := node.Editor()
		if ed == nil || ed.Layout() == Page {
			continue
		}
		e.Editors = append(e.Editors, ed)
		ed.Initialize(e.branch, Block, e.Path, e.Aliases)
		e.branch.ListenForEditorChanges(ed.Listen().Ch)
		e.AppendChild(ed)
	}
}

func (e *ObjectEditor) initializeTable() error {

	table, err := NewObjectSummary(e.Node, e.Path, e.Aliases)
	if err != nil {
		return kerr.New("VYJPBBMEBS", err, "NewObjectSummary")
	}
	e.AppendChild(table)

	return nil
}

func (e *ObjectEditor) AddChildTreeEntry(child EditorInterface) bool {
	return child.Layout() == Page
}

func (e *ObjectEditor) Value() interface{} {
	return e.Node.Value
}

func (e *ObjectEditor) AddField(node *Node) {
	node.Missing = false
	node.Null = false
	// TODO: update e.Node.Value and other Node things
	// TODO: add block editor, or somehow get the branch to add a child :/
	node.changes.Send(node)
}

type objectSummary struct {
	*summary
}

type objectSummaryRow struct {
	*summaryRow
	name    *summaryCell
	origin  *summaryCell
	holds   *summaryCell
	value   *summaryCell
	options *summaryCell
}

func NewObjectSummary(node *Node, path string, aliases map[string]string) (*objectSummary, error) {
	s := &objectSummary{summary: &summary{TableStruct: mdl.Table(), path: path, aliases: aliases}}
	s.Head("name", "origin", "holds", "value", "options")
	for _, child := range node.Map {
		r := s.newRow(child)
		if err := r.update(); err != nil {
			return nil, kerr.New("UNVCXHWGRF", err, "update")
		}
	}
	s.Upgrade()
	return s, nil
}

func (s *objectSummary) newRow(node *Node) *objectSummaryRow {
	r := &objectSummaryRow{summaryRow: &summaryRow{TableRowStruct: s.Row(), node: node, table: s.summary}}
	r.name = &summaryCell{TableCellStruct: r.Cell()}
	r.origin = &summaryCell{TableCellStruct: r.Cell()}
	r.holds = &summaryCell{TableCellStruct: r.Cell()}
	r.value = &summaryCell{TableCellStruct: r.Cell()}
	r.options = &summaryCell{TableCellStruct: r.Cell()}

	changes := node.changes.Listen().Ch
	go func() {
		for e := range changes {
			_ = e.(*Node)
			r.update()
		}
	}()

	return r
}

func (r *objectSummaryRow) update() error {
	node := r.node

	if !node.Missing && !node.Null {
		ed := node.Editor()
		r.Click(func(e dom.Event) {
			e.(*dom.MouseEvent).PreventDefault()
			ed.Select()
			ed.Focus()
		})
	}
	r.name.Text(node.Key)

	origin, err := node.Origin.ValueContext(r.table.path, r.table.aliases)
	if err != nil {
		return kerr.New("ACQLJXWYQX", err, "ValueContext")
	}
	r.origin.Text(origin)

	hold, err := node.Rule.HoldsDisplayType(r.table.path, r.table.aliases)
	if err != nil {
		return kerr.New("OYMARPFDGA", err, "ValueContext")
	}
	r.holds.Text(hold)

	if node.Missing || node.Null {
		r.value.Text("")
	} else {
		value, err := node.Type.Id.ValueContext(r.table.path, r.table.aliases)
		if err != nil {
			return kerr.New("LDAMPELUCM", err, "ValueContext")
		}
		r.value.Text(value)
	}

	// TODO: optimize this with state
	for _, child := range r.options.ChildNodes() {
		r.options.RemoveChild(child)
	}
	if node.Missing || node.Null {
		a := mdl.Anchor().Text("add").Click(func(e dom.Event) {
			e.(*dom.MouseEvent).PreventDefault()
			node.Parent.editor.(*ObjectEditor).AddField(node)

		})
		r.options.AppendChild(a)
	}
	return nil
}
<|endoftext|>


<commit_before>// errchk $G -e $D/$F.go

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import "io"

func whatis(x interface{}) string {
	switch x.(type) {
	case int:
		return "int"
	case int: // ERROR "duplicate"
		return "int8"
	case io.Reader:
		return "Reader1"
	case io.Reader: // ERROR "duplicate"
		return "Reader2"
	case interface { r(); w() }:
		return "rw"
	case interface { w(); r() }:	// ERROR "duplicate"
		return "wr"

	}
	return ""
}
<commit_msg>type switches: test for pathological case<commit_after>// errchk $G -e $D/$F.go

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import "io"

func whatis(x interface{}) string {
	switch x.(type) {
	case int:
		return "int"
	case int: // ERROR "duplicate"
		return "int8"
	case io.Reader:
		return "Reader1"
	case io.Reader: // ERROR "duplicate"
		return "Reader2"
	case interface {
		r()
		w()
	}:
		return "rw"
	case interface {
		w()
		r()
	}: // ERROR "duplicate"
		return "wr"

	}
	return ""
}

func notused(x interface{}) {
	// The first t is in a different scope than the 2nd t; it cannot
	// be accessed (=> declared and not used error); but it is legal
	// to declare it.
	switch t := 0; t := x.(type) { // ERROR "declared and not used"
	case int:
		_ = t // this is using the t of "t := x.(type)"
	}
}
<|endoftext|>


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

// +build !js

package oto

// #cgo LDFLAGS: -framework AudioToolbox
//
// #import <AudioToolbox/AudioToolbox.h>
//
// void oto_render(void* inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer);
import "C"

import (
	"fmt"
	"runtime"
	"sync"
	"time"
	"unsafe"
)

const queueBufferSize = 4096

type driver struct {
	audioQueue      C.AudioQueueRef
	buf             []byte
	bufSize         int
	sampleRate      int
	channelNum      int
	bitDepthInBytes int
	buffers         []C.AudioQueueBufferRef

	chWrite   chan []byte
	chWritten chan int
}

var (
	theDriver *driver
	driverM   sync.Mutex
)

func setDriver(d *driver) {
	driverM.Lock()
	defer driverM.Unlock()

	if theDriver != nil && d != nil {
		panic("oto: at most one driver object can exist")
	}
	theDriver = d

	setNotificationHandler(d)
}

func getDriver() *driver {
	driverM.Lock()
	defer driverM.Unlock()

	return theDriver
}

// TOOD: Convert the error code correctly.
// See https://stackoverflow.com/questions/2196869/how-do-you-convert-an-iphone-osstatus-code-to-something-useful

func newDriver(sampleRate, channelNum, bitDepthInBytes, bufferSizeInBytes int) (tryWriteCloser, error) {
	flags := C.kAudioFormatFlagIsPacked
	if bitDepthInBytes != 1 {
		flags |= C.kAudioFormatFlagIsSignedInteger
	}
	desc := C.AudioStreamBasicDescription{
		mSampleRate:       C.double(sampleRate),
		mFormatID:         C.kAudioFormatLinearPCM,
		mFormatFlags:      C.UInt32(flags),
		mBytesPerPacket:   C.UInt32(channelNum * bitDepthInBytes),
		mFramesPerPacket:  1,
		mBytesPerFrame:    C.UInt32(channelNum * bitDepthInBytes),
		mChannelsPerFrame: C.UInt32(channelNum),
		mBitsPerChannel:   C.UInt32(8 * bitDepthInBytes),
	}
	var audioQueue C.AudioQueueRef
	if osstatus := C.AudioQueueNewOutput(
		&desc,
		(C.AudioQueueOutputCallback)(C.oto_render),
		nil,
		(C.CFRunLoopRef)(0),
		(C.CFStringRef)(0),
		0,
		&audioQueue); osstatus != C.noErr {
		return nil, fmt.Errorf("oto: AudioQueueNewFormat with StreamFormat failed: %d", osstatus)
	}

	nbuf := bufferSizeInBytes / queueBufferSize
	if nbuf <= 1 {
		nbuf = 2
	}

	d := &driver{
		audioQueue:      audioQueue,
		sampleRate:      sampleRate,
		channelNum:      channelNum,
		bitDepthInBytes: bitDepthInBytes,
		bufSize:         nbuf * queueBufferSize,
		buffers:         make([]C.AudioQueueBufferRef, nbuf),
		chWrite:         make(chan []byte),
		chWritten:       make(chan int),
	}
	runtime.SetFinalizer(d, (*driver).Close)
	// Set the driver before setting the rendering callback.
	setDriver(d)

	for i := 0; i < len(d.buffers); i++ {
		C.AudioQueueAllocateBuffer(audioQueue, C.UInt32(queueBufferSize), &d.buffers[i])
		d.buffers[i].mAudioDataByteSize = C.UInt32(queueBufferSize)
		for j := 0; j < queueBufferSize; j++ {
			*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(d.buffers[i].mAudioData)) + uintptr(j))) = 0
		}
		C.AudioQueueEnqueueBuffer(audioQueue, d.buffers[i], 0, nil)
	}

	C.AudioQueueStart(audioQueue, nil)

	return d, nil
}

//export oto_render
func oto_render(inUserData unsafe.Pointer, inAQ C.AudioQueueRef, inBuffer C.AudioQueueBufferRef) {
	d := getDriver()

	var buf []byte
loop:
	for len(buf) < queueBufferSize {
		// Set the timer. When the application is in background or being switched, the driver's buffer is not
		// updated and it is needed to fill the buffer with zeros.
		s := time.Second * queueBufferSize / time.Duration(d.sampleRate*d.channelNum*d.bitDepthInBytes)
		t := time.NewTicker(s)
		defer t.Stop()

		select {
		case dbuf := <-d.chWrite:
			n := queueBufferSize - len(buf)
			if n > len(dbuf) {
				n = len(dbuf)
			}
			buf = append(buf, dbuf[:n]...)
			d.chWritten <- n
		case <-t.C:
			buf = append(buf, make([]byte, queueBufferSize-len(buf))...)
			break loop
		}
	}

	for i := 0; i < queueBufferSize; i++ {
		*(*byte)(unsafe.Pointer(uintptr(inBuffer.mAudioData) + uintptr(i))) = buf[i]
	}
	// Do not update mAudioDataByteSize, or the buffer is not used correctly any more.

	C.AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nil)
}

func (d *driver) TryWrite(data []byte) (int, error) {
	n := d.bufSize - len(d.buf)
	if n > len(data) {
		n = len(data)
	}
	d.buf = append(d.buf, data[:n]...)
	// Use the buffer only when the buffer length is enough to avoid choppy sound.
	for len(d.buf) >= queueBufferSize {
		d.chWrite <- d.buf
		n := <-d.chWritten
		d.buf = d.buf[n:]
	}
	return n, nil
}

func (d *driver) Close() error {
	runtime.SetFinalizer(d, nil)

	for _, b := range d.buffers {
		if osstatus := C.AudioQueueFreeBuffer(d.audioQueue, b); osstatus != C.noErr {
			return fmt.Errorf("oto: AudioQueueFreeBuffer failed: %d", osstatus)
		}
	}

	if osstatus := C.AudioQueueStop(d.audioQueue, C.false); osstatus != C.noErr {
		return fmt.Errorf("oto: AudioQueueStop failed: %d", osstatus)
	}
	if osstatus := C.AudioQueueDispose(d.audioQueue, C.false); osstatus != C.noErr {
		return fmt.Errorf("oto: AudioQueueDispose failed: %d", osstatus)
	}
	d.audioQueue = nil
	setDriver(nil)
	return nil
}
<commit_msg>darwin: Add error handlings<commit_after>// Copyright 2019 The Oto Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !js

package oto

// #cgo LDFLAGS: -framework AudioToolbox
//
// #import <AudioToolbox/AudioToolbox.h>
//
// void oto_render(void* inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer);
import "C"

import (
	"fmt"
	"runtime"
	"sync"
	"time"
	"unsafe"
)

const queueBufferSize = 4096

type driver struct {
	audioQueue      C.AudioQueueRef
	buf             []byte
	bufSize         int
	sampleRate      int
	channelNum      int
	bitDepthInBytes int
	buffers         []C.AudioQueueBufferRef

	chWrite   chan []byte
	chWritten chan int
}

var (
	theDriver *driver
	driverM   sync.Mutex
)

func setDriver(d *driver) {
	driverM.Lock()
	defer driverM.Unlock()

	if theDriver != nil && d != nil {
		panic("oto: at most one driver object can exist")
	}
	theDriver = d

	setNotificationHandler(d)
}

func getDriver() *driver {
	driverM.Lock()
	defer driverM.Unlock()

	return theDriver
}

// TOOD: Convert the error code correctly.
// See https://stackoverflow.com/questions/2196869/how-do-you-convert-an-iphone-osstatus-code-to-something-useful

func newDriver(sampleRate, channelNum, bitDepthInBytes, bufferSizeInBytes int) (tryWriteCloser, error) {
	flags := C.kAudioFormatFlagIsPacked
	if bitDepthInBytes != 1 {
		flags |= C.kAudioFormatFlagIsSignedInteger
	}
	desc := C.AudioStreamBasicDescription{
		mSampleRate:       C.double(sampleRate),
		mFormatID:         C.kAudioFormatLinearPCM,
		mFormatFlags:      C.UInt32(flags),
		mBytesPerPacket:   C.UInt32(channelNum * bitDepthInBytes),
		mFramesPerPacket:  1,
		mBytesPerFrame:    C.UInt32(channelNum * bitDepthInBytes),
		mChannelsPerFrame: C.UInt32(channelNum),
		mBitsPerChannel:   C.UInt32(8 * bitDepthInBytes),
	}
	var audioQueue C.AudioQueueRef
	if osstatus := C.AudioQueueNewOutput(
		&desc,
		(C.AudioQueueOutputCallback)(C.oto_render),
		nil,
		(C.CFRunLoopRef)(0),
		(C.CFStringRef)(0),
		0,
		&audioQueue); osstatus != C.noErr {
		return nil, fmt.Errorf("oto: AudioQueueNewFormat with StreamFormat failed: %d", osstatus)
	}

	nbuf := bufferSizeInBytes / queueBufferSize
	if nbuf <= 1 {
		nbuf = 2
	}

	d := &driver{
		audioQueue:      audioQueue,
		sampleRate:      sampleRate,
		channelNum:      channelNum,
		bitDepthInBytes: bitDepthInBytes,
		bufSize:         nbuf * queueBufferSize,
		buffers:         make([]C.AudioQueueBufferRef, nbuf),
		chWrite:         make(chan []byte),
		chWritten:       make(chan int),
	}
	runtime.SetFinalizer(d, (*driver).Close)
	// Set the driver before setting the rendering callback.
	setDriver(d)

	for i := 0; i < len(d.buffers); i++ {
		if osstatus := C.AudioQueueAllocateBuffer(audioQueue, C.UInt32(queueBufferSize), &d.buffers[i]); osstatus != C.noErr {
			return nil, fmt.Errorf("oto: AudioQueueAllocateBuffer failed: %d", osstatus)
		}
		d.buffers[i].mAudioDataByteSize = C.UInt32(queueBufferSize)
		for j := 0; j < queueBufferSize; j++ {
			*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(d.buffers[i].mAudioData)) + uintptr(j))) = 0
		}
		if osstatus := C.AudioQueueEnqueueBuffer(audioQueue, d.buffers[i], 0, nil); osstatus != C.noErr {
			return nil, fmt.Errorf("oto: AudioQueueEnqueueBuffer failed: %d", osstatus)
		}
	}

	if osstatus := C.AudioQueueStart(audioQueue, nil); osstatus != C.noErr {
		return nil, fmt.Errorf("oto: AudioQueueStart failed: %d", osstatus)
	}

	return d, nil
}

//export oto_render
func oto_render(inUserData unsafe.Pointer, inAQ C.AudioQueueRef, inBuffer C.AudioQueueBufferRef) {
	d := getDriver()

	var buf []byte
loop:
	for len(buf) < queueBufferSize {
		// Set the timer. When the application is in background or being switched, the driver's buffer is not
		// updated and it is needed to fill the buffer with zeros.
		s := time.Second * queueBufferSize / time.Duration(d.sampleRate*d.channelNum*d.bitDepthInBytes)
		t := time.NewTicker(s)
		defer t.Stop()

		select {
		case dbuf := <-d.chWrite:
			n := queueBufferSize - len(buf)
			if n > len(dbuf) {
				n = len(dbuf)
			}
			buf = append(buf, dbuf[:n]...)
			d.chWritten <- n
		case <-t.C:
			buf = append(buf, make([]byte, queueBufferSize-len(buf))...)
			break loop
		}
	}

	for i := 0; i < queueBufferSize; i++ {
		*(*byte)(unsafe.Pointer(uintptr(inBuffer.mAudioData) + uintptr(i))) = buf[i]
	}
	// Do not update mAudioDataByteSize, or the buffer is not used correctly any more.

	// TODO: Check errors?
	C.AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nil)
}

func (d *driver) TryWrite(data []byte) (int, error) {
	n := d.bufSize - len(d.buf)
	if n > len(data) {
		n = len(data)
	}
	d.buf = append(d.buf, data[:n]...)
	// Use the buffer only when the buffer length is enough to avoid choppy sound.
	for len(d.buf) >= queueBufferSize {
		d.chWrite <- d.buf
		n := <-d.chWritten
		d.buf = d.buf[n:]
	}
	return n, nil
}

func (d *driver) Close() error {
	runtime.SetFinalizer(d, nil)

	for _, b := range d.buffers {
		if osstatus := C.AudioQueueFreeBuffer(d.audioQueue, b); osstatus != C.noErr {
			return fmt.Errorf("oto: AudioQueueFreeBuffer failed: %d", osstatus)
		}
	}

	if osstatus := C.AudioQueueStop(d.audioQueue, C.false); osstatus != C.noErr {
		return fmt.Errorf("oto: AudioQueueStop failed: %d", osstatus)
	}
	if osstatus := C.AudioQueueDispose(d.audioQueue, C.false); osstatus != C.noErr {
		return fmt.Errorf("oto: AudioQueueDispose failed: %d", osstatus)
	}
	d.audioQueue = nil
	setDriver(nil)
	return nil
}
<|endoftext|>


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

// +build !js

package oto

// #cgo LDFLAGS: -framework AudioToolbox
//
// #import <AudioToolbox/AudioToolbox.h>
//
// void oto_render(void* inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer);
//
// void oto_setNotificationHandler(AudioQueueRef audioQueue);
import "C"

import (
	"fmt"
	"runtime"
	"sync"
	"time"
	"unsafe"
)

const baseQueueBufferSize = 1024

type audioInfo struct {
	channelNum      int
	bitDepthInBytes int
}

type driver struct {
	audioQueue C.AudioQueueRef
	buf        []byte
	bufSize    int
	sampleRate int
	audioInfo  *audioInfo
	buffers    []C.AudioQueueBufferRef
	paused     bool

	err error

	chWrite   chan []byte
	chWritten chan int

	m sync.Mutex
}

var (
	theDriver *driver
	driverM   sync.Mutex
)

func setDriver(d *driver) {
	driverM.Lock()
	defer driverM.Unlock()

	if theDriver != nil && d != nil {
		panic("oto: at most one driver object can exist")
	}
	theDriver = d

	if d != nil {
		setNotificationHandler(d)
	}
}

func getDriver() *driver {
	driverM.Lock()
	defer driverM.Unlock()

	return theDriver
}

// TOOD: Convert the error code correctly.
// See https://stackoverflow.com/questions/2196869/how-do-you-convert-an-iphone-osstatus-code-to-something-useful

func newDriver(sampleRate, channelNum, bitDepthInBytes, bufferSizeInBytes int) (tryWriteCloser, error) {
	flags := C.kAudioFormatFlagIsPacked
	if bitDepthInBytes != 1 {
		flags |= C.kAudioFormatFlagIsSignedInteger
	}
	desc := C.AudioStreamBasicDescription{
		mSampleRate:       C.double(sampleRate),
		mFormatID:         C.kAudioFormatLinearPCM,
		mFormatFlags:      C.UInt32(flags),
		mBytesPerPacket:   C.UInt32(channelNum * bitDepthInBytes),
		mFramesPerPacket:  1,
		mBytesPerFrame:    C.UInt32(channelNum * bitDepthInBytes),
		mChannelsPerFrame: C.UInt32(channelNum),
		mBitsPerChannel:   C.UInt32(8 * bitDepthInBytes),
	}

	audioInfo := &audioInfo{
		channelNum:      channelNum,
		bitDepthInBytes: bitDepthInBytes,
	}

	var audioQueue C.AudioQueueRef
	if osstatus := C.AudioQueueNewOutput(
		&desc,
		(C.AudioQueueOutputCallback)(C.oto_render),
		unsafe.Pointer(audioInfo),
		(C.CFRunLoopRef)(0),
		(C.CFStringRef)(0),
		0,
		&audioQueue); osstatus != C.noErr {
		return nil, fmt.Errorf("oto: AudioQueueNewFormat with StreamFormat failed: %d", osstatus)
	}

	queueBufferSize := baseQueueBufferSize * channelNum * bitDepthInBytes
	nbuf := bufferSizeInBytes / queueBufferSize
	if nbuf <= 1 {
		nbuf = 2
	}

	d := &driver{
		audioQueue: audioQueue,
		sampleRate: sampleRate,
		audioInfo:  audioInfo,
		bufSize:    nbuf * queueBufferSize,
		buffers:    make([]C.AudioQueueBufferRef, nbuf),
		chWrite:    make(chan []byte),
		chWritten:  make(chan int),
	}
	runtime.SetFinalizer(d, (*driver).Close)
	// Set the driver before setting the rendering callback.
	setDriver(d)

	for i := 0; i < len(d.buffers); i++ {
		if osstatus := C.AudioQueueAllocateBuffer(audioQueue, C.UInt32(queueBufferSize), &d.buffers[i]); osstatus != C.noErr {
			return nil, fmt.Errorf("oto: AudioQueueAllocateBuffer failed: %d", osstatus)
		}
		d.buffers[i].mAudioDataByteSize = C.UInt32(queueBufferSize)
		for j := 0; j < queueBufferSize; j++ {
			*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(d.buffers[i].mAudioData)) + uintptr(j))) = 0
		}
		if osstatus := C.AudioQueueEnqueueBuffer(audioQueue, d.buffers[i], 0, nil); osstatus != C.noErr {
			return nil, fmt.Errorf("oto: AudioQueueEnqueueBuffer failed: %d", osstatus)
		}
	}

	if osstatus := C.AudioQueueStart(audioQueue, nil); osstatus != C.noErr {
		return nil, fmt.Errorf("oto: AudioQueueStart failed: %d", osstatus)
	}

	return d, nil
}

//export oto_render
func oto_render(inUserData unsafe.Pointer, inAQ C.AudioQueueRef, inBuffer C.AudioQueueBufferRef) {
	audioInfo := (*audioInfo)(inUserData)
	queueBufferSize := baseQueueBufferSize * audioInfo.channelNum * audioInfo.bitDepthInBytes

	d := getDriver()

	var buf []byte

	// Set the timer. When the input does not come, the audio must be paused.
	s := time.Second * time.Duration(queueBufferSize) / time.Duration(d.sampleRate*d.audioInfo.channelNum*d.audioInfo.bitDepthInBytes)
	t := time.NewTicker(s)
	defer t.Stop()
	ch := t.C

	for len(buf) < queueBufferSize {
		select {
		case dbuf := <-d.chWrite:
			d.resume()
			n := queueBufferSize - len(buf)
			if n > len(dbuf) {
				n = len(dbuf)
			}
			buf = append(buf, dbuf[:n]...)
			d.chWritten <- n
		case <-ch:
			d.pause()
			ch = nil
		}
	}

	for i := 0; i < queueBufferSize; i++ {
		*(*byte)(unsafe.Pointer(uintptr(inBuffer.mAudioData) + uintptr(i))) = buf[i]
	}
	// Do not update mAudioDataByteSize, or the buffer is not used correctly any more.

	d.enqueueBuffer(inBuffer)
}

func (d *driver) TryWrite(data []byte) (int, error) {
	d.m.Lock()
	err := d.err
	d.m.Unlock()
	if err != nil {
		return 0, err
	}

	n := d.bufSize - len(d.buf)
	if n > len(data) {
		n = len(data)
	}
	d.buf = append(d.buf, data[:n]...)
	// Use the buffer only when the buffer length is enough to avoid choppy sound.
	queueBufferSize := baseQueueBufferSize * d.audioInfo.channelNum * d.audioInfo.bitDepthInBytes
	for len(d.buf) >= queueBufferSize {
		d.chWrite <- d.buf
		n := <-d.chWritten
		d.buf = d.buf[n:]
	}
	return n, nil
}

func (d *driver) Close() error {
	runtime.SetFinalizer(d, nil)

	if osstatus := C.AudioQueueStop(d.audioQueue, C.false); osstatus != C.noErr {
		return fmt.Errorf("oto: AudioQueueStop failed: %d", osstatus)
	}
	if osstatus := C.AudioQueueDispose(d.audioQueue, C.false); osstatus != C.noErr {
		return fmt.Errorf("oto: AudioQueueDispose failed: %d", osstatus)
	}
	d.audioQueue = nil
	setDriver(nil)
	return nil
}

func (d *driver) enqueueBuffer(buffer C.AudioQueueBufferRef) {
	d.m.Lock()
	defer d.m.Unlock()

	if osstatus := C.AudioQueueEnqueueBuffer(d.audioQueue, buffer, 0, nil); osstatus != C.noErr && d.err == nil {
		d.err = fmt.Errorf("oto: AudioQueueEnqueueBuffer failed: %d", osstatus)
		return
	}
}

func (d *driver) resume() {
	d.m.Lock()
	defer d.m.Unlock()

	if !d.paused {
		return
	}
	if osstatus := C.AudioQueueStart(d.audioQueue, nil); osstatus != C.noErr && d.err == nil {
		d.err = fmt.Errorf("oto: AudioQueueStart failed: %d", osstatus)
		return
	}
	d.paused = false
}

func (d *driver) pause() {
	d.m.Lock()
	defer d.m.Unlock()

	if d.paused {
		return
	}
	if osstatus := C.AudioQueuePause(d.audioQueue); osstatus != C.noErr && d.err == nil {
		d.err = fmt.Errorf("oto: AudioQueuePause failed: %d", osstatus)
		return
	}
	d.paused = true
}

func setNotificationHandler(driver *driver) {
	C.oto_setNotificationHandler(driver.audioQueue)
}

//export oto_setGlobalPause
func oto_setGlobalPause(paused C.int) {
	if paused != 0 {
		theDriver.pause()
	} else {
		// Audio doesn't work soon after recovering from sleeping. Wait for a while
		// (hajimehoshi/ebiten#1259).
		time.Sleep(500 * time.Millisecond)
		theDriver.resume()
	}
}

//export oto_setErrorByNotification
func oto_setErrorByNotification(s C.OSStatus, from *C.char) {
	if theDriver.err != nil {
		return
	}

	gofrom := C.GoString(from)
	theDriver.err = fmt.Errorf("oto: %s at notification failed: %d", gofrom, s)
}
<commit_msg>darwin: Longer sleep on resuming from sleeping<commit_after>// Copyright 2019 The Oto Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !js

package oto

// #cgo LDFLAGS: -framework AudioToolbox
//
// #import <AudioToolbox/AudioToolbox.h>
//
// void oto_render(void* inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer);
//
// void oto_setNotificationHandler(AudioQueueRef audioQueue);
import "C"

import (
	"fmt"
	"runtime"
	"sync"
	"time"
	"unsafe"
)

const baseQueueBufferSize = 1024

type audioInfo struct {
	channelNum      int
	bitDepthInBytes int
}

type driver struct {
	audioQueue    C.AudioQueueRef
	buf           []byte
	bufSize       int
	sampleRate    int
	audioInfo     *audioInfo
	buffers       []C.AudioQueueBufferRef
	paused        bool
	lastPauseTime time.Time

	err error

	chWrite   chan []byte
	chWritten chan int

	m sync.Mutex
}

var (
	theDriver *driver
	driverM   sync.Mutex
)

func setDriver(d *driver) {
	driverM.Lock()
	defer driverM.Unlock()

	if theDriver != nil && d != nil {
		panic("oto: at most one driver object can exist")
	}
	theDriver = d

	if d != nil {
		setNotificationHandler(d)
	}
}

func getDriver() *driver {
	driverM.Lock()
	defer driverM.Unlock()

	return theDriver
}

// TOOD: Convert the error code correctly.
// See https://stackoverflow.com/questions/2196869/how-do-you-convert-an-iphone-osstatus-code-to-something-useful

func newDriver(sampleRate, channelNum, bitDepthInBytes, bufferSizeInBytes int) (tryWriteCloser, error) {
	flags := C.kAudioFormatFlagIsPacked
	if bitDepthInBytes != 1 {
		flags |= C.kAudioFormatFlagIsSignedInteger
	}
	desc := C.AudioStreamBasicDescription{
		mSampleRate:       C.double(sampleRate),
		mFormatID:         C.kAudioFormatLinearPCM,
		mFormatFlags:      C.UInt32(flags),
		mBytesPerPacket:   C.UInt32(channelNum * bitDepthInBytes),
		mFramesPerPacket:  1,
		mBytesPerFrame:    C.UInt32(channelNum * bitDepthInBytes),
		mChannelsPerFrame: C.UInt32(channelNum),
		mBitsPerChannel:   C.UInt32(8 * bitDepthInBytes),
	}

	audioInfo := &audioInfo{
		channelNum:      channelNum,
		bitDepthInBytes: bitDepthInBytes,
	}

	var audioQueue C.AudioQueueRef
	if osstatus := C.AudioQueueNewOutput(
		&desc,
		(C.AudioQueueOutputCallback)(C.oto_render),
		unsafe.Pointer(audioInfo),
		(C.CFRunLoopRef)(0),
		(C.CFStringRef)(0),
		0,
		&audioQueue); osstatus != C.noErr {
		return nil, fmt.Errorf("oto: AudioQueueNewFormat with StreamFormat failed: %d", osstatus)
	}

	queueBufferSize := baseQueueBufferSize * channelNum * bitDepthInBytes
	nbuf := bufferSizeInBytes / queueBufferSize
	if nbuf <= 1 {
		nbuf = 2
	}

	d := &driver{
		audioQueue: audioQueue,
		sampleRate: sampleRate,
		audioInfo:  audioInfo,
		bufSize:    nbuf * queueBufferSize,
		buffers:    make([]C.AudioQueueBufferRef, nbuf),
		chWrite:    make(chan []byte),
		chWritten:  make(chan int),
	}
	runtime.SetFinalizer(d, (*driver).Close)
	// Set the driver before setting the rendering callback.
	setDriver(d)

	for i := 0; i < len(d.buffers); i++ {
		if osstatus := C.AudioQueueAllocateBuffer(audioQueue, C.UInt32(queueBufferSize), &d.buffers[i]); osstatus != C.noErr {
			return nil, fmt.Errorf("oto: AudioQueueAllocateBuffer failed: %d", osstatus)
		}
		d.buffers[i].mAudioDataByteSize = C.UInt32(queueBufferSize)
		for j := 0; j < queueBufferSize; j++ {
			*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(d.buffers[i].mAudioData)) + uintptr(j))) = 0
		}
		if osstatus := C.AudioQueueEnqueueBuffer(audioQueue, d.buffers[i], 0, nil); osstatus != C.noErr {
			return nil, fmt.Errorf("oto: AudioQueueEnqueueBuffer failed: %d", osstatus)
		}
	}

	if osstatus := C.AudioQueueStart(audioQueue, nil); osstatus != C.noErr {
		return nil, fmt.Errorf("oto: AudioQueueStart failed: %d", osstatus)
	}

	return d, nil
}

//export oto_render
func oto_render(inUserData unsafe.Pointer, inAQ C.AudioQueueRef, inBuffer C.AudioQueueBufferRef) {
	audioInfo := (*audioInfo)(inUserData)
	queueBufferSize := baseQueueBufferSize * audioInfo.channelNum * audioInfo.bitDepthInBytes

	d := getDriver()

	var buf []byte

	// Set the timer. When the input does not come, the audio must be paused.
	s := time.Second * time.Duration(queueBufferSize) / time.Duration(d.sampleRate*d.audioInfo.channelNum*d.audioInfo.bitDepthInBytes)
	t := time.NewTicker(s)
	defer t.Stop()
	ch := t.C

	for len(buf) < queueBufferSize {
		select {
		case dbuf := <-d.chWrite:
			d.resume(false)
			n := queueBufferSize - len(buf)
			if n > len(dbuf) {
				n = len(dbuf)
			}
			buf = append(buf, dbuf[:n]...)
			d.chWritten <- n
		case <-ch:
			d.pause()
			ch = nil
		}
	}

	for i := 0; i < queueBufferSize; i++ {
		*(*byte)(unsafe.Pointer(uintptr(inBuffer.mAudioData) + uintptr(i))) = buf[i]
	}
	// Do not update mAudioDataByteSize, or the buffer is not used correctly any more.

	d.enqueueBuffer(inBuffer)
}

func (d *driver) TryWrite(data []byte) (int, error) {
	d.m.Lock()
	err := d.err
	d.m.Unlock()
	if err != nil {
		return 0, err
	}

	n := d.bufSize - len(d.buf)
	if n > len(data) {
		n = len(data)
	}
	d.buf = append(d.buf, data[:n]...)
	// Use the buffer only when the buffer length is enough to avoid choppy sound.
	queueBufferSize := baseQueueBufferSize * d.audioInfo.channelNum * d.audioInfo.bitDepthInBytes
	for len(d.buf) >= queueBufferSize {
		d.chWrite <- d.buf
		n := <-d.chWritten
		d.buf = d.buf[n:]
	}
	return n, nil
}

func (d *driver) Close() error {
	runtime.SetFinalizer(d, nil)

	if osstatus := C.AudioQueueStop(d.audioQueue, C.false); osstatus != C.noErr {
		return fmt.Errorf("oto: AudioQueueStop failed: %d", osstatus)
	}
	if osstatus := C.AudioQueueDispose(d.audioQueue, C.false); osstatus != C.noErr {
		return fmt.Errorf("oto: AudioQueueDispose failed: %d", osstatus)
	}
	d.audioQueue = nil
	setDriver(nil)
	return nil
}

func (d *driver) enqueueBuffer(buffer C.AudioQueueBufferRef) {
	d.m.Lock()
	defer d.m.Unlock()

	if osstatus := C.AudioQueueEnqueueBuffer(d.audioQueue, buffer, 0, nil); osstatus != C.noErr && d.err == nil {
		d.err = fmt.Errorf("oto: AudioQueueEnqueueBuffer failed: %d", osstatus)
		return
	}
}

func (d *driver) resume(afterSleep bool) {
	d.m.Lock()
	defer d.m.Unlock()

	// Audio doesn't work soon after recovering from sleeping. Wait for a while
	// (hajimehoshi/ebiten#1259).
	if afterSleep {
		// After short-time sleeping, 500ms more sleeping is enough. However, after long-time sleeping, it
		// looks like 1 second more sleeping are required (hajimehoshi/ebiten#1280).
		// This is tested on MacBook Pro 2020 macOS 10.15.6.
		if time.Now().Sub(d.lastPauseTime) < 30*time.Second {
			time.Sleep(500 * time.Millisecond)
		} else {
			time.Sleep(time.Second)
		}
	}

	if !d.paused {
		return
	}
	if osstatus := C.AudioQueueStart(d.audioQueue, nil); osstatus != C.noErr && d.err == nil {
		d.err = fmt.Errorf("oto: AudioQueueStart failed: %d", osstatus)
		return
	}
	d.paused = false
}

func (d *driver) pause() {
	d.m.Lock()
	defer d.m.Unlock()

	if d.paused {
		return
	}
	if osstatus := C.AudioQueuePause(d.audioQueue); osstatus != C.noErr && d.err == nil {
		d.err = fmt.Errorf("oto: AudioQueuePause failed: %d", osstatus)
		return
	}
	d.paused = true
	d.lastPauseTime = time.Now()
}

func setNotificationHandler(driver *driver) {
	C.oto_setNotificationHandler(driver.audioQueue)
}

//export oto_setGlobalPause
func oto_setGlobalPause(paused C.int) {
	if paused != 0 {
		theDriver.pause()
	} else {
		theDriver.resume(true)
	}
}

//export oto_setErrorByNotification
func oto_setErrorByNotification(s C.OSStatus, from *C.char) {
	if theDriver.err != nil {
		return
	}

	gofrom := C.GoString(from)
	theDriver.err = fmt.Errorf("oto: %s at notification failed: %d", gofrom, s)
}
<|endoftext|>


<commit_before>package engine

import (
	"crypto/tls"
	"io/ioutil"
	"log"
	"net"

	"github.com/imdario/mergo"
	"github.com/influx6/relay"
	"gopkg.in/yaml.v2"
)

// DefaultConfig provides a default configuration for the app
var DefaultConfig = Config{
	Addr:    ":8080",
	Folders: Folders{},
}

//TLSConfig provides a base config for tls configuration
type TLSConfig struct {
	Certs *tls.Config
	Key   string `yaml:"key"`
	Cert  string `yaml:"cert"`
}

type tlsconf struct {
	Key  string `yaml:"key"`
	Cert string `yaml:"cert"`
}

// UnmarshalYaml unmarshalls the incoming data for use
func (t *TLSConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
	toc := tlsconf{}

	if err := unmarshal(&toc); err != nil {
		return err
	}

	co, err := relay.LoadTLS(toc.Cert, toc.Key)

	if err != nil {
		return err
	}

	t.Certs = co
	return nil
}

// Folders provide a configuration for app-used folders
type Folders struct {
	Assets string `yaml:"assets"`
	Models string `yaml:"models"`
	Views  string `yaml:"views"`
}

// Config provides configuration for Afro
type Config struct {
	Addr    string    `yaml:"addr"`
	C       TLSConfig `yaml:"tls"`
	Folders Folders   `yaml:"folders"`
}

// NewConfig returns a new configuration file
func NewConfig() *Config {
	c := DefaultConfig
	return &c
}

// Load loads the configuration from a yaml file
func (c *Config) Load(file string) error {
	data, err := ioutil.ReadFile(file)

	if err != nil {
		return err
	}

	conf := Config{}
	err = yaml.Unmarshal(data, &conf)

	if err != nil {
		return err
	}

	return mergo.MergeWithOverwrite(c, conf)
}

//Engine provides a base luncher for a service
type Engine struct {
	*relay.Routes
	li     net.Listener
	config *Config
}

//NewEngine returns a new app configuration
func NewEngine(c *Config) *Engine {
	return &Engine{
		Routes: relay.NewRoutes(),
		config: c,
	}
}

// Serve serves the app and configuration and loads the routes and serivices settings
func (a *Engine) Serve() error {
	var err error
	var li net.Listener

	if a.config.C.Certs != nil {
		_, li, err = relay.CreateTLS(a.config.Addr, a.config.C.Certs, a)
	} else {
		_, li, err = relay.CreateHTTP(a.config.Addr, a)
	}

	if err != nil {
		log.Fatalf("Server failed to start: %+s", err.Error())
		return err
	}

	a.li = li

	return nil
}

// Addr returns the address of the app
func (a *Engine) Addr() net.Addr {
	return a.li.Addr()
}

// Close closes and returns an error of the internal listener
func (a *Engine) Close() error {
	return a.li.Close()
}
<commit_msg>adding certification fixes<commit_after>package engine

import (
	"crypto/tls"
	"io/ioutil"
	"log"
	"net"

	"github.com/imdario/mergo"
	"github.com/influx6/relay"
	"gopkg.in/yaml.v2"
)

// DefaultConfig provides a default configuration for the app
var DefaultConfig = Config{
	Addr:    ":8080",
	Folders: Folders{},
}

//TLSConfig provides a base config for tls configuration
type TLSConfig struct {
	Certs *tls.Config
	Key   string `yaml:"key"`
	Cert  string `yaml:"cert"`
}

// UnmarshalYaml unmarshalls the incoming data for use
func (t *TLSConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
	if err := unmarshal(t); err != nil {
		return err
	}

	co, err := relay.LoadTLS(t.Cert, t.Key)

	if err != nil {
		return err
	}

	t.Certs = co
	return nil
}

// Folders provide a configuration for app-used folders
type Folders struct {
	Assets string `yaml:"assets"`
	Models string `yaml:"models"`
	Views  string `yaml:"views"`
}

// Config provides configuration for Afro
type Config struct {
	Addr    string    `yaml:"addr"`
	C       TLSConfig `yaml:"tls"`
	Folders Folders   `yaml:"folders"`
}

// NewConfig returns a new configuration file
func NewConfig() *Config {
	c := DefaultConfig
	return &c
}

// Load loads the configuration from a yaml file
func (c *Config) Load(file string) error {
	data, err := ioutil.ReadFile(file)

	if err != nil {
		return err
	}

	conf := Config{}
	err = yaml.Unmarshal(data, &conf)

	if err != nil {
		return err
	}

	return mergo.MergeWithOverwrite(c, conf)
}

//Engine provides a base luncher for a service
type Engine struct {
	*relay.Routes
	li     net.Listener
	config *Config
}

//NewEngine returns a new app configuration
func NewEngine(c *Config) *Engine {
	return &Engine{
		Routes: relay.NewRoutes(),
		config: c,
	}
}

// Serve serves the app and configuration and loads the routes and serivices settings
func (a *Engine) Serve() error {
	var err error
	var li net.Listener

	if a.config.C.Certs != nil {
		_, li, err = relay.CreateTLS(a.config.Addr, a.config.C.Certs, a)
	} else {
		_, li, err = relay.CreateHTTP(a.config.Addr, a)
	}

	if err != nil {
		log.Fatalf("Server failed to start: %+s", err.Error())
		return err
	}

	a.li = li

	return nil
}

// Addr returns the address of the app
func (a *Engine) Addr() net.Addr {
	return a.li.Addr()
}

// Close closes and returns an error of the internal listener
func (a *Engine) Close() error {
	return a.li.Close()
}
<|endoftext|>