<commit_before>package dynago

import (
	"encoding/json"
	"time"
)

const (
	SELECT_ALL        = "ALL_ATTRIBUTES"
	SELECT_PROJECTED  = "ALL_PROJECTED_ATTRIBUTES"
	SELECT_ATTRIBUTES = "SPECIFIC_ATTRIBUTES"
	SELECT_COUNT      = "COUNT"
)

var (
	RETURN_CONSUMED = map[bool]string{true: "TOTAL", false: "NONE"}

	RETURN_TOTAL_CONSUMED = "TOTAL"
	RETURN_IDEX_CONSUMED  = "INDEXED"

	RETURN_METRICS = map[bool]string{true: "SIZE", false: "NONE"}

	RETURN_NONE        = "NONE"
	RETURN_ALL_OLD     = "ALL_OLD"
	RETURN_ALL_NEW     = "ALL_NEW"
	RETURN_UPDATED_OLD = "UPDATED_OLD"
	RETURN_UPDATED_NEW = "UPDATED_NEW"
)

type ConsumedCapacityDescription struct {
	CapacityUnits float32
	TableName     string
}

type KeyValue struct {
	Key   AttributeDefinition
	Value interface{}
}

// Items are maps of name/value pairs
type Item map[string]interface{}

func (pi *Item) UnmarshalJSON(data []byte) error {
	var dbitem DBItem

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

	item := make(Item)

	for k, v := range dbitem {
		item[k] = DecodeValue(v)
	}

	*pi = item
	return nil
}

func (pi *Item) MarshalJSON() ([]byte, error) {
	dbitem := DBItem{}

	for k, v := range *pi {
		dbitem[k] = EncodeValue(v)
	}

	return json.Marshal(dbitem)
}

//////////////////////////////////////////////////////////////////////////////
//
// PutItem
//

type PutItemRequest struct {
	TableName string
	Item      Item

	ConditionalExpression string `json:",omitempty"`
	ConditionalOperator   string `json:",omitempty"`

	ExpressionAttributeNames  map[string]string  `json:",omitempty"`
	ExpressionAttributeValues AttributeNameValue `json:",omitempty"`

	ReturnConsumedCapacity      string `json:",omitempty"` // INDEXED | TOTAL | NONE
	ReturnItemCollectionMetrics string `json:",omitempty"` // SIZE | NONE
	ReturnValues                string `json:",omitempty"` // NONE | ALL_OLD | UPDATED_OLD | ALL_NEW | UPDATED_NEW
}

type PutItemResult struct {
	Attributes Item

	ConsumedCapacity      ConsumedCapacityDescription
	ItemCollectionMetrics ItemCollectionMetrics
}

type ItemCollectionMetrics struct {
	ItemCollectionKey   AttributeNameValue
	SizeEstimateRangeGB []float64
}

type PutOption func(*PutItemRequest)

func PutConditionalExpression(expr string) PutOption {
	return func(putReq *PutItemRequest) {
		putReq.ConditionalExpression = expr
	}
}

func PutConditionalOperator(and bool) PutOption {
	return func(putReq *PutItemRequest) {
		if and {
			putReq.ConditionalOperator = "AND"
		} else {
			putReq.ConditionalOperator = "OR"
		}
	}
}

func PutExpressionAttributeNames(names map[string]string) PutOption {
	return func(putReq *PutItemRequest) {
		putReq.ExpressionAttributeNames = names
	}
}

func PutExpressionAttributeValues(values AttributeNameValue) PutOption {
	return func(putReq *PutItemRequest) {
		putReq.ExpressionAttributeValues = values
	}
}

func PutReturnConsumed(target string) PutOption {
	return func(putReq *PutItemRequest) {
		putReq.ReturnConsumedCapacity = target
	}
}

func PutReturnMetrics(ret bool) PutOption {
	return func(putReq *PutItemRequest) {
		putReq.ReturnItemCollectionMetrics = RETURN_METRICS[ret]
	}
}

func PutReturnValues(target string) PutOption {
	return func(putReq *PutItemRequest) {
		putReq.ReturnValues = target
	}
}

func (db *DBClient) PutItem(tableName string, item Item, options ...PutOption) (*Item, float32, error) {
	var putReq = PutItemRequest{TableName: tableName, Item: item}
	var putRes PutItemResult

	for _, option := range options {
		option(&putReq)
	}

	if err := db.Query("PutItem", &putReq).Decode(&putRes); err != nil {
		return nil, 0.0, err
	} else {
		return &putRes.Attributes, putRes.ConsumedCapacity.CapacityUnits, err
	}
}

//////////////////////////////////////////////////////////////////////////////
//
// GetItem
//

type GetItemRequest struct {
	TableName              string
	Key                    map[string]AttributeValue
	AttributesToGet        []string
	ConsistentRead         bool
	ReturnConsumedCapacity string
}

type GetItemResult struct {
	ConsumedCapacity ConsumedCapacityDescription

	Item Item
}

func (db *DBClient) GetItem(tableName string, hashKey *KeyValue, rangeKey *KeyValue, attributes []string, consistent bool, consumed bool) (map[string]interface{}, float32, error) {

	getReq := GetItemRequest{TableName: tableName, AttributesToGet: attributes, ConsistentRead: consistent, ReturnConsumedCapacity: RETURN_CONSUMED[consumed]}
	getReq.Key = EncodeAttribute(hashKey.Key, hashKey.Value)
	if rangeKey != nil {
		getReq.Key[rangeKey.Key.AttributeName] = EncodeAttributeValue(rangeKey.Key, rangeKey.Value)
	}

	var getRes GetItemResult

	if err := db.Query("GetItem", getReq).Decode(&getRes); err != nil {
		return nil, 0.0, err
	}

	if len(getRes.Item) == 0 {
		return nil, getRes.ConsumedCapacity.CapacityUnits, nil
	}

	return getRes.Item, getRes.ConsumedCapacity.CapacityUnits, nil
}

//////////////////////////////////////////////////////////////////////////////
//
// Query
//

type QueryRequest struct {
	TableName              string
	AttributesToGet        []string `json:",omitempty"`
	ScanIndexForward       bool
	ExclusiveStartKey      AttributeNameValue   `json:",omitempty"`
	KeyConditions          map[string]Condition `json:",omitempty"`
	IndexName              string               `json:",omitempty"`
	Limit                  *int                 `json:",omitempty"`
	Select                 string               `json:",omitempty"`
	ReturnConsumedCapacity string               `json:",omitempty"`

	table *TableInstance
}

type QueryResult struct {
	Items            []Item
	ConsumedCapacity ConsumedCapacityDescription
	LastEvaluatedKey AttributeNameValue
	Count            int
	ScannedCount     int
}

func QueryTable(table *TableInstance) *QueryRequest {
	return &QueryRequest{TableName: table.Name, ScanIndexForward: true, KeyConditions: make(map[string]Condition), table: table}
}

func Query(tableName string) *QueryRequest {
	return &QueryRequest{TableName: tableName, ScanIndexForward: true, KeyConditions: make(map[string]Condition)}
}

func (queryReq *QueryRequest) WithAttributes(attributes []string) *QueryRequest {
	queryReq.AttributesToGet = attributes
	return queryReq
}

func (queryReq *QueryRequest) WithStartKey(startKey AttributeNameValue) *QueryRequest {
	queryReq.ExclusiveStartKey = startKey
	return queryReq
}

func (queryReq *QueryRequest) WithIndex(indexName string) *QueryRequest {
	queryReq.IndexName = indexName
	return queryReq
}

func (queryReq *QueryRequest) WithCondition(attrName string, condition Condition) *QueryRequest {
	queryReq.KeyConditions[attrName] = condition
	return queryReq
}

func (queryReq *QueryRequest) WithAttrCondition(cond AttrCondition) *QueryRequest {
	for k, v := range cond {
		queryReq.KeyConditions[k] = v
	}

	return queryReq
}

func (queryReq *QueryRequest) WithLimit(limit int) *QueryRequest {
	queryReq.Limit = &limit
	return queryReq
}

func (queryReq *QueryRequest) WithSelect(selectValue string) *QueryRequest {
	queryReq.Select = selectValue
	return queryReq
}

func (queryReq *QueryRequest) WithConsumed(consumed bool) *QueryRequest {
	queryReq.ReturnConsumedCapacity = RETURN_CONSUMED[consumed]
	return queryReq
}

func (queryReq *QueryRequest) Exec(db *DBClient) ([]Item, AttributeNameValue, float32, error) {
	if db == nil && queryReq.table != nil {
		db = queryReq.table.DB
	}

	var queryRes QueryResult

	if err := db.Query("Query", queryReq).Decode(&queryRes); err != nil {
		return nil, nil, 0.0, err
	}

	return queryRes.Items, queryRes.LastEvaluatedKey, queryRes.ConsumedCapacity.CapacityUnits, nil
}

//////////////////////////////////////////////////////////////////////////////
//
// Scan
//

type ScanRequest struct {
	TableName              string
	AttributesToGet        []string
	ExclusiveStartKey      AttributeNameValue
	ScanFilter             map[string]Condition
	Limit                  *int   `json:",omitempty"`
	Segment                *int   `json:",omitempty"`
	TotalSegments          *int   `json:",omitempty"`
	Select                 string `json:",omitempty"`
	ReturnConsumedCapacity string `json:",omitempty"`

	table *TableInstance
}

func ScanTable(table *TableInstance) *ScanRequest {
	return &ScanRequest{TableName: table.Name, table: table}
}

func Scan(tableName string) *ScanRequest {
	return &ScanRequest{TableName: tableName}
}

func (scanReq *ScanRequest) WithAttributes(attributes []string) *ScanRequest {
	scanReq.AttributesToGet = attributes
	return scanReq
}

func (scanReq *ScanRequest) WithStartKey(startKey AttributeNameValue) *ScanRequest {
	scanReq.ExclusiveStartKey = startKey
	return scanReq
}

func (scanReq *ScanRequest) WithFilter(attrName string, condition Condition) *ScanRequest {
	if scanReq.ScanFilter == nil {
		scanReq.ScanFilter = map[string]Condition{attrName: condition}
	} else {
		scanReq.ScanFilter[attrName] = condition
	}
	return scanReq
}

func (scanReq *ScanRequest) WithFilters(filters AttrCondition) *ScanRequest {
	scanReq.ScanFilter = filters
	return scanReq
}

func (scanReq *ScanRequest) WithLimit(limit int) *ScanRequest {
	scanReq.Limit = &limit
	return scanReq
}

func (scanReq *ScanRequest) WithSegment(segment, totalSegments int) *ScanRequest {
	scanReq.Segment = &segment
	scanReq.TotalSegments = &totalSegments
	return scanReq
}

func (scanReq *ScanRequest) WithSelect(selectValue string) *ScanRequest {
	scanReq.Select = selectValue
	return scanReq
}

func (scanReq *ScanRequest) WithConsumed(consumed bool) *ScanRequest {
	scanReq.ReturnConsumedCapacity = RETURN_CONSUMED[consumed]
	return scanReq
}

func (scanReq *ScanRequest) Exec(db *DBClient) ([]Item, AttributeNameValue, float32, error) {
	var scanRes QueryResult

	if err := db.Query("Scan", scanReq).Decode(&scanRes); err != nil {
		return nil, nil, 0.0, err
	}

	return scanRes.Items, scanRes.LastEvaluatedKey, scanRes.ConsumedCapacity.CapacityUnits, nil
}

func (scanReq *ScanRequest) Count(db *DBClient) (count int, scount int, consumed float32, err error) {
	return scanReq.CountWithDelay(db, 0)
}

func (scanReq *ScanRequest) CountWithDelay(db *DBClient, delay time.Duration) (count int, scount int, consumed float32, err error) {
	var scanRes QueryResult

	req := *scanReq
	req.Select = SELECT_COUNT

	for {
		scanRes.LastEvaluatedKey = nil

		if err = db.Query("Scan", &req).Decode(&scanRes); err != nil {
			break
		}

		count += scanRes.Count
		scount += scanRes.ScannedCount
		consumed += scanRes.ConsumedCapacity.CapacityUnits

		if scanRes.LastEvaluatedKey == nil {
			break
		}

		req.ExclusiveStartKey = make(AttributeNameValue)
		for k, v := range scanRes.LastEvaluatedKey {
			req.ExclusiveStartKey[k] = v
		}

		if delay > 0 {
			time.Sleep(delay)
		}
	}

	return
}
<commit_msg>all requests are req, all results are res<commit_after>package dynago

import (
	"encoding/json"
	"time"
)

const (
	SELECT_ALL        = "ALL_ATTRIBUTES"
	SELECT_PROJECTED  = "ALL_PROJECTED_ATTRIBUTES"
	SELECT_ATTRIBUTES = "SPECIFIC_ATTRIBUTES"
	SELECT_COUNT      = "COUNT"
)

var (
	RETURN_CONSUMED = map[bool]string{true: "TOTAL", false: "NONE"}

	RETURN_TOTAL_CONSUMED = "TOTAL"
	RETURN_IDEX_CONSUMED  = "INDEXED"

	RETURN_METRICS = map[bool]string{true: "SIZE", false: "NONE"}

	RETURN_NONE        = "NONE"
	RETURN_ALL_OLD     = "ALL_OLD"
	RETURN_ALL_NEW     = "ALL_NEW"
	RETURN_UPDATED_OLD = "UPDATED_OLD"
	RETURN_UPDATED_NEW = "UPDATED_NEW"
)

