<commit_before>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ssm"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsSsmAssociation() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSsmAssociationCreate,
		Read:   resourceAwsSsmAssociationRead,
		Update: resourceAwsSsmAssocationUpdate,
		Delete: resourceAwsSsmAssociationDelete,

		MigrateState:  resourceAwsSsmAssociationMigrateState,
		SchemaVersion: 1,

		Schema: map[string]*schema.Schema{
			"association_name": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"association_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"instance_id": {
				Type:     schema.TypeString,
				ForceNew: true,
				Optional: true,
			},
			"document_version": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"name": {
				Type:     schema.TypeString,
				ForceNew: true,
				Required: true,
			},
			"parameters": {
				Type:     schema.TypeMap,
				Optional: true,
				Computed: true,
			},
			"schedule_expression": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"output_location": {
				Type:     schema.TypeList,
				MaxItems: 1,
				Optional: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"s3_bucket_name": {
							Type:     schema.TypeString,
							Required: true,
						},
						"s3_key_prefix": {
							Type:     schema.TypeString,
							Optional: true,
						},
					},
				},
			},
			"targets": {
				Type:     schema.TypeList,
				Optional: true,
				Computed: true,
				MaxItems: 5,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"key": {
							Type:     schema.TypeString,
							Required: true,
						},
						"values": {
							Type:     schema.TypeList,
							Required: true,
							Elem:     &schema.Schema{Type: schema.TypeString},
						},
					},
				},
			},
		},
	}
}

func resourceAwsSsmAssociationCreate(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[DEBUG] SSM association create: %s", d.Id())

	assosciationInput := &ssm.CreateAssociationInput{
		Name: aws.String(d.Get("name").(string)),
	}

	if v, ok := d.GetOk("association_name"); ok {
		assosciationInput.AssociationName = aws.String(v.(string))
	}

	if v, ok := d.GetOk("instance_id"); ok {
		assosciationInput.InstanceId = aws.String(v.(string))
	}

	if v, ok := d.GetOk("document_version"); ok {
		assosciationInput.DocumentVersion = aws.String(v.(string))
	}

	if v, ok := d.GetOk("schedule_expression"); ok {
		assosciationInput.ScheduleExpression = aws.String(v.(string))
	}

	if v, ok := d.GetOk("parameters"); ok {
		assosciationInput.Parameters = expandSSMDocumentParameters(v.(map[string]interface{}))
	}

	if _, ok := d.GetOk("targets"); ok {
		assosciationInput.Targets = expandAwsSsmTargets(d.Get("targets").([]interface{}))
	}

	if v, ok := d.GetOk("output_location"); ok {
		assosciationInput.OutputLocation = expandSSMAssociationOutputLocation(v.([]interface{}))
	}

	resp, err := ssmconn.CreateAssociation(assosciationInput)
	if err != nil {
		return fmt.Errorf("[ERROR] Error creating SSM association: %s", err)
	}

	if resp.AssociationDescription == nil {
		return fmt.Errorf("[ERROR] AssociationDescription was nil")
	}

	d.SetId(*resp.AssociationDescription.AssociationId)
	d.Set("association_id", resp.AssociationDescription.AssociationId)

	return resourceAwsSsmAssociationRead(d, meta)
}

func resourceAwsSsmAssociationRead(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[DEBUG] Reading SSM Association: %s", d.Id())

	params := &ssm.DescribeAssociationInput{
		AssociationId: aws.String(d.Id()),
	}

	resp, err := ssmconn.DescribeAssociation(params)

	if err != nil {
		if isAWSErr(err, ssm.ErrCodeAssociationDoesNotExist, "") {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("[ERROR] Error reading SSM association: %s", err)
	}
	if resp.AssociationDescription == nil {
		return fmt.Errorf("[ERROR] AssociationDescription was nil")
	}

	association := resp.AssociationDescription
	d.Set("association_name", association.AssociationName)
	d.Set("instance_id", association.InstanceId)
	d.Set("name", association.Name)
	d.Set("parameters", association.Parameters)
	d.Set("association_id", association.AssociationId)
	d.Set("schedule_expression", association.ScheduleExpression)
	d.Set("document_version", association.DocumentVersion)

	if err := d.Set("targets", flattenAwsSsmTargets(association.Targets)); err != nil {
		return fmt.Errorf("[DEBUG] Error setting targets error: %#v", err)
	}

	if err := d.Set("output_location", flattenAwsSsmAssociationOutoutLocation(association.OutputLocation)); err != nil {
		return fmt.Errorf("[DEBUG] Error setting output_location error: %#v", err)
	}

	return nil
}

func resourceAwsSsmAssocationUpdate(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[DEBUG] SSM association update: %s", d.Id())

	associationInput := &ssm.UpdateAssociationInput{
		AssociationId: aws.String(d.Get("association_id").(string)),
	}

	if d.HasChange("association_name") {
		associationInput.AssociationName = aws.String(d.Get("association_name").(string))
	}

	if d.HasChange("schedule_expression") {
		associationInput.ScheduleExpression = aws.String(d.Get("schedule_expression").(string))
	}

	if d.HasChange("document_version") {
		associationInput.DocumentVersion = aws.String(d.Get("document_version").(string))
	}

	if d.HasChange("parameters") {
		associationInput.Parameters = expandSSMDocumentParameters(d.Get("parameters").(map[string]interface{}))
	}

	if d.HasChange("output_location") {
		associationInput.OutputLocation = expandSSMAssociationOutputLocation(d.Get("output_location").([]interface{}))
	}

	if d.HasChange("targets") {
		associationInput.Targets = expandAwsSsmTargets(d.Get("targets").([]interface{}))
	}

	_, err := ssmconn.UpdateAssociation(associationInput)
	if err != nil {
		return fmt.Errorf("[ERROR] Error updating SSM association: %s", err)
	}

	return resourceAwsSsmAssociationRead(d, meta)
}

func resourceAwsSsmAssociationDelete(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[DEBUG] Deleting SSM Assosciation: %s", d.Id())

	params := &ssm.DeleteAssociationInput{
		AssociationId: aws.String(d.Get("association_id").(string)),
	}

	_, err := ssmconn.DeleteAssociation(params)

	if err != nil {
		return fmt.Errorf("[ERROR] Error deleting SSM association: %s", err)
	}

	return nil
}

func expandSSMDocumentParameters(params map[string]interface{}) map[string][]*string {
	var docParams = make(map[string][]*string)
	for k, v := range params {
		values := make([]*string, 1)
		values[0] = aws.String(v.(string))
		docParams[k] = values
	}

	return docParams
}

func expandSSMAssociationOutputLocation(config []interface{}) *ssm.InstanceAssociationOutputLocation {
	if config == nil {
		return nil
	}

	//We only allow 1 Item so we can grab the first in the list only
	locationConfig := config[0].(map[string]interface{})

	S3OutputLocation := &ssm.S3OutputLocation{
		OutputS3BucketName: aws.String(locationConfig["s3_bucket_name"].(string)),
	}

	if v, ok := locationConfig["s3_key_prefix"]; ok {
		S3OutputLocation.OutputS3KeyPrefix = aws.String(v.(string))
	}

	return &ssm.InstanceAssociationOutputLocation{
		S3Location: S3OutputLocation,
	}
}

func flattenAwsSsmAssociationOutoutLocation(location *ssm.InstanceAssociationOutputLocation) []map[string]interface{} {
	if location == nil {
		return nil
	}

	result := make([]map[string]interface{}, 0)
	item := make(map[string]interface{})

	item["s3_bucket_name"] = *location.S3Location.OutputS3BucketName

	if location.S3Location.OutputS3KeyPrefix != nil {
		item["s3_key_prefix"] = *location.S3Location.OutputS3KeyPrefix
	}

	result = append(result, item)

	return result
}
<commit_msg>Typo - Correcting Assoscation to Assocation<commit_after>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ssm"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsSsmAssociation() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSsmAssociationCreate,
		Read:   resourceAwsSsmAssociationRead,
		Update: resourceAwsSsmAssocationUpdate,
		Delete: resourceAwsSsmAssociationDelete,

		MigrateState:  resourceAwsSsmAssociationMigrateState,
		SchemaVersion: 1,

		Schema: map[string]*schema.Schema{
			"association_name": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"association_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"instance_id": {
				Type:     schema.TypeString,
				ForceNew: true,
				Optional: true,
			},
			"document_version": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"name": {
				Type:     schema.TypeString,
				ForceNew: true,
				Required: true,
			},
			"parameters": {
				Type:     schema.TypeMap,
				Optional: true,
				Computed: true,
			},
			"schedule_expression": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"output_location": {
				Type:     schema.TypeList,
				MaxItems: 1,
				Optional: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"s3_bucket_name": {
							Type:     schema.TypeString,
							Required: true,
						},
						"s3_key_prefix": {
							Type:     schema.TypeString,
							Optional: true,
						},
					},
				},
			},
			"targets": {
				Type:     schema.TypeList,
				Optional: true,
				Computed: true,
				MaxItems: 5,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"key": {
							Type:     schema.TypeString,
							Required: true,
						},
						"values": {
							Type:     schema.TypeList,
							Required: true,
							Elem:     &schema.Schema{Type: schema.TypeString},
						},
					},
				},
			},
		},
	}
}

func resourceAwsSsmAssociationCreate(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[DEBUG] SSM association create: %s", d.Id())

	associationInput := &ssm.CreateAssociationInput{
		Name: aws.String(d.Get("name").(string)),
	}

	if v, ok := d.GetOk("association_name"); ok {
		associationInput.AssociationName = aws.String(v.(string))
	}

	if v, ok := d.GetOk("instance_id"); ok {
		associationInput.InstanceId = aws.String(v.(string))
	}

	if v, ok := d.GetOk("document_version"); ok {
		associationInput.DocumentVersion = aws.String(v.(string))
	}

	if v, ok := d.GetOk("schedule_expression"); ok {
		associationInput.ScheduleExpression = aws.String(v.(string))
	}

	if v, ok := d.GetOk("parameters"); ok {
		associationInput.Parameters = expandSSMDocumentParameters(v.(map[string]interface{}))
	}

	if _, ok := d.GetOk("targets"); ok {
		associationInput.Targets = expandAwsSsmTargets(d.Get("targets").([]interface{}))
	}

	if v, ok := d.GetOk("output_location"); ok {
		associationInput.OutputLocation = expandSSMAssociationOutputLocation(v.([]interface{}))
	}

	resp, err := ssmconn.CreateAssociation(associationInput)
	if err != nil {
		return fmt.Errorf("[ERROR] Error creating SSM association: %s", err)
	}

	if resp.AssociationDescription == nil {
		return fmt.Errorf("[ERROR] AssociationDescription was nil")
	}

	d.SetId(*resp.AssociationDescription.AssociationId)
	d.Set("association_id", resp.AssociationDescription.AssociationId)

	return resourceAwsSsmAssociationRead(d, meta)
}