type ConsumedCapacityDescription struct {
	CapacityUnits float32
	TableName     string
}

type KeyValue struct {
	Key   AttributeDefinition
	Value interface{}
}

// Items are maps of name/value pairs
type Item map[string]interface{}

func (pi *Item) UnmarshalJSON(data []byte) error {
	var dbitem DBItem

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

	item := make(Item)

	for k, v := range dbitem {
		item[k] = DecodeValue(v)
	}

	*pi = item
	return nil
}

func (pi *Item) MarshalJSON() ([]byte, error) {
	dbitem := DBItem{}

	for k, v := range *pi {
		dbitem[k] = EncodeValue(v)
	}

	return json.Marshal(dbitem)
}

//////////////////////////////////////////////////////////////////////////////
//
// PutItem
//

type PutItemRequest struct {
	TableName string
	Item      Item

	ConditionalExpression string `json:",omitempty"`
	ConditionalOperator   string `json:",omitempty"`

	ExpressionAttributeNames  map[string]string  `json:",omitempty"`
	ExpressionAttributeValues AttributeNameValue `json:",omitempty"`

	ReturnConsumedCapacity      string `json:",omitempty"` // INDEXED | TOTAL | NONE
	ReturnItemCollectionMetrics string `json:",omitempty"` // SIZE | NONE
	ReturnValues                string `json:",omitempty"` // NONE | ALL_OLD | UPDATED_OLD | ALL_NEW | UPDATED_NEW
}

type PutItemResult struct {
	Attributes Item

	ConsumedCapacity      ConsumedCapacityDescription
	ItemCollectionMetrics ItemCollectionMetrics
}

type ItemCollectionMetrics struct {
	ItemCollectionKey   AttributeNameValue
	SizeEstimateRangeGB []float64
}

type PutOption func(*PutItemRequest)

func PutConditionalExpression(expr string) PutOption {
	return func(req *PutItemRequest) {
		req.ConditionalExpression = expr
	}
}

func PutConditionalOperator(and bool) PutOption {
	return func(req *PutItemRequest) {
		if and {
			req.ConditionalOperator = "AND"
		} else {
			req.ConditionalOperator = "OR"
		}
	}
}

func PutExpressionAttributeNames(names map[string]string) PutOption {
	return func(req *PutItemRequest) {
		req.ExpressionAttributeNames = names
	}
}

func PutExpressionAttributeValues(values AttributeNameValue) PutOption {
	return func(req *PutItemRequest) {
		req.ExpressionAttributeValues = values
	}
}

func PutReturnConsumed(target string) PutOption {
	return func(req *PutItemRequest) {
		req.ReturnConsumedCapacity = target
	}
}

func PutReturnMetrics(ret bool) PutOption {
	return func(req *PutItemRequest) {
		req.ReturnItemCollectionMetrics = RETURN_METRICS[ret]
	}
}

func PutReturnValues(target string) PutOption {
	return func(req *PutItemRequest) {
		req.ReturnValues = target
	}
}

func (db *DBClient) PutItem(tableName string, item Item, options ...PutOption) (*Item, float32, error) {
	var req = PutItemRequest{TableName: tableName, Item: item}
	var res PutItemResult

	for _, option := range options {
		option(&req)
	}

	if err := db.Query("PutItem", &req).Decode(&res); err != nil {
		return nil, 0.0, err
	} else {
		return &res.Attributes, res.ConsumedCapacity.CapacityUnits, err
	}
}

//////////////////////////////////////////////////////////////////////////////
//
// GetItem
//

type GetItemRequest struct {
	TableName              string
	Key                    map[string]AttributeValue
	AttributesToGet        []string
	ConsistentRead         bool
	ReturnConsumedCapacity string
}

type GetItemResult struct {
	ConsumedCapacity ConsumedCapacityDescription

	Item Item
}

func (db *DBClient) GetItem(tableName string, hashKey *KeyValue, rangeKey *KeyValue, attributes []string, consistent bool, consumed bool) (map[string]interface{}, float32, error) {

	req := GetItemRequest{TableName: tableName, AttributesToGet: attributes, ConsistentRead: consistent, ReturnConsumedCapacity: RETURN_CONSUMED[consumed]}
	req.Key = EncodeAttribute(hashKey.Key, hashKey.Value)
	if rangeKey != nil {
		req.Key[rangeKey.Key.AttributeName] = EncodeAttributeValue(rangeKey.Key, rangeKey.Value)
	}

	var res GetItemResult

	if err := db.Query("GetItem", req).Decode(&res); err != nil {
		return nil, 0.0, err
	}

	if len(res.Item) == 0 {
		return nil, res.ConsumedCapacity.CapacityUnits, nil
	}

	return res.Item, res.ConsumedCapacity.CapacityUnits, nil
}

//////////////////////////////////////////////////////////////////////////////
//
// Query
//

type QueryRequest struct {
	TableName              string
	AttributesToGet        []string `json:",omitempty"`
	ScanIndexForward       bool
	ExclusiveStartKey      AttributeNameValue   `json:",omitempty"`
	KeyConditions          map[string]Condition `json:",omitempty"`
	IndexName              string               `json:",omitempty"`
	Limit                  *int                 `json:",omitempty"`
	Select                 string               `json:",omitempty"`
	ReturnConsumedCapacity string               `json:",omitempty"`

	table *TableInstance
}

type QueryResult struct {
	Items            []Item
	ConsumedCapacity ConsumedCapacityDescription
	LastEvaluatedKey AttributeNameValue
	Count            int
	ScannedCount     int
}

func QueryTable(table *TableInstance) *QueryRequest {
	return &QueryRequest{TableName: table.Name, ScanIndexForward: true, KeyConditions: make(map[string]Condition), table: table}
}

func Query(tableName string) *QueryRequest {
	return &QueryRequest{TableName: tableName, ScanIndexForward: true, KeyConditions: make(map[string]Condition)}
}

func (req *QueryRequest) WithAttributes(attributes []string) *QueryRequest {
	req.AttributesToGet = attributes
	return req
}

func (req *QueryRequest) WithStartKey(startKey AttributeNameValue) *QueryRequest {
	req.ExclusiveStartKey = startKey
	return req
}

func (req *QueryRequest) WithIndex(indexName string) *QueryRequest {
	req.IndexName = indexName
	return req
}

func (req *QueryRequest) WithCondition(attrName string, condition Condition) *QueryRequest {
	req.KeyConditions[attrName] = condition
	return req
}

func (req *QueryRequest) WithAttrCondition(cond AttrCondition) *QueryRequest {
	for k, v := range cond {
		req.KeyConditions[k] = v
	}

	return req
}

func (req *QueryRequest) WithLimit(limit int) *QueryRequest {
	req.Limit = &limit
	return req
}

func (req *QueryRequest) WithSelect(selectValue string) *QueryRequest {
	req.Select = selectValue
	return req
}

func (req *QueryRequest) WithConsumed(consumed bool) *QueryRequest {
	req.ReturnConsumedCapacity = RETURN_CONSUMED[consumed]
	return req
}

func (req *QueryRequest) Exec(db *DBClient) ([]Item, AttributeNameValue, float32, error) {
	if db == nil && req.table != nil {
		db = req.table.DB
	}

	var res QueryResult

	if err := db.Query("Query", req).Decode(&res); err != nil {
		return nil, nil, 0.0, err
	}

	return res.Items, res.LastEvaluatedKey, res.ConsumedCapacity.CapacityUnits, nil
}

//////////////////////////////////////////////////////////////////////////////
//
// Scan
//

type ScanRequest struct {
	TableName              string
	AttributesToGet        []string
	ExclusiveStartKey      AttributeNameValue
	ScanFilter             map[string]Condition
	Limit                  *int   `json:",omitempty"`
	Segment                *int   `json:",omitempty"`
	TotalSegments          *int   `json:",omitempty"`
	Select                 string `json:",omitempty"`
	ReturnConsumedCapacity string `json:",omitempty"`

	table *TableInstance
}

func ScanTable(table *TableInstance) *ScanRequest {
	return &ScanRequest{TableName: table.Name, table: table}
}

func Scan(tableName string) *ScanRequest {
	return &ScanRequest{TableName: tableName}
}

func (scanReq *ScanRequest) WithAttributes(attributes []string) *ScanRequest {
	scanReq.AttributesToGet = attributes
	return scanReq
}

func (scanReq *ScanRequest) WithStartKey(startKey AttributeNameValue) *ScanRequest {
	scanReq.ExclusiveStartKey = startKey
	return scanReq
}

func (scanReq *ScanRequest) WithFilter(attrName string, condition Condition) *ScanRequest {
	if scanReq.ScanFilter == nil {
		scanReq.ScanFilter = map[string]Condition{attrName: condition}
	} else {
		scanReq.ScanFilter[attrName] = condition
	}
	return scanReq
}

func (scanReq *ScanRequest) WithFilters(filters AttrCondition) *ScanRequest {
	scanReq.ScanFilter = filters
	return scanReq
}

func (scanReq *ScanRequest) WithLimit(limit int) *ScanRequest {
	scanReq.Limit = &limit
	return scanReq
}

func (scanReq *ScanRequest) WithSegment(segment, totalSegments int) *ScanRequest {
	scanReq.Segment = &segment
	scanReq.TotalSegments = &totalSegments
	return scanReq
}

func (scanReq *ScanRequest) WithSelect(selectValue string) *ScanRequest {
	scanReq.Select = selectValue
	return scanReq
}

func (scanReq *ScanRequest) WithConsumed(consumed bool) *ScanRequest {
	scanReq.ReturnConsumedCapacity = RETURN_CONSUMED[consumed]
	return scanReq
}

func (scanReq *ScanRequest) Exec(db *DBClient) ([]Item, AttributeNameValue, float32, error) {
	var scanRes QueryResult

	if err := db.Query("Scan", scanReq).Decode(&scanRes); err != nil {
		return nil, nil, 0.0, err
	}

	return scanRes.Items, scanRes.LastEvaluatedKey, scanRes.ConsumedCapacity.CapacityUnits, nil
}

func (scanReq *ScanRequest) Count(db *DBClient) (count int, scount int, consumed float32, err error) {
	return scanReq.CountWithDelay(db, 0)
}

func (scanReq *ScanRequest) CountWithDelay(db *DBClient, delay time.Duration) (count int, scount int, consumed float32, err error) {
	var scanRes QueryResult

	req := *scanReq
	req.Select = SELECT_COUNT

	for {
		scanRes.LastEvaluatedKey = nil

		if err = db.Query("Scan", &req).Decode(&scanRes); err != nil {
			break
		}

		count += scanRes.Count
		scount += scanRes.ScannedCount
		consumed += scanRes.ConsumedCapacity.CapacityUnits

		if scanRes.LastEvaluatedKey == nil {
			break
		}

		req.ExclusiveStartKey = make(AttributeNameValue)
		for k, v := range scanRes.LastEvaluatedKey {
			req.ExclusiveStartKey[k] = v
		}

		if delay > 0 {
			time.Sleep(delay)
		}
	}

	return
}
<|endoftext|>


<commit_before>package googlecompute

import (
	"fmt"

	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
)

// StepCheckExistingImage represents a Packer build step that checks if the
// target image already exists, and aborts immediately if so.
type StepCheckExistingImage int

// Run executes the Packer build step that checks if the image already exists.
func (s *StepCheckExistingImage) Run(state multistep.StateBag) multistep.StepAction {
	c := state.Get("config").(*Config)
	d := state.Get("driver").(Driver)
	ui := state.Get("ui").(packer.Ui)

	if !c.PackerForce {
		ui.Say("Checking image does not exist...")
		c.imageAlreadyExists = d.ImageExists(c.ImageName)
		if c.imageAlreadyExists {
			err := fmt.Errorf("Image %s already exists.\n"+
				"Use the force flag to delete it prior to building.", c.ImageName)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}
	}
	return multistep.ActionContinue
}

// Cleanup.
func (s *StepCheckExistingImage) Cleanup(state multistep.StateBag) {}
<commit_msg>Fix unreachable code<commit_after>package googlecompute

import (
	"fmt"

	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
)

// StepCheckExistingImage represents a Packer build step that checks if the
// target image already exists, and aborts immediately if so.
type StepCheckExistingImage int

// Run executes the Packer build step that checks if the image already exists.
func (s *StepCheckExistingImage) Run(state multistep.StateBag) multistep.StepAction {
	c := state.Get("config").(*Config)
	d := state.Get("driver").(Driver)
	ui := state.Get("ui").(packer.Ui)

	if !c.PackerForce {
		ui.Say("Checking image does not exist...")
	}

	c.imageAlreadyExists = d.ImageExists(c.ImageName)
	if !c.PackerForce && c.imageAlreadyExists {
		err := fmt.Errorf("Image %s already exists.\n"+
			"Use the force flag to delete it prior to building.", c.ImageName)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}
	return multistep.ActionContinue
}

// Cleanup.
func (s *StepCheckExistingImage) Cleanup(state multistep.StateBag) {}
<|endoftext|>


<commit_before>package aws

import (
	"testing"

	"fmt"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
)