func resourceAwsSsmAssociationRead(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[DEBUG] Reading SSM Association: %s", d.Id())

	params := &ssm.DescribeAssociationInput{
		AssociationId: aws.String(d.Id()),
	}

	resp, err := ssmconn.DescribeAssociation(params)

	if err != nil {
		if isAWSErr(err, ssm.ErrCodeAssociationDoesNotExist, "") {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("[ERROR] Error reading SSM association: %s", err)
	}
	if resp.AssociationDescription == nil {
		return fmt.Errorf("[ERROR] AssociationDescription was nil")
	}

	association := resp.AssociationDescription
	d.Set("association_name", association.AssociationName)
	d.Set("instance_id", association.InstanceId)
	d.Set("name", association.Name)
	d.Set("parameters", association.Parameters)
	d.Set("association_id", association.AssociationId)
	d.Set("schedule_expression", association.ScheduleExpression)
	d.Set("document_version", association.DocumentVersion)

	if err := d.Set("targets", flattenAwsSsmTargets(association.Targets)); err != nil {
		return fmt.Errorf("[DEBUG] Error setting targets error: %#v", err)
	}

	if err := d.Set("output_location", flattenAwsSsmAssociationOutoutLocation(association.OutputLocation)); err != nil {
		return fmt.Errorf("[DEBUG] Error setting output_location error: %#v", err)
	}

	return nil
}

func resourceAwsSsmAssocationUpdate(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[DEBUG] SSM Association update: %s", d.Id())

	associationInput := &ssm.UpdateAssociationInput{
		AssociationId: aws.String(d.Get("association_id").(string)),
	}

	if d.HasChange("association_name") {
		associationInput.AssociationName = aws.String(d.Get("association_name").(string))
	}

	if d.HasChange("schedule_expression") {
		associationInput.ScheduleExpression = aws.String(d.Get("schedule_expression").(string))
	}

	if d.HasChange("document_version") {
		associationInput.DocumentVersion = aws.String(d.Get("document_version").(string))
	}

	if d.HasChange("parameters") {
		associationInput.Parameters = expandSSMDocumentParameters(d.Get("parameters").(map[string]interface{}))
	}

	if d.HasChange("output_location") {
		associationInput.OutputLocation = expandSSMAssociationOutputLocation(d.Get("output_location").([]interface{}))
	}

	if d.HasChange("targets") {
		associationInput.Targets = expandAwsSsmTargets(d.Get("targets").([]interface{}))
	}

	_, err := ssmconn.UpdateAssociation(associationInput)
	if err != nil {
		return fmt.Errorf("[ERROR] Error updating SSM association: %s", err)
	}

	return resourceAwsSsmAssociationRead(d, meta)
}

func resourceAwsSsmAssociationDelete(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[DEBUG] Deleting SSM Association: %s", d.Id())

	params := &ssm.DeleteAssociationInput{
		AssociationId: aws.String(d.Get("association_id").(string)),
	}

	_, err := ssmconn.DeleteAssociation(params)

	if err != nil {
		return fmt.Errorf("[ERROR] Error deleting SSM association: %s", err)
	}

	return nil
}

func expandSSMDocumentParameters(params map[string]interface{}) map[string][]*string {
	var docParams = make(map[string][]*string)
	for k, v := range params {
		values := make([]*string, 1)
		values[0] = aws.String(v.(string))
		docParams[k] = values
	}

	return docParams
}

func expandSSMAssociationOutputLocation(config []interface{}) *ssm.InstanceAssociationOutputLocation {
	if config == nil {
		return nil
	}

	//We only allow 1 Item so we can grab the first in the list only
	locationConfig := config[0].(map[string]interface{})

	S3OutputLocation := &ssm.S3OutputLocation{
		OutputS3BucketName: aws.String(locationConfig["s3_bucket_name"].(string)),
	}

	if v, ok := locationConfig["s3_key_prefix"]; ok {
		S3OutputLocation.OutputS3KeyPrefix = aws.String(v.(string))
	}

	return &ssm.InstanceAssociationOutputLocation{
		S3Location: S3OutputLocation,
	}
}

func flattenAwsSsmAssociationOutoutLocation(location *ssm.InstanceAssociationOutputLocation) []map[string]interface{} {
	if location == nil {
		return nil
	}

	result := make([]map[string]interface{}, 0)
	item := make(map[string]interface{})

	item["s3_bucket_name"] = *location.S3Location.OutputS3BucketName

	if location.S3Location.OutputS3KeyPrefix != nil {
		item["s3_key_prefix"] = *location.S3Location.OutputS3KeyPrefix
	}

	result = append(result, item)

	return result
}
<|endoftext|>


<commit_before>package vault

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/99designs/keyring"
	"github.com/aws/aws-sdk-go/service/sts"
)

type KeyringSessions struct {
	Keyring  keyring.Keyring
	Profiles Profiles
}

func NewKeyringSessions(k keyring.Keyring, p Profiles) (*KeyringSessions, error) {
	return &KeyringSessions{
		Keyring:  k,
		Profiles: p,
	}, nil
}

func (s *KeyringSessions) key(profile string, duration time.Duration) string {
	source := s.Profiles.SourceProfile(profile)
	hasher := md5.New()
	hasher.Write([]byte(duration.String()))

	if p, ok := s.Profiles[profile]; ok {
		enc := json.NewEncoder(hasher)
		enc.Encode(p)
	}

	return fmt.Sprintf("%s session (%x)", source, hex.EncodeToString(hasher.Sum(nil))[0:10])
}

func (s *KeyringSessions) Retrieve(profile string, duration time.Duration) (session sts.Credentials, err error) {
	item, err := s.Keyring.Get(s.key(profile, duration))
	if err != nil {
		return session, err
	}

	if err = json.Unmarshal(item.Data, &session); err != nil {
		return session, err
	}

	if session.Expiration.Before(time.Now()) {
		return session, errors.New("Session is expired")
	}

	return
}

func (s *KeyringSessions) Store(profile string, session sts.Credentials, duration time.Duration) error {
	bytes, err := json.Marshal(session)
	if err != nil {
		return err
	}

	log.Printf("Writing session for %s to keyring", profile)
	s.Keyring.Set(keyring.Item{
		Key:         s.key(profile, duration),
		Label:       "aws-vault session for " + profile,
		Description: "aws-vault session for " + profile,
		Data:        bytes,
		TrustSelf:   true,
	})

	return nil
}

func (s *KeyringSessions) Delete(profile string) (n int, err error) {
	keys, err := s.Keyring.Keys()
	if err != nil {
		return n, err
	}

	for _, k := range keys {
		if strings.HasPrefix(k, fmt.Sprintf("%s session", s.Profiles.SourceProfile(profile))) {
			if err = s.Keyring.Remove(k); err != nil {
				return n, err
			}
			n++
		}
	}

	return
}
<commit_msg>Don't implicitly trust aws-vault for access to cached sessions<commit_after>package vault

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/99designs/keyring"
	"github.com/aws/aws-sdk-go/service/sts"
)

type KeyringSessions struct {
	Keyring  keyring.Keyring
	Profiles Profiles
}

func NewKeyringSessions(k keyring.Keyring, p Profiles) (*KeyringSessions, error) {
	return &KeyringSessions{
		Keyring:  k,
		Profiles: p,
	}, nil
}

func (s *KeyringSessions) key(profile string, duration time.Duration) string {
	source := s.Profiles.SourceProfile(profile)
	hasher := md5.New()
	hasher.Write([]byte(duration.String()))

	if p, ok := s.Profiles[profile]; ok {
		enc := json.NewEncoder(hasher)
		enc.Encode(p)
	}

	return fmt.Sprintf("%s session (%x)", source, hex.EncodeToString(hasher.Sum(nil))[0:10])
}

func (s *KeyringSessions) Retrieve(profile string, duration time.Duration) (session sts.Credentials, err error) {
	item, err := s.Keyring.Get(s.key(profile, duration))
	if err != nil {
		return session, err
	}

	if err = json.Unmarshal(item.Data, &session); err != nil {
		return session, err
	}

	if session.Expiration.Before(time.Now()) {
		return session, errors.New("Session is expired")
	}

	return
}

func (s *KeyringSessions) Store(profile string, session sts.Credentials, duration time.Duration) error {
	bytes, err := json.Marshal(session)
	if err != nil {
		return err
	}

	log.Printf("Writing session for %s to keyring", profile)
	s.Keyring.Set(keyring.Item{
		Key:         s.key(profile, duration),
		Label:       "aws-vault session for " + profile,
		Description: "aws-vault session for " + profile,
		Data:        bytes,
		TrustSelf:   false,
	})

	return nil
}

func (s *KeyringSessions) Delete(profile string) (n int, err error) {
	keys, err := s.Keyring.Keys()
	if err != nil {
		return n, err
	}

	for _, k := range keys {
		if strings.HasPrefix(k, fmt.Sprintf("%s session", s.Profiles.SourceProfile(profile))) {
			if err = s.Keyring.Remove(k); err != nil {
				return n, err
			}
			n++
		}
	}

	return
}
<|endoftext|>


<commit_before>// Package common contains struts and interfaces shared between multiple components
package common

import (
	"encoding/json"
	"fmt"
)

// Command result status
const (
	SUCCESS = iota
	FAILURE
	ERROR
)

const (
	ActionCableV1JSON = "actioncable-v1-json"
)

func ActionCableProtocols() []string {
	return []string{ActionCableV1JSON}
}

// Outgoing message types (according to Action Cable protocol)
const (
	WelcomeType    = "welcome"
	PingType       = "ping"
	ReplyType      = "message"
	DisconnectType = "disconnect"
	ConfirmedType  = "confirm_subscription"
	RejectedType   = "reject_subscription"
	// Not suppurted by Action Cable currently
	UnsubscribedType = "unsubscribed"
)

// SessionEnv represents the underlying HTTP connection data:
// URL and request headers
type SessionEnv struct {
	URL             string
	Headers         *map[string]string
	ConnectionState *map[string]string
	ChannelStates   *map[string]map[string]string
}

// NewSessionEnv builds a new SessionEnv
func NewSessionEnv(url string, headers *map[string]string) *SessionEnv {
	state := make(map[string]string)
	channels := make(map[string]map[string]string)
	return &SessionEnv{
		URL:             url,
		Headers:         headers,
		ConnectionState: &state,
		ChannelStates:   &channels,
	}
}

// MergeConnectionState updates the current ConnectionState from the given map.
// If the value is an empty string then remove the key,
// otherswise add or rewrite.
func (st *SessionEnv) MergeConnectionState(other *map[string]string) {
	for k, v := range *other {
		if v == "" {
			delete(*st.ConnectionState, k)
		} else {
			(*st.ConnectionState)[k] = v
		}
	}
}

// MergeChannelState updates the current ChannelStates for the given identifier.
// If the value is an empty string then remove the key,
// otherswise add or rewrite.
func (st *SessionEnv) MergeChannelState(id string, other *map[string]string) {
	if _, ok := (*st.ChannelStates)[id]; !ok {
		(*st.ChannelStates)[id] = make(map[string]string)
	}

	for k, v := range *other {
		if v == "" {
			delete((*st.ChannelStates)[id], k)
		} else {
			(*st.ChannelStates)[id][k] = v
		}
	}
}

// Returns a value for the specified key of the specified channel
func (st *SessionEnv) GetChannelStateField(id string, field string) string {
	cst, ok := (*st.ChannelStates)[id]

	if !ok {
		return ""
	}

	return cst[field]
}

// Returns a value for the specified connection state field
func (st *SessionEnv) GetConnectionStateField(field string) string {
	if st.ConnectionState == nil {
		return ""
	}

	return (*st.ConnectionState)[field]
}

// SetHeader adds a header to the headers list
func (st *SessionEnv) SetHeader(key string, val string) {
	if st.Headers == nil {
		headers := map[string]string{key: val}
		st.Headers = &headers
		return
	}

	(*st.Headers)[key] = val
}

// CallResult contains shared RPC result fields
type CallResult struct {
	Transmissions []string
	Broadcasts    []*StreamMessage
	CState        map[string]string
	IState        map[string]string
}

// ConnectResult is a result of initializing a connection (calling a Connect method)
type ConnectResult struct {
	Identifier    string
	Transmissions []string
	Broadcasts    []*StreamMessage
	CState        map[string]string
	IState        map[string]string
	Status        int
}

// ToCallResult returns the corresponding CallResult
func (c *ConnectResult) ToCallResult() *CallResult {
	res := CallResult{Transmissions: c.Transmissions, Broadcasts: c.Broadcasts}
	if c.CState != nil {
		res.CState = c.CState
	}
	if c.IState != nil {
		res.IState = c.IState
	}
	return &res
}

// CommandResult is a result of performing controller action,
// which contains informations about streams to subscribe,
// messages to sent and broadcast.
// It's a communication "protocol" between a node and a controller.
type CommandResult struct {
	StopAllStreams bool
	Disconnect     bool
	Streams        []string
	StoppedStreams []string
	Transmissions  []string
	Broadcasts     []*StreamMessage
	CState         map[string]string
	IState         map[string]string
	Status         int
}

// ToCallResult returns the corresponding CallResult
func (c *CommandResult) ToCallResult() *CallResult {
	res := CallResult{Transmissions: c.Transmissions, Broadcasts: c.Broadcasts}
	if c.CState != nil {
		res.CState = c.CState
	}
	if c.IState != nil {
		res.IState = c.IState
	}
	return &res
}

// Message represents incoming client message
type Message struct {
	Command    string      `json:"command"`
	Identifier string      `json:"identifier"`
	Data       interface{} `json:"data"`
}

// StreamMessage represents a pub/sub message to be sent to stream
type StreamMessage struct {
	Stream string `json:"stream"`
	Data   string `json:"data"`
}

// RemoteCommandMessage represents a pub/sub message with a remote command (e.g., disconnect)
type RemoteCommandMessage struct {
	Command string          `json:"command,omitempty"`
	Payload json.RawMessage `json:"payload,omitempty"`
}

// RemoteDisconnectMessage contains information required to disconnect a session
type RemoteDisconnectMessage struct {
	Identifier string `json:"identifier"`
	Reconnect  bool   `json:"reconnect"`
}

// PingMessage represents a server ping
type PingMessage struct {
	Type    string      `json:"type"`
	Message interface{} `json:"message"`
}

func (p *PingMessage) GetType() string {
	return PingType
}

// DisconnectMessage represents a server disconnect message
type DisconnectMessage struct {
	Type      string `json:"type"`
	Reason    string `json:"reason"`
	Reconnect bool   `json:"reconnect"`
}

func (d *DisconnectMessage) GetType() string {
	return DisconnectType
}

// Reply represents an outgoing client message
type Reply struct {
	Type       string      `json:"type,omitempty"`
	Identifier string      `json:"identifier"`
	Message    interface{} `json:"message,omitempty"`
	Reason     string      `json:"reason,omitempty"`
	Reconnect  bool        `json:"reconnect,omitempty"`
}

func (r *Reply) GetType() string {
	return ReplyType
}

// PubSubMessageFromJSON takes raw JSON byte array and return the corresponding struct
func PubSubMessageFromJSON(raw []byte) (interface{}, error) {
	smsg := StreamMessage{}

	if err := json.Unmarshal(raw, &smsg); err == nil {
		if smsg.Stream != "" {
			return smsg, nil
		}
	}

	rmsg := RemoteCommandMessage{}

	if err := json.Unmarshal(raw, &rmsg); err != nil {
		return nil, err
	}

	if rmsg.Command == "disconnect" {
		dmsg := RemoteDisconnectMessage{}

		if err := json.Unmarshal(rmsg.Payload, &dmsg); err != nil {
			return nil, err
		}

		return dmsg, nil
	}

	return nil, fmt.Errorf("Unknown message: %s", raw)
}

// ConfirmationMessage returns a subscription confirmation message for a specified identifier
func ConfirmationMessage(identifier string) string {
	return string(toJSON(Reply{Identifier: identifier, Type: ConfirmedType}))
}

// RejectionMessage returns a subscription rejection message for a specified identifier
func RejectionMessage(identifier string) string {
	return string(toJSON(Reply{Identifier: identifier, Type: RejectedType}))
}

func toJSON(msg Reply) []byte {
	b, err := json.Marshal(&msg)
	if err != nil {
		panic("Failed to build JSON 😲")
	}

	return b
}
<commit_msg>chore: upd common.Reply<commit_after>// Package common contains struts and interfaces shared between multiple components
package common

import (
	"encoding/json"
	"fmt"
)

// Command result status
const (
	SUCCESS = iota
	FAILURE
	ERROR
)

const (
	ActionCableV1JSON = "actioncable-v1-json"
)

func ActionCableProtocols() []string {
	return []string{ActionCableV1JSON}
}

// Outgoing message types (according to Action Cable protocol)
const (
	WelcomeType    = "welcome"
	PingType       = "ping"
	DisconnectType = "disconnect"
	ConfirmedType  = "confirm_subscription"
	RejectedType   = "reject_subscription"
	// Not suppurted by Action Cable currently
	UnsubscribedType = "unsubscribed"
)

// SessionEnv represents the underlying HTTP connection data:
// URL and request headers
type SessionEnv struct {
	URL             string
	Headers         *map[string]string
	ConnectionState *map[string]string
	ChannelStates   *map[string]map[string]string
}

// NewSessionEnv builds a new SessionEnv
func NewSessionEnv(url string, headers *map[string]string) *SessionEnv {
	state := make(map[string]string)
	channels := make(map[string]map[string]string)
	return &SessionEnv{
		URL:             url,
		Headers:         headers,
		ConnectionState: &state,
		ChannelStates:   &channels,
	}
}

// MergeConnectionState updates the current ConnectionState from the given map.
// If the value is an empty string then remove the key,
// otherswise add or rewrite.
func (st *SessionEnv) MergeConnectionState(other *map[string]string) {
	for k, v := range *other {
		if v == "" {
			delete(*st.ConnectionState, k)
		} else {
			(*st.ConnectionState)[k] = v
		}
	}
}

// MergeChannelState updates the current ChannelStates for the given identifier.
// If the value is an empty string then remove the key,
// otherswise add or rewrite.
func (st *SessionEnv) MergeChannelState(id string, other *map[string]string) {
	if _, ok := (*st.ChannelStates)[id]; !ok {
		(*st.ChannelStates)[id] = make(map[string]string)
	}

	for k, v := range *other {
		if v == "" {
			delete((*st.ChannelStates)[id], k)
		} else {
			(*st.ChannelStates)[id][k] = v
		}
	}
}

// Returns a value for the specified key of the specified channel
func (st *SessionEnv) GetChannelStateField(id string, field string) string {
	cst, ok := (*st.ChannelStates)[id]

	if !ok {
		return ""
	}

	return cst[field]
}

// Returns a value for the specified connection state field
func (st *SessionEnv) GetConnectionStateField(field string) string {
	if st.ConnectionState == nil {
		return ""
	}

	return (*st.ConnectionState)[field]
}

// SetHeader adds a header to the headers list
func (st *SessionEnv) SetHeader(key string, val string) {
	if st.Headers == nil {
		headers := map[string]string{key: val}
		st.Headers = &headers
		return
	}

	(*st.Headers)[key] = val
}

// CallResult contains shared RPC result fields
type CallResult struct {
	Transmissions []string
	Broadcasts    []*StreamMessage
	CState        map[string]string
	IState        map[string]string
}

// ConnectResult is a result of initializing a connection (calling a Connect method)
type ConnectResult struct {
	Identifier    string
	Transmissions []string
	Broadcasts    []*StreamMessage
	CState        map[string]string
	IState        map[string]string
	Status        int
}

// ToCallResult returns the corresponding CallResult
func (c *ConnectResult) ToCallResult() *CallResult {
	res := CallResult{Transmissions: c.Transmissions, Broadcasts: c.Broadcasts}
	if c.CState != nil {
		res.CState = c.CState
	}
	if c.IState != nil {
		res.IState = c.IState
	}
	return &res
}

// CommandResult is a result of performing controller action,
// which contains informations about streams to subscribe,
// messages to sent and broadcast.
// It's a communication "protocol" between a node and a controller.
type CommandResult struct {
	StopAllStreams bool
	Disconnect     bool
	Streams        []string
	StoppedStreams []string
	Transmissions  []string
	Broadcasts     []*StreamMessage
	CState         map[string]string
	IState         map[string]string
	Status         int
}

// ToCallResult returns the corresponding CallResult
func (c *CommandResult) ToCallResult() *CallResult {
	res := CallResult{Transmissions: c.Transmissions, Broadcasts: c.Broadcasts}
	if c.CState != nil {
		res.CState = c.CState
	}
	if c.IState != nil {
		res.IState = c.IState
	}
	return &res
}

// Message represents incoming client message
type Message struct {
	Command    string      `json:"command"`
	Identifier string      `json:"identifier"`
	Data       interface{} `json:"data,omitempty"`
}

// StreamMessage represents a pub/sub message to be sent to stream
type StreamMessage struct {
	Stream string `json:"stream"`
	Data   string `json:"data"`
}

// RemoteCommandMessage represents a pub/sub message with a remote command (e.g., disconnect)
type RemoteCommandMessage struct {
	Command string          `json:"command,omitempty"`
	Payload json.RawMessage `json:"payload,omitempty"`
}

// RemoteDisconnectMessage contains information required to disconnect a session
type RemoteDisconnectMessage struct {
	Identifier string `json:"identifier"`
	Reconnect  bool   `json:"reconnect"`
}

// PingMessage represents a server ping
type PingMessage struct {
	Type    string      `json:"type"`
	Message interface{} `json:"message,omitempty"`
}

func (p *PingMessage) GetType() string {
	return PingType
}

// DisconnectMessage represents a server disconnect message
type DisconnectMessage struct {
	Type      string `json:"type"`
	Reason    string `json:"reason"`
	Reconnect bool   `json:"reconnect"`
}

func (d *DisconnectMessage) GetType() string {
	return DisconnectType
}

// Reply represents an outgoing client message
type Reply struct {
	Type       string      `json:"type,omitempty"`
	Identifier string      `json:"identifier,omitempty"`
	Message    interface{} `json:"message,omitempty"`
	Reason     string      `json:"reason,omitempty"`
	Reconnect  bool        `json:"reconnect,omitempty"`
}

func (r *Reply) GetType() string {
	return r.Type
}

// PubSubMessageFromJSON takes raw JSON byte array and return the corresponding struct
func PubSubMessageFromJSON(raw []byte) (interface{}, error) {
	smsg := StreamMessage{}

	if err := json.Unmarshal(raw, &smsg); err == nil {
		if smsg.Stream != "" {
			return smsg, nil
		}
	}

	rmsg := RemoteCommandMessage{}

	if err := json.Unmarshal(raw, &rmsg); err != nil {
		return nil, err
	}

	if rmsg.Command == "disconnect" {
		dmsg := RemoteDisconnectMessage{}

		if err := json.Unmarshal(rmsg.Payload, &dmsg); err != nil {
			return nil, err
		}

		return dmsg, nil
	}

	return nil, fmt.Errorf("Unknown message: %s", raw)
}

// ConfirmationMessage returns a subscription confirmation message for a specified identifier
func ConfirmationMessage(identifier string) string {
	return string(toJSON(Reply{Identifier: identifier, Type: ConfirmedType}))
}

// RejectionMessage returns a subscription rejection message for a specified identifier
func RejectionMessage(identifier string) string {
	return string(toJSON(Reply{Identifier: identifier, Type: RejectedType}))
}

func toJSON(msg Reply) []byte {
	b, err := json.Marshal(&msg)
	if err != nil {
		panic("Failed to build JSON 😲")
	}

	return b
}
<|endoftext|>


<commit_before>// MIT License
//
// Copyright (c) 2017 Stefan Wichmann
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package main

import (
	"github.com/dillonhafer/go.hue"
	"log"
	"strconv"
	"strings"
	"time"
)

var lightsSupportingDimming = []string{"Dimmable Light", "Color Temperature Light", "Color Light", "Extended Color Light"}
var lightsSupportingColorTemperature = []string{"Color Temperature Light", "Extended Color Light"}
var lightsSupportingXYColor = []string{"Color Light", "Extended Color Light"}

// Light represents a single physical hue light in your system.
// It is used to read and write it's state.
type Light struct {
	id                       int
	name                     string
	hueLight                 hue.Light
	savedLightState          LightState
	currentLightState        LightState
	targetLightState         LightState
	on                       bool
	reachable                bool
	tracking                 bool
	manually                 bool
	dimmable                 bool
	supportsColorTemperature bool
	supportsXYColor          bool
}

const lightUpdateIntervalInSeconds = 1
const lightTransitionIntervalInSeconds = 1

func (light *Light) updateCyclic(channel <-chan LightState) {
	log.Printf("💡 Starting cyclic update for %v\n", light.name)
	time.Sleep(2 * time.Second)
	for {
		select {
		case newLightState, ok := <-channel:
			if !ok {
				log.Printf("💡 Channel closed for light %v\n", light.name)
				return
			}
			light.targetLightState = newLightState
			light.update()
			light.clearChannel(channel)
		default:
			light.update()
			time.Sleep(lightUpdateIntervalInSeconds * time.Second)
		}
	}
}

func (light *Light) initialize() error {
	attr, err := light.hueLight.GetLightAttributes()
	if err != nil {
		return err
	}

	// initialize non changing values
	light.name = attr.Name
	light.dimmable = contains(lightsSupportingDimming, attr.Type)
	light.supportsColorTemperature = contains(lightsSupportingColorTemperature, attr.Type)
	light.supportsXYColor = contains(lightsSupportingXYColor, attr.Type)

	// initialize changing values
	light.on = attr.State.On
	light.reachable = attr.State.Reachable
	light.currentLightState = lightStateFromHueValues(attr.State.Ct, attr.State.Xy, attr.State.Bri)

	return nil
}

func (light *Light) updateCurrentLightState() error {
	attr, err := light.hueLight.GetLightAttributes()
	if err != nil {
		return err
	}
	light.reachable = attr.State.Reachable
	light.on = attr.State.On
	light.currentLightState = lightStateFromHueValues(attr.State.Ct, attr.State.Xy, attr.State.Bri)
	return nil
}