func TestAccAWSSQSQueue_importBasic(t *testing.T) {
	resourceName := "aws_sqs_queue.queue-with-defaults"
	queueName := fmt.Sprintf("sqs-queue-%s", acctest.RandString(5))

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSSQSQueueDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSSQSConfigWithDefaults(queueName),
			},

			resource.TestStep{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
				//The name is never returned after the initial create of the queue.
				//It is part of the URL and can be split down if needed
				//ImportStateVerifyIgnore: []string{"name"},
			},
		},
	})
}
<commit_msg>provider/aws: Change the resource name expected as part of sqs queue import test<commit_after>package aws

import (
	"testing"

	"fmt"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
)

func TestAccAWSSQSQueue_importBasic(t *testing.T) {
	resourceName := "aws_sqs_queue.queue"
	queueName := fmt.Sprintf("sqs-queue-%s", acctest.RandString(5))

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSSQSQueueDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSSQSConfigWithDefaults(queueName),
			},

			resource.TestStep{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"strings"
	"time"

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

func resourceAwsSsmDocument() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSsmDocumentCreate,
		Read:   resourceAwsSsmDocumentRead,
		Update: resourceAwsSsmDocumentUpdate,
		Delete: resourceAwsSsmDocumentDelete,

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
			},
			"content": {
				Type:     schema.TypeString,
				Required: true,
			},
			"document_type": {
				Type:         schema.TypeString,
				Required:     true,
				ValidateFunc: validateAwsSSMDocumentType,
			},
			"created_date": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"default_version": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"description": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"hash": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"hash_type": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"latest_version": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"owner": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"status": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"platform_types": {
				Type:     schema.TypeList,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
			"parameter": {
				Type:     schema.TypeList,
				Computed: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"name": {
							Type:     schema.TypeString,
							Optional: true,
						},
						"default_value": {
							Type:     schema.TypeString,
							Optional: true,
						},
						"description": {
							Type:     schema.TypeString,
							Optional: true,
						},
						"type": {
							Type:     schema.TypeString,
							Optional: true,
						},
					},
				},
			},
			"permissions": {
				Type:     schema.TypeMap,
				Optional: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"type": {
							Type:     schema.TypeString,
							Required: true,
						},
						"account_ids": {
							Type:     schema.TypeString,
							Required: true,
						},
					},
				},
			},
		},
	}
}

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

	log.Printf("[INFO] Creating SSM Document: %s", d.Get("name").(string))

	docInput := &ssm.CreateDocumentInput{
		Name:         aws.String(d.Get("name").(string)),
		Content:      aws.String(d.Get("content").(string)),
		DocumentType: aws.String(d.Get("document_type").(string)),
	}

	log.Printf("[DEBUG] Waiting for SSM Document %q to be created", d.Get("name").(string))
	err := resource.Retry(5*time.Minute, func() *resource.RetryError {
		resp, err := ssmconn.CreateDocument(docInput)

		if err != nil {
			return resource.NonRetryableError(err)
		}

		d.SetId(*resp.DocumentDescription.Name)
		return nil
	})

	if err != nil {
		return errwrap.Wrapf("[ERROR] Error creating SSM document: {{err}}", err)
	}

	if v, ok := d.GetOk("permissions"); ok && v != nil {
		if err := setDocumentPermissions(d, meta); err != nil {
			return err
		}
	} else {
		log.Printf("[DEBUG] Not setting permissions for %q", d.Id())
	}

	return resourceAwsSsmDocumentRead(d, meta)
}

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

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

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

	resp, err := ssmconn.DescribeDocument(docInput)

	if err != nil {
		return errwrap.Wrapf("[ERROR] Error describing SSM document: {{err}}", err)
	}

	doc := resp.Document
	d.Set("created_date", doc.CreatedDate)
	d.Set("default_version", doc.DefaultVersion)
	d.Set("description", doc.Description)

	if _, ok := d.GetOk("document_type"); ok {
		d.Set("document_type", doc.DocumentType)
	}

	d.Set("document_version", doc.DocumentVersion)
	d.Set("hash", doc.Hash)
	d.Set("hash_type", doc.HashType)
	d.Set("latest_version", doc.LatestVersion)
	d.Set("name", doc.Name)
	d.Set("owner", doc.Owner)
	d.Set("platform_types", flattenStringList(doc.PlatformTypes))

	d.Set("status", doc.Status)

	gp, err := getDocumentPermissions(d, meta)

	if err != nil {
		return errwrap.Wrapf("[ERROR] Error reading SSM document permissions: {{err}}", err)
	}

	d.Set("permissions", gp)

	params := make([]map[string]interface{}, 0)
	for i := 0; i < len(doc.Parameters); i++ {

		dp := doc.Parameters[i]
		param := make(map[string]interface{})

		if dp.DefaultValue != nil {
			param["default_value"] = *dp.DefaultValue
		}
		param["description"] = *dp.Description
		param["name"] = *dp.Name
		param["type"] = *dp.Type
		params = append(params, param)
	}

	if len(params) == 0 {
		params = make([]map[string]interface{}, 1)
	}

	if err := d.Set("parameter", params); err != nil {
		return err
	}

	return nil
}

func resourceAwsSsmDocumentUpdate(d *schema.ResourceData, meta interface{}) error {

	if _, ok := d.GetOk("permissions"); ok {
		if err := setDocumentPermissions(d, meta); err != nil {
			return err
		}
	} else {
		log.Printf("[DEBUG] Not setting document permissions on %q", d.Id())
	}

	return resourceAwsSsmDocumentRead(d, meta)
}

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

	if err := deleteDocumentPermissions(d, meta); err != nil {
		return err
	}

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

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

	_, err := ssmconn.DeleteDocument(params)
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] Waiting for SSM Document %q to be deleted", d.Get("name").(string))
	err = resource.Retry(10*time.Minute, func() *resource.RetryError {
		_, err := ssmconn.DescribeDocument(&ssm.DescribeDocumentInput{
			Name: aws.String(d.Get("name").(string)),
		})

		if err != nil {
			awsErr, ok := err.(awserr.Error)
			if !ok {
				return resource.NonRetryableError(err)
			}

			if awsErr.Code() == "InvalidDocument" {
				return nil
			}

			return resource.NonRetryableError(err)
		}

		return resource.RetryableError(
			fmt.Errorf("%q: Timeout while waiting for the document to be deleted", d.Id()))
	})
	if err != nil {
		return err
	}

	d.SetId("")

	return nil
}

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

	log.Printf("[INFO] Setting permissions for document: %s", d.Id())
	permission := d.Get("permissions").(map[string]interface{})

	ids := aws.StringSlice([]string{permission["account_ids"].(string)})

	if strings.Contains(permission["account_ids"].(string), ",") {
		ids = aws.StringSlice(strings.Split(permission["account_ids"].(string), ","))
	}

	permInput := &ssm.ModifyDocumentPermissionInput{
		Name:            aws.String(d.Get("name").(string)),
		PermissionType:  aws.String(permission["type"].(string)),
		AccountIdsToAdd: ids,
	}

	_, err := ssmconn.ModifyDocumentPermission(permInput)

	if err != nil {
		return errwrap.Wrapf("[ERROR] Error setting permissions for SSM document: {{err}}", err)
	}

	return nil
}

func getDocumentPermissions(d *schema.ResourceData, meta interface{}) (map[string]interface{}, error) {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[INFO] Getting permissions for document: %s", d.Id())

	//How to get from nested scheme resource?
	permissionType := "Share"

	permInput := &ssm.DescribeDocumentPermissionInput{
		Name:           aws.String(d.Get("name").(string)),
		PermissionType: aws.String(permissionType),
	}

	resp, err := ssmconn.DescribeDocumentPermission(permInput)

	if err != nil {
		return nil, errwrap.Wrapf("[ERROR] Error setting permissions for SSM document: {{err}}", err)
	}

	var account_ids = make([]string, len(resp.AccountIds))
	for i := 0; i < len(resp.AccountIds); i++ {
		account_ids[i] = *resp.AccountIds[i]
	}

	var ids = ""
	if len(account_ids) == 1 {
		ids = account_ids[0]
	} else if len(account_ids) > 1 {
		ids = strings.Join(account_ids, ",")
	} else {
		ids = ""
	}

	if ids == "" {
		return nil, nil
	}

	perms := make(map[string]interface{})
	perms["type"] = permissionType
	perms["account_ids"] = ids

	return perms, nil
}

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

	log.Printf("[INFO] Removing permissions from document: %s", d.Id())

	permInput := &ssm.ModifyDocumentPermissionInput{
		Name:               aws.String(d.Get("name").(string)),
		PermissionType:     aws.String("Share"),
		AccountIdsToRemove: aws.StringSlice(strings.Split("all", ",")),
	}

	_, err := ssmconn.ModifyDocumentPermission(permInput)

	if err != nil {
		return errwrap.Wrapf("[ERROR] Error removing permissions for SSM document: {{err}}", err)
	}

	return nil
}

func validateAwsSSMDocumentType(v interface{}, k string) (ws []string, errors []error) {
	value := v.(string)
	types := map[string]bool{
		"Command":    true,
		"Policy":     true,
		"Automation": true,
	}

	if !types[value] {
		errors = append(errors, fmt.Errorf("CodeBuild: Arifacts Namespace Type can only be NONE / BUILD_ID"))
	}
	return
}
<commit_msg>provider/aws: Fix error message returned when an invalid SSM document type is provided. The original error appears to have been copied from the CodeBuild resource and provides incorrect information about valid types.<commit_after>package aws

import (
	"fmt"
	"log"
	"strings"
	"time"

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

func resourceAwsSsmDocument() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSsmDocumentCreate,
		Read:   resourceAwsSsmDocumentRead,
		Update: resourceAwsSsmDocumentUpdate,
		Delete: resourceAwsSsmDocumentDelete,

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
			},
			"content": {
				Type:     schema.TypeString,
				Required: true,
			},
			"document_type": {
				Type:         schema.TypeString,
				Required:     true,
				ValidateFunc: validateAwsSSMDocumentType,
			},
			"created_date": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"default_version": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"description": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"hash": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"hash_type": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"latest_version": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"owner": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"status": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"platform_types": {
				Type:     schema.TypeList,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
			"parameter": {
				Type:     schema.TypeList,
				Computed: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"name": {
							Type:     schema.TypeString,
							Optional: true,
						},
						"default_value": {
							Type:     schema.TypeString,
							Optional: true,
						},
						"description": {
							Type:     schema.TypeString,
							Optional: true,
						},
						"type": {
							Type:     schema.TypeString,
							Optional: true,
						},
					},
				},
			},
			"permissions": {
				Type:     schema.TypeMap,
				Optional: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"type": {
							Type:     schema.TypeString,
							Required: true,
						},
						"account_ids": {
							Type:     schema.TypeString,
							Required: true,
						},
					},
				},
			},
		},
	}
}

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

	log.Printf("[INFO] Creating SSM Document: %s", d.Get("name").(string))

	docInput := &ssm.CreateDocumentInput{
		Name:         aws.String(d.Get("name").(string)),
		Content:      aws.String(d.Get("content").(string)),
		DocumentType: aws.String(d.Get("document_type").(string)),
	}

	log.Printf("[DEBUG] Waiting for SSM Document %q to be created", d.Get("name").(string))
	err := resource.Retry(5*time.Minute, func() *resource.RetryError {
		resp, err := ssmconn.CreateDocument(docInput)

		if err != nil {
			return resource.NonRetryableError(err)
		}

		d.SetId(*resp.DocumentDescription.Name)
		return nil
	})

	if err != nil {
		return errwrap.Wrapf("[ERROR] Error creating SSM document: {{err}}", err)
	}

	if v, ok := d.GetOk("permissions"); ok && v != nil {
		if err := setDocumentPermissions(d, meta); err != nil {
			return err
		}
	} else {
		log.Printf("[DEBUG] Not setting permissions for %q", d.Id())
	}

	return resourceAwsSsmDocumentRead(d, meta)
}

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

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

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

	resp, err := ssmconn.DescribeDocument(docInput)

	if err != nil {
		return errwrap.Wrapf("[ERROR] Error describing SSM document: {{err}}", err)
	}

	doc := resp.Document
	d.Set("created_date", doc.CreatedDate)
	d.Set("default_version", doc.DefaultVersion)
	d.Set("description", doc.Description)

	if _, ok := d.GetOk("document_type"); ok {
		d.Set("document_type", doc.DocumentType)
	}

	d.Set("document_version", doc.DocumentVersion)
	d.Set("hash", doc.Hash)
	d.Set("hash_type", doc.HashType)
	d.Set("latest_version", doc.LatestVersion)
	d.Set("name", doc.Name)
	d.Set("owner", doc.Owner)
	d.Set("platform_types", flattenStringList(doc.PlatformTypes))

	d.Set("status", doc.Status)

	gp, err := getDocumentPermissions(d, meta)

	if err != nil {
		return errwrap.Wrapf("[ERROR] Error reading SSM document permissions: {{err}}", err)
	}

	d.Set("permissions", gp)

	params := make([]map[string]interface{}, 0)
	for i := 0; i < len(doc.Parameters); i++ {

		dp := doc.Parameters[i]
		param := make(map[string]interface{})

		if dp.DefaultValue != nil {
			param["default_value"] = *dp.DefaultValue
		}
		param["description"] = *dp.Description
		param["name"] = *dp.Name
		param["type"] = *dp.Type
		params = append(params, param)
	}

	if len(params) == 0 {
		params = make([]map[string]interface{}, 1)
	}

	if err := d.Set("parameter", params); err != nil {
		return err
	}

	return nil
}