func (light *Light) update() error {
	// refresh current state
	light.updateCurrentLightState()

	// Light reachable or on?
	if !light.reachable || !light.on {
		if light.tracking {
			log.Printf("💡 Light %s is no longer reachable or turned on. Clearing state.\n", light.name)
			light.tracking = false
			light.manually = false
			return nil
		}

		// ignore light because we are not tracking it.
		return nil
	}

	// did the light just appear?
	if !light.tracking {
		log.Printf("💡 Light %s just appeared. Initializing state to %vK at %v%s\n", light.name, light.targetLightState.colorTemperature, light.targetLightState.brightness, "%")

		// For initialization we set the state again and again for 10 seconds because during startup the zigbee communication is unstable
		for index := 0; index < 9; index++ {
			light.setLightState(light.targetLightState)
		}
		// safe the state of the last iteration
		setLightState, err := light.setLightState(light.targetLightState)
		if err != nil {
			return err
		}
		light.savedLightState = setLightState
		light.targetLightState = setLightState
		light.tracking = true
		return nil
	}

	// light in manual state
	if light.manually {
		return nil
	}

	// did the user manually change the light state?
	if !light.currentLightState.equals(light.savedLightState) {
		log.Printf("💡 Light %s was manually changed - current: %+v - saved: %+v\n", light.name, light.currentLightState, light.savedLightState)
		light.manually = true
		return nil
	}

	// Update needed?
	if light.currentLightState.equals(light.targetLightState) {
		return nil
	}

	// Light is reachable, on and in automatic state. Update to new color!
	log.Printf("💡 Updating light %s to %vK at %v%s\n", light.name, light.targetLightState.colorTemperature, light.targetLightState.brightness, "%")

	setLightState, err := light.setLightState(light.targetLightState)
	if err != nil {
		return err
	}

	// Debug: compare values
	if !setLightState.equals(light.targetLightState) {
		log.Printf("Target and Set state differ: %v, %v\n", light.targetLightState, setLightState)
	}

	light.savedLightState = setLightState
	light.targetLightState = setLightState
	return nil
}

func (light *Light) setLightState(state LightState) (LightState, error) {
	// Don't send repeated "On" as this slows the bridge down (see https://developers.meethue.com/faq-page #Performance)
	var newLightState hue.SetLightState
	colorTemperature, color, brightness := state.convertValuesToHue()
	if light.supportsXYColor {
		newLightState.Xy = []float32{color[0], color[1]}
		newLightState.Ct = strconv.Itoa(colorTemperature)
	} else if light.supportsColorTemperature {
		newLightState.Ct = strconv.Itoa(colorTemperature)
	}
	if light.dimmable {
		newLightState.Bri = strconv.Itoa(brightness)
	}
	newLightState.TransitionTime = strconv.Itoa(lightTransitionIntervalInSeconds * 10) // conversion to 100ms-value

	results, err := light.hueLight.SetState(newLightState)
	if err != nil {
		return LightState{0, []float32{0, 0}, 0}, err
	}

	// iterate over result to acquire set values
	for _, result := range results {
		for key, value := range result.Success {
			path := strings.Split(key, "/")

			switch path[len(path)-1] {
			case "xy":
				for _, elem := range value.([]interface{}) {
					color = append(color, float32(elem.(float64)))
				}
			case "bri":
				brightness = int(value.(float64))
			case "ct":
				colorTemperature = int(value.(float64))
			}
		}
	}
	setLightState := lightStateFromHueValues(colorTemperature, color, brightness)

	// wait while the light is in transition before returning
	time.Sleep(lightTransitionIntervalInSeconds + 1*time.Second)
	return setLightState, nil
}

func (light *Light) clearChannel(c <-chan LightState) {
	for {
		select {
		case state, ok := <-c:
			if !ok {
				return
			}
			log.Printf("Cleared light state from channel: %+v\n", state)
		default:
			return
		}
	}
}
<commit_msg>Correctly read set light values from hue bridge<commit_after>// MIT License
//
// Copyright (c) 2017 Stefan Wichmann
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package main

import (
	"github.com/dillonhafer/go.hue"
	"log"
	"strconv"
	"strings"
	"time"
)

var lightsSupportingDimming = []string{"Dimmable Light", "Color Temperature Light", "Color Light", "Extended Color Light"}
var lightsSupportingColorTemperature = []string{"Color Temperature Light", "Extended Color Light"}
var lightsSupportingXYColor = []string{"Color Light", "Extended Color Light"}

// Light represents a single physical hue light in your system.
// It is used to read and write it's state.
type Light struct {
	id                       int
	name                     string
	hueLight                 hue.Light
	savedLightState          LightState
	currentLightState        LightState
	targetLightState         LightState
	on                       bool
	reachable                bool
	tracking                 bool
	manually                 bool
	dimmable                 bool
	supportsColorTemperature bool
	supportsXYColor          bool
}

const lightUpdateIntervalInSeconds = 1
const lightTransitionIntervalInSeconds = 1

func (light *Light) updateCyclic(channel <-chan LightState) {
	log.Printf("💡 Starting cyclic update for %v\n", light.name)
	time.Sleep(2 * time.Second)
	for {
		select {
		case newLightState, ok := <-channel:
			if !ok {
				log.Printf("💡 Channel closed for light %v\n", light.name)
				return
			}
			light.targetLightState = newLightState
			light.update()
			light.clearChannel(channel)
		default:
			light.update()
			time.Sleep(lightUpdateIntervalInSeconds * time.Second)
		}
	}
}

func (light *Light) initialize() error {
	attr, err := light.hueLight.GetLightAttributes()
	if err != nil {
		return err
	}

	// initialize non changing values
	light.name = attr.Name
	light.dimmable = contains(lightsSupportingDimming, attr.Type)
	light.supportsColorTemperature = contains(lightsSupportingColorTemperature, attr.Type)
	light.supportsXYColor = contains(lightsSupportingXYColor, attr.Type)

	// initialize changing values
	light.on = attr.State.On
	light.reachable = attr.State.Reachable
	light.currentLightState = lightStateFromHueValues(attr.State.Ct, attr.State.Xy, attr.State.Bri)

	return nil
}

func (light *Light) updateCurrentLightState() error {
	attr, err := light.hueLight.GetLightAttributes()
	if err != nil {
		return err
	}
	light.reachable = attr.State.Reachable
	light.on = attr.State.On
	light.currentLightState = lightStateFromHueValues(attr.State.Ct, attr.State.Xy, attr.State.Bri)
	return nil
}

func (light *Light) update() error {
	// refresh current state
	light.updateCurrentLightState()

	// Light reachable or on?
	if !light.reachable || !light.on {
		if light.tracking {
			log.Printf("💡 Light %s is no longer reachable or turned on. Clearing state.\n", light.name)
			light.tracking = false
			light.manually = false
			return nil
		}

		// ignore light because we are not tracking it.
		return nil
	}

	// did the light just appear?
	if !light.tracking {
		log.Printf("💡 Light %s just appeared. Initializing state to %vK at %v%s\n", light.name, light.targetLightState.colorTemperature, light.targetLightState.brightness, "%")

		// For initialization we set the state again and again for 10 seconds because during startup the zigbee communication is unstable
		for index := 0; index < 9; index++ {
			light.setLightState(light.targetLightState)
		}
		// safe the state of the last iteration
		setLightState, err := light.setLightState(light.targetLightState)
		if err != nil {
			return err
		}
		light.savedLightState = setLightState
		light.targetLightState = setLightState
		light.tracking = true
		return nil
	}

	// light in manual state
	if light.manually {
		return nil
	}

	// did the user manually change the light state?
	if !light.currentLightState.equals(light.savedLightState) {
		log.Printf("💡 Light %s was manually changed - current: %+v - saved: %+v\n", light.name, light.currentLightState, light.savedLightState)
		light.manually = true
		return nil
	}

	// Update needed?
	if light.currentLightState.equals(light.targetLightState) {
		return nil
	}

	// Light is reachable, on and in automatic state. Update to new color!
	log.Printf("💡 Updating light %s to %vK at %v%s\n", light.name, light.targetLightState.colorTemperature, light.targetLightState.brightness, "%")

	setLightState, err := light.setLightState(light.targetLightState)
	if err != nil {
		return err
	}

	// Debug: compare values
	if !setLightState.equals(light.targetLightState) {
		log.Printf("Target and Set state differ: %v, %v\n", light.targetLightState, setLightState)
	}

	light.savedLightState = setLightState
	light.targetLightState = setLightState
	return nil
}

func (light *Light) setLightState(state LightState) (LightState, error) {
	// Don't send repeated "On" as this slows the bridge down (see https://developers.meethue.com/faq-page #Performance)
	var newLightState hue.SetLightState
	colorTemperature, color, brightness := state.convertValuesToHue()
	if light.supportsXYColor {
		newLightState.Xy = []float32{color[0], color[1]}
		newLightState.Ct = strconv.Itoa(colorTemperature)
	} else if light.supportsColorTemperature {
		newLightState.Ct = strconv.Itoa(colorTemperature)
	}
	if light.dimmable {
		newLightState.Bri = strconv.Itoa(brightness)
	}
	newLightState.TransitionTime = strconv.Itoa(lightTransitionIntervalInSeconds * 10) // conversion to 100ms-value

	results, err := light.hueLight.SetState(newLightState)
	if err != nil {
		return LightState{0, []float32{0, 0}, 0}, err
	}

	// iterate over result to acquire set values
	color = []float32{} // clear old color values
	for _, result := range results {
		for key, value := range result.Success {
			path := strings.Split(key, "/")

			switch path[len(path)-1] {
			case "xy":
				for _, elem := range value.([]interface{}) {
					color = append(color, float32(elem.(float64)))
				}
			case "bri":
				brightness = int(value.(float64))
			case "ct":
				colorTemperature = int(value.(float64))
			}
		}
	}
	setLightState := lightStateFromHueValues(colorTemperature, color, brightness)

	// wait while the light is in transition before returning
	time.Sleep(lightTransitionIntervalInSeconds + 1*time.Second)
	return setLightState, nil
}

func (light *Light) clearChannel(c <-chan LightState) {
	for {
		select {
		case state, ok := <-c:
			if !ok {
				return
			}
			log.Printf("Cleared light state from channel: %+v\n", state)
		default:
			return
		}
	}
}
<|endoftext|>


<commit_before>package system

import "golang.org/x/sys/unix"

// Returns a []byte slice if the xattr is set and nil otherwise
// Requires path and its attribute as arguments
func Lgetxattr(path string, attr string) ([]byte, error) {
	var sz int
	// Start with a 128 length byte array
	dest := make([]byte, 128)
	sz, errno := unix.Lgetxattr(path, attr, dest)

	//nolint:errorlint // unix errors are bare
	switch {
	case errno == unix.ENODATA:
		return nil, errno
	case errno == unix.ENOTSUP:
		return nil, errno
	case errno == unix.ERANGE:
		// 128 byte array might just not be good enough,
		// A dummy buffer is used to get the real size
		// of the xattrs on disk
		sz, errno = unix.Lgetxattr(path, attr, []byte{})
		if errno != nil {
			return nil, errno
		}
		dest = make([]byte, sz)
		sz, errno = unix.Lgetxattr(path, attr, dest)
		if errno != nil {
			return nil, errno
		}
	case errno != nil:
		return nil, errno
	}
	return dest[:sz], nil
}
<commit_msg>libct/system/xattrs: remove<commit_after><|endoftext|>


<commit_before>// Copyright (c) Ilia Kravets, 2015. All rights reserved. PROVIDED "AS IS"
// WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. See LICENSE file for details.

package rec

import (
	"bytes"
	"encoding/csv"
	"fmt"
	"io"
	"log"
	"strconv"
	"time"

	"my/itto/verify/sim"
)

var _ sim.Observer = &AvtLogger{}

type AvtLogger struct {
	w           io.Writer
	location    *time.Location
	oid2AvtName map[int]string
	TobLogger
	stream Stream
}

func NewAvtLogger(w io.Writer, rDict io.Reader) *AvtLogger {
	l := &AvtLogger{
		w:         w,
		TobLogger: *NewTobLogger(),
		stream:    *NewStream(),
	}
	var err error
	if l.location, err = time.LoadLocation("EST"); err != nil {
		log.Fatal(err)
	}

	if rDict != nil {
		r := csv.NewReader(rDict)
		records, err := r.ReadAll()
		if err != nil {
			log.Fatal(err)
		}
		l.oid2AvtName = make(map[int]string)
		for _, rec := range records {
			if len(rec) != 2 {
				log.Fatalf("unexpected dict csv record: %#v\n", rec)
			}
			if rec[0] == "avtSymbol" && rec[1] == "exchangeId" {
				continue // header
			}
			if oid, err := strconv.Atoi(rec[1]); err != nil {
				log.Fatal(err)
			} else {
				l.oid2AvtName[oid] = rec[0]
			}
		}
	}
	return l
}

func (l *AvtLogger) MessageArrived(idm *sim.SimMessage) {
	l.stream.MessageArrived(idm)
	l.TobLogger.MessageArrived(idm)
}

func (l *AvtLogger) AfterBookUpdate(book sim.Book, operation sim.SimOperation) {
	if l.TobLogger.AfterBookUpdate(book, operation, TobUpdateNewForce) {
		l.genUpdate()
	}
}

func (l *AvtLogger) genUpdate() {
	var optName, underlying string
	if l.oid2AvtName != nil {
		optName = l.oid2AvtName[int(l.lastOptionId.ToUint32())]
		bs := []byte(optName)
		for i := range bs {
			if bs[i] < 'A' || bs[i] > 'Z' {
				bs = bs[:i]
				break
			}
		}
		underlying = string(bs)

	}
	if optName == "" {
		optName = fmt.Sprintf("<%d>", l.lastOptionId)
		underlying = "<?>"
	}
	packetTimestamp := l.stream.getPacketTimestamp().In(l.location)
	dateTime := packetTimestamp.Format("20060102,15:04:05.000.")
	dateTime += fmt.Sprintf("%03d", packetTimestamp.Nanosecond()/1000%1000)
	ittoTimestamp := l.stream.getTimestamp()
	avtTimestamp := time.Date(packetTimestamp.Year(), packetTimestamp.Month(), packetTimestamp.Day(), 0, 0, 0, 0, l.location).Add(time.Duration(ittoTimestamp)).UnixNano() / 1000000
	if false {
		// OptionMarketDataNASDAQ2,date,time,Security,Underlying,SecurityType,BidSize,BidPrice,OrderBidSize,AskSize,AskPrice,OrderAskSize,TradeStatus,TickCondition,ExchangeTimestamp
		fmt.Fprintf(l.w, "OptionMarketDataNASDAQ2,%s,%s,%s,1,%d,%s,0,%d,%s,0,,,%d\n",
			dateTime,
			optName,
			underlying,
			l.bid.New.Size,
			priceString(l.bid.New.Price),
			l.ask.New.Size,
			priceString(l.ask.New.Price),
			avtTimestamp,
		)
	} else {
		if underlying == "<?>" {
			return
		}
		// ExchangeTimestamp, Date, Time, Security, BidSize, BidPrice, AskSize, AskPrice,
		fmt.Fprintf(l.w, "%d,%s,%s,%d,%s,%d,%s\n",
			avtTimestamp,
			dateTime[0:21],
			optName,
			l.bid.New.Size,
			priceString(l.bid.New.Price),
			l.ask.New.Size,
			priceString(l.ask.New.Price),
		)
	}
}

func priceString(price int) string {
	var buf bytes.Buffer
	buf.Write(strconv.AppendInt(nil, int64(price/10000), 10))
	buf.WriteByte('.')
	decimal := price % 10000
	for denom := 1000; denom > 0; denom /= 10 {
		d := decimal / denom % 10
		buf.WriteByte('0' + byte(d))
		if decimal%denom == 0 {
			break
		}
	}
	return buf.String()
}
<commit_msg>rec:AvtLogger: trivial refactoring<commit_after>// Copyright (c) Ilia Kravets, 2015. All rights reserved. PROVIDED "AS IS"
// WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. See LICENSE file for details.

package rec

import (
	"bytes"
	"encoding/csv"
	"fmt"
	"io"
	"log"
	"strconv"
	"time"

	"my/itto/verify/sim"
)

var _ sim.Observer = &AvtLogger{}

type AvtLogger struct {
	w           io.Writer
	location    *time.Location
	oid2AvtName map[int]string
	TobLogger
	stream Stream
}

func NewAvtLogger(w io.Writer, rDict io.Reader) *AvtLogger {
	l := &AvtLogger{
		w:         w,
		TobLogger: *NewTobLogger(),
		stream:    *NewStream(),
	}
	var err error
	if l.location, err = time.LoadLocation("EST"); err != nil {
		log.Fatal(err)
	}

	if rDict != nil {
		r := csv.NewReader(rDict)
		records, err := r.ReadAll()
		if err != nil {
			log.Fatal(err)
		}
		l.oid2AvtName = make(map[int]string)
		for _, rec := range records {
			if len(rec) != 2 {
				log.Fatalf("unexpected dict csv record: %#v\n", rec)
			}
			if rec[0] == "avtSymbol" && rec[1] == "exchangeId" {
				continue // header
			}
			if oid, err := strconv.Atoi(rec[1]); err != nil {
				log.Fatal(err)
			} else {
				l.oid2AvtName[oid] = rec[0]
			}
		}
	}
	return l
}

func (l *AvtLogger) MessageArrived(idm *sim.SimMessage) {
	l.stream.MessageArrived(idm)
	l.TobLogger.MessageArrived(idm)
}

func (l *AvtLogger) AfterBookUpdate(book sim.Book, operation sim.SimOperation) {
	if l.TobLogger.AfterBookUpdate(book, operation, TobUpdateNewForce) {
		l.genUpdate()
	}
}

func (l *AvtLogger) genUpdate() {
	var optName, underlying string
	if l.oid2AvtName != nil {
		optName = l.oid2AvtName[int(l.lastOptionId.ToUint32())]
		bs := []byte(optName)
		for i := range bs {
			if bs[i] < 'A' || bs[i] > 'Z' {
				bs = bs[:i]
				break
			}
		}
		underlying = string(bs)

	}
	if optName == "" {
		optName = fmt.Sprintf("<%d>", l.lastOptionId)
		underlying = "<?>"
	}
	packetTimestamp := l.stream.getPacketTimestamp().In(l.location)
	dateTime := packetTimestamp.Format("20060102,15:04:05.000.")
	dateTime += fmt.Sprintf("%03d", packetTimestamp.Nanosecond()/1000%1000)
	dayStart := time.Date(packetTimestamp.Year(), packetTimestamp.Month(), packetTimestamp.Day(), 0, 0, 0, 0, l.location)
	avtTimestamp := dayStart.Add(time.Duration(l.stream.getTimestamp())).UnixNano() / 1000000
	if false {
		// OptionMarketDataNASDAQ2,date,time,Security,Underlying,SecurityType,BidSize,BidPrice,OrderBidSize,AskSize,AskPrice,OrderAskSize,TradeStatus,TickCondition,ExchangeTimestamp
		fmt.Fprintf(l.w, "OptionMarketDataNASDAQ2,%s,%s,%s,1,%d,%s,0,%d,%s,0,,,%d\n",
			dateTime,
			optName,
			underlying,
			l.bid.New.Size,
			priceString(l.bid.New.Price),
			l.ask.New.Size,
			priceString(l.ask.New.Price),
			avtTimestamp,
		)
	} else {
		if underlying == "<?>" {
			return
		}
		// ExchangeTimestamp, Date, Time, Security, BidSize, BidPrice, AskSize, AskPrice,
		fmt.Fprintf(l.w, "%d,%s,%s,%d,%s,%d,%s\n",
			avtTimestamp,
			dateTime[0:21],
			optName,
			l.bid.New.Size,
			priceString(l.bid.New.Price),
			l.ask.New.Size,
			priceString(l.ask.New.Price),
		)
	}
}