func resourceAwsSsmDocumentUpdate(d *schema.ResourceData, meta interface{}) error {

	if _, ok := d.GetOk("permissions"); ok {
		if err := setDocumentPermissions(d, meta); err != nil {
			return err
		}
	} else {
		log.Printf("[DEBUG] Not setting document permissions on %q", d.Id())
	}

	return resourceAwsSsmDocumentRead(d, meta)
}

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

	if err := deleteDocumentPermissions(d, meta); err != nil {
		return err
	}

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

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

	_, err := ssmconn.DeleteDocument(params)
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] Waiting for SSM Document %q to be deleted", d.Get("name").(string))
	err = resource.Retry(10*time.Minute, func() *resource.RetryError {
		_, err := ssmconn.DescribeDocument(&ssm.DescribeDocumentInput{
			Name: aws.String(d.Get("name").(string)),
		})

		if err != nil {
			awsErr, ok := err.(awserr.Error)
			if !ok {
				return resource.NonRetryableError(err)
			}

			if awsErr.Code() == "InvalidDocument" {
				return nil
			}

			return resource.NonRetryableError(err)
		}

		return resource.RetryableError(
			fmt.Errorf("%q: Timeout while waiting for the document to be deleted", d.Id()))
	})
	if err != nil {
		return err
	}

	d.SetId("")

	return nil
}

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

	log.Printf("[INFO] Setting permissions for document: %s", d.Id())
	permission := d.Get("permissions").(map[string]interface{})

	ids := aws.StringSlice([]string{permission["account_ids"].(string)})

	if strings.Contains(permission["account_ids"].(string), ",") {
		ids = aws.StringSlice(strings.Split(permission["account_ids"].(string), ","))
	}

	permInput := &ssm.ModifyDocumentPermissionInput{
		Name:            aws.String(d.Get("name").(string)),
		PermissionType:  aws.String(permission["type"].(string)),
		AccountIdsToAdd: ids,
	}

	_, err := ssmconn.ModifyDocumentPermission(permInput)

	if err != nil {
		return errwrap.Wrapf("[ERROR] Error setting permissions for SSM document: {{err}}", err)
	}

	return nil
}

func getDocumentPermissions(d *schema.ResourceData, meta interface{}) (map[string]interface{}, error) {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[INFO] Getting permissions for document: %s", d.Id())

	//How to get from nested scheme resource?
	permissionType := "Share"

	permInput := &ssm.DescribeDocumentPermissionInput{
		Name:           aws.String(d.Get("name").(string)),
		PermissionType: aws.String(permissionType),
	}

	resp, err := ssmconn.DescribeDocumentPermission(permInput)

	if err != nil {
		return nil, errwrap.Wrapf("[ERROR] Error setting permissions for SSM document: {{err}}", err)
	}

	var account_ids = make([]string, len(resp.AccountIds))
	for i := 0; i < len(resp.AccountIds); i++ {
		account_ids[i] = *resp.AccountIds[i]
	}

	var ids = ""
	if len(account_ids) == 1 {
		ids = account_ids[0]
	} else if len(account_ids) > 1 {
		ids = strings.Join(account_ids, ",")
	} else {
		ids = ""
	}

	if ids == "" {
		return nil, nil
	}

	perms := make(map[string]interface{})
	perms["type"] = permissionType
	perms["account_ids"] = ids

	return perms, nil
}

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

	log.Printf("[INFO] Removing permissions from document: %s", d.Id())

	permInput := &ssm.ModifyDocumentPermissionInput{
		Name:               aws.String(d.Get("name").(string)),
		PermissionType:     aws.String("Share"),
		AccountIdsToRemove: aws.StringSlice(strings.Split("all", ",")),
	}

	_, err := ssmconn.ModifyDocumentPermission(permInput)

	if err != nil {
		return errwrap.Wrapf("[ERROR] Error removing permissions for SSM document: {{err}}", err)
	}

	return nil
}

func validateAwsSSMDocumentType(v interface{}, k string) (ws []string, errors []error) {
	value := v.(string)
	types := map[string]bool{
		"Command":    true,
		"Policy":     true,
		"Automation": true,
	}

	if !types[value] {
		errors = append(errors, fmt.Errorf("Document type %s is invalid. Valid types are Command, Policy or Automation", value))
	}
	return
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package features

import (
	"net/http"
	"os"

	"github.com/NYTimes/gziphandler"
	"github.com/golang/glog"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"

	"k8s.io/kubernetes/pkg/util/sets"
	"k8s.io/test-infra/mungegithub/github"
	"k8s.io/test-infra/mungegithub/mungeopts"
	"k8s.io/test-infra/mungegithub/options"
	"k8s.io/test-infra/mungegithub/sharedmux"
)

const (
	ServerFeatureName = "server"
)

// ServerFeature runs a server and allows mungers to register handlers for paths, or
// prometheus metrics.
type ServerFeature struct {
	*sharedmux.ConcurrentMux
	Enabled bool

	Address string
	WWWRoot string

	prometheus struct {
		loops      prometheus.Counter
		tokenUsage prometheus.Gauge
	}

	getTokenUsage func() (int, error)
}

func init() {
	s := &ServerFeature{}
	RegisterFeature(s)
}

// Name is just going to return the name mungers use to request this feature
func (s *ServerFeature) Name() string {
	return ServerFeatureName
}

// Initialize will initialize the feature.
func (s *ServerFeature) Initialize(config *github.Config) error {
	s.ConcurrentMux = sharedmux.NewConcurrentMux(http.DefaultServeMux)
	if len(s.Address) == 0 {
		return nil
	}
	if len(s.WWWRoot) > 0 {
		wwwStat, err := os.Stat(s.WWWRoot)
		if !os.IsNotExist(err) && wwwStat.IsDir() {
			s.ConcurrentMux.Handle("/", gziphandler.GzipHandler(http.FileServer(http.Dir(s.WWWRoot))))
		}
	}
	// config indicates that ServerFeature should be enabled.
	s.Enabled = true

	s.prometheus.loops = prometheus.NewCounter(prometheus.CounterOpts{
		Name:        "mungegithub_loops",
		Help:        "Number of loops performed by the munger",
		ConstLabels: map[string]string{"org": config.Org, "repo": config.Project, "app": mungeopts.App},
	})
	s.prometheus.tokenUsage = prometheus.NewGauge(prometheus.GaugeOpts{
		Name:        "github_token_usage",
		Help:        "Number of github tokens used by the munger",
		ConstLabels: map[string]string{"org": config.Org, "repo": config.Project, "app": mungeopts.App},
	})
	prometheus.MustRegister(s.prometheus.loops)
	prometheus.MustRegister(s.prometheus.tokenUsage)
	s.getTokenUsage = config.GetTokenUsage
	s.ConcurrentMux.Handle("/prometheus", promhttp.Handler())

	go http.ListenAndServe(s.Address, s.ConcurrentMux)
	return nil
}

// EachLoop is called at the start of every munge loop
func (s *ServerFeature) EachLoop() error {
	s.prometheus.loops.Inc()
	tokenUsage, err := s.getTokenUsage()
	if err != nil {
		glog.Warningf("Cannot get token usage: %v", err)
	} else {
		s.prometheus.tokenUsage.Set(float64(tokenUsage))
	}
	return nil
}

// RegisterOptions registers options for this feature; returns any that require a restart when changed.
func (s *ServerFeature) RegisterOptions(opts *options.Options) sets.String {
	opts.RegisterString(&s.Address, "address", ":8080", "The address to listen on for HTTP Status")
	opts.RegisterString(&s.WWWRoot, "www", "www", "Path to static web files to serve from the webserver")
	return sets.NewString("address", "www")
}
<commit_msg>Make server feature serve api usage analytics.<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package features

import (
	"net/http"
	"os"

	"github.com/NYTimes/gziphandler"
	"github.com/golang/glog"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"

	"k8s.io/kubernetes/pkg/util/sets"
	"k8s.io/test-infra/mungegithub/github"
	"k8s.io/test-infra/mungegithub/mungeopts"
	"k8s.io/test-infra/mungegithub/options"
	"k8s.io/test-infra/mungegithub/sharedmux"
)

const (
	ServerFeatureName = "server"
)

// ServerFeature runs a server and allows mungers to register handlers for paths, or
// prometheus metrics.
type ServerFeature struct {
	*sharedmux.ConcurrentMux
	Enabled bool

	Address string
	WWWRoot string

	prometheus struct {
		loops      prometheus.Counter
		tokenUsage prometheus.Gauge
	}

	getTokenUsage func() (int, error)
}

func init() {
	s := &ServerFeature{}
	RegisterFeature(s)
}

// Name is just going to return the name mungers use to request this feature
func (s *ServerFeature) Name() string {
	return ServerFeatureName
}

// Initialize will initialize the feature.
func (s *ServerFeature) Initialize(config *github.Config) error {
	config.ServeDebugStats("/stats")
	s.ConcurrentMux = sharedmux.NewConcurrentMux(http.DefaultServeMux)
	if len(s.Address) == 0 {
		return nil
	}
	if len(s.WWWRoot) > 0 {
		wwwStat, err := os.Stat(s.WWWRoot)
		if !os.IsNotExist(err) && wwwStat.IsDir() {
			s.ConcurrentMux.Handle("/", gziphandler.GzipHandler(http.FileServer(http.Dir(s.WWWRoot))))
		}
	}
	// config indicates that ServerFeature should be enabled.
	s.Enabled = true

	s.prometheus.loops = prometheus.NewCounter(prometheus.CounterOpts{
		Name:        "mungegithub_loops",
		Help:        "Number of loops performed by the munger",
		ConstLabels: map[string]string{"org": config.Org, "repo": config.Project, "app": mungeopts.App},
	})
	s.prometheus.tokenUsage = prometheus.NewGauge(prometheus.GaugeOpts{
		Name:        "github_token_usage",
		Help:        "Number of github tokens used by the munger",
		ConstLabels: map[string]string{"org": config.Org, "repo": config.Project, "app": mungeopts.App},
	})
	prometheus.MustRegister(s.prometheus.loops)
	prometheus.MustRegister(s.prometheus.tokenUsage)
	s.getTokenUsage = config.GetTokenUsage
	s.ConcurrentMux.Handle("/prometheus", promhttp.Handler())

	go http.ListenAndServe(s.Address, s.ConcurrentMux)
	return nil
}

// EachLoop is called at the start of every munge loop
func (s *ServerFeature) EachLoop() error {
	s.prometheus.loops.Inc()
	tokenUsage, err := s.getTokenUsage()
	if err != nil {
		glog.Warningf("Cannot get token usage: %v", err)
	} else {
		s.prometheus.tokenUsage.Set(float64(tokenUsage))
	}
	return nil
}

// RegisterOptions registers options for this feature; returns any that require a restart when changed.
func (s *ServerFeature) RegisterOptions(opts *options.Options) sets.String {
	opts.RegisterString(&s.Address, "address", ":8080", "The address to listen on for HTTP Status")
	opts.RegisterString(&s.WWWRoot, "www", "www", "Path to static web files to serve from the webserver")
	return sets.NewString("address", "www")
}
<|endoftext|>


<commit_before>package jira

import (
	"github.com/coryb/oreo"
	logging "gopkg.in/op/go-logging.v1"
)

var log = logging.MustGetLogger("jira")

const VERSION = "1.0.3"

type Jira struct {
	Endpoint string     `json:"endpoint,omitempty" yaml:"endpoint,omitempty"`
	UA       HttpClient `json:"-" yaml:"-"`
}

func NewJira(endpoint string) *Jira {
	return &Jira{
		Endpoint: endpoint,
		UA:       oreo.New(),
	}
}
<commit_msg>version bump<commit_after>package jira

import (
	"github.com/coryb/oreo"
	logging "gopkg.in/op/go-logging.v1"
)

var log = logging.MustGetLogger("jira")

const VERSION = "1.0.4"

type Jira struct {
	Endpoint string     `json:"endpoint,omitempty" yaml:"endpoint,omitempty"`
	UA       HttpClient `json:"-" yaml:"-"`
}

func NewJira(endpoint string) *Jira {
	return &Jira{
		Endpoint: endpoint,
		UA:       oreo.New(),
	}
}
<|endoftext|>


<commit_before>package gong

import (
	"errors"
	"fmt"
	"regexp"
	"strings"

	"github.com/andygrunwald/go-jira"
)

// JiraClient : Struct implementing the generic Client interface
type JiraClient struct {
	client *jira.Client
	config map[string]string
}

func (j *JiraClient) Create() (string, error) {
	domain, err := j.GetDomain()

	if err != nil {
		return "", err
	}

	return domain, nil
}

// NewJiraClient : Returns a pointer to JiraClient
func NewJiraClient() *JiraClient {
	return &JiraClient{}
}

// GetName : Return the string name of the struct eg: jira
func (j *JiraClient) GetName() string {
	return "jira"
}

// PrepareCommitMessage : Returns a string with the issue id in the link
func (j *JiraClient) PrepareCommitMessage(branchName, commitMessage string) string {
	issueID := GetIssueID(branchName)
	url, err := j.Browse(branchName)

	if err != nil {
		return commitMessage
	}

	patchedCommitMessage := fmt.Sprintf(`[%s](%s)`, issueID, url)

	return patchedCommitMessage
}

// GetIssueID : returns the issue id from a branch name
func GetIssueID(branchName string) string {
	re := regexp.MustCompile(`([A-Z]+-[\d]+)`)
	matches := re.FindAllString(branchName, -1)

	if len(matches) == 0 {
		return ""
	}

	return matches[0]
}

// Comment : Post a comment on a jira issue
func (j *JiraClient) Comment(branchName, comment string) error {
	fmt.Println(branchName)
	issueID := GetIssueID(branchName)
	fmt.Println(issueID)

	jiraComment := &jira.Comment{
		Body: comment,
	}
	_, _, err := j.client.Issue.AddComment(issueID, jiraComment)

	return err
}

func (j *JiraClient) GetDomain() (string, error) {

	domain, ok := j.config["domain"]

	if !ok {
		return "", errors.New("Could not locate domain in config")
	}

	return domain, nil
}

// Browse : Browse to the URL of the issue related to the branch name
func (j *JiraClient) Browse(branchName string) (string, error) {
	issueID := GetIssueID(branchName)

	domain, err := j.GetDomain()

	if err != nil {
		return "", err
	}

	if issueID == "" {
		return "", errors.New("Could not find issue id in the branch name")
	}

	url := fmt.Sprintf("%s/browse/%s", domain, issueID)

	return url, nil
}

// GetBranchName : Return the branch name from the issue id and issue type
func (j *JiraClient) GetBranchName(issueType string, issueID string) (string, error) {
	issue, _, err := j.client.Issue.Get(issueID, nil)

	if err != nil {
		return "", err
	}

	issueTitleSlug := SlugifyTitle(issue.Fields.Summary)
	return fmt.Sprintf("%s/%s-%s", issueType, issueID, issueTitleSlug), nil
}

func indexOf(status string, data []string) int {
	for k, v := range data {
		if status == v {
			return k
		}
	}
	return -1
}

// Start : Start an issue
func (j *JiraClient) Start(issueType string, issueID string) (string, error) {
	allowed := strings.Split(j.config["transitions"], ",")

	fmt.Println(issueID)

	transitions, response, err := j.client.Issue.GetTransitions(issueID)
	fmt.Println(transitions)

	if err != nil {
		fmt.Println(err)
		fmt.Println(response.Body)
		return "", err
	}

	nextTransition := transitions[0]

	if indexOf(nextTransition.Name, allowed) > -1 {
		_, err := j.client.Issue.DoTransition(issueID, nextTransition.ID)

		if err != nil {
			return "", err
		}

		_, _ = j.Start(issueType, issueID)
	}

	branchName, err := j.GetBranchName(issueType, issueID)

	if err != nil {
		return "", err
	}

	return branchName, nil
}

// FormatField : Returns a formatted field based on internal rules
func (j *JiraClient) FormatField(fieldName string, value string) string {
	if fieldName == "domain" {
		return fmt.Sprintf("https://%s", value)
	}

	return value
}

// GetAuthFields : Get a map of auth fields
func (j *JiraClient) GetAuthFields() map[string]bool {
	return map[string]bool{
		"username":       true,
		"domain":         true,
		"password":       true,
		"project_prefix": false,
		"transitions":    false,
	}
}

// Authenticate : Authenticates using the fields passed in
func (j *JiraClient) Authenticate(fields map[string]string) bool {
	jiraClient, err := jira.NewClient(nil, fields["domain"])

	if err != nil {
		return false
	}

	res, err := jiraClient.Authentication.AcquireSessionCookie(fields["username"], fields["password"])

	if err != nil || res == false {
		return false
	}

	j.client = jiraClient
	j.config = fields

	return true
}
<commit_msg>jira reformatting<commit_after>package gong

import (
	"errors"
	"fmt"
	"regexp"
	"strings"

	"github.com/andygrunwald/go-jira"
)

// JiraClient : Struct implementing the generic Client interface
type JiraClient struct {
	client *jira.Client
	config map[string]string
}

func (j *JiraClient) Create() (string, error) {
	domain, err := j.GetDomain()

	if err != nil {
		return "", err
	}

	return domain, nil
}

// NewJiraClient : Returns a pointer to JiraClient
func NewJiraClient() *JiraClient {
	return &JiraClient{}
}

// GetName : Return the string name of the struct eg: jira
func (j *JiraClient) GetName() string {
	return "jira"
}

// PrepareCommitMessage : Returns a string with the issue id in the link
func (j *JiraClient) PrepareCommitMessage(branchName, commitMessage string) string {
	issueID := GetIssueID(branchName)
	url, err := j.Browse(branchName)

	if err != nil {
		return commitMessage
	}

	patchedCommitMessage := fmt.Sprintf(`[%s](%s)`, issueID, url)

	return patchedCommitMessage
}

// GetIssueID : returns the issue id from a branch name
func GetIssueID(branchName string) string {
	re := regexp.MustCompile(`([A-Z]+-[\d]+)`)
	matches := re.FindAllString(branchName, -1)

	if len(matches) == 0 {
		return ""
	}

	return matches[0]
}

// Comment : Post a comment on a jira issue
func (j *JiraClient) Comment(branchName, comment string) error {
	fmt.Println(branchName)
	issueID := GetIssueID(branchName)
	fmt.Println(issueID)

	jiraComment := &jira.Comment{
		Body: comment,
	}
	_, _, err := j.client.Issue.AddComment(issueID, jiraComment)

	return err
}

func (j *JiraClient) GetDomain() (string, error) {

	domain, ok := j.config["domain"]

	if !ok {
		return "", errors.New("Could not locate domain in config")
	}

	return domain, nil
}

// Browse : Browse to the URL of the issue related to the branch name
func (j *JiraClient) Browse(branchName string) (string, error) {
	issueID := GetIssueID(branchName)

	domain, err := j.GetDomain()

	if err != nil {
		return "", err
	}

	if issueID == "" {
		return "", errors.New("Could not find issue id in the branch name")
	}

	url := fmt.Sprintf("%s/browse/%s", domain, issueID)

	return url, nil
}

// GetBranchName : Return the branch name from the issue id and issue type
func (j *JiraClient) GetBranchName(issueType string, issueID string) (string, error) {
	issue, _, err := j.client.Issue.Get(issueID, nil)

	if err != nil {
		return "", err
	}

	issueTitleSlug := SlugifyTitle(issue.Fields.Summary)
	return fmt.Sprintf("%s/%s-%s", issueType, issueID, issueTitleSlug), nil
}

func indexOf(status string, data []string) int {
	for k, v := range data {
		if status == v {
			return k
		}
	}
	return -1
}

// Start : Start an issue
func (j *JiraClient) Start(issueType string, issueID string) (string, error) {
	allowed := strings.Split(j.config["transitions"], ",")

	fmt.Println(issueID)

	transitions, response, err := j.client.Issue.GetTransitions(issueID)
	fmt.Println(transitions)

	if err != nil {
		fmt.Println(err)
		fmt.Println(response.Body)
		return "", err
	}

	nextTransition := transitions[0]

	if indexOf(nextTransition.Name, allowed) > -1 {
		_, err := j.client.Issue.DoTransition(issueID, nextTransition.ID)

		if err != nil {
			return "", err
		}

		_, _ = j.Start(issueType, issueID)
	}

	branchName, err := j.GetBranchName(issueType, issueID)

	if err != nil {
		return "", err
	}

	return branchName, nil
}

// FormatField : Returns a formatted field based on internal rules
func (j *JiraClient) FormatField(fieldName string, value string) string {
	if fieldName == "domain" {
		return fmt.Sprintf("https://%s", value)
	}

	return value
}

// GetAuthFields : Get a map of auth fields
func (j *JiraClient) GetAuthFields() map[string]bool {
	return map[string]bool{
		"username":       false,
		"domain":         false,
		"password":       true,
		"project_prefix": false,
		"transitions":    false,
	}
}

// Authenticate : Authenticates using the fields passed in
func (j *JiraClient) Authenticate(fields map[string]string) bool {
	jiraClient, err := jira.NewClient(nil, fields["domain"])

	if err != nil {
		return false
	}

	res, err := jiraClient.Authentication.AcquireSessionCookie(fields["username"], fields["password"])

	if err != nil || res == false {
		return false
	}

	j.client = jiraClient
	j.config = fields

	return true
}
<|endoftext|>


<commit_before>package jval

import (
	"github.com/thwd/jsem"
	"regexp"
	"strconv"
)

type Error struct {
	Label   string
	Field   string
	Context interface{}
}

// Error implements native "error" interface
func (e Error) Error() string {
	return e.Label
}

var noErrors = []Error{}

type Validator interface {
	Validate(value *jsem.Value, field string) []Error
}

type Lambda func(v *jsem.Value, f string) []Error

func (l Lambda) Validate(v *jsem.Value, f string) []Error {
	return l(v, f)
}

var Anything Validator = Lambda(func(v *jsem.Value, f string) []Error {
	return noErrors
})

var String Validator = Lambda(func(v *jsem.Value, f string) []Error {
	if v.IsString() {
		return noErrors
	}
	return []Error{Error{"value_must_be_string", f, nil}}
})

var Number Validator = Lambda(func(v *jsem.Value, f string) []Error {
	if v.IsNumber() {
		return noErrors
	}
	return []Error{Error{"value_must_be_number", f, nil}}
})

var Boolean Validator = Lambda(func(v *jsem.Value, f string) []Error {
	if v.IsBoolean() {
		return noErrors
	}
	return []Error{Error{"value_must_be_boolean", f, nil}}
})

var Null Validator = Lambda(func(v *jsem.Value, f string) []Error {
	if v.IsNull() {
		return noErrors
	}
	return []Error{Error{"value_must_be_null", f, nil}}
})

var NotNull Validator = Lambda(func(v *jsem.Value, f string) []Error {
	if v.IsNull() {
		return []Error{Error{"value_must_not_be_null", f, nil}}
	}
	return noErrors
})

func And(vs ...Validator) Validator {
	return Lambda(func(v *jsem.Value, f string) []Error {
		for _, a := range vs {
			es := a.Validate(v, f)
			if len(es) != 0 {
				return es
			}
		}
		return noErrors
	})
}

func Or(vs ...Validator) Validator {
	return Lambda(func(v *jsem.Value, f string) []Error {
		ae := make([]Error, 0, len(vs))
		for _, a := range vs {
			es := a.Validate(v, f)
			if len(es) == 0 {
				return noErrors
			}
			ae = append(ae, es...)
		}
		return ae // TODO(thwd): merge errors into one?
	})
}

func Object(d map[string]Validator) Validator {
	return Lambda(func(v *jsem.Value, f string) []Error {
		if !v.IsObject() {
			return []Error{Error{"value_must_be_object", f, nil}}
		}
		ae := make([]Error, 0, len(d))
		v.ObjectForEach(func(k string, u *jsem.Value) {
			if _, ok := d[k]; !ok {
				ae = append(ae, Error{"unexpected_object_key", f + "." + k, nil})
			}
		})
		for k, a := range d {
			u, _ := v.ObjectKey(k)
			ae = append(ae, a.Validate(u, f+"."+k)...)
		}
		return ae
	})
}

func Array(e Validator) Validator {
	return Lambda(func(v *jsem.Value, f string) []Error {
		if !v.IsArray() {
			return []Error{Error{"value_must_be_array", f, nil}}
		}
		ae := make([]Error, 0, 8)
		v.ArrayForEach(func(i int, u *jsem.Value) {
			ae = append(ae, e.Validate(u, f+"."+strconv.Itoa(i))...)
		})
		return ae
	})
}

func Regex(r *regexp.Regexp) Validator {
	return And(String, Lambda(func(v *jsem.Value, f string) []Error {
		s, _ := v.String()
		if r.Match([]byte(s)) {
			return noErrors
		}
		return []Error{
			Error{"value_must_match_regex", f, map[string]string{"regex": r.String()}},
		}
	}))
}

func Optional(a Validator) Validator {
	return Lambda(func(v *jsem.Value, f string) []Error {
		if v == nil {
			return noErrors
		}
		return a.Validate(v, f)
	})
}

// func Relabel(l string, a Validator) Validator {
// 	return Lambda(func(v *jsem.Value, f string) []Error {
// 		es := a.Validate(v, f)
// 		for i, e := range es {
// 			e
// 		}
// 	})
// }

func LengthBetween(x, y int) Validator {
	if y < x {
		panic("LengthBetween: y < x")
	}
	return And(Or(String, Array(Anything)), Lambda(func(v *jsem.Value, f string) []Error {
		l := -1
		if v.IsArray() {
			l, _ = v.ArrayLength()
		} else {
			l, _ = v.StringLength()
		}
		if l < x || l > y {
			return []Error{
				Error{"value_must_have_length_between", f, map[string]int{"min": x, "max": y}},
			}
		}
		return noErrors
	}))
}

func Length(x int) Validator {
	return LengthBetween(x, x)
}

func NumberBetween(x, y float64) Validator {
	if y < x {
		panic("NumberBetween: y < x")
	}
	return And(Number, Lambda(func(v *jsem.Value, f string) []Error {
		l, _ := v.Float64()
		if l < x || l > y {
			return []Error{
				Error{"value_must_have_value_between", f, map[string]float64{"min": x, "max": y}},
			}
		}
		return noErrors
	}))
}

func Exactly(j *jsem.Value) Validator {
	return Lambda(func(v *jsem.Value, f string) []Error {
		if !v.Equals(j) {
			return []Error{Error{"value_not_matched_exactly", f, nil}}
		}
		return noErrors
	})
}
<commit_msg>extended interface to implement json.Marshaler, split types, implemented json.Marshaler for every type<commit_after>package jval

import (
	"bytes"
	"encoding/json"
	"github.com/thwd/jsem"
	"regexp"
	"strconv"
	"sync"
)

type Error struct {
	Label   string
	Field   string
	Context interface{}
}

// Error implements native "error" interface
func (e Error) Error() string {
	return e.Label
}

var noErrors = []Error{}

type Validator interface {
	Validate(value *jsem.Value, field string) []Error
	json.Marshaler
}

type lambda func(v *jsem.Value, f string) []Error

func (l lambda) Validate(v *jsem.Value, f string) []Error {
	return l(v, f)
}

func (l lambda) MarshalJSON() ([]byte, error) {
	return []byte(``), nil // dummy, never exposed
}

type AnythingValidator struct{}

func Anything() Validator {
	return AnythingValidator{}
}

func (a AnythingValidator) Validate(v *jsem.Value, f string) []Error {
	return noErrors
}

func (a AnythingValidator) MarshalJSON() ([]byte, error) {
	return []byte(`["anything",[]]`), nil
}

type StringValidator struct{}

func String() Validator {
	return StringValidator{}
}

func (a StringValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsString() {
		return noErrors
	}
	return []Error{Error{"value_must_be_string", f, nil}}
}

func (a StringValidator) MarshalJSON() ([]byte, error) {
	return []byte(`["string",[]]`), nil
}

type NumberValidator struct{}

func Number() Validator {
	return NumberValidator{}
}

func (a NumberValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsNumber() {
		return noErrors
	}
	return []Error{Error{"value_must_be_number", f, nil}}
}

func (a NumberValidator) MarshalJSON() ([]byte, error) {
	return []byte(`["number",[]]`), nil
}

type BooleanValidator struct{}

func Boolean() Validator {
	return BooleanValidator{}
}

func (a BooleanValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsBoolean() {
		return noErrors
	}
	return []Error{Error{"value_must_be_boolean", f, nil}}
}

func (a BooleanValidator) MarshalJSON() ([]byte, error) {
	return []byte(`["boolean",[]]`), nil
}

type NullValidator struct{}

func Null() Validator {
	return NullValidator{}
}

func (a NullValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsNull() {
		return noErrors
	}
	return []Error{Error{"value_must_be_null", f, nil}}
}

func (a NullValidator) MarshalJSON() ([]byte, error) {
	return []byte(`["null",[]]`), nil
}

type NotNullValidator struct{}

func NotNull() Validator {
	return NotNullValidator{}
}

func (a NotNullValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsNull() {
		return []Error{Error{"value_must_not_be_null", f, nil}}
	}
	return noErrors
}

func (a NotNullValidator) MarshalJSON() ([]byte, error) {
	return []byte(`["notNull",[]]`), nil
}

type AndValidator []Validator

func And(vs ...Validator) Validator {
	return AndValidator(vs)
}

func (a AndValidator) Validate(v *jsem.Value, f string) []Error {
	for _, b := range a {
		es := b.Validate(v, f)
		if len(es) != 0 {
			return es
		}
	}
	return noErrors
}

func (a AndValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["and",[`)
	for i, l := 0, len(a); i < l; i++ {
		bs, _ := a[i].MarshalJSON()
		bf.Write(bs)
		if i < (l - 1) {
			bf.WriteString(`,`)
		}
	}
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

type TupleValidator []Validator

func Tuple(vs ...Validator) Validator {
	return TupleValidator(vs)
}

func (a TupleValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Array(Anything()), Length(len(a)), lambda(func(v *jsem.Value, f string) []Error {
		for i, b := range a {
			u, _ := v.ArrayIndex(i)
			es := b.Validate(u, f+"."+strconv.Itoa(i))
			if len(es) != 0 {
				return es
			}
		}
		return noErrors
	})).Validate(v, f)
}

func (a TupleValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["tuple",[`)
	for i, l := 0, len(a); i < l; i++ {
		bs, _ := a[i].MarshalJSON()
		bf.Write(bs)
		if i < (l - 1) {
			bf.WriteString(`,`)
		}
	}
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

type OrValidator []Validator

func Or(vs ...Validator) Validator {
	return OrValidator(vs)
}

func (b OrValidator) Validate(v *jsem.Value, f string) []Error {
	ae := make([]Error, 0, len(b))
	for _, a := range b {
		es := a.Validate(v, f)
		if len(es) == 0 {
			return noErrors
		}
		ae = append(ae, es...)
	}
	return ae // TODO(thwd): merge errors into one?
}

func (a OrValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["or",[`)
	for i, l := 0, len(a); i < l; i++ {
		bs, _ := a[i].MarshalJSON()
		bf.Write(bs)
		if i < (l - 1) {
			bf.WriteString(`,`)
		}
	}
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

type ObjectValidator map[string]Validator

func Object(d map[string]Validator) Validator {
	return ObjectValidator(d)
}

func (d ObjectValidator) Validate(v *jsem.Value, f string) []Error {
	if !v.IsObject() {
		return []Error{Error{"value_must_be_object", f, nil}}
	}
	ae := make([]Error, 0, len(d))
	v.ObjectForEach(func(k string, u *jsem.Value) {
		if _, ok := d[k]; !ok {
			ae = append(ae, Error{"unexpected_object_key", f + "." + k, nil})
		}
	})
	for k, a := range d {
		u, _ := v.ObjectKey(k)
		ae = append(ae, a.Validate(u, f+"."+k)...)
	}
	return ae
}

func (a ObjectValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["object",[`)
	json.NewEncoder(bf).Encode(map[string]Validator(a)) // TODO(thwd): TEST THIS!
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

type MapValidator struct {
	e Validator
}

func Map(e Validator) Validator {
	return MapValidator{e}
}

func (a MapValidator) Validate(v *jsem.Value, f string) []Error {
	if !v.IsObject() {
		return []Error{Error{"value_must_be_object", f, nil}}
	}
	ae := make([]Error, 0, 8)
	v.ObjectForEach(func(k string, u *jsem.Value) {
		ae = append(ae, a.e.Validate(u, f+"."+k)...)
	})
	return ae
}

func (a MapValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["map",[`)
	bs, _ := a.e.MarshalJSON()
	bf.Write(bs)
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

type ArrayValidator struct {
	e Validator
}

func Array(e Validator) Validator {
	return ArrayValidator{e}
}

func (a ArrayValidator) Validate(v *jsem.Value, f string) []Error {
	if !v.IsArray() {
		return []Error{Error{"value_must_be_array", f, nil}}
	}
	ae := make([]Error, 0, 8)
	v.ArrayForEach(func(i int, u *jsem.Value) {
		ae = append(ae, a.e.Validate(u, f+"."+strconv.Itoa(i))...)
	})
	return ae
}

func (a ArrayValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["array",[`)
	bs, _ := a.e.MarshalJSON()
	bf.Write(bs)
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

type RegexValidator struct {
	r *regexp.Regexp
}

func Regex(r *regexp.Regexp) Validator {
	return RegexValidator{r}
}

func (a RegexValidator) Validate(v *jsem.Value, f string) []Error {
	return And(String(), lambda(func(v *jsem.Value, f string) []Error {
		s, _ := v.String()
		if a.r.Match([]byte(s)) {
			return noErrors
		}
		return []Error{
			Error{"value_must_match_regex", f, map[string]string{"regex": a.r.String()}},
		}
	})).Validate(v, f)
}

func (a RegexValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["regex",[`)
	json.NewEncoder(bf).Encode(a.r.String())
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

type OptionalValidator struct {
	e Validator
}

func Optional(e Validator) Validator {
	return OptionalValidator{e}
}

func (a OptionalValidator) Validate(v *jsem.Value, f string) []Error {
	if v == nil {
		return noErrors
	}
	return a.e.Validate(v, f)
}

func (a OptionalValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["optional",[`)
	bs, _ := a.e.MarshalJSON()
	bf.Write(bs)
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

type LengthBetweenValidator struct {
	x, y int
}

func LengthBetween(x, y int) Validator {
	if y < x {
		panic("LengthBetween: y < x")
	}
	return LengthBetweenValidator{x, y}
}

func (a LengthBetweenValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Or(String(), Array(Anything())), lambda(func(v *jsem.Value, f string) []Error {
		l := -1
		if v.IsArray() {
			l, _ = v.ArrayLength()
		} else {
			l, _ = v.StringLength()
		}
		if l < a.x || l > a.y {
			return []Error{
				Error{"value_must_have_length_between", f, map[string]int{"min": a.x, "max": a.y}},
			}
		}
		return noErrors
	})).Validate(v, f)
}

func (a LengthBetweenValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["lengthBetween",[`)
	json.NewEncoder(bf).Encode(a.x)
	bf.WriteString(`,`)
	json.NewEncoder(bf).Encode(a.y)
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

func Length(x int) Validator {
	return LengthBetween(x, x)
}

type NumberBetweenValidator struct {
	x, y float64
}

func NumberBetween(x, y float64) Validator {
	if y < x {
		panic("NumberBetween: y < x")
	}
	return NumberBetweenValidator{x, y}
}

func (a NumberBetweenValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Number(), lambda(func(v *jsem.Value, f string) []Error {
		l, _ := v.Float64()
		if l < a.x || l > a.y {
			return []Error{
				Error{"value_must_have_value_between", f, map[string]float64{"min": a.x, "max": a.y}},
			}
		}
		return noErrors
	})).Validate(v, f)
}

func (a NumberBetweenValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["numberBetween",[`)
	json.NewEncoder(bf).Encode(a.x)
	bf.WriteString(`,`)
	json.NewEncoder(bf).Encode(a.y)
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

type ExactlyValidator struct {
	j *jsem.Value
}

func Exactly(j *jsem.Value) Validator {
	return ExactlyValidator{j}
}

func (a ExactlyValidator) Validate(v *jsem.Value, f string) []Error {
	if !v.Equals(a.j) {
		return []Error{Error{"value_not_matched_exactly", f, nil}}
	}
	return noErrors
}

func (a ExactlyValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["exactly",[`)
	bs, _ := a.j.MarshalJSON()
	bf.Write(bs)
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

// here be dragons! use only if you know exactly what you're doing

type RecursiveValidator struct {
	v Validator
}

func (r *RecursiveValidator) Validate(v *jsem.Value, f string) []Error {
	return r.v.Validate(v, f)
}

func (r *RecursiveValidator) Define(v Validator) {
	r.v = v
}

var recursionMap = struct {
	m map[*RecursiveValidator]struct{}
	l *sync.Mutex
}{
	make(map[*RecursiveValidator]struct{}),
	&sync.Mutex{},
}

func (a *RecursiveValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	recursionMap.l.Lock()
	_, k := recursionMap.m[a]
	if !k {
		recursionMap.m[a] = struct{}{}
		recursionMap.l.Unlock()
		bf := bytes.NewBuffer(nil)
		bf.WriteString(`["recursion",[`)
		bs, _ := a.v.MarshalJSON()
		bf.Write(bs)
		bf.WriteString(`]]`)
		recursionMap.l.Lock()
		delete(recursionMap.m, a)
		recursionMap.l.Unlock()
		return bf.Bytes(), nil
	}
	recursionMap.l.Unlock()
	bf.WriteString(`["recurse",[]]`)
	return bf.Bytes(), nil
}
<|endoftext|>


<commit_before>// Copyright 2014 Rob Pike. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Kana accepts, as argument text or as standard input, Japanese
UTF-8 text. It copies the text to standard output, converting
katakana and hiragana to romaji, leaving the rest (including
kanji) unmodified.
*/
package main // import "robpike.io/cmd/kana"

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"unicode/utf8"
)

func main() {
	if len(os.Args) == 1 {
		data, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			log.Fatal(err)
		}
		do(string(data))
		return
	}
	do(strings.Join(os.Args[1:], " "))
}

func do(s string) {
	prevKana := true
	skip := false
	for i, r := range s {
		k, ok := kana[r]
		if skip {
			skip = false
			continue
		}
		if !ok {
			if prevKana {
				fmt.Printf(" ")
			}
			fmt.Printf("%c", r)
			prevKana = false
			continue
		}
		if !prevKana {
			fmt.Printf(" ")
		}
		prevKana = true
		// Is there a modifier?
		r2, _ := utf8.DecodeRuneInString(s[i+utf8.RuneLen(r):])
		if small[r2] {
			skip = true
			k2, ok := mod[r2]
			if ok {
				fmt.Printf("%s%s", k[:len(k)-1], k2[1:])
				continue
			}
			k2, ok = vowel[r2]
			if ok {
				fmt.Printf("%s-", k)
				continue
			}
			// Otherwise it's just odd.
			fmt.Printf("<%s.%s>", k, k2)
			continue
		}
		fmt.Printf("%s", k)
	}
	fmt.Print("\n")
}

var kana = map[rune]string{
	'あ': "a",
	'い': "i",
	'う': "u",
	'え': "e",
	'お': "o",
	'か': "ka",
	'が': "ga",
	'き': "ki",
	'ぎ': "gi",
	'く': "ku",
	'ぐ': "gu",
	'け': "ke",
	'げ': "ge",
	'こ': "ko",
	'ご': "go",
	'さ': "sa",
	'ざ': "za",
	'し': "shi",
	'じ': "zi",
	'す': "su",
	'ず': "zu",
	'せ': "se",
	'ぜ': "ze",
	'そ': "so",
	'ぞ': "zo",
	'た': "ta",
	'だ': "da",
	'ち': "chi",
	'ぢ': "di",
	'つ': "tsu",
	'づ': "du",
	'て': "te",
	'で': "de",
	'と': "to",
	'ど': "do",
	'な': "na",
	'に': "ni",
	'ぬ': "nu",
	'ね': "ne",
	'の': "no",
	'は': "ha",
	'ば': "va",
	'ぱ': "pa",
	'ひ': "hi",
	'び': "vi",
	'ぴ': "pi",
	'ふ': "fu",
	'ぶ': "vu",
	'ぷ': "pu",
	'へ': "he",
	'べ': "ve",
	'ぺ': "pe",
	'ほ': "ho",
	'ぼ': "vo",
	'ぽ': "po",
	'ま': "ma",
	'み': "mi",
	'む': "mu",
	'め': "me",
	'も': "mo",
	'や': "ya",
	'ゆ': "yu",
	'よ': "yo",
	'ら': "ra",
	'り': "ri",
	'る': "ru",
	'れ': "re",
	'ろ': "ro",
	'わ': "wa",
	'ゐ': "wi",
	'ゑ': "we",
	'を': "wo",
	'ん': "n",
	'ゔ': "vu",
	'ア': "a",
	'イ': "i",
	'ウ': "u",
	'エ': "e",
	'オ': "o",
	'カ': "ka",
	'ガ': "ga",
	'キ': "ki",
	'ギ': "gi",
	'ク': "ku",
	'グ': "gu",
	'ケ': "ke",
	'ゲ': "ge",
	'コ': "ko",
	'ゴ': "go",
	'サ': "sa",
	'ザ': "za",
	'シ': "shi",
	'ジ': "zi",
	'ス': "su",
	'ズ': "zu",
	'セ': "se",
	'ゼ': "ze",
	'ソ': "so",
	'ゾ': "zo",
	'タ': "ta",
	'ダ': "da",
	'チ': "chi",
	'ヂ': "di",
	'ツ': "tsu",
	'ヅ': "du",
	'テ': "te",
	'デ': "de",
	'ト': "to",
	'ド': "do",
	'ナ': "na",
	'ニ': "ni",
	'ヌ': "nu",
	'ネ': "ne",
	'ノ': "no",
	'ハ': "ha",
	'バ': "va",
	'パ': "pa",
	'ヒ': "hi",
	'ビ': "vi",
	'ピ': "pi",
	'フ': "fu",
	'ブ': "vu",
	'プ': "pu",
	'ヘ': "he",
	'ベ': "ve",
	'ペ': "pe",
	'ホ': "ho",
	'ボ': "vo",
	'ポ': "po",
	'マ': "ma",
	'ミ': "mi",
	'ム': "mu",
	'メ': "me",
	'モ': "mo",
	'ヤ': "ya",
	'ユ': "yu",
	'ヨ': "yo",
	'ラ': "ra",
	'リ': "ri",
	'ル': "ru",
	'レ': "re",
	'ロ': "ro",
	'ワ': "wa",
	'ヰ': "wi",
	'ヱ': "we",
	'ヲ': "wo",
	'ン': "n",
	'ヴ': "vu",
}

var small = map[rune]bool{
	'ぁ': true,
	'ぃ': true,
	'ぅ': true,
	'ぇ': true,
	'ぉ': true,
	'っ': true, // tsu == hold consonant
	'ゃ': true,
	'ゅ': true,
	'ょ': true,
	'ゎ': true,
	'ゕ': true, // ka == counting mark
	'ゖ': true, // ke == counting mark

	'ァ': true,
	'ィ': true,
	'ゥ': true,
	'ェ': true,
	'ォ': true,
	'ッ': true, // tsu == hold consonant
	'ャ': true,
	'ュ': true,
	'ョ': true,
	'ヮ': true,
	'ヵ': true, // ka == counting mark
	'ヶ': true, // ke == counting mark
}

var vowel = map[rune]string{
	'ぁ': "a",
	'ぃ': "i",
	'ぅ': "u",
	'ぇ': "e",
	'ぉ': "o",

	'ァ': "a",
	'ィ': "i",
	'ゥ': "u",
	'ェ': "e",
	'ォ': "o",
}

var mod = map[rune]string{
	'ゃ': "ya",
	'ゅ': "yu",
	'ょ': "yo",

	'ャ': "ya",
	'ュ': "yu",
	'ョ': "yo",
}
<commit_msg>kana: fix handling of oddball modifiers At least, print something meaningful<commit_after>// Copyright 2014 Rob Pike. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Kana accepts, as argument text or as standard input, Japanese
UTF-8 text. It copies the text to standard output, converting
katakana and hiragana to romaji, leaving the rest (including
kanji) unmodified.
*/
package main // import "robpike.io/cmd/kana"

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"unicode/utf8"
)

func main() {
	if len(os.Args) == 1 {
		data, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			log.Fatal(err)
		}
		do(string(data))
		return
	}
	do(strings.Join(os.Args[1:], " "))
}

func do(s string) {
	prevKana := true
	skip := false
	for i, r := range s {
		k, ok := kana[r]
		if skip {
			skip = false
			continue
		}
		if !ok {
			if prevKana {
				fmt.Printf(" ")
			}
			fmt.Printf("%c", r)
			prevKana = false
			continue
		}
		if !prevKana {
			fmt.Printf(" ")
		}
		prevKana = true
		// Is there a modifier?
		r2, _ := utf8.DecodeRuneInString(s[i+utf8.RuneLen(r):])
		if small[r2] {
			skip = true
			k2, ok := mod[r2]
			if ok {
				fmt.Printf("%s%s", k[:len(k)-1], k2[1:])
				continue
			}
			k2, ok = vowel[r2]
			if ok {
				fmt.Printf("%s-", k)
				continue
			}
			// Otherwise it's just odd.
			fmt.Printf("<%s.%s>", k, odd[r2])
			continue
		}
		fmt.Printf("%s", k)
	}
	fmt.Print("\n")
}

var kana = map[rune]string{
	'あ': "a",
	'い': "i",
	'う': "u",
	'え': "e",
	'お': "o",
	'か': "ka",
	'が': "ga",
	'き': "ki",
	'ぎ': "gi",
	'く': "ku",
	'ぐ': "gu",
	'け': "ke",
	'げ': "ge",
	'こ': "ko",
	'ご': "go",
	'さ': "sa",
	'ざ': "za",
	'し': "shi",
	'じ': "zi",
	'す': "su",
	'ず': "zu",
	'せ': "se",
	'ぜ': "ze",
	'そ': "so",
	'ぞ': "zo",
	'た': "ta",
	'だ': "da",
	'ち': "chi",
	'ぢ': "di",
	'つ': "tsu",
	'づ': "du",
	'て': "te",
	'で': "de",
	'と': "to",
	'ど': "do",
	'な': "na",
	'に': "ni",
	'ぬ': "nu",
	'ね': "ne",
	'の': "no",
	'は': "ha",
	'ば': "va",
	'ぱ': "pa",
	'ひ': "hi",
	'び': "vi",
	'ぴ': "pi",
	'ふ': "fu",
	'ぶ': "vu",
	'ぷ': "pu",
	'へ': "he",
	'べ': "ve",
	'ぺ': "pe",
	'ほ': "ho",
	'ぼ': "vo",
	'ぽ': "po",
	'ま': "ma",
	'み': "mi",
	'む': "mu",
	'め': "me",
	'も': "mo",
	'や': "ya",
	'ゆ': "yu",
	'よ': "yo",
	'ら': "ra",
	'り': "ri",
	'る': "ru",
	'れ': "re",
	'ろ': "ro",
	'わ': "wa",
	'ゐ': "wi",
	'ゑ': "we",
	'を': "wo",
	'ん': "n",
	'ゔ': "vu",
	'ア': "a",
	'イ': "i",
	'ウ': "u",
	'エ': "e",
	'オ': "o",
	'カ': "ka",
	'ガ': "ga",
	'キ': "ki",
	'ギ': "gi",
	'ク': "ku",
	'グ': "gu",
	'ケ': "ke",
	'ゲ': "ge",
	'コ': "ko",
	'ゴ': "go",
	'サ': "sa",
	'ザ': "za",
	'シ': "shi",
	'ジ': "zi",
	'ス': "su",
	'ズ': "zu",
	'セ': "se",
	'ゼ': "ze",
	'ソ': "so",
	'ゾ': "zo",
	'タ': "ta",
	'ダ': "da",
	'チ': "chi",
	'ヂ': "di",
	'ツ': "tsu",
	'ヅ': "du",
	'テ': "te",
	'デ': "de",
	'ト': "to",
	'ド': "do",
	'ナ': "na",
	'ニ': "ni",
	'ヌ': "nu",
	'ネ': "ne",
	'ノ': "no",
	'ハ': "ha",
	'バ': "va",
	'パ': "pa",
	'ヒ': "hi",
	'ビ': "vi",
	'ピ': "pi",
	'フ': "fu",
	'ブ': "vu",
	'プ': "pu",
	'ヘ': "he",
	'ベ': "ve",
	'ペ': "pe",
	'ホ': "ho",
	'ボ': "vo",
	'ポ': "po",
	'マ': "ma",
	'ミ': "mi",
	'ム': "mu",
	'メ': "me",
	'モ': "mo",
	'ヤ': "ya",
	'ユ': "yu",
	'ヨ': "yo",
	'ラ': "ra",
	'リ': "ri",
	'ル': "ru",
	'レ': "re",
	'ロ': "ro",
	'ワ': "wa",
	'ヰ': "wi",
	'ヱ': "we",
	'ヲ': "wo",
	'ン': "n",
	'ヴ': "vu",
}

var small = map[rune]bool{
	'ぁ': true,
	'ぃ': true,
	'ぅ': true,
	'ぇ': true,
	'ぉ': true,
	'っ': true,
	'ゃ': true,
	'ゅ': true,
	'ょ': true,
	'ゎ': true,
	'ゕ': true,
	'ゖ': true,

	'ァ': true,
	'ィ': true,
	'ゥ': true,
	'ェ': true,
	'ォ': true,
	'ッ': true,
	'ャ': true,
	'ュ': true,
	'ョ': true,
	'ヮ': true,
	'ヵ': true,
	'ヶ': true,
}

var vowel = map[rune]string{
	'ぁ': "a",
	'ぃ': "i",
	'ぅ': "u",
	'ぇ': "e",
	'ぉ': "o",

	'ァ': "a",
	'ィ': "i",
	'ゥ': "u",
	'ェ': "e",
	'ォ': "o",
}

var mod = map[rune]string{
	'ゃ': "ya",
	'ゅ': "yu",
	'ょ': "yo",

	'ャ': "ya",
	'ュ': "yu",
	'ョ': "yo",
}

var odd = map[rune]string{
	'っ': "hold",  // tsu == hold consonant
	'ゕ': "count", // ka == counting mark
	'ゖ': "count", // ke == counting mark
	'ッ': "hold",  // tsu == hold consonant
	'ヵ': "count", // ka == counting mark
	'ヶ': "count", // ke == counting mark
}
<|endoftext|>


<commit_before>/* Copyright 2016-2017 Vector Creations Ltd
 *
 * 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 gomatrixserverlib

import (
	"bufio"
	"crypto/tls"
	"encoding/json"
	"net"
	"net/http"
	"strings"
	"time"
)

// ServerKeys are the ed25519 signing keys published by a matrix server.
// Contains SHA256 fingerprints of the TLS X509 certificates used by the server.
type ServerKeys struct {
	// Copy of the raw JSON for signature checking.
	Raw []byte
	// The decoded JSON fields.
	ServerKeyFields
}

// A TLSFingerprint is a SHA256 hash of an X509 certificate.
type TLSFingerprint struct {
	SHA256 Base64String `json:"sha256"`
}

// A VerifyKey is a ed25519 public key for a server.
type VerifyKey struct {
	// The public key.
	Key Base64String `json:"key"`
}

// An OldVerifyKey is an old ed25519 public key that is no longer valid.
type OldVerifyKey struct {
	VerifyKey
	// When this key stopped being valid for event signing in milliseconds.
	ExpiredTS Timestamp `json:"expired_ts"`
}

// ServerKeyFields are the parsed JSON contents of the ed25519 signing keys published by a matrix server.
type ServerKeyFields struct {
	// The name of the server
	ServerName ServerName `json:"server_name"`
	// List of SHA256 fingerprints of X509 certificates used by this server.
	TLSFingerprints []TLSFingerprint `json:"tls_fingerprints"`
	// The current signing keys in use on this server.
	// The keys of the map are the IDs of the keys.
	// These are valid while this response is valid.
	VerifyKeys map[KeyID]VerifyKey `json:"verify_keys"`
	// When this result is valid until in milliseconds.
	ValidUntilTS Timestamp `json:"valid_until_ts"`
	// Old keys that are now only valid for checking historic events.
	// The keys of the map are the IDs of the keys.
	OldVerifyKeys map[KeyID]OldVerifyKey `json:"old_verify_keys"`
}

// UnmarshalJSON implements json.Unmarshaler
func (keys *ServerKeys) UnmarshalJSON(data []byte) error {
	keys.Raw = data
	return json.Unmarshal(data, &keys.ServerKeyFields)
}

// MarshalJSON implements json.Marshaler
func (keys ServerKeys) MarshalJSON() ([]byte, error) {
	// We already have a copy of the serialised JSON for the keys so we can return that directly.
	return keys.Raw, nil
}

// PublicKey returns a public key with the given ID valid at the given TS or nil if no such key exists.
func (keys ServerKeys) PublicKey(keyID KeyID, atTS Timestamp) []byte {
	if currentKey, ok := keys.VerifyKeys[keyID]; ok && (atTS <= keys.ValidUntilTS) {
		return currentKey.Key
	}
	if oldKey, ok := keys.OldVerifyKeys[keyID]; ok && (atTS <= oldKey.ExpiredTS) {
		return oldKey.Key
	}
	return nil
}

// FetchKeysDirect fetches the matrix keys directly from the given address.
// Optionally sets a SNI header if ``sni`` is not empty.
// Returns the server keys and the state of the TLS connection used to retrieve them.
func FetchKeysDirect(serverName ServerName, addr, sni string) (*ServerKeys, *tls.ConnectionState, error) {
	// Create a TLS connection.
	tcpconn, err := net.Dial("tcp", addr)
	if err != nil {
		return nil, nil, err
	}
	defer tcpconn.Close() // nolint: errcheck
	tlsconn := tls.Client(tcpconn, &tls.Config{
		ServerName: sni,

		// This must be specified even though the TLS library will ignore it.
		InsecureSkipVerify: true, // nolint: gas
	})
	if err = tlsconn.Handshake(); err != nil {
		return nil, nil, err
	}
	connectionState := tlsconn.ConnectionState()

	// Write a GET /_matrix/key/v2/server down the connection.
	requestURL := "matrix://" + string(serverName) + "/_matrix/key/v2/server"
	request, err := http.NewRequest("GET", requestURL, nil)
	if err != nil {
		return nil, nil, err
	}
	request.Header.Set("Connection", "close")
	if err = request.Write(tlsconn); err != nil {
		return nil, nil, err
	}

	// Read the 200 OK from the server.
	response, err := http.ReadResponse(bufio.NewReader(tlsconn), request)
	if response != nil {
		defer response.Body.Close() // nolint: errcheck
	}
	if err != nil {
		return nil, nil, err
	}
	var keys ServerKeys
	if err = json.NewDecoder(response.Body).Decode(&keys); err != nil {
		return nil, nil, err
	}
	return &keys, &connectionState, nil
}

// Ed25519Checks are the checks that are applied to Ed25519 keys in ServerKey responses.
type Ed25519Checks struct {
	ValidEd25519      bool // The verify key is valid Ed25519 keys.
	MatchingSignature bool // The verify key has a valid signature.
}

// KeyChecks are the checks that should be applied to ServerKey responses.
type KeyChecks struct {
	AllChecksOK        bool                    // Did all the checks pass?
	MatchingServerName bool                    // Does the server name match what was requested.
	FutureValidUntilTS bool                    // The valid until TS is in the future.
	HasEd25519Key      bool                    // The server has at least one ed25519 key.
	AllEd25519ChecksOK *bool                   // All the Ed25519 checks are ok. or null if there weren't any to check.
	Ed25519Checks      map[KeyID]Ed25519Checks // Checks for Ed25519 keys.
}

// CheckKeys checks the keys returned from a server to make sure they are valid.
// If the checks pass then also return a map of key_id to Ed25519 public key
func CheckKeys(
	serverName ServerName,
	now time.Time,
	keys ServerKeys,
) (
	checks KeyChecks,
	ed25519Keys map[KeyID]Base64String,
) {
	checks.MatchingServerName = serverName == keys.ServerName
	checks.FutureValidUntilTS = keys.ValidUntilTS.Time().After(now)
	checks.AllChecksOK = checks.MatchingServerName && checks.FutureValidUntilTS

	ed25519Keys = checkVerifyKeys(keys, &checks)

	if !checks.AllChecksOK {
		ed25519Keys = nil
	}
	return
}

func checkVerifyKeys(keys ServerKeys, checks *KeyChecks) map[KeyID]Base64String {
	allEd25519ChecksOK := true
	checks.Ed25519Checks = map[KeyID]Ed25519Checks{}
	verifyKeys := map[KeyID]Base64String{}
	for keyID, keyData := range keys.VerifyKeys {
		algorithm := strings.SplitN(string(keyID), ":", 2)[0]
		publicKey := keyData.Key
		if algorithm == "ed25519" {
			checks.HasEd25519Key = true
			checks.AllEd25519ChecksOK = &allEd25519ChecksOK
			entry := Ed25519Checks{
				ValidEd25519: len(publicKey) == 32,
			}
			if entry.ValidEd25519 {
				err := VerifyJSON(string(keys.ServerName), keyID, []byte(publicKey), keys.Raw)
				entry.MatchingSignature = err == nil
			}
			checks.Ed25519Checks[keyID] = entry
			if entry.MatchingSignature {
				verifyKeys[keyID] = publicKey
			} else {
				allEd25519ChecksOK = false
			}
		}
	}
	if checks.AllChecksOK {
		checks.AllChecksOK = checks.HasEd25519Key && allEd25519ChecksOK
	}
	return verifyKeys
}
<commit_msg>Make FetchKeysDirect generate nicer human-readable messages so they display on the federation tester in a nicer way (#115)<commit_after>/* Copyright 2016-2017 Vector Creations Ltd
 *
 * 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 gomatrixserverlib

import (
	"bufio"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"strings"
	"time"

	"github.com/pkg/errors"
)

// ServerKeys are the ed25519 signing keys published by a matrix server.
// Contains SHA256 fingerprints of the TLS X509 certificates used by the server.
type ServerKeys struct {
	// Copy of the raw JSON for signature checking.
	Raw []byte
	// The decoded JSON fields.
	ServerKeyFields
}

// A TLSFingerprint is a SHA256 hash of an X509 certificate.
type TLSFingerprint struct {
	SHA256 Base64String `json:"sha256"`
}

// A VerifyKey is a ed25519 public key for a server.
type VerifyKey struct {
	// The public key.
	Key Base64String `json:"key"`
}

// An OldVerifyKey is an old ed25519 public key that is no longer valid.
type OldVerifyKey struct {
	VerifyKey
	// When this key stopped being valid for event signing in milliseconds.
	ExpiredTS Timestamp `json:"expired_ts"`
}

// ServerKeyFields are the parsed JSON contents of the ed25519 signing keys published by a matrix server.
type ServerKeyFields struct {
	// The name of the server
	ServerName ServerName `json:"server_name"`
	// List of SHA256 fingerprints of X509 certificates used by this server.
	TLSFingerprints []TLSFingerprint `json:"tls_fingerprints"`
	// The current signing keys in use on this server.
	// The keys of the map are the IDs of the keys.
	// These are valid while this response is valid.
	VerifyKeys map[KeyID]VerifyKey `json:"verify_keys"`
	// When this result is valid until in milliseconds.
	ValidUntilTS Timestamp `json:"valid_until_ts"`
	// Old keys that are now only valid for checking historic events.
	// The keys of the map are the IDs of the keys.
	OldVerifyKeys map[KeyID]OldVerifyKey `json:"old_verify_keys"`
}

// UnmarshalJSON implements json.Unmarshaler
func (keys *ServerKeys) UnmarshalJSON(data []byte) error {
	keys.Raw = data
	return json.Unmarshal(data, &keys.ServerKeyFields)
}

// MarshalJSON implements json.Marshaler
func (keys ServerKeys) MarshalJSON() ([]byte, error) {
	// We already have a copy of the serialised JSON for the keys so we can return that directly.
	return keys.Raw, nil
}

// PublicKey returns a public key with the given ID valid at the given TS or nil if no such key exists.
func (keys ServerKeys) PublicKey(keyID KeyID, atTS Timestamp) []byte {
	if currentKey, ok := keys.VerifyKeys[keyID]; ok && (atTS <= keys.ValidUntilTS) {
		return currentKey.Key
	}
	if oldKey, ok := keys.OldVerifyKeys[keyID]; ok && (atTS <= oldKey.ExpiredTS) {
		return oldKey.Key
	}
	return nil
}

// FetchKeysDirect fetches the matrix keys directly from the given address.
// Optionally sets a SNI header if ``sni`` is not empty.
// Returns the server keys and the state of the TLS connection used to retrieve them.
func FetchKeysDirect(serverName ServerName, addr, sni string) (*ServerKeys, *tls.ConnectionState, error) {
	// Create a TLS connection.
	tcpconn, err := net.Dial("tcp", addr)
	if err != nil {
		return nil, nil, err
	}
	defer tcpconn.Close() // nolint: errcheck
	tlsconn := tls.Client(tcpconn, &tls.Config{
		ServerName: sni,

		// This must be specified even though the TLS library will ignore it.
		InsecureSkipVerify: true, // nolint: gas
	})
	if err = tlsconn.Handshake(); err != nil {
		return nil, nil, err
	}
	connectionState := tlsconn.ConnectionState()

	// Write a GET /_matrix/key/v2/server down the connection.
	requestURL := "matrix://" + string(serverName) + "/_matrix/key/v2/server"
	request, err := http.NewRequest("GET", requestURL, nil)
	if err != nil {
		return nil, nil, err
	}
	request.Header.Set("Connection", "close")
	if err = request.Write(tlsconn); err != nil {
		return nil, nil, err
	}

	// Read the 200 OK from the server.
	response, err := http.ReadResponse(bufio.NewReader(tlsconn), request)
	if response != nil {
		defer response.Body.Close() // nolint: errcheck
	}
	if err != nil {
		return nil, nil, err
	}
	if response.StatusCode != http.StatusOK {
		return nil, nil, fmt.Errorf("Non-200 response %d from remote server", response.StatusCode)
	}
	var keys ServerKeys
	if err = json.NewDecoder(response.Body).Decode(&keys); err != nil {
		return nil, nil, errors.Wrap(err, "Unable to decode JSON from remote server")
	}
	return &keys, &connectionState, nil
}

// Ed25519Checks are the checks that are applied to Ed25519 keys in ServerKey responses.
type Ed25519Checks struct {
	ValidEd25519      bool // The verify key is valid Ed25519 keys.
	MatchingSignature bool // The verify key has a valid signature.
}

// KeyChecks are the checks that should be applied to ServerKey responses.
type KeyChecks struct {
	AllChecksOK        bool                    // Did all the checks pass?
	MatchingServerName bool                    // Does the server name match what was requested.
	FutureValidUntilTS bool                    // The valid until TS is in the future.
	HasEd25519Key      bool                    // The server has at least one ed25519 key.
	AllEd25519ChecksOK *bool                   // All the Ed25519 checks are ok. or null if there weren't any to check.
	Ed25519Checks      map[KeyID]Ed25519Checks // Checks for Ed25519 keys.
}

// CheckKeys checks the keys returned from a server to make sure they are valid.
// If the checks pass then also return a map of key_id to Ed25519 public key
func CheckKeys(
	serverName ServerName,
	now time.Time,
	keys ServerKeys,
) (
	checks KeyChecks,
	ed25519Keys map[KeyID]Base64String,
) {
	checks.MatchingServerName = serverName == keys.ServerName
	checks.FutureValidUntilTS = keys.ValidUntilTS.Time().After(now)
	checks.AllChecksOK = checks.MatchingServerName && checks.FutureValidUntilTS

	ed25519Keys = checkVerifyKeys(keys, &checks)

	if !checks.AllChecksOK {
		ed25519Keys = nil
	}
	return
}

func checkVerifyKeys(keys ServerKeys, checks *KeyChecks) map[KeyID]Base64String {
	allEd25519ChecksOK := true
	checks.Ed25519Checks = map[KeyID]Ed25519Checks{}
	verifyKeys := map[KeyID]Base64String{}
	for keyID, keyData := range keys.VerifyKeys {
		algorithm := strings.SplitN(string(keyID), ":", 2)[0]
		publicKey := keyData.Key
		if algorithm == "ed25519" {
			checks.HasEd25519Key = true
			checks.AllEd25519ChecksOK = &allEd25519ChecksOK
			entry := Ed25519Checks{
				ValidEd25519: len(publicKey) == 32,
			}
			if entry.ValidEd25519 {
				err := VerifyJSON(string(keys.ServerName), keyID, []byte(publicKey), keys.Raw)
				entry.MatchingSignature = err == nil
			}
			checks.Ed25519Checks[keyID] = entry
			if entry.MatchingSignature {
				verifyKeys[keyID] = publicKey
			} else {
				allEd25519ChecksOK = false
			}
		}
	}
	if checks.AllChecksOK {
		checks.AllChecksOK = checks.HasEd25519Key && allEd25519ChecksOK
	}
	return verifyKeys
}
<|endoftext|>