func priceString(price int) string {
	var buf bytes.Buffer
	buf.Write(strconv.AppendInt(nil, int64(price/10000), 10))
	buf.WriteByte('.')
	decimal := price % 10000
	for denom := 1000; denom > 0; denom /= 10 {
		d := decimal / denom % 10
		buf.WriteByte('0' + byte(d))
		if decimal%denom == 0 {
			break
		}
	}
	return buf.String()
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Tekton 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 v1alpha1_test

import (
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1"
	tb "github.com/tektoncd/pipeline/test/builder"
	"github.com/tektoncd/pipeline/test/names"
	corev1 "k8s.io/api/core/v1"
)

func Test_Invalid_NewGitResource(t *testing.T) {
	if _, err := v1alpha1.NewGitResource("override-with-git:latest", tb.PipelineResource("git-resource", "default", tb.PipelineResourceSpec(v1alpha1.PipelineResourceTypeGCS))); err == nil {
		t.Error("Expected error creating Git resource")
	}
}

func Test_Valid_NewGitResource(t *testing.T) {
	for _, tc := range []struct {
		desc             string
		pipelineResource *v1alpha1.PipelineResource
		want             *v1alpha1.GitResource
	}{{
		desc: "With Revision",
		pipelineResource: tb.PipelineResource("git-resource", "default",
			tb.PipelineResourceSpec(v1alpha1.PipelineResourceTypeGit,
				tb.PipelineResourceSpecParam("URL", "git@github.com:test/test.git"),
				tb.PipelineResourceSpecParam("Revision", "test"),
			),
		),
		want: &v1alpha1.GitResource{
			Name:       "git-resource",
			Type:       v1alpha1.PipelineResourceTypeGit,
			URL:        "git@github.com:test/test.git",
			Revision:   "test",
			GitImage:   "override-with-git:latest",
			Submodules: true,
		},
	}, {
		desc: "Without Revision",
		pipelineResource: tb.PipelineResource("git-resource", "default",
			tb.PipelineResourceSpec(v1alpha1.PipelineResourceTypeGit,
				tb.PipelineResourceSpecParam("URL", "git@github.com:test/test.git"),
			),
		),
		want: &v1alpha1.GitResource{
			Name:       "git-resource",
			Type:       v1alpha1.PipelineResourceTypeGit,
			URL:        "git@github.com:test/test.git",
			Revision:   "master",
			GitImage:   "override-with-git:latest",
			Submodules: true,
		},
	}, {
		desc: "With Submodules",
		pipelineResource: tb.PipelineResource("git-resource", "default",
			tb.PipelineResourceSpec(v1alpha1.PipelineResourceTypeGit,
				tb.PipelineResourceSpecParam("URL", "git@github.com:test/test.git"),
				tb.PipelineResourceSpecParam("Revision", "test"),
				tb.PipelineResourceSpecParam("Submodules", "false"),
			),
		),
		want: &v1alpha1.GitResource{
			Name:       "git-resource",
			Type:       v1alpha1.PipelineResourceTypeGit,
			URL:        "git@github.com:test/test.git",
			Revision:   "test",
			GitImage:   "override-with-git:latest",
			Submodules: false,
		},
	}, {
		desc: "Without Submodules",
		pipelineResource: tb.PipelineResource("git-resource", "default",
			tb.PipelineResourceSpec(v1alpha1.PipelineResourceTypeGit,
				tb.PipelineResourceSpecParam("URL", "git@github.com:test/test.git"),
				tb.PipelineResourceSpecParam("Revision", "test"),
			),
		),
		want: &v1alpha1.GitResource{
			Name:       "git-resource",
			Type:       v1alpha1.PipelineResourceTypeGit,
			URL:        "git@github.com:test/test.git",
			Revision:   "test",
			GitImage:   "override-with-git:latest",
			Submodules: true,
		},
	}} {
		t.Run(tc.desc, func(t *testing.T) {
			got, err := v1alpha1.NewGitResource("override-with-git:latest", tc.pipelineResource)
			if err != nil {
				t.Fatalf("Unexpected error creating Git resource: %s", err)
			}

			if diff := cmp.Diff(tc.want, got); diff != "" {
				t.Errorf("Mismatch of Git resource: %s", diff)
			}
		})
	}
}

func Test_GitResource_Replacements(t *testing.T) {
	r := &v1alpha1.GitResource{
		Name:     "git-resource",
		Type:     v1alpha1.PipelineResourceTypeGit,
		URL:      "git@github.com:test/test.git",
		Revision: "master",
	}

	want := map[string]string{
		"name":     "git-resource",
		"type":     string(v1alpha1.PipelineResourceTypeGit),
		"url":      "git@github.com:test/test.git",
		"revision": "master",
	}

	got := r.Replacements()

	if diff := cmp.Diff(want, got); diff != "" {
		t.Errorf("Mismatch of GitResource Replacements: %s", diff)
	}
}

func Test_GitResource_GetDownloadTaskModifier(t *testing.T) {
	names.TestingSeed()

	r := &v1alpha1.GitResource{
		Name:     "git-resource",
		Type:     v1alpha1.PipelineResourceTypeGit,
		URL:      "git@github.com:test/test.git",
		Revision: "master",
		GitImage: "override-with-git:latest",
	}

	ts := v1alpha1.TaskSpec{}
	modifier, err := r.GetInputTaskModifier(&ts, "/test/test")
	if err != nil {
		t.Fatalf("Unexpected error getting GetDownloadTaskModifier: %s", err)
	}

	want := []v1alpha1.Step{{Container: corev1.Container{
		Name:    "git-source-git-resource-9l9zj",
		Image:   "override-with-git:latest",
		Command: []string{"/ko-app/git-init"},
		Args: []string{
			"-url",
			"git@github.com:test/test.git",
			"-revision",
			"master",
			"-path",
			"/test/test",
		},
		WorkingDir: "/workspace",
		Env:        []corev1.EnvVar{{Name: "TEKTON_RESOURCE_NAME", Value: "git-resource"}},
	}}}

	if diff := cmp.Diff(want, modifier.GetStepsToPrepend()); diff != "" {
		t.Errorf("Mismatch of GitResource DownloadContainerSpec: %s", diff)
	}
}
<commit_msg>Fix git resource submodule test descriptions<commit_after>/*
Copyright 2019 The Tekton 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 v1alpha1_test

import (
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1"
	tb "github.com/tektoncd/pipeline/test/builder"
	"github.com/tektoncd/pipeline/test/names"
	corev1 "k8s.io/api/core/v1"
)

func Test_Invalid_NewGitResource(t *testing.T) {
	if _, err := v1alpha1.NewGitResource("override-with-git:latest", tb.PipelineResource("git-resource", "default", tb.PipelineResourceSpec(v1alpha1.PipelineResourceTypeGCS))); err == nil {
		t.Error("Expected error creating Git resource")
	}
}

func Test_Valid_NewGitResource(t *testing.T) {
	for _, tc := range []struct {
		desc             string
		pipelineResource *v1alpha1.PipelineResource
		want             *v1alpha1.GitResource
	}{{
		desc: "With Revision",
		pipelineResource: tb.PipelineResource("git-resource", "default",
			tb.PipelineResourceSpec(v1alpha1.PipelineResourceTypeGit,
				tb.PipelineResourceSpecParam("URL", "git@github.com:test/test.git"),
				tb.PipelineResourceSpecParam("Revision", "test"),
			),
		),
		want: &v1alpha1.GitResource{
			Name:       "git-resource",
			Type:       v1alpha1.PipelineResourceTypeGit,
			URL:        "git@github.com:test/test.git",
			Revision:   "test",
			GitImage:   "override-with-git:latest",
			Submodules: true,
		},
	}, {
		desc: "Without Revision",
		pipelineResource: tb.PipelineResource("git-resource", "default",
			tb.PipelineResourceSpec(v1alpha1.PipelineResourceTypeGit,
				tb.PipelineResourceSpecParam("URL", "git@github.com:test/test.git"),
			),
		),
		want: &v1alpha1.GitResource{
			Name:       "git-resource",
			Type:       v1alpha1.PipelineResourceTypeGit,
			URL:        "git@github.com:test/test.git",
			Revision:   "master",
			GitImage:   "override-with-git:latest",
			Submodules: true,
		},
	}, {
		desc: "With Submodules",
		pipelineResource: tb.PipelineResource("git-resource", "default",
			tb.PipelineResourceSpec(v1alpha1.PipelineResourceTypeGit,
				tb.PipelineResourceSpecParam("URL", "git@github.com:test/test.git"),
				tb.PipelineResourceSpecParam("Revision", "test"),
			),
		),
		want: &v1alpha1.GitResource{
			Name:       "git-resource",
			Type:       v1alpha1.PipelineResourceTypeGit,
			URL:        "git@github.com:test/test.git",
			Revision:   "test",
			GitImage:   "override-with-git:latest",
			Submodules: true,
		},
	}, {
		desc: "Without Submodules",
		pipelineResource: tb.PipelineResource("git-resource", "default",
			tb.PipelineResourceSpec(v1alpha1.PipelineResourceTypeGit,
				tb.PipelineResourceSpecParam("URL", "git@github.com:test/test.git"),
				tb.PipelineResourceSpecParam("Revision", "test"),
				tb.PipelineResourceSpecParam("Submodules", "false"),
			),
		),
		want: &v1alpha1.GitResource{
			Name:       "git-resource",
			Type:       v1alpha1.PipelineResourceTypeGit,
			URL:        "git@github.com:test/test.git",
			Revision:   "test",
			GitImage:   "override-with-git:latest",
			Submodules: false,
		},
	}} {
		t.Run(tc.desc, func(t *testing.T) {
			got, err := v1alpha1.NewGitResource("override-with-git:latest", tc.pipelineResource)
			if err != nil {
				t.Fatalf("Unexpected error creating Git resource: %s", err)
			}

			if diff := cmp.Diff(tc.want, got); diff != "" {
				t.Errorf("Mismatch of Git resource: %s", diff)
			}
		})
	}
}

func Test_GitResource_Replacements(t *testing.T) {
	r := &v1alpha1.GitResource{
		Name:     "git-resource",
		Type:     v1alpha1.PipelineResourceTypeGit,
		URL:      "git@github.com:test/test.git",
		Revision: "master",
	}

	want := map[string]string{
		"name":     "git-resource",
		"type":     string(v1alpha1.PipelineResourceTypeGit),
		"url":      "git@github.com:test/test.git",
		"revision": "master",
	}

	got := r.Replacements()

	if diff := cmp.Diff(want, got); diff != "" {
		t.Errorf("Mismatch of GitResource Replacements: %s", diff)
	}
}

func Test_GitResource_GetDownloadTaskModifier(t *testing.T) {
	names.TestingSeed()

	r := &v1alpha1.GitResource{
		Name:     "git-resource",
		Type:     v1alpha1.PipelineResourceTypeGit,
		URL:      "git@github.com:test/test.git",
		Revision: "master",
		GitImage: "override-with-git:latest",
	}

	ts := v1alpha1.TaskSpec{}
	modifier, err := r.GetInputTaskModifier(&ts, "/test/test")
	if err != nil {
		t.Fatalf("Unexpected error getting GetDownloadTaskModifier: %s", err)
	}

	want := []v1alpha1.Step{{Container: corev1.Container{
		Name:    "git-source-git-resource-9l9zj",
		Image:   "override-with-git:latest",
		Command: []string{"/ko-app/git-init"},
		Args: []string{
			"-url",
			"git@github.com:test/test.git",
			"-revision",
			"master",
			"-path",
			"/test/test",
		},
		WorkingDir: "/workspace",
		Env:        []corev1.EnvVar{{Name: "TEKTON_RESOURCE_NAME", Value: "git-resource"}},
	}}}

	if diff := cmp.Diff(want, modifier.GetStepsToPrepend()); diff != "" {
		t.Errorf("Mismatch of GitResource DownloadContainerSpec: %s", diff)
	}
}
<|endoftext|>


<commit_before>package chevalier

import (
	"testing"
)

func TestDecodeContentsEnd(t *testing.T) {
	pkt := make([]byte, 1)
	pkt[0] = 3
	res, err := unpackContentsResponse(pkt)
	if err != nil {
		t.Errorf("Error decoding packet: %v", err)
	} else if res.opCode != EndOfContentsList {
		t.Errorf("Wrong opcode: expected EndOfContentsList, got %v", res.opCode)
	}
}
<commit_msg>Add test for contents entry decoding<commit_after>package chevalier

import (
	"testing"
	"bytes"
	"encoding/binary"
)

func TestDecodeContentsEnd(t *testing.T) {
	pkt := make([]byte, 1)
	pkt[0] = 3
	res, err := unpackContentsResponse(pkt)
	if err != nil {
		t.Errorf("Error decoding packet: %v", err)
	} else if res.opCode != EndOfContentsList {
		t.Errorf("Wrong opcode: expected EndOfContentsList, got %v", res.opCode)
	}
}

func TestDecodeContentsEntry(t *testing.T) {
	buf := new(bytes.Buffer)
	buf.WriteByte(2) // ContentsListEntry
	addr := 42
	dict := []byte("foo:bar,baz:quux,")
	dictLen := len(dict)
	binary.Write(buf, binary.LittleEndian, addr)
	binary.Write(buf, binary.LittleEndian, dictLen)
	buf.Write(dict)
	res, err := unpackContentsResponse(buf.Bytes())
	if err != nil {
		t.Errorf("Error decoding packet: %v", err)
	} else if res.opCode != ContentsListEntry {
		t.Errorf("Wrong opcode: expected ContentsListEntry, got %v", res.opCode)
	}
	if res.entry == nil {
		t.Errorf("Error parsing contents entry, got a nil pointer")
	}
	t.Logf("tags: %v", res.entry.tags)
	if v, ok := res.entry.tags["foo"]; ok {
		if v != "bar" {
			t.Errorf("Corrupted tag: expected bar, got %v (in source dict %v)", v, res.entry.tags)
		}
	} else {
		t.Errorf("Expected tag foo in source dict %v", res.entry.tags)
	}
}
<|endoftext|>


<commit_before>//  Copyright Istio 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 namespace

import (
	"context"
	"fmt"
	"io"
	"math/rand"
	"strings"
	"sync"
	"time"

	kubeApiCore "k8s.io/api/core/v1"
	kubeApiMeta "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"

	"istio.io/api/label"
	"istio.io/istio/pkg/test/framework/image"
	"istio.io/istio/pkg/test/framework/resource"
	kube2 "istio.io/istio/pkg/test/kube"
	"istio.io/istio/pkg/test/scopes"
)

var (
	idctr int64
	rnd   = rand.New(rand.NewSource(time.Now().UnixNano()))
	mu    sync.Mutex
)

// kubeNamespace represents a Kubernetes namespace. It is tracked as a resource.
type kubeNamespace struct {
	id   resource.ID
	name string
	ctx  resource.Context
}

func (n *kubeNamespace) Dump(ctx resource.Context) {
	scopes.Framework.Errorf("=== Dumping Namespace %s State...", n.name)

	d, err := ctx.CreateTmpDirectory(n.name + "-state")
	if err != nil {
		scopes.Framework.Errorf("Unable to create directory for dumping %s contents: %v", n.name, err)
		return
	}

	kube2.DumpPods(n.ctx, d, n.name)
}

var (
	_ Instance          = &kubeNamespace{}
	_ io.Closer         = &kubeNamespace{}
	_ resource.Resource = &kubeNamespace{}
	_ resource.Dumper   = &kubeNamespace{}
)

func (n *kubeNamespace) Name() string {
	return n.name
}

func (n *kubeNamespace) SetLabel(key, value string) error {
	return n.setNamespaceLabel(key, value)
}

func (n *kubeNamespace) RemoveLabel(key string) error {
	return n.removeNamespaceLabel(key)
}

func (n *kubeNamespace) ID() resource.ID {
	return n.id
}

// Close implements io.Closer
func (n *kubeNamespace) Close() (err error) {
	if n.name != "" {
		scopes.Framework.Debugf("%s deleting namespace", n.id)
		ns := n.name
		n.name = ""

		for _, c := range n.ctx.Clusters().Kube() {
			err = c.CoreV1().Namespaces().Delete(context.TODO(), ns, kube2.DeleteOptionsForeground())
		}
	}

	scopes.Framework.Debugf("%s close complete (err:%v)", n.id, err)
	return
}

func claimKube(ctx resource.Context, nsConfig *Config) (Instance, error) {
	for _, cluster := range ctx.Clusters().Kube() {
		if !kube2.NamespaceExists(cluster, nsConfig.Prefix) {
			if _, err := cluster.CoreV1().Namespaces().Create(context.TODO(), &kubeApiCore.Namespace{
				ObjectMeta: kubeApiMeta.ObjectMeta{
					Name:   nsConfig.Prefix,
					Labels: createNamespaceLabels(nsConfig),
				},
			}, kubeApiMeta.CreateOptions{}); err != nil {
				return nil, err
			}
		}
	}
	return &kubeNamespace{name: nsConfig.Prefix}, nil
}

// setNamespaceLabel labels a namespace with the given key, value pair
func (n *kubeNamespace) setNamespaceLabel(key, value string) error {
	// need to convert '/' to '~1' as per the JSON patch spec http://jsonpatch.com/#operations
	jsonPatchEscapedKey := strings.ReplaceAll(key, "/", "~1")
	for _, cluster := range n.ctx.Clusters().Kube() {
		nsLabelPatch := fmt.Sprintf(`[{"op":"replace","path":"/metadata/labels/%s","value":"%s"}]`, jsonPatchEscapedKey, value)
		if _, err := cluster.CoreV1().Namespaces().Patch(context.TODO(), n.name, types.JSONPatchType, []byte(nsLabelPatch), kubeApiMeta.PatchOptions{}); err != nil {
			return err
		}
	}

	return nil
}

// removeNamespaceLabel removes namespace label with the given key
func (n *kubeNamespace) removeNamespaceLabel(key string) error {
	// need to convert '/' to '~1' as per the JSON patch spec http://jsonpatch.com/#operations
	jsonPatchEscapedKey := strings.ReplaceAll(key, "/", "~1")
	for _, cluster := range n.ctx.Clusters().Kube() {
		nsLabelPatch := fmt.Sprintf(`[{"op":"remove","path":"/metadata/labels/%s"}]`, jsonPatchEscapedKey)
		if _, err := cluster.CoreV1().Namespaces().Patch(context.TODO(), n.name, types.JSONPatchType, []byte(nsLabelPatch), kubeApiMeta.PatchOptions{}); err != nil {
			return err
		}
	}

	return nil
}

// NewNamespace allocates a new testing namespace.
func newKube(ctx resource.Context, nsConfig *Config) (Instance, error) {
	mu.Lock()
	idctr++
	nsid := idctr
	r := rnd.Intn(99999)
	mu.Unlock()

	ns := fmt.Sprintf("%s-%d-%d", nsConfig.Prefix, nsid, r)
	n := &kubeNamespace{
		name: ns,
		ctx:  ctx,
	}
	id := ctx.TrackResource(n)
	n.id = id

	for _, cluster := range n.ctx.Clusters().Kube() {
		if _, err := cluster.CoreV1().Namespaces().Create(context.TODO(), &kubeApiCore.Namespace{
			ObjectMeta: kubeApiMeta.ObjectMeta{
				Name:   ns,
				Labels: createNamespaceLabels(nsConfig),
			},
		}, kubeApiMeta.CreateOptions{}); err != nil {
			return nil, err
		}
		settings, err := image.SettingsFromCommandLine()
		if err != nil {
			return nil, err
		}
		if settings.ImagePullSecret != "" {
			if err := cluster.ApplyYAMLFiles(n.name, settings.ImagePullSecret); err != nil {
				return nil, err
			}
		}
	}

	return n, nil
}

// createNamespaceLabels will take a namespace config and generate the proper k8s labels
func createNamespaceLabels(cfg *Config) map[string]string {
	l := make(map[string]string)
	l["istio-testing"] = "istio-test"
	if cfg.Inject {
		if cfg.Revision != "" {
			l[label.IoIstioRev.Name] = cfg.Revision
		} else {
			l["istio-injection"] = "enabled"
		}
	}

	// bring over supplied labels
	for k, v := range cfg.Labels {
		l[k] = v
	}
	return l
}
<commit_msg>allow stable namespaces to be created/claimed in parallel (#30901)<commit_after>//  Copyright Istio 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 namespace

import (
	"context"
	"fmt"
	"io"
	"math/rand"
	"strings"
	"sync"
	"time"

	kubeApiCore "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	kubeApiMeta "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"

	"istio.io/api/label"
	"istio.io/istio/pkg/test/framework/image"
	"istio.io/istio/pkg/test/framework/resource"
	kube2 "istio.io/istio/pkg/test/kube"
	"istio.io/istio/pkg/test/scopes"
)

var (
	idctr int64
	rnd   = rand.New(rand.NewSource(time.Now().UnixNano()))
	mu    sync.Mutex
)

// kubeNamespace represents a Kubernetes namespace. It is tracked as a resource.
type kubeNamespace struct {
	id   resource.ID
	name string
	ctx  resource.Context
}

func (n *kubeNamespace) Dump(ctx resource.Context) {
	scopes.Framework.Errorf("=== Dumping Namespace %s State...", n.name)

	d, err := ctx.CreateTmpDirectory(n.name + "-state")
	if err != nil {
		scopes.Framework.Errorf("Unable to create directory for dumping %s contents: %v", n.name, err)
		return
	}

	kube2.DumpPods(n.ctx, d, n.name)
}

var (
	_ Instance          = &kubeNamespace{}
	_ io.Closer         = &kubeNamespace{}
	_ resource.Resource = &kubeNamespace{}
	_ resource.Dumper   = &kubeNamespace{}
)

func (n *kubeNamespace) Name() string {
	return n.name
}

func (n *kubeNamespace) SetLabel(key, value string) error {
	return n.setNamespaceLabel(key, value)
}

func (n *kubeNamespace) RemoveLabel(key string) error {
	return n.removeNamespaceLabel(key)
}

func (n *kubeNamespace) ID() resource.ID {
	return n.id
}

// Close implements io.Closer
func (n *kubeNamespace) Close() (err error) {
	if n.name != "" {
		scopes.Framework.Debugf("%s deleting namespace", n.id)
		ns := n.name
		n.name = ""

		for _, c := range n.ctx.Clusters().Kube() {
			err = c.CoreV1().Namespaces().Delete(context.TODO(), ns, kube2.DeleteOptionsForeground())
		}
	}

	scopes.Framework.Debugf("%s close complete (err:%v)", n.id, err)
	return
}

func claimKube(ctx resource.Context, nsConfig *Config) (Instance, error) {
	for _, cluster := range ctx.Clusters().Kube() {
		if !kube2.NamespaceExists(cluster, nsConfig.Prefix) {
			if _, err := cluster.CoreV1().Namespaces().Create(context.TODO(), &kubeApiCore.Namespace{
				ObjectMeta: kubeApiMeta.ObjectMeta{
					Name:   nsConfig.Prefix,
					Labels: createNamespaceLabels(nsConfig),
				},
			}, kubeApiMeta.CreateOptions{}); err != nil && !errors.IsAlreadyExists(err) {
				return nil, err
			}
		}
	}
	return &kubeNamespace{name: nsConfig.Prefix}, nil
}

// setNamespaceLabel labels a namespace with the given key, value pair
func (n *kubeNamespace) setNamespaceLabel(key, value string) error {
	// need to convert '/' to '~1' as per the JSON patch spec http://jsonpatch.com/#operations
	jsonPatchEscapedKey := strings.ReplaceAll(key, "/", "~1")
	for _, cluster := range n.ctx.Clusters().Kube() {
		nsLabelPatch := fmt.Sprintf(`[{"op":"replace","path":"/metadata/labels/%s","value":"%s"}]`, jsonPatchEscapedKey, value)
		if _, err := cluster.CoreV1().Namespaces().Patch(context.TODO(), n.name, types.JSONPatchType, []byte(nsLabelPatch), kubeApiMeta.PatchOptions{}); err != nil {
			return err
		}
	}

	return nil
}

// removeNamespaceLabel removes namespace label with the given key
func (n *kubeNamespace) removeNamespaceLabel(key string) error {
	// need to convert '/' to '~1' as per the JSON patch spec http://jsonpatch.com/#operations
	jsonPatchEscapedKey := strings.ReplaceAll(key, "/", "~1")
	for _, cluster := range n.ctx.Clusters().Kube() {
		nsLabelPatch := fmt.Sprintf(`[{"op":"remove","path":"/metadata/labels/%s"}]`, jsonPatchEscapedKey)
		if _, err := cluster.CoreV1().Namespaces().Patch(context.TODO(), n.name, types.JSONPatchType, []byte(nsLabelPatch), kubeApiMeta.PatchOptions{}); err != nil {
			return err
		}
	}

	return nil
}

// NewNamespace allocates a new testing namespace.
func newKube(ctx resource.Context, nsConfig *Config) (Instance, error) {
	mu.Lock()
	idctr++
	nsid := idctr
	r := rnd.Intn(99999)
	mu.Unlock()

	ns := fmt.Sprintf("%s-%d-%d", nsConfig.Prefix, nsid, r)
	n := &kubeNamespace{
		name: ns,
		ctx:  ctx,
	}
	id := ctx.TrackResource(n)
	n.id = id

	for _, cluster := range n.ctx.Clusters().Kube() {
		if _, err := cluster.CoreV1().Namespaces().Create(context.TODO(), &kubeApiCore.Namespace{
			ObjectMeta: kubeApiMeta.ObjectMeta{
				Name:   ns,
				Labels: createNamespaceLabels(nsConfig),
			},
		}, kubeApiMeta.CreateOptions{}); err != nil {
			return nil, err
		}
		settings, err := image.SettingsFromCommandLine()
		if err != nil {
			return nil, err
		}
		if settings.ImagePullSecret != "" {
			if err := cluster.ApplyYAMLFiles(n.name, settings.ImagePullSecret); err != nil {
				return nil, err
			}
		}
	}

	return n, nil
}

// createNamespaceLabels will take a namespace config and generate the proper k8s labels
func createNamespaceLabels(cfg *Config) map[string]string {
	l := make(map[string]string)
	l["istio-testing"] = "istio-test"
	if cfg.Inject {
		if cfg.Revision != "" {
			l[label.IoIstioRev.Name] = cfg.Revision
		} else {
			l["istio-injection"] = "enabled"
		}
	}

	// bring over supplied labels
	for k, v := range cfg.Labels {
		l[k] = v
	}
	return l
}
<|endoftext|>


<commit_before>package impl

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"tools/lib/cmdline"
	"tools/lib/util"
)

// cmdGo represents the 'go' command of the veyron tool.
var cmdGo = &cmdline.Command{
	Run:   runGo,
	Name:  "go",
	Short: "Execute the go tool using the veyron environment",
	Long: `
Wrapper around the 'go' tool that can be used for compilation of
veyron Go sources. It takes care of veyron-specific setup, such as
setting up the Go specific environment variables or making sure that
VDL generated files are regenerated before compilation.

In particular, the tool invokes the following command before invoking
any go tool commands that compile veyron Go code:

vdl generate -lang=go all
`,
	ArgsName: "<arg ...>",
	ArgsLong: "<arg ...> is a list of arguments for the go tool.",
}

func runGo(command *cmdline.Command, args []string) error {
	if len(args) == 0 {
		return command.UsageErrorf("not enough arguments")
	}
	return setupAndGo(util.HostPlatform(), command, args)
}

// cmdXGo represents the 'xgo' command of the veyron tool.
var cmdXGo = &cmdline.Command{
	Run:   runXGo,
	Name:  "xgo",
	Short: "Execute the go tool using the veyron environment and cross-compilation",
	Long: `
Wrapper around the 'go' tool that can be used for cross-compilation of
veyron Go sources. It takes care of veyron-specific setup, such as
setting up the Go specific environment variables or making sure that
VDL generated files are regenerated before compilation.

In particular, the tool invokes the following command before invoking
any go tool commands that compile veyron Go code:

vdl generate -lang=go all

`,
	ArgsName: "<platform> <arg ...>",
	ArgsLong: `
<platform> is the cross-compilation target and has the general format
<arch><sub>-<os> or <arch><sub>-<os>-<env> where:
- <arch> is the platform architecture (e.g. 386, amd64 or arm)
- <sub> is the platform sub-architecture (e.g. v6 or v7 for arm)
- <os> is the platform operating system (e.g. linux or darwin)
- <env> is the platform environment (e.g. gnu or android)

<arg ...> is a list of arguments for the go tool."
`,
}

func runXGo(command *cmdline.Command, args []string) error {
	if len(args) < 2 {
		return command.UsageErrorf("not enough arguments")
	}
	platform, err := util.ParsePlatform(args[0])
	if err != nil {
		return err
	}
	return setupAndGo(platform, command, args[1:])
}

func setupAndGo(platform util.Platform, command *cmdline.Command, args []string) error {
	switch args[0] {
	case "build", "install", "run", "test":
		if err := generateVDL(); err != nil {
			return err
		}
	}
	if err := util.SetupVeyronEnvironment(platform); err != nil {
		return err
	}
	goCmd := exec.Command("go", args...)
	goCmd.Stdout = command.Stdout()
	goCmd.Stderr = command.Stderr()
	return translateExitCode(goCmd.Run())
}

func generateVDL() error {
	if novdlFlag {
		return nil
	}
	root, err := util.VeyronRoot()
	if err != nil {
		return err
	}
	vdlDir := filepath.Join(root, "veyron", "go", "src", "veyron.io", "veyron", "veyron2", "vdl", "vdl")
	args := []string{"run"}
	fis, err := ioutil.ReadDir(vdlDir)
	if err != nil {
		return fmt.Errorf("ReadDir(%v) failed: %v", vdlDir, err)
	}
	for _, fi := range fis {
		if strings.HasSuffix(fi.Name(), ".go") {
			args = append(args, filepath.Join(vdlDir, fi.Name()))
		}
	}
	// TODO(toddw): We should probably only generate vdl for the packages
	// specified for the corresponding "go" command.  This isn't trivial; we'd
	// need to grab the transitive go dependencies for the specified packages, and
	// then look for transitive vdl dependencies based on that set.
	args = append(args, "generate", "-lang=go", "all")
	vdlCmd := exec.Command("go", args...)
	conf, err := util.VeyronConfig()
	if err != nil {
		return err
	}
	gopath := []string{}
	for _, repo := range conf.GoRepos {
		gopath = append(gopath, filepath.Join(root, repo, "go"))
	}
	vdlCmd.Env = append(vdlCmd.Env, fmt.Sprintf("GOPATH=%v", strings.Join(gopath, ":")))
	if out, err := vdlCmd.CombinedOutput(); err != nil {
		return fmt.Errorf("failed to generate vdl: %v\n%v\n%s", err, strings.Join(vdlCmd.Args, " "), out)
	}
	return nil
}

// cmdGoExt represents the 'goext' command of the veyron tool.
var cmdGoExt = &cmdline.Command{
	Name:     "goext",
	Short:    "Veyron extensions of the go tool",
	Long:     "Veyron extension of the go tool.",
	Children: []*cmdline.Command{cmdGoExtDistClean},
}

// cmdGoExtDistClean represents the 'distclean' sub-command of 'goext'
// command of the veyron tool.
var cmdGoExtDistClean = &cmdline.Command{
	Run:   runGoExtDistClean,
	Name:  "distclean",
	Short: "Restore the veyron Go repositories to their pristine state",
	Long: `
Unlike the 'go clean' command, which only removes object files for
packages in the source tree, the 'goext disclean' command removes all
object files from veyron Go workspaces. This functionality is needed
to avoid accidental use of stale object files that correspond to
packages that no longer exist in the source tree.
`,
}

func runGoExtDistClean(command *cmdline.Command, _ []string) error {
	if err := util.SetupVeyronEnvironment(util.HostPlatform()); err != nil {
		return err
	}
	goPath := os.Getenv("GOPATH")
	failed := false
	for _, workspace := range strings.Split(goPath, ":") {
		if workspace == "" {
			continue
		}
		for _, name := range []string{"bin", "pkg"} {
			dir := filepath.Join(workspace, name)
			// TODO(jsimsa): Use the new logging library
			// for this when it is checked in.
			fmt.Fprintf(command.Stdout(), "Removing %v\n", dir)
			if err := os.RemoveAll(dir); err != nil {
				failed = true
				fmt.Fprintf(command.Stderr(), "RemoveAll(%v) failed: %v", dir, err)
			}
		}
	}
	if failed {
		return cmdline.ErrExitCode(2)
	}
	return nil
}
<commit_msg>tools/veyron: Make sure vdl update is run with correct env.<commit_after>package impl

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"tools/lib/cmdline"
	"tools/lib/util"
)

// cmdGo represents the 'go' command of the veyron tool.
var cmdGo = &cmdline.Command{
	Run:   runGo,
	Name:  "go",
	Short: "Execute the go tool using the veyron environment",
	Long: `
Wrapper around the 'go' tool that can be used for compilation of
veyron Go sources. It takes care of veyron-specific setup, such as
setting up the Go specific environment variables or making sure that
VDL generated files are regenerated before compilation.

In particular, the tool invokes the following command before invoking
any go tool commands that compile veyron Go code:

vdl generate -lang=go all
`,
	ArgsName: "<arg ...>",
	ArgsLong: "<arg ...> is a list of arguments for the go tool.",
}

func runGo(command *cmdline.Command, args []string) error {
	if len(args) == 0 {
		return command.UsageErrorf("not enough arguments")
	}
	return setupAndGo(util.HostPlatform(), command, args)
}

// cmdXGo represents the 'xgo' command of the veyron tool.
var cmdXGo = &cmdline.Command{
	Run:   runXGo,
	Name:  "xgo",
	Short: "Execute the go tool using the veyron environment and cross-compilation",
	Long: `
Wrapper around the 'go' tool that can be used for cross-compilation of
veyron Go sources. It takes care of veyron-specific setup, such as
setting up the Go specific environment variables or making sure that
VDL generated files are regenerated before compilation.

In particular, the tool invokes the following command before invoking
any go tool commands that compile veyron Go code:

vdl generate -lang=go all

`,
	ArgsName: "<platform> <arg ...>",
	ArgsLong: `
<platform> is the cross-compilation target and has the general format
<arch><sub>-<os> or <arch><sub>-<os>-<env> where:
- <arch> is the platform architecture (e.g. 386, amd64 or arm)
- <sub> is the platform sub-architecture (e.g. v6 or v7 for arm)
- <os> is the platform operating system (e.g. linux or darwin)
- <env> is the platform environment (e.g. gnu or android)

<arg ...> is a list of arguments for the go tool."
`,
}

func runXGo(command *cmdline.Command, args []string) error {
	if len(args) < 2 {
		return command.UsageErrorf("not enough arguments")
	}
	platform, err := util.ParsePlatform(args[0])
	if err != nil {
		return err
	}
	return setupAndGo(platform, command, args[1:])
}

func setupAndGo(platform util.Platform, command *cmdline.Command, args []string) error {
	// First set up the environment for the host platform, and run vdl.
	if err := util.SetupVeyronEnvironment(util.HostPlatform()); err != nil {
		return err
	}
	switch args[0] {
	case "build", "install", "run", "test":
		if err := generateVDL(); err != nil {
			return err
		}
	}
	// Now set up the specified platform, and run go.
	if err := util.SetupVeyronEnvironment(platform); err != nil {
		return err
	}
	goCmd := exec.Command("go", args...)
	goCmd.Stdout = command.Stdout()
	goCmd.Stderr = command.Stderr()
	return translateExitCode(goCmd.Run())
}

func generateVDL() error {
	if novdlFlag {
		return nil
	}
	root, err := util.VeyronRoot()
	if err != nil {
		return err
	}
	// Initialize args with the *.go files under the vdl directory to run.
	args := []string{"run"}
	vdlDir := filepath.Join(root, "veyron", "go", "src", "veyron.io", "veyron", "veyron2", "vdl", "vdl")
	fis, err := ioutil.ReadDir(vdlDir)
	if err != nil {
		return fmt.Errorf("ReadDir(%v) failed: %v", vdlDir, err)
	}
	for _, fi := range fis {
		if strings.HasSuffix(fi.Name(), ".go") {
			args = append(args, filepath.Join(vdlDir, fi.Name()))
		}
	}
	// TODO(toddw): We should probably only generate vdl for the packages
	// specified for the corresponding "go" command.  This isn't trivial; we'd
	// need to grab the transitive go dependencies for the specified packages, and
	// then look for transitive vdl dependencies based on that set.
	args = append(args, "generate", "-lang=go", "all")
	vdlCmd := exec.Command("go", args...)
	if out, err := vdlCmd.CombinedOutput(); err != nil {
		return fmt.Errorf("failed to generate vdl: %v\n%v\n%s", err, strings.Join(vdlCmd.Args, " "), out)
	}
	return nil
}

// cmdGoExt represents the 'goext' command of the veyron tool.
var cmdGoExt = &cmdline.Command{
	Name:     "goext",
	Short:    "Veyron extensions of the go tool",
	Long:     "Veyron extension of the go tool.",
	Children: []*cmdline.Command{cmdGoExtDistClean},
}

// cmdGoExtDistClean represents the 'distclean' sub-command of 'goext'
// command of the veyron tool.
var cmdGoExtDistClean = &cmdline.Command{
	Run:   runGoExtDistClean,
	Name:  "distclean",
	Short: "Restore the veyron Go repositories to their pristine state",
	Long: `
Unlike the 'go clean' command, which only removes object files for
packages in the source tree, the 'goext disclean' command removes all
object files from veyron Go workspaces. This functionality is needed
to avoid accidental use of stale object files that correspond to
packages that no longer exist in the source tree.
`,
}

func runGoExtDistClean(command *cmdline.Command, _ []string) error {
	if err := util.SetupVeyronEnvironment(util.HostPlatform()); err != nil {
		return err
	}
	goPath := os.Getenv("GOPATH")
	failed := false
	for _, workspace := range strings.Split(goPath, ":") {
		if workspace == "" {
			continue
		}
		for _, name := range []string{"bin", "pkg"} {
			dir := filepath.Join(workspace, name)
			// TODO(jsimsa): Use the new logging library
			// for this when it is checked in.
			fmt.Fprintf(command.Stdout(), "Removing %v\n", dir)
			if err := os.RemoveAll(dir); err != nil {
				failed = true
				fmt.Fprintf(command.Stderr(), "RemoveAll(%v) failed: %v", dir, err)
			}
		}
	}
	if failed {
		return cmdline.ErrExitCode(2)
	}
	return nil
}
<|endoftext|>


<commit_before>package redis

func (c *Client) BLPop(key string, timeout int, keys ...string) (string, error) {
	// TODO(ttacon): ✔
	return "", nil
}

func (c *Client) BRPop(key string, timeout int, keys ...string) (string, error) {
	// TODO(ttacon): ✔
	return "", nil
}

func (c *Client) BRPopLPush(source, destination string, timeout int) error {
	// TODO(ttacon): ✔
	return nil
}

func (c *Client) LIndex(key string, index int) (string, error) {
	// TODO(ttacon): ✔
	return "", nil
}

type BeforeAfter string

const (
	BEFORE BeforeAfter = "BEFORE"
	AFTER  BeforeAfter = "AFTER"
)

func (b BeforeAfter) String() string { return string(b) }

// TODO(ttacon): value is interface so can use int, float or string
func (c *Client) LInsert(key string, insert BeforeAfter, pivot string, value interface{}) (string, error) {
	// TODO(ttacon): ✔
	return "", nil
}

func (c *Client) LLen(key string) (int, error) {
	// TODO(ttacon): ✔
	return 0, nil
}

func (c *Client) LPop(key string) (string, error) {
	// TODO(ttacon): ✔
	return "", nil
}

func (c *Client) LPush(key, value string, values ...string) (int, error) {
	// TODO(ttacon): ✔
	return 0, nil
}

func (c *Client) LRange(key string, start, stop int) ([]string, error) {
	// TODO(ttacon): ✔
	return nil, nil
}

func (c *Client) LRem(key string, count int, value string) (int, error) {
	// TODO(ttacon): ✔
	return 0, nil
}

func (c *Client) LSet(key string, index int, value string) error {
	// TODO(ttacon): ✔
	return nil
}

func (c *Client) LTrim(key string, start, stop int) error {
	// TODO(ttacon): ✔
	return nil
}

func (c *Client) RPop(key string) (string, error) {
	// TODO(ttacon): ✔
	return "", nil
}

func (c *Client) RPopLPush(source, destination string) error {
	// TODO(ttacon): ✔
	return nil
}

func (c *Client) RPush(key, value string, values ...string) (int, error) {
	// TODO(ttacon): ✔
	return 0, nil
}

func (c *Client) RPushX(key, value string, values ...string) (int, error) {
	// TODO(ttacon): ✔
	return 0, nil
}
<commit_msg>BLPOP<commit_after>package redis

import "strconv"

func (c *Client) BLPop(key string, timeout int, keys ...string) ([]string, error) {
	var vals = []string{key}
	vals = append(vals, keys...)
	vals = append(vals, strconv.Itoa(timeout))
	resp, err := c.exec("BLPOP", vals...)
	if err != nil {
		return nil, err
	}
	return c.stringSlice(resp)
}

func (c *Client) BRPop(key string, timeout int, keys ...string) (string, error) {
	// TODO(ttacon): ✔
	return "", nil
}

func (c *Client) BRPopLPush(source, destination string, timeout int) error {
	// TODO(ttacon): ✔
	return nil
}

func (c *Client) LIndex(key string, index int) (string, error) {
	// TODO(ttacon): ✔
	return "", nil
}

type BeforeAfter string

const (
	BEFORE BeforeAfter = "BEFORE"
	AFTER  BeforeAfter = "AFTER"
)

func (b BeforeAfter) String() string { return string(b) }

// TODO(ttacon): value is interface so can use int, float or string
func (c *Client) LInsert(key string, insert BeforeAfter, pivot string, value interface{}) (string, error) {
	// TODO(ttacon): ✔
	return "", nil
}

func (c *Client) LLen(key string) (int, error) {
	// TODO(ttacon): ✔
	return 0, nil
}

func (c *Client) LPop(key string) (string, error) {
	// TODO(ttacon): ✔
	return "", nil
}

func (c *Client) LPush(key, value string, values ...string) (int, error) {
	// TODO(ttacon): ✔
	return 0, nil
}

func (c *Client) LRange(key string, start, stop int) ([]string, error) {
	// TODO(ttacon): ✔
	return nil, nil
}

func (c *Client) LRem(key string, count int, value string) (int, error) {
	// TODO(ttacon): ✔
	return 0, nil
}

func (c *Client) LSet(key string, index int, value string) error {
	// TODO(ttacon): ✔
	return nil
}

func (c *Client) LTrim(key string, start, stop int) error {
	// TODO(ttacon): ✔
	return nil
}

func (c *Client) RPop(key string) (string, error) {
	// TODO(ttacon): ✔
	return "", nil
}

func (c *Client) RPopLPush(source, destination string) error {
	// TODO(ttacon): ✔
	return nil
}

func (c *Client) RPush(key, value string, values ...string) (int, error) {
	// TODO(ttacon): ✔
	return 0, nil
}

func (c *Client) RPushX(key, value string, values ...string) (int, error) {
	// TODO(ttacon): ✔
	return 0, nil
}
<|endoftext|>