<commit_before>package tenho

import (
	"fmt"
	"math/rand"
	"sort"
	"time"
)

func Start() {
	start := time.Now()
	// 処理
	var i float64
	i = 0
	for {
		i++
		end := time.Now()
		diff := float64(end.Sub(start) / 1000 / 1000 / 1000)
		m := int(i / diff)
		out := 0
		if m >= 0 {
			out = m
		}
		fmt.Printf("\r")
		fmt.Printf("\r%v回試行  %v秒経過 %v回/秒", i, diff, out)
		seed := time.Now().UnixNano()
		var hai string
		var ok bool
		hai, ok = tryOnce(seed)
		fmt.Printf("%v", hai)
		if ok {
			break
		}
	}
	fmt.Printf("\n")
}

type Hand []int

func tryOnce(seed int64) (string, bool) {
	hand := ShuffledHand(seed)
	hai := hand.HaiString()
	ok := Solve(hand)
	return hai, ok
}

// http://d.hatena.ne.jp/hake/20150930/p1
func shuffle(hand Hand) {
	for i := len(hand); i > 1; i-- {
		j := rand.Intn(i) // 0 .. i-1 の乱数発生
		hand[i-1], hand[j] = hand[j], hand[i-1]
	}
}

func ShuffledHand(seed int64) Hand {
	rand.Seed(seed)

	// データ要素数指定、および初期データ作成
	size := 136
	hand := make(Hand, size, size)
	for i := 0; i < size; i++ {
		hand[i] = i / 4
	}

	// シャッフル
	shuffle(hand)

	return hand[:14]
}

// 牌文字への変換(スペース区切り)
func (c Hand) HaiString() string {
	// http://qiita.com/ruiu/items/2bb83b29baeae2433a79
	// サイズ0、内部バッファの長さ69の[]byteの値を割り当てる
	b := make([]byte, 0, 70)

	// bに文字列を追加
	for j := 0; j < 14; j++ {
		// コードポイント上、普通の麻雀牌はU+1F000からの34個。
		// U+1F000 is 'MAHJONG TILE EAST WIND' ('東')
		// https://codepoints.net/U+1F000
		b = append(b, string(c[j]+0x1F000)...) // appendするには...が必要
		// 自分のMacではスペース区切りでないとうまく表示されないためスペースを挿入する
		// U+0020 is 'SPACE'
		// https://codepoints.net/U+0020
		b = append(b, string(0x20)...) // appendするには...が必要
	}
	return string(b)
}

// あがり判定する
func Solve(hand Hand) bool {
	if is_chitoitsu(hand) {
		return true
	}
	suit_grouped_hand := hand.GroupSuit()
	//fmt.Printf("%v", suit_grouped_hand)

	return suit_grouped_hand.Solve()
}

type SuitGroup []int

type SuitsGroupedHand []SuitGroup

// スート分類
func (hand Hand) GroupSuit() SuitsGroupedHand {
	m := SuitsGroupedHand{{}, {}, {}, {}}
	for _, i := range hand {
		switch {
		case i < 7:
			m[0] = append(m[0], i)
		case i < 7+(9*1):
			m[1] = append(m[1], i-7)
		case i < 7+(9*2):
			m[2] = append(m[2], i-7-(9*1))
		case i < 7+(9*3):
			m[3] = append(m[3], i-7-(9*2))
		}
	}
	return m
}

func (m SuitsGroupedHand) Solve() bool {
	return m.a_pair_existible() && m.valid_33332()
}

func (m SuitsGroupedHand) a_pair_existible() bool {
	//スートのサイズを3で割った時
	//あまりが2であるスートグループが1つであること
	c := 0
	for _, a := range m {
		switch len(a) % 3 {
		case 0:
			// noop
		case 1:
			return false
		case 2:
			c++
		}
	}
	return c == 1
}

func (m SuitsGroupedHand) valid_33332() bool {
	for i := 0; i < 4; i++ {
		if !m[i].valid_suit_group(i) {
			return false
		}
	}
	return true
}

// 33332形を形成するスートグループがどうかを判定
func (a SuitGroup) valid_suit_group(i int) bool {
	// 対子が含まれているスートグループがただ1つある前提

	//ソート
	sort.Ints(a)
	if len(a)%3 == 2 {
		//ペアを探す
		pair_numbers := a.pairable_numbers()
		//ペア候補がなかったらぬける
		if len(pair_numbers) == 0 {
			return false
		}
		//ペア候補毎に繰り返し処理
		for _, v := range pair_numbers {
			//ペアとなる２枚を除去
			rest := SuitGroup{}
			c := 2
			for _, w := range a {
				// ペア候補以外は新スライスに入れる
				// ペア候補は３枚目以降は新スライスに入れる
				if w != v || c <= 0 {
					rest = append(rest, w)
				}
				if w == v {
					c--
				}
			}
			if rest.valid_3cards(i) {
				return true
			} else {
				continue
			}
		}
		return false
	} else if len(a)%3 == 0 {
		return a.valid_3cards(i)
	}
	// 到達しないはず
	panic("到達しないはず")
}

func (a SuitGroup) valid_3cards(i int) bool {
	// 刻子や順子のみで構成されている場合true
	// a is sorted
	// a.size % 3 is0
	// 引数は字牌のとき0
	ok := false
	for {
		a, ok = a.remove_kotsu()
		if ok {
			continue
		}
		if i > 0 {
			a, ok = a.remove_shuntsu()
			if ok {
				continue
			}
		}
		return len(a) == 0
	}
}

func (a SuitGroup) remove_kotsu() (SuitGroup, bool) {
	// 刻子を除去できればtrue
	// a is sorted
	retval := a
	if len(a) < 3 {
		return retval, false
	}
	if a[0] == a[1] && a[0] == a[2] {
		retval = a[3:]
		return retval, true
	}
	return retval, false
}

func (a SuitGroup) remove_shuntsu() (SuitGroup, bool) {
	// 順子を除去できればtrue
	// a is sorted
	rest := SuitGroup{}
	first := -1
	second := -1
	found := false
	for _, v := range a {
		if found {
			rest = append(rest, v)
			continue
		}
		if first == -1 {
			first = v
		} else if second == -1 && first+1 == v {
			second = v
		} else if second != -1 && first+2 == v {
			//flush
			first = -1
			second = -1
			found = true
		} else {
			rest = append(rest, v)
		}
	}
	return rest, found
}

func (a SuitGroup) pairable_numbers() SuitGroup {
	// a is sorted
	counter := []int{}
	x := 999 // 2つ前
	y := 999 // 1つ前
	for _, v := range a {
		if y == v && x != v {
			counter = append(counter, v)
		} else {
			y = v
		}
	}
	return counter
}

func is_chitoitsu(hand Hand) bool {
	//カウンタ
	c := map[int]int{}

	for _, v := range hand {
		count, ok := c[v]
		if ok {
			if count == 1 {
				c[v] = 2
			} else {
				// c[v] == 2
				return false
			}
		} else {
			c[v] = 1
		}
		//8個チェック
		if len(c) >= 8 {
			return false
		}
	}
	return true
}
<commit_msg>Hand#is_chitoitsu<commit_after>package tenho

import (
	"fmt"
	"math/rand"
	"sort"
	"time"
)

func Start() {
	start := time.Now()
	// 処理
	var i float64
	i = 0
	for {
		i++
		end := time.Now()
		diff := float64(end.Sub(start) / 1000 / 1000 / 1000)
		m := int(i / diff)
		out := 0
		if m >= 0 {
			out = m
		}
		fmt.Printf("\r")
		fmt.Printf("\r%v回試行  %v秒経過 %v回/秒", i, diff, out)
		seed := time.Now().UnixNano()
		var hai string
		var ok bool
		hai, ok = tryOnce(seed)
		fmt.Printf("%v", hai)
		if ok {
			break
		}
	}
	fmt.Printf("\n")
}

type Hand []int

func tryOnce(seed int64) (string, bool) {
	hand := ShuffledHand(seed)
	hai := hand.HaiString()
	ok := Solve(hand)
	return hai, ok
}

// http://d.hatena.ne.jp/hake/20150930/p1
func shuffle(hand Hand) {
	for i := len(hand); i > 1; i-- {
		j := rand.Intn(i) // 0 .. i-1 の乱数発生
		hand[i-1], hand[j] = hand[j], hand[i-1]
	}
}

func ShuffledHand(seed int64) Hand {
	rand.Seed(seed)

	// データ要素数指定、および初期データ作成
	size := 136
	hand := make(Hand, size, size)
	for i := 0; i < size; i++ {
		hand[i] = i / 4
	}

	// シャッフル
	shuffle(hand)

	return hand[:14]
}

// 牌文字への変換(スペース区切り)
func (c Hand) HaiString() string {
	// http://qiita.com/ruiu/items/2bb83b29baeae2433a79
	// サイズ0、内部バッファの長さ69の[]byteの値を割り当てる
	b := make([]byte, 0, 70)

	// bに文字列を追加
	for j := 0; j < 14; j++ {
		// コードポイント上、普通の麻雀牌はU+1F000からの34個。
		// U+1F000 is 'MAHJONG TILE EAST WIND' ('東')
		// https://codepoints.net/U+1F000
		b = append(b, string(c[j]+0x1F000)...) // appendするには...が必要
		// 自分のMacではスペース区切りでないとうまく表示されないためスペースを挿入する
		// U+0020 is 'SPACE'
		// https://codepoints.net/U+0020
		b = append(b, string(0x20)...) // appendするには...が必要
	}
	return string(b)
}

// あがり判定する
func Solve(hand Hand) bool {
	if hand.is_chitoitsu() {
		return true
	}
	suit_grouped_hand := hand.GroupSuit()
	//fmt.Printf("%v", suit_grouped_hand)

	return suit_grouped_hand.Solve()
}

type SuitGroup []int

type SuitsGroupedHand []SuitGroup

// スート分類
func (hand Hand) GroupSuit() SuitsGroupedHand {
	m := SuitsGroupedHand{{}, {}, {}, {}}
	for _, i := range hand {
		switch {
		case i < 7:
			m[0] = append(m[0], i)
		case i < 7+(9*1):
			m[1] = append(m[1], i-7)
		case i < 7+(9*2):
			m[2] = append(m[2], i-7-(9*1))
		case i < 7+(9*3):
			m[3] = append(m[3], i-7-(9*2))
		}
	}
	return m
}

func (m SuitsGroupedHand) Solve() bool {
	return m.a_pair_existible() && m.valid_33332()
}

func (m SuitsGroupedHand) a_pair_existible() bool {
	//スートのサイズを3で割った時
	//あまりが2であるスートグループが1つであること
	c := 0
	for _, a := range m {
		switch len(a) % 3 {
		case 0:
			// noop
		case 1:
			return false
		case 2:
			c++
		}
	}
	return c == 1
}

func (m SuitsGroupedHand) valid_33332() bool {
	for i := 0; i < 4; i++ {
		if !m[i].valid_suit_group(i) {
			return false
		}
	}
	return true
}

// 33332形を形成するスートグループがどうかを判定
func (a SuitGroup) valid_suit_group(i int) bool {
	// 対子が含まれているスートグループがただ1つある前提

	//ソート
	sort.Ints(a)
	if len(a)%3 == 2 {
		//ペアを探す
		pair_numbers := a.pairable_numbers()
		//ペア候補がなかったらぬける
		if len(pair_numbers) == 0 {
			return false
		}
		//ペア候補毎に繰り返し処理
		for _, v := range pair_numbers {
			//ペアとなる２枚を除去
			rest := SuitGroup{}
			c := 2
			for _, w := range a {
				// ペア候補以外は新スライスに入れる
				// ペア候補は３枚目以降は新スライスに入れる
				if w != v || c <= 0 {
					rest = append(rest, w)
				}
				if w == v {
					c--
				}
			}
			if rest.valid_3cards(i) {
				return true
			} else {
				continue
			}
		}
		return false
	} else if len(a)%3 == 0 {
		return a.valid_3cards(i)
	}
	// 到達しないはず
	panic("到達しないはず")
}

func (a SuitGroup) valid_3cards(i int) bool {
	// 刻子や順子のみで構成されている場合true
	// a is sorted
	// a.size % 3 is0
	// 引数は字牌のとき0
	ok := false
	for {
		a, ok = a.remove_kotsu()
		if ok {
			continue
		}
		if i > 0 {
			a, ok = a.remove_shuntsu()
			if ok {
				continue
			}
		}
		return len(a) == 0
	}
}

func (a SuitGroup) remove_kotsu() (SuitGroup, bool) {
	// 刻子を除去できればtrue
	// a is sorted
	retval := a
	if len(a) < 3 {
		return retval, false
	}
	if a[0] == a[1] && a[0] == a[2] {
		retval = a[3:]
		return retval, true
	}
	return retval, false
}

func (a SuitGroup) remove_shuntsu() (SuitGroup, bool) {
	// 順子を除去できればtrue
	// a is sorted
	rest := SuitGroup{}
	first := -1
	second := -1
	found := false
	for _, v := range a {
		if found {
			rest = append(rest, v)
			continue
		}
		if first == -1 {
			first = v
		} else if second == -1 && first+1 == v {
			second = v
		} else if second != -1 && first+2 == v {
			//flush
			first = -1
			second = -1
			found = true
		} else {
			rest = append(rest, v)
		}
	}
	return rest, found
}

func (a SuitGroup) pairable_numbers() SuitGroup {
	// a is sorted
	counter := []int{}
	x := 999 // 2つ前
	y := 999 // 1つ前
	for _, v := range a {
		if y == v && x != v {
			counter = append(counter, v)
		} else {
			y = v
		}
	}
	return counter
}

func (hand Hand) is_chitoitsu() bool {
	//カウンタ
	c := map[int]int{}

	for _, v := range hand {
		count, ok := c[v]
		if ok {
			if count == 1 {
				c[v] = 2
			} else {
				// c[v] == 2
				return false
			}
		} else {
			c[v] = 1
		}
		//8個チェック
		if len(c) >= 8 {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"regexp"
	"strconv"
	"testing"
	"time"

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

func TestAccAWSLambdaEventSourceMapping_basic(t *testing.T) {
	var conf lambda.EventSourceMappingConfiguration

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLambdaEventSourceMappingDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSLambdaEventSourceMappingConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAwsLambdaEventSourceMappingExists("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", &conf),
					testAccCheckAWSLambdaEventSourceMappingAttributes(&conf),
				),
			},
			resource.TestStep{
				Config: testAccAWSLambdaEventSourceMappingConfigUpdate,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAwsLambdaEventSourceMappingExists("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", &conf),
					resource.TestCheckResourceAttr("aws_lambda_event_source_mapping.lambda_event_source_mapping_test",
						"batch_size",
						strconv.Itoa(200)),
					resource.TestCheckResourceAttr("aws_lambda_event_source_mapping.lambda_event_source_mapping_test",
						"enabled",
						strconv.FormatBool(false)),
					resource.TestMatchResourceAttr(
						"aws_lambda_event_source_mapping.lambda_event_source_mapping_test",
						"function_arn",
						regexp.MustCompile("example_lambda_name_update$"),
					),
				),
			},
		},
	})
}

func TestAccAWSLambdaEventSourceMapping_disappears(t *testing.T) {
	var conf lambda.EventSourceMappingConfiguration

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLambdaEventSourceMappingDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSLambdaEventSourceMappingConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAwsLambdaEventSourceMappingExists("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", &conf),
					testAccCheckAWSLambdaEventSourceMappingDisappears(&conf),
				),
				ExpectNonEmptyPlan: true,
			},
		},
	})
}

func testAccCheckAWSLambdaEventSourceMappingDisappears(conf *lambda.EventSourceMappingConfiguration) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		conn := testAccProvider.Meta().(*AWSClient).lambdaconn

		params := &lambda.DeleteEventSourceMappingInput{
			UUID: conf.UUID,
		}

		_, err := conn.DeleteEventSourceMapping(params)
		if err != nil {
			if err != nil {
				return err
			}
		}

		return resource.Retry(10*time.Minute, func() *resource.RetryError {
			params := &lambda.GetEventSourceMappingInput{
				UUID: conf.UUID,
			}
			_, err := conn.GetEventSourceMapping(params)
			if err != nil {
				cgw, ok := err.(awserr.Error)
				if ok && cgw.Code() == "ResourceNotFoundException" {
					return nil
				}
				return resource.NonRetryableError(
					fmt.Errorf("Error retrieving Lambda Event Source Mapping: %s", err))
			}
			return resource.RetryableError(fmt.Errorf(
				"Waiting for Lambda Event Source Mapping: %v", conf.UUID))
		})
	}
}

func testAccCheckLambdaEventSourceMappingDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).lambdaconn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_lambda_event_source_mapping" {
			continue
		}

		_, err := conn.GetEventSourceMapping(&lambda.GetEventSourceMappingInput{
			UUID: aws.String(rs.Primary.ID),
		})

		if err == nil {
			return fmt.Errorf("Lambda event source mapping was not deleted")
		}

	}

	return nil

}

func testAccCheckAwsLambdaEventSourceMappingExists(n string, mapping *lambda.EventSourceMappingConfiguration) resource.TestCheckFunc {
	// Wait for IAM role
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Lambda event source mapping not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("Lambda event source mapping ID not set")
		}

		conn := testAccProvider.Meta().(*AWSClient).lambdaconn

		params := &lambda.GetEventSourceMappingInput{
			UUID: aws.String(rs.Primary.ID),
		}

		getSourceMappingConfiguration, err := conn.GetEventSourceMapping(params)
		if err != nil {
			return err
		}

		*mapping = *getSourceMappingConfiguration

		return nil
	}
}

func testAccCheckAWSLambdaEventSourceMappingAttributes(mapping *lambda.EventSourceMappingConfiguration) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		uuid := *mapping.UUID
		if uuid == "" {
			return fmt.Errorf("Could not read Lambda event source mapping's UUID")
		}

		return nil
	}
}

const testAccAWSLambdaEventSourceMappingConfig = `
resource "aws_iam_role" "iam_for_lambda" {
    name = "iam_for_lambda"
    assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_policy" "policy_for_role" {
    name = "policy_for_role"
    path = "/"
    description = "IAM policy for for Lamda event mapping testing"
    policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
      {
          "Effect": "Allow",
          "Action": [
            "kinesis:GetRecords",
            "kinesis:GetShardIterator",
            "kinesis:DescribeStream"
          ],
          "Resource": "*"
      },
      {
          "Effect": "Allow",
          "Action": [
            "kinesis:ListStreams"
          ],
          "Resource": "*"
      }
  ]
}
EOF
}

resource "aws_iam_policy_attachment" "policy_attachment_for_role" {
    name = "policy_attachment_for_role"
    roles = ["${aws_iam_role.iam_for_lambda.name}"]
    policy_arn = "${aws_iam_policy.policy_for_role.arn}"
}

resource "aws_kinesis_stream" "kinesis_stream_test" {
    name = "kinesis_stream_test"
    shard_count = 1
}

resource "aws_lambda_function" "lambda_function_test_create" {
    filename = "test-fixtures/lambdatest.zip"
    function_name = "example_lambda_name_create"
    role = "${aws_iam_role.iam_for_lambda.arn}"
    handler = "exports.example"
    runtime = "nodejs4.3"
}

resource "aws_lambda_function" "lambda_function_test_update" {
    filename = "test-fixtures/lambdatest.zip"
    function_name = "example_lambda_name_update"
    role = "${aws_iam_role.iam_for_lambda.arn}"
    handler = "exports.example"
}

resource "aws_lambda_event_source_mapping" "lambda_event_source_mapping_test" {
		batch_size = 100
		event_source_arn = "${aws_kinesis_stream.kinesis_stream_test.arn}"
		enabled = true
		depends_on = ["aws_iam_policy_attachment.policy_attachment_for_role"]
		function_name = "${aws_lambda_function.lambda_function_test_create.arn}"
		starting_position = "TRIM_HORIZON"
}
`

const testAccAWSLambdaEventSourceMappingConfigUpdate = `
resource "aws_iam_role" "iam_for_lambda" {
    name = "iam_for_lambda"
    assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_policy" "policy_for_role" {
    name = "policy_for_role"
    path = "/"
    description = "IAM policy for for Lamda event mapping testing"
    policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
      {
          "Effect": "Allow",
          "Action": [
            "kinesis:GetRecords",
            "kinesis:GetShardIterator",
            "kinesis:DescribeStream"
          ],
          "Resource": "*"
      },
      {
          "Effect": "Allow",
          "Action": [
            "kinesis:ListStreams"
          ],
          "Resource": "*"
      }
  ]
}
EOF
}

resource "aws_iam_policy_attachment" "policy_attachment_for_role" {
    name = "policy_attachment_for_role"
    roles = ["${aws_iam_role.iam_for_lambda.name}"]
    policy_arn = "${aws_iam_policy.policy_for_role.arn}"
}

resource "aws_kinesis_stream" "kinesis_stream_test" {
    name = "kinesis_stream_test"
    shard_count = 1
}

resource "aws_lambda_function" "lambda_function_test_create" {
    filename = "test-fixtures/lambdatest.zip"
    function_name = "example_lambda_name_create"
    role = "${aws_iam_role.iam_for_lambda.arn}"
    handler = "exports.example"
    runtime = "nodejs4.3"
}

resource "aws_lambda_function" "lambda_function_test_update" {
    filename = "test-fixtures/lambdatest.zip"
    function_name = "example_lambda_name_update"
    role = "${aws_iam_role.iam_for_lambda.arn}"
    handler = "exports.example"
}

resource "aws_lambda_event_source_mapping" "lambda_event_source_mapping_test" {
		batch_size = 200
		event_source_arn = "${aws_kinesis_stream.kinesis_stream_test.arn}"
		enabled = false
		depends_on = ["aws_iam_policy_attachment.policy_attachment_for_role"]
		function_name = "${aws_lambda_function.lambda_function_test_update.arn}"
		starting_position = "TRIM_HORIZON"
}
`
<commit_msg>provider/aws: Fix lambda source mapping tests<commit_after>package aws

import (
	"fmt"
	"regexp"
	"strconv"
	"testing"
	"time"

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

func TestAccAWSLambdaEventSourceMapping_basic(t *testing.T) {
	var conf lambda.EventSourceMappingConfiguration

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLambdaEventSourceMappingDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSLambdaEventSourceMappingConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAwsLambdaEventSourceMappingExists("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", &conf),
					testAccCheckAWSLambdaEventSourceMappingAttributes(&conf),
				),
			},
			resource.TestStep{
				Config: testAccAWSLambdaEventSourceMappingConfigUpdate,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAwsLambdaEventSourceMappingExists("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", &conf),
					resource.TestCheckResourceAttr("aws_lambda_event_source_mapping.lambda_event_source_mapping_test",
						"batch_size",
						strconv.Itoa(200)),
					resource.TestCheckResourceAttr("aws_lambda_event_source_mapping.lambda_event_source_mapping_test",
						"enabled",
						strconv.FormatBool(false)),
					resource.TestMatchResourceAttr(
						"aws_lambda_event_source_mapping.lambda_event_source_mapping_test",
						"function_arn",
						regexp.MustCompile("example_lambda_name_update$"),
					),
				),
			},
		},
	})
}

func TestAccAWSLambdaEventSourceMapping_disappears(t *testing.T) {
	var conf lambda.EventSourceMappingConfiguration

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLambdaEventSourceMappingDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSLambdaEventSourceMappingConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAwsLambdaEventSourceMappingExists("aws_lambda_event_source_mapping.lambda_event_source_mapping_test", &conf),
					testAccCheckAWSLambdaEventSourceMappingDisappears(&conf),
				),
				ExpectNonEmptyPlan: true,
			},
		},
	})
}

func testAccCheckAWSLambdaEventSourceMappingDisappears(conf *lambda.EventSourceMappingConfiguration) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		conn := testAccProvider.Meta().(*AWSClient).lambdaconn

		params := &lambda.DeleteEventSourceMappingInput{
			UUID: conf.UUID,
		}

		_, err := conn.DeleteEventSourceMapping(params)
		if err != nil {
			if err != nil {
				return err
			}
		}

		return resource.Retry(10*time.Minute, func() *resource.RetryError {
			params := &lambda.GetEventSourceMappingInput{
				UUID: conf.UUID,
			}
			_, err := conn.GetEventSourceMapping(params)
			if err != nil {
				cgw, ok := err.(awserr.Error)
				if ok && cgw.Code() == "ResourceNotFoundException" {
					return nil
				}
				return resource.NonRetryableError(
					fmt.Errorf("Error retrieving Lambda Event Source Mapping: %s", err))
			}
			return resource.RetryableError(fmt.Errorf(
				"Waiting for Lambda Event Source Mapping: %v", conf.UUID))
		})
	}
}

func testAccCheckLambdaEventSourceMappingDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).lambdaconn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_lambda_event_source_mapping" {
			continue
		}

		_, err := conn.GetEventSourceMapping(&lambda.GetEventSourceMappingInput{
			UUID: aws.String(rs.Primary.ID),
		})

		if err == nil {
			return fmt.Errorf("Lambda event source mapping was not deleted")
		}

	}

	return nil

}

func testAccCheckAwsLambdaEventSourceMappingExists(n string, mapping *lambda.EventSourceMappingConfiguration) resource.TestCheckFunc {
	// Wait for IAM role
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Lambda event source mapping not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("Lambda event source mapping ID not set")
		}

		conn := testAccProvider.Meta().(*AWSClient).lambdaconn

		params := &lambda.GetEventSourceMappingInput{
			UUID: aws.String(rs.Primary.ID),
		}

		getSourceMappingConfiguration, err := conn.GetEventSourceMapping(params)
		if err != nil {
			return err
		}

		*mapping = *getSourceMappingConfiguration

		return nil
	}
}

func testAccCheckAWSLambdaEventSourceMappingAttributes(mapping *lambda.EventSourceMappingConfiguration) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		uuid := *mapping.UUID
		if uuid == "" {
			return fmt.Errorf("Could not read Lambda event source mapping's UUID")
		}

		return nil
	}
}

const testAccAWSLambdaEventSourceMappingConfig = `
resource "aws_iam_role" "iam_for_lambda" {
    name = "iam_for_lambda"
    assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_policy" "policy_for_role" {
    name = "policy_for_role"
    path = "/"
    description = "IAM policy for for Lamda event mapping testing"
    policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
      {
          "Effect": "Allow",
          "Action": [
            "kinesis:GetRecords",
            "kinesis:GetShardIterator",
            "kinesis:DescribeStream"
          ],
          "Resource": "*"
      },
      {
          "Effect": "Allow",
          "Action": [
            "kinesis:ListStreams"
          ],
          "Resource": "*"
      }
  ]
}
EOF
}

resource "aws_iam_policy_attachment" "policy_attachment_for_role" {
    name = "policy_attachment_for_role"
    roles = ["${aws_iam_role.iam_for_lambda.name}"]
    policy_arn = "${aws_iam_policy.policy_for_role.arn}"
}

resource "aws_kinesis_stream" "kinesis_stream_test" {
    name = "kinesis_stream_test"
    shard_count = 1
}

resource "aws_lambda_function" "lambda_function_test_create" {
    filename = "test-fixtures/lambdatest.zip"
    function_name = "example_lambda_name_create"
    role = "${aws_iam_role.iam_for_lambda.arn}"
    handler = "exports.example"
    runtime = "nodejs4.3"
}

resource "aws_lambda_function" "lambda_function_test_update" {
    filename = "test-fixtures/lambdatest.zip"
    function_name = "example_lambda_name_update"
    role = "${aws_iam_role.iam_for_lambda.arn}"
    handler = "exports.example"
    runtime = "nodejs4.3"
}

resource "aws_lambda_event_source_mapping" "lambda_event_source_mapping_test" {
		batch_size = 100
		event_source_arn = "${aws_kinesis_stream.kinesis_stream_test.arn}"
		enabled = true
		depends_on = ["aws_iam_policy_attachment.policy_attachment_for_role"]
		function_name = "${aws_lambda_function.lambda_function_test_create.arn}"
		starting_position = "TRIM_HORIZON"
}
`

const testAccAWSLambdaEventSourceMappingConfigUpdate = `
resource "aws_iam_role" "iam_for_lambda" {
    name = "iam_for_lambda"
    assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_policy" "policy_for_role" {
    name = "policy_for_role"
    path = "/"
    description = "IAM policy for for Lamda event mapping testing"
    policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
      {
          "Effect": "Allow",
          "Action": [
            "kinesis:GetRecords",
            "kinesis:GetShardIterator",
            "kinesis:DescribeStream"
          ],
          "Resource": "*"
      },
      {
          "Effect": "Allow",
          "Action": [
            "kinesis:ListStreams"
          ],
          "Resource": "*"
      }
  ]
}
EOF
}

resource "aws_iam_policy_attachment" "policy_attachment_for_role" {
    name = "policy_attachment_for_role"
    roles = ["${aws_iam_role.iam_for_lambda.name}"]
    policy_arn = "${aws_iam_policy.policy_for_role.arn}"
}

resource "aws_kinesis_stream" "kinesis_stream_test" {
    name = "kinesis_stream_test"
    shard_count = 1
}

resource "aws_lambda_function" "lambda_function_test_create" {
    filename = "test-fixtures/lambdatest.zip"
    function_name = "example_lambda_name_create"
    role = "${aws_iam_role.iam_for_lambda.arn}"
    handler = "exports.example"
    runtime = "nodejs4.3"
}

resource "aws_lambda_function" "lambda_function_test_update" {
    filename = "test-fixtures/lambdatest.zip"
    function_name = "example_lambda_name_update"
    role = "${aws_iam_role.iam_for_lambda.arn}"
    handler = "exports.example"
    runtime = "nodejs4.3"
}

resource "aws_lambda_event_source_mapping" "lambda_event_source_mapping_test" {
		batch_size = 200
		event_source_arn = "${aws_kinesis_stream.kinesis_stream_test.arn}"
		enabled = false
		depends_on = ["aws_iam_policy_attachment.policy_attachment_for_role"]
		function_name = "${aws_lambda_function.lambda_function_test_update.arn}"
		starting_position = "TRIM_HORIZON"
}
`
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"strings"
)

// HelpForShow outputs usage information for the show commands: show, show-vm, show-group, show-account.
func (cmds *CommandSet) HelpForShow() {
	fmt.Println("go-bigv show")
	fmt.Println()
	fmt.Println("usage: go-bigv show [--json] <name>")
	fmt.Println("       go-bigv show vm [--json] <virtual machine>")
	fmt.Println("       go-bigv show group [--json] [--list-vms] [--verbose] <group>")
	fmt.Println("       go-bigv show account [--json] [--list-groups] [--list-vms] [--verbose] <account>")
	fmt.Println()
	fmt.Println("Displays information about the given virtual machine, group, or account.")
	fmt.Println("If the --verbose flag is given to bigv show group or bigv show account, full details are given for each VM.")
	fmt.Println()
}

// ShowVM implements the show-vm command, which is used to display information about BigV VMs. See HelpForShow for the usage information.
func (cmds *CommandSet) ShowVM(args []string) ExitCode {
	flags := MakeCommonFlagSet()
	jsonOut := flags.Bool("json", false, "")
	flags.Parse(args)
	args = cmds.config.ImportFlags(flags)

	cmds.EnsureAuth()

	name := cmds.bigv.ParseVirtualMachineName(args[0])

	vm, err := cmds.bigv.GetVirtualMachine(name)

	if err != nil {
		return processError(err)
	}
	if !cmds.config.Silent() {
		if *jsonOut {
			js, _ := json.MarshalIndent(vm, "", "    ")
			fmt.Println(string(js))
		} else {
			fmt.Println(FormatVirtualMachine(vm))
		}
	}
	return E_SUCCESS

}

// ShowGroup implements the show-group command, which is used to show the BigV group name and ID, as well as the VMs within it.
func (cmds *CommandSet) ShowGroup(args []string) ExitCode {
	flags := MakeCommonFlagSet()
	list := flags.Bool("list-vms", false, "")
	verbose := flags.Bool("verbose", false, "")
	jsonOut := flags.Bool("json", false, "")
	flags.Parse(args)
	args = cmds.config.ImportFlags(flags)

	name := cmds.bigv.ParseGroupName(args[0])

	cmds.EnsureAuth()

	group, err := cmds.bigv.GetGroup(name)

	if err != nil {
		return processError(err)
	}

	if !cmds.config.Silent() {

		if *jsonOut {
			js, _ := json.MarshalIndent(group, "", "    ")
			fmt.Println(string(js))
		} else {
			fmt.Printf("Group %d: %s\r\n", group.ID, group.Name)
			fmt.Println()
			if *list {
				for _, vm := range group.VirtualMachines {
					fmt.Println(vm.Name)
				}
			} else if *verbose {
				fmt.Println(strings.Join(FormatVirtualMachines(group.VirtualMachines), "\r\n"))

			}
		}
	}
	return E_SUCCESS

}

// ShowAccount implements the show-account command, which is used to show the BigV account name, as well as the groups and VMs within it.
func (cmds *CommandSet) ShowAccount(args []string) ExitCode {
	flags := MakeCommonFlagSet()
	listgroups := flags.Bool("list-groups", false, "")
	listvms := flags.Bool("list-vms", false, "")
	verbose := flags.Bool("verbose", false, "")
	jsonOut := flags.Bool("json", false, "")
	flags.Parse(args)
	args = cmds.config.ImportFlags(flags)

	name := cmds.bigv.ParseAccountName(args[0])

	cmds.EnsureAuth()

	acc, err := cmds.bigv.GetAccount(name)

	if err != nil {
		return processError(err)
	}

	if *jsonOut {
		js, _ := json.MarshalIndent(acc, "", "    ")
		fmt.Println(string(js))
	} else {
		fmt.Printf("Account %d: %s", acc.ID, acc.Name)
		fmt.Println()
		switch {
		case *verbose:
			for _, g := range acc.Groups {
				fmt.Printf("Group %s\r\n", g.Name)
				fmt.Println(strings.Join(FormatVirtualMachines(g.VirtualMachines), "\r\n"))
			}
		case *listgroups:
			fmt.Println("Groups:")
			for _, g := range acc.Groups {
				fmt.Println("%s", g.Name)
			}
		case *listvms:
			fmt.Println("Virtual machines:")
			for _, g := range acc.Groups {
				for _, vm := range g.VirtualMachines {
					fmt.Println("%s.%s\r\n", vm.Name, g.Name)
				}
			}
		default:
			vms := 0
			for _, g := range acc.Groups {
				vms += len(g.VirtualMachines)
			}
			fmt.Println("%d groups containing %d virtual machines\r\n", len(acc.Groups), vms)
		}

	}
	return E_SUCCESS

}
<commit_msg>Initial implementation of the show command. Not hooked up yet because I'm too scared<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"strings"
)

// HelpForShow outputs usage information for the show commands: show, show-vm, show-group, show-account.
func (cmds *CommandSet) HelpForShow() {
	fmt.Println("go-bigv show")
	fmt.Println()
	fmt.Println("usage: go-bigv show [--json] <name>")
	fmt.Println("       go-bigv show vm [--json] <virtual machine>")
	fmt.Println("       go-bigv show group [--json] [--list-vms] [--verbose] <group>")
	fmt.Println("       go-bigv show account [--json] [--list-groups] [--list-vms] [--verbose] <account>")
	fmt.Println()
	fmt.Println("Displays information about the given virtual machine, group, or account.")
	fmt.Println("If the --verbose flag is given to bigv show group or bigv show account, full details are given for each VM.")
	fmt.Println()
}

// Show implements the show command which is a stupendous badass of a command
func (cmds *CommandSet) Show(args []string) ExitCode {
	if len(args) == 0 {
		cmds.HelpForShow()
		return E_USAGE_DISPLAYED
	}

	switch strings.ToLower(args[0]) {
	case "vm":
		return cmds.ShowVM(args[1:])
	case "account":
		return cmds.ShowAccount(args[1:])
	case "user":
		fmt.Printf("Leave me alone! I'm grumpy.")
		return 666
		//return ShowUser(args[1:])
	case "group":
		return cmds.ShowGroup(args[1:])
	case "key", "keys":
		fmt.Printf("Leave me alone, I'm grumpy!")
		return 666
		//return cmds.ShowKeys(args[1:])
	}

	name := strings.TrimSuffix(args[0], cmds.config.EndpointName())
	dots := strings.Count(name, ".")
	switch dots {
	case 2:
		return cmds.ShowVM(args)
	case 1:
		return cmds.ShowGroup(args)
	case 0:
		return cmds.ShowAccount(args)
		// TODO: should also try show-vm sprintf("%s.%s.%s", args[0], "default", config.get("user"))
	}
	return E_SUCCESS
}

// ShowVM implements the show-vm command, which is used to display information about BigV VMs. See HelpForShow for the usage information.
func (cmds *CommandSet) ShowVM(args []string) ExitCode {
	flags := MakeCommonFlagSet()
	jsonOut := flags.Bool("json", false, "")
	flags.Parse(args)
	args = cmds.config.ImportFlags(flags)

	cmds.EnsureAuth()

	name := cmds.bigv.ParseVirtualMachineName(args[0])

	vm, err := cmds.bigv.GetVirtualMachine(name)

	if err != nil {
		return processError(err)
	}
	if !cmds.config.Silent() {
		if *jsonOut {
			js, _ := json.MarshalIndent(vm, "", "    ")
			fmt.Println(string(js))
		} else {
			fmt.Println(FormatVirtualMachine(vm))
		}
	}
	return E_SUCCESS

}

// ShowGroup implements the show-group command, which is used to show the BigV group name and ID, as well as the VMs within it.
func (cmds *CommandSet) ShowGroup(args []string) ExitCode {
	flags := MakeCommonFlagSet()
	list := flags.Bool("list-vms", false, "")
	verbose := flags.Bool("verbose", false, "")
	jsonOut := flags.Bool("json", false, "")
	flags.Parse(args)
	args = cmds.config.ImportFlags(flags)

	name := cmds.bigv.ParseGroupName(args[0])

	cmds.EnsureAuth()

	group, err := cmds.bigv.GetGroup(name)

	if err != nil {
		return processError(err)
	}

	if !cmds.config.Silent() {

		if *jsonOut {
			js, _ := json.MarshalIndent(group, "", "    ")
			fmt.Println(string(js))
		} else {
			fmt.Printf("Group %d: %s\r\n", group.ID, group.Name)
			fmt.Println()
			if *list {
				for _, vm := range group.VirtualMachines {
					fmt.Println(vm.Name)
				}
			} else if *verbose {
				fmt.Println(strings.Join(FormatVirtualMachines(group.VirtualMachines), "\r\n"))

			}
		}
	}
	return E_SUCCESS

}

// ShowAccount implements the show-account command, which is used to show the BigV account name, as well as the groups and VMs within it.
func (cmds *CommandSet) ShowAccount(args []string) ExitCode {
	flags := MakeCommonFlagSet()
	listgroups := flags.Bool("list-groups", false, "")
	listvms := flags.Bool("list-vms", false, "")
	verbose := flags.Bool("verbose", false, "")
	jsonOut := flags.Bool("json", false, "")
	flags.Parse(args)
	args = cmds.config.ImportFlags(flags)

	name := cmds.bigv.ParseAccountName(args[0])

	cmds.EnsureAuth()

	acc, err := cmds.bigv.GetAccount(name)

	if err != nil {
		return processError(err)
	}

	if *jsonOut {
		js, _ := json.MarshalIndent(acc, "", "    ")
		fmt.Println(string(js))
	} else {
		fmt.Printf("Account %d: %s", acc.ID, acc.Name)
		fmt.Println()
		switch {
		case *verbose:
			for _, g := range acc.Groups {
				fmt.Printf("Group %s\r\n", g.Name)
				fmt.Println(strings.Join(FormatVirtualMachines(g.VirtualMachines), "\r\n"))
			}
		case *listgroups:
			fmt.Println("Groups:")
			for _, g := range acc.Groups {
				fmt.Println("%s", g.Name)
			}
		case *listvms:
			fmt.Println("Virtual machines:")
			for _, g := range acc.Groups {
				for _, vm := range g.VirtualMachines {
					fmt.Println("%s.%s\r\n", vm.Name, g.Name)
				}
			}
		default:
			vms := 0
			for _, g := range acc.Groups {
				vms += len(g.VirtualMachines)
			}
			fmt.Println("%d groups containing %d virtual machines\r\n", len(acc.Groups), vms)
		}

	}
	return E_SUCCESS

}
<|endoftext|>


<commit_before>package tokens

import (
	"github.com/dgrijalva/jwt-go"
	"github.com/leanovate/microzon-auth-go/certificates"
	"testing"
	"time"
)

func assertNoError(t *testing.T, err error) {
	if err != nil {
		t.Errorf("Test failed: %s", err.Error())
	}
}

func assertEquals(t *testing.T, x interface{}, y interface{}) {
	if x != y {
		t.Errorf("Test failed: %s != %s", x, y)
	}
}

func TestCreateToken(t *testing.T) {
	cert, err := certificates.NewCertWithKey("test", 10*time.Minute, 10*time.Minute)
	tokeninfo, err := newTokenInfo("test", "user", time.Now(), cert)
	assertNoError(t, err)
	assertRawTokenInfo(t, tokeninfo.Raw, cert)
	assertEquals(t, tokeninfo.X5T, cert.Thumbprint)
}

func assertRawTokenInfo(t *testing.T, tokeninfo string, cert *certificates.CertWithKey) {
	handler := func(token *jwt.Token) (interface{}, error) {
		return cert.Certificate.PublicKey, nil
	}
	result, err := jwt.Parse(tokeninfo, handler)
	assertNoError(t, err)
	x5t := result.Header["x5t"]
	assertEquals(t, x5t, cert.Thumbprint)
}
<commit_msg>Stabilize test<commit_after>package tokens

import (
	"github.com/dgrijalva/jwt-go"
	"github.com/leanovate/microzon-auth-go/certificates"
	"testing"
	"time"
)

func assertNoError(t *testing.T, err error) {
	if err != nil {
		t.Errorf("Test failed: %s", err.Error())
	}
}

func assertEquals(t *testing.T, x interface{}, y interface{}) {
	if x != y {
		t.Errorf("Test failed: %s != %s", x, y)
	}
}

func TestCreateToken(t *testing.T) {
	cert, err := certificates.NewCertWithKey("test", 10*time.Minute, 10*time.Minute)
	tokeninfo, err := newTokenInfo("test", "user", time.Now().Add(time.Minute), cert)
	assertNoError(t, err)
	assertRawTokenInfo(t, tokeninfo.Raw, cert)
	assertEquals(t, tokeninfo.X5T, cert.Thumbprint)
}

func assertRawTokenInfo(t *testing.T, tokeninfo string, cert *certificates.CertWithKey) {
	handler := func(token *jwt.Token) (interface{}, error) {
		return cert.Certificate.PublicKey, nil
	}
	result, err := jwt.Parse(tokeninfo, handler)
	assertNoError(t, err)
	x5t := result.Header["x5t"]
	assertEquals(t, x5t, cert.Thumbprint)
}
<|endoftext|>


<commit_before>package effio

// encoding/csv doesn't strip whitespace and does a fair bit of
// work to handle strings & quoting which are totally unnecessary
// overhead for these files so skip it

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
	"strconv"
	"strings"
)

// the input is ints but plotinum uses float64 so might as well
// start there and avoid the type conversions later
type LatRec struct {
	time float64 // time offset from beginning of fio run
	perf float64 // latency value
	ddir uint8   // 0 = read, 1 = write, 2 = trim
	bsz  uint16  // block size
}

type LatRecs []LatRec

// Loads the CSV output by fio into an LatRecs array of LatRec structs.
func LoadCSV(filename string) LatRecs {
	fmt.Printf("Parsing file: '%s' ... ", filename)

	fd, err := os.Open(filename)
	if err != nil {
		fmt.Printf(" Failed.\nCould not open file '%s' for read: %s\n", filename, err)
		return LatRecs{}
	}
	defer fd.Close()

	records := make(LatRecs, 0)
	var time, perf float64
	var ddir, bsz int
	bfd := bufio.NewReader(fd)
	var lno int = 0
	for {
		line, _, err := bfd.ReadLine()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatalf("\nRead from file '%s' failed: %s", filename, err)
		}
		lno++

		if lno%10000 == 0 {
			fmt.Printf(".")
		}

		// fio always uses ", " instead of "," as far as I can tell
		r := strings.SplitN(string(line), ", ", 4)
		// probably an impartial record at the end of the file
		if len(r) < 4 || r[0] == "" || r[1] == "" {
			continue
		}

		time, err = strconv.ParseFloat(r[0], 64)
		if err != nil {
			log.Fatalf("\nParsing field 0 failed in file '%s' at line %d: %s", filename, lno, err)
		}
		perf, err = strconv.ParseFloat(r[1], 64)
		if err != nil {
			log.Fatalf("\nParsing field 1 in file '%s' at line %d: %s", filename, lno, err)
		}
		ddir, err = strconv.Atoi(r[2])
		if err != nil {
			log.Fatalf("\nParsing field 2 failed in file '%s' at line %d: %s", filename, lno, err)
		}
		bsz, err = strconv.Atoi(r[3])
		if err != nil {
			log.Fatalf("\nParsing field 3 failed in file '%s' at line %d: %s", filename, lno, err)
		}

		lr := LatRec{time, perf, uint8(ddir), uint16(bsz)}
		records = append(records, lr)
	}
	fmt.Println(" Done.")

	return records
}

// implement some plotinum interfaces
func (lrs LatRecs) Len() int {
	return len(lrs)
}

func (lrs LatRecs) XY(i int) (float64, float64) {
	return lrs[i].time, lrs[i].perf
}

func (lrs LatRecs) Value(i int) float64 {
	return lrs[i].perf
}

func (lrs LatRecs) Values(i int) (vals []float64) {
	for _, l := range lrs {
		vals = append(vals, l.perf)
	}
	return
}

// m := lrs.Map(func (in LatRec) (out LatRec, err error) { return })
func (lrs LatRecs) Map(fun func(LatRec)(LatRec, error)) (out LatRecs, err error) {
	out = make(LatRecs, len(lrs))

	for i, lr := range lrs {
		out[i], err = fun(lr)
		if err != nil {
			return
		}
	}

	return
}

// filtered := lrs.Filter(func (in LatRec) (bool, error) { return true, nil })
func (lrs LatRecs) Filter(fun func(LatRec)(bool, error)) (out LatRecs, err error) {
	out = make(LatRecs, len(lrs))

	var c int
	var ok bool
	for _, lr := range lrs {
		ok, err = fun(lr)
		if err != nil {
			return
		}
		if ok {
			out[c] = lr
			c++
		}
	}

	return out[0:c], nil
}

// lr := lrs.Reduce(func (in LatRec, acc LatRec) (out LatRec, err error) { })
func (lrs LatRecs) Reduce(fun func(LatRec, LatRec)(LatRec, error), init LatRec) (out LatRec, err error) {
	out = init
	for _, lr := range lrs {
		out, err = fun(lr, out)
		if err != nil {
			return
		}
	}
	return
}

// reduces the number of data points to sz by taking the mean across buckets
// TODO: this is kinda broken on bidirectional tests since it will merge the IOs
// down to one direction blindly
func (lrs LatRecs) Histogram(sz int) (out LatRecs) {
	if sz > len(lrs) {
		log.Fatalf("Error: Histogram(%d) is smaller than the dataset of length %d.", sz, len(lrs))
	}

	bktsz := len(lrs) / sz
	log.Printf("Bucket size for %d/%d is %d\n", len(lrs), sz, bktsz)

	var total, time float64
	var bsz uint16
	var ddir uint8
	var count int = 0
	for _, v := range lrs {
		if count == 0 {
			time = v.time
			bsz = v.bsz
			ddir = v.ddir // wrong!
			total = 0.0
		}

		total += v.perf
		count++

		if count == bktsz {
			val := total / float64(count)
			out = append(out, LatRec{time, val, ddir, bsz})
			count = 0
			continue
		}
	}
	return
}
<commit_msg>go fmt<commit_after>package effio

// encoding/csv doesn't strip whitespace and does a fair bit of
// work to handle strings & quoting which are totally unnecessary
// overhead for these files so skip it

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
	"strconv"
	"strings"
)

// the input is ints but plotinum uses float64 so might as well
// start there and avoid the type conversions later
type LatRec struct {
	time float64 // time offset from beginning of fio run
	perf float64 // latency value
	ddir uint8   // 0 = read, 1 = write, 2 = trim
	bsz  uint16  // block size
}

type LatRecs []LatRec

// Loads the CSV output by fio into an LatRecs array of LatRec structs.
func LoadCSV(filename string) LatRecs {
	fmt.Printf("Parsing file: '%s' ... ", filename)

	fd, err := os.Open(filename)
	if err != nil {
		fmt.Printf(" Failed.\nCould not open file '%s' for read: %s\n", filename, err)
		return LatRecs{}
	}
	defer fd.Close()

	records := make(LatRecs, 0)
	var time, perf float64
	var ddir, bsz int
	bfd := bufio.NewReader(fd)
	var lno int = 0
	for {
		line, _, err := bfd.ReadLine()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatalf("\nRead from file '%s' failed: %s", filename, err)
		}
		lno++

		if lno%10000 == 0 {
			fmt.Printf(".")
		}

		// fio always uses ", " instead of "," as far as I can tell
		r := strings.SplitN(string(line), ", ", 4)
		// probably an impartial record at the end of the file
		if len(r) < 4 || r[0] == "" || r[1] == "" {
			continue
		}

		time, err = strconv.ParseFloat(r[0], 64)
		if err != nil {
			log.Fatalf("\nParsing field 0 failed in file '%s' at line %d: %s", filename, lno, err)
		}
		perf, err = strconv.ParseFloat(r[1], 64)
		if err != nil {
			log.Fatalf("\nParsing field 1 in file '%s' at line %d: %s", filename, lno, err)
		}
		ddir, err = strconv.Atoi(r[2])
		if err != nil {
			log.Fatalf("\nParsing field 2 failed in file '%s' at line %d: %s", filename, lno, err)
		}
		bsz, err = strconv.Atoi(r[3])
		if err != nil {
			log.Fatalf("\nParsing field 3 failed in file '%s' at line %d: %s", filename, lno, err)
		}

		lr := LatRec{time, perf, uint8(ddir), uint16(bsz)}
		records = append(records, lr)
	}
	fmt.Println(" Done.")

	return records
}

// implement some plotinum interfaces
func (lrs LatRecs) Len() int {
	return len(lrs)
}

func (lrs LatRecs) XY(i int) (float64, float64) {
	return lrs[i].time, lrs[i].perf
}

func (lrs LatRecs) Value(i int) float64 {
	return lrs[i].perf
}

func (lrs LatRecs) Values(i int) (vals []float64) {
	for _, l := range lrs {
		vals = append(vals, l.perf)
	}
	return
}

// m := lrs.Map(func (in LatRec) (out LatRec, err error) { return })
func (lrs LatRecs) Map(fun func(LatRec) (LatRec, error)) (out LatRecs, err error) {
	out = make(LatRecs, len(lrs))

	for i, lr := range lrs {
		out[i], err = fun(lr)
		if err != nil {
			return
		}
	}

	return
}

// filtered := lrs.Filter(func (in LatRec) (bool, error) { return true, nil })
func (lrs LatRecs) Filter(fun func(LatRec) (bool, error)) (out LatRecs, err error) {
	out = make(LatRecs, len(lrs))

	var c int
	var ok bool
	for _, lr := range lrs {
		ok, err = fun(lr)
		if err != nil {
			return
		}
		if ok {
			out[c] = lr
			c++
		}
	}

	return out[0:c], nil
}

// lr := lrs.Reduce(func (in LatRec, acc LatRec) (out LatRec, err error) { })
func (lrs LatRecs) Reduce(fun func(LatRec, LatRec) (LatRec, error), init LatRec) (out LatRec, err error) {
	out = init
	for _, lr := range lrs {
		out, err = fun(lr, out)
		if err != nil {
			return
		}
	}
	return
}

// reduces the number of data points to sz by taking the mean across buckets
// TODO: this is kinda broken on bidirectional tests since it will merge the IOs
// down to one direction blindly
func (lrs LatRecs) Histogram(sz int) (out LatRecs) {
	if sz > len(lrs) {
		log.Fatalf("Error: Histogram(%d) is smaller than the dataset of length %d.", sz, len(lrs))
	}

	bktsz := len(lrs) / sz
	log.Printf("Bucket size for %d/%d is %d\n", len(lrs), sz, bktsz)

	var total, time float64
	var bsz uint16
	var ddir uint8
	var count int = 0
	for _, v := range lrs {
		if count == 0 {
			time = v.time
			bsz = v.bsz
			ddir = v.ddir // wrong!
			total = 0.0
		}

		total += v.perf
		count++

		if count == bktsz {
			val := total / float64(count)
			out = append(out, LatRec{time, val, ddir, bsz})
			count = 0
			continue
		}
	}
	return
}
<|endoftext|>


<commit_before>package apply_test

import (
	"testing"

	"fmt"

	"github.com/codegangsta/cli"
	"github.com/syou6162/go-active-learning/lib/command"
	"github.com/syou6162/go-active-learning/lib/db"
	"github.com/syou6162/go-active-learning/lib/util/file"
)

func TestDoApply(t *testing.T) {
	inputFilename := "../../tech_input_example.txt"
	train, err := file.ReadExamples(inputFilename)
	if err != nil {
		t.Error(err)
	}

	conn, err := db.CreateDBConnection()
	if err != nil {
		t.Error(err)
	}
	defer conn.Close()

	_, err = db.DeleteAllExamples(conn)
	if err != nil {
		t.Error(err)
	}

	for _, example := range train {
		_, err = db.InsertOrUpdateExample(conn, example)
		if err != nil {
			t.Error(err)
		}
	}

	app := cli.NewApp()
	app.Commands = command.Commands
	args := []string{
		"go-active-learning",
		"apply",
		"--filter-status-code-ok",
		"--json-output",
		"--subset-selection",
		"-r=0.75",
		"--size-constraint=20",
		"--score-threshold=0.1",
	}

	if err := app.Run(args); err != nil {
		t.Error(err)
	}
}
<commit_msg>unused<commit_after>package apply_test

import (
	"testing"

	"github.com/codegangsta/cli"
	"github.com/syou6162/go-active-learning/lib/command"
	"github.com/syou6162/go-active-learning/lib/db"
	"github.com/syou6162/go-active-learning/lib/util/file"
)

func TestDoApply(t *testing.T) {
	inputFilename := "../../tech_input_example.txt"
	train, err := file.ReadExamples(inputFilename)
	if err != nil {
		t.Error(err)
	}

	conn, err := db.CreateDBConnection()
	if err != nil {
		t.Error(err)
	}
	defer conn.Close()

	_, err = db.DeleteAllExamples(conn)
	if err != nil {
		t.Error(err)
	}

	for _, example := range train {
		_, err = db.InsertOrUpdateExample(conn, example)
		if err != nil {
			t.Error(err)
		}
	}

	app := cli.NewApp()
	app.Commands = command.Commands
	args := []string{
		"go-active-learning",
		"apply",
		"--filter-status-code-ok",
		"--json-output",
		"--subset-selection",
		"-r=0.75",
		"--size-constraint=20",
		"--score-threshold=0.1",
	}

	if err := app.Run(args); err != nil {
		t.Error(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/sha1"
	"encoding/base64"
	"encoding/binary"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/boltdb/bolt"
	"github.com/golang/protobuf/proto"
	"golang.org/x/crypto/pbkdf2"
	"golang.org/x/net/context"
	"google.golang.org/grpc"

	pb "../proto"
)

const (
	bnotifyPackageName = "cc.bran.bnotify"
	gcmSendAddress     = "https://android.googleapis.com/gcm/send"
	aesKeySize         = 16
	pbkdfIterCount     = 400000
	serverIDSize       = 16
)

var (
	port             = flag.Int("port", 50051, "port to listen for RPCs on")
	settingsFilename = flag.String("settings", "bnotify.conf", "filename of settings file")
	stateFilename    = flag.String("state", "bnotify.state", "filename of state file")

	waits = []time.Duration{0, time.Second, 2 * time.Second, 4 * time.Second, 8 * time.Second, 16 * time.Second, time.Minute, 2 * time.Minute, 4 * time.Minute, 8 * time.Minute, 16 * time.Minute}
)

type notificationService struct {
	db             *bolt.DB
	apiKey         string
	registrationID string
	gcmCipher      cipher.AEAD
}

func (ns *notificationService) SendNotification(ctx context.Context, req *pb.SendNotificationRequest) (*pb.SendNotificationResponse, error) {
	// Verify request.
	if req.Notification.Title == "" {
		return nil, errors.New("notification missing title")
	}
	if req.Notification.Text == "" {
		return nil, errors.New("notification missing text")
	}

	// Enqueue request into state.
	var seq uint64
	if err := ns.db.Batch(func(tx *bolt.Tx) error {
		// Read server ID & allocate sequence number.
		settingsBucket := tx.Bucket([]byte("settings"))
		if settingsBucket == nil {
			return errors.New("missing settings bucket")
		}
		serverID := settingsBucket.Get([]byte("serverID"))
		if serverID == nil {
			return errors.New("missing serverID")
		}

		messagesBucket := tx.Bucket([]byte("pending_messages"))
		if messagesBucket == nil {
			return errors.New("missing pending_messages bucket")
		}
		theSeq, err := messagesBucket.NextSequence()
		if err != nil {
			return fmt.Errorf("could not allocate sequence number: %v", err)
		}
		seq = theSeq

		// Marshal request.
		plaintextMessage, err := proto.Marshal(&pb.Message{
			ServerId:     serverID,
			Seq:          seq,
			Notification: req.Notification,
		})
		if err != nil {
			return fmt.Errorf("could not marshal message proto: %v", err)
		}

		// Compute nonce = serverID || seq & encrypt.
		key := make([]byte, binary.Size(seq))
		binary.BigEndian.PutUint64(key, seq)
		nonce := append(serverID, key...)
		message := ns.gcmCipher.Seal(nil, nonce, plaintextMessage, nil)

		// Fill out final envelope & pending payload protos, then write to storage.
		payload, err := proto.Marshal(&pb.Envelope{
			Message: message,
			Nonce:   nonce,
		})
		if err != nil {
			return fmt.Errorf("could not marshal envelope proto: %v", err)
		}
		pendingPayload, err := proto.Marshal(&pb.PendingPayload{
			Payload: payload,
		})
		if err != nil {
			return fmt.Errorf("could not marshal pending payload proto: %v", err)
		}
		if err := messagesBucket.Put(key, pendingPayload); err != nil {
			return fmt.Errorf("could not write message to state: %v", err)
		}
		return nil
	}); err != nil {
		log.Printf("Error while posting notification: %v", err)
		return nil, errors.New("internal error")
	}

	// Kick off goroutine to actually send notification and return success.
	go ns.sendPayload(seq)
	return &pb.SendNotificationResponse{}, nil
}

func (ns *notificationService) sendPayload(seq uint64) {
	key := make([]byte, binary.Size(seq))
	binary.BigEndian.PutUint64(key, seq)

	for {
		// Read & update payload in state.
		var payload []byte
		var sendAttempts int
		if err := ns.db.Batch(func(tx *bolt.Tx) error {
			messagesBucket := tx.Bucket([]byte("pending_messages"))
			if messagesBucket == nil {
				return errors.New("missing pending_messages bucket")
			}
			ppBytes := messagesBucket.Get(key)
			if ppBytes == nil {
				return errors.New("pending payload missing from state")
			}
			pendingPayload := &pb.PendingPayload{}
			if err := proto.Unmarshal(ppBytes, pendingPayload); err != nil {
				return fmt.Errorf("could not unmarshal pending payload: %v", err)
			}
			payload = pendingPayload.Payload
			sendAttempts = int(pendingPayload.SendAttempts)
			if sendAttempts < len(waits) {
				pendingPayload.SendAttempts++
				ppBytes, err := proto.Marshal(pendingPayload)
				if err != nil {
					return fmt.Errorf("could not marshal pending payload: %v", err)
				}
				if err := messagesBucket.Put(key, ppBytes); err != nil {
					return fmt.Errorf("could not write pending payload: %v", err)
				}
			} else {
				// We are out of retries.
				if err := messagesBucket.Delete(key); err != nil {
					return fmt.Errorf("could not delete pending payload: %v", err)
				}
			}
			return nil
		}); err != nil {
			// Most/all errors that occur here are unrecoverable, so give up.
			log.Printf("[%d] Could not read and update payload: %v", seq, err)
			return
		}
		if sendAttempts >= len(waits) {
			log.Printf("[%d] Too many retries, giving up", seq)
		}
		waitTime := waits[sendAttempts]
		if waitTime > 0 {
			log.Printf("[%d] Waiting %v before retry", seq, waitTime)
			time.Sleep(waitTime)
		}

		// Post notification.
		if err := ns.postPayloadToGCM(payload); err != nil {
			log.Printf("[%d] Could not post notification: %v", seq, err)
			continue
		}

		// Remove sent notification from the pending queue.
		if err := ns.db.Batch(func(tx *bolt.Tx) error {
			messagesBucket := tx.Bucket([]byte("pending_messages"))
			if messagesBucket == nil {
				return errors.New("missing pending_messages bucket")
			}
			if err := messagesBucket.Delete(key); err != nil {
				return fmt.Errorf("error while deleting sent message: %v", err)
			}
			return nil
		}); err != nil {
			// We'll return; I guess we'll try to clean up again whenever the server restarts.
			log.Printf("[%d] Could not remove notification: %v", seq, err)
		}
		return
	}
}

func (ns *notificationService) postPayloadToGCM(payload []byte) error {
	// Set up request.
	values := url.Values{}
	values.Set("restricted_package_name", bnotifyPackageName)
	values.Set("registration_id", ns.registrationID)
	values.Set("data.payload", base64.StdEncoding.EncodeToString(payload))

	req, err := http.NewRequest("POST", gcmSendAddress, strings.NewReader(values.Encode()))
	if err != nil {
		return err
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
	req.Header.Add("Authorization", fmt.Sprintf("key=%s", ns.apiKey))

	// Make request to GCM server.
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// Check for HTTP error code.
	if resp.StatusCode != 200 {
		return fmt.Errorf("GCM HTTP error: %v", resp.Status)
	}

	// Read the first line of the response and figure out if it indicates a GCM-level error.
	bodyReader := bufio.NewReader(resp.Body)
	lineBytes, _, err := bodyReader.ReadLine()
	if err != nil {
		return err
	}
	line := string(lineBytes)
	if strings.HasPrefix(line, "Error=") {
		return fmt.Errorf("GCM error: %v", strings.TrimPrefix(line, "Error="))
	}
	return nil
}

func main() {
	flag.Parse()

	// Read settings.
	settingsBytes, err := ioutil.ReadFile(*settingsFilename)
	if err != nil {
		log.Fatalf("Error reading settings file: %v", err)
	}
	settings := &pb.BNotifySettings{}
	err = proto.UnmarshalText(string(settingsBytes), settings)
	if err != nil {
		log.Fatalf("Error reading settings file: %v", err)
	}

	// Open state database & initialize if need be.
	db, err := bolt.Open(*stateFilename, 0640, &bolt.Options{Timeout: time.Second})
	if err != nil {
		log.Fatalf("Error opening state file: %v", err)
	}
	defer db.Close()

	var serverID []byte
	var pendingSeqs []uint64
	if err := db.Update(func(tx *bolt.Tx) error {
		messagesBucket, err := tx.CreateBucketIfNotExists([]byte("pending_messages"))
		if err != nil {
			return fmt.Errorf("could not create pending_messages bucket: %v", err)
		}
		messagesBucket.ForEach(func(key, _ []byte) error {
			pendingSeqs = append(pendingSeqs, binary.BigEndian.Uint64(key))
			return nil
		})

		settingsBucket, err := tx.CreateBucketIfNotExists([]byte("settings"))
		if err != nil {
			return fmt.Errorf("error creating settings bucket: %v", err)
		}
		if serverID = settingsBucket.Get([]byte("serverID")); serverID == nil {
			serverID = make([]byte, serverIDSize)
			if _, err := rand.Read(serverID); err != nil {
				return fmt.Errorf("error generating server ID: %v", err)
			}
			if err := settingsBucket.Put([]byte("serverID"), serverID); err != nil {
				return fmt.Errorf("error setting server ID: %v", err)
			}
		}
		return nil
	}); err != nil {
		log.Fatalf("Error initializing state file: %v", err)
	}

	// Derive key from password & salt (registration ID).
	key := pbkdf2.Key([]byte(settings.Password), []byte(settings.RegistrationId), pbkdfIterCount, aesKeySize, sha1.New)

	// Initialize cipher based on key.
	blockCipher, err := aes.NewCipher(key)
	if err != nil {
		log.Fatalf("Error initializing block cipher: %v", err)
	}
	gcmCipher, err := cipher.NewGCMWithNonceSize(blockCipher, len(serverID)+binary.Size(uint64(0)))
	if err != nil {
		log.Fatalf("Error initializing GCM cipher: %v", err)
	}

	// Create service, socket, and gRPC server objects.
	service := &notificationService{
		db:             db,
		apiKey:         settings.ApiKey,
		registrationID: settings.RegistrationId,
		gcmCipher:      gcmCipher,
	}
	listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", *port))
	if err != nil {
		log.Fatalf("Error listening on port %d: %v", *port, err)
	}
	defer listener.Close()
	server := grpc.NewServer()
	pb.RegisterNotificationServiceServer(server, service)

	// Begin serving.
	for _, seq := range pendingSeqs {
		go service.sendPayload(seq)
	}
	log.Printf("Listening for requests on port %d", *port)
	server.Serve(listener)
}
<commit_msg>bnotifyd: fix crash on running out of retries.<commit_after>package main

import (
	"bufio"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/sha1"
	"encoding/base64"
	"encoding/binary"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/boltdb/bolt"
	"github.com/golang/protobuf/proto"
	"golang.org/x/crypto/pbkdf2"
	"golang.org/x/net/context"
	"google.golang.org/grpc"

	pb "../proto"
)

const (
	bnotifyPackageName = "cc.bran.bnotify"
	gcmSendAddress     = "https://android.googleapis.com/gcm/send"
	aesKeySize         = 16
	pbkdfIterCount     = 400000
	serverIDSize       = 16
)

var (
	port             = flag.Int("port", 50051, "port to listen for RPCs on")
	settingsFilename = flag.String("settings", "bnotify.conf", "filename of settings file")
	stateFilename    = flag.String("state", "bnotify.state", "filename of state file")

	waits = []time.Duration{0, time.Second, 2 * time.Second, 4 * time.Second, 8 * time.Second, 16 * time.Second, time.Minute, 2 * time.Minute, 4 * time.Minute, 8 * time.Minute, 16 * time.Minute}
)

type notificationService struct {
	db             *bolt.DB
	apiKey         string
	registrationID string
	gcmCipher      cipher.AEAD
}

func (ns *notificationService) SendNotification(ctx context.Context, req *pb.SendNotificationRequest) (*pb.SendNotificationResponse, error) {
	// Verify request.
	if req.Notification.Title == "" {
		return nil, errors.New("notification missing title")
	}
	if req.Notification.Text == "" {
		return nil, errors.New("notification missing text")
	}

	// Enqueue request into state.
	var seq uint64
	if err := ns.db.Batch(func(tx *bolt.Tx) error {
		// Read server ID & allocate sequence number.
		settingsBucket := tx.Bucket([]byte("settings"))
		if settingsBucket == nil {
			return errors.New("missing settings bucket")
		}
		serverID := settingsBucket.Get([]byte("serverID"))
		if serverID == nil {
			return errors.New("missing serverID")
		}

		messagesBucket := tx.Bucket([]byte("pending_messages"))
		if messagesBucket == nil {
			return errors.New("missing pending_messages bucket")
		}
		theSeq, err := messagesBucket.NextSequence()
		if err != nil {
			return fmt.Errorf("could not allocate sequence number: %v", err)
		}
		seq = theSeq

		// Marshal request.
		plaintextMessage, err := proto.Marshal(&pb.Message{
			ServerId:     serverID,
			Seq:          seq,
			Notification: req.Notification,
		})
		if err != nil {
			return fmt.Errorf("could not marshal message proto: %v", err)
		}

		// Compute nonce = serverID || seq & encrypt.
		key := make([]byte, binary.Size(seq))
		binary.BigEndian.PutUint64(key, seq)
		nonce := append(serverID, key...)
		message := ns.gcmCipher.Seal(nil, nonce, plaintextMessage, nil)

		// Fill out final envelope & pending payload protos, then write to storage.
		payload, err := proto.Marshal(&pb.Envelope{
			Message: message,
			Nonce:   nonce,
		})
		if err != nil {
			return fmt.Errorf("could not marshal envelope proto: %v", err)
		}
		pendingPayload, err := proto.Marshal(&pb.PendingPayload{
			Payload: payload,
		})
		if err != nil {
			return fmt.Errorf("could not marshal pending payload proto: %v", err)
		}
		if err := messagesBucket.Put(key, pendingPayload); err != nil {
			return fmt.Errorf("could not write message to state: %v", err)
		}
		return nil
	}); err != nil {
		log.Printf("Error while posting notification: %v", err)
		return nil, errors.New("internal error")
	}

	// Kick off goroutine to actually send notification and return success.
	go ns.sendPayload(seq)
	return &pb.SendNotificationResponse{}, nil
}

func (ns *notificationService) sendPayload(seq uint64) {
	key := make([]byte, binary.Size(seq))
	binary.BigEndian.PutUint64(key, seq)

	for {
		// Read & update payload in state.
		var payload []byte
		var sendAttempts int
		if err := ns.db.Batch(func(tx *bolt.Tx) error {
			messagesBucket := tx.Bucket([]byte("pending_messages"))
			if messagesBucket == nil {
				return errors.New("missing pending_messages bucket")
			}
			ppBytes := messagesBucket.Get(key)
			if ppBytes == nil {
				return errors.New("pending payload missing from state")
			}
			pendingPayload := &pb.PendingPayload{}
			if err := proto.Unmarshal(ppBytes, pendingPayload); err != nil {
				return fmt.Errorf("could not unmarshal pending payload: %v", err)
			}
			payload = pendingPayload.Payload
			sendAttempts = int(pendingPayload.SendAttempts)
			if sendAttempts < len(waits) {
				pendingPayload.SendAttempts++
				ppBytes, err := proto.Marshal(pendingPayload)
				if err != nil {
					return fmt.Errorf("could not marshal pending payload: %v", err)
				}
				if err := messagesBucket.Put(key, ppBytes); err != nil {
					return fmt.Errorf("could not write pending payload: %v", err)
				}
			} else {
				// We are out of retries.
				if err := messagesBucket.Delete(key); err != nil {
					return fmt.Errorf("could not delete pending payload: %v", err)
				}
			}
			return nil
		}); err != nil {
			// Most/all errors that occur here are unrecoverable, so give up.
			log.Printf("[%d] Could not read and update payload: %v", seq, err)
			return
		}
		if sendAttempts >= len(waits) {
			log.Printf("[%d] Too many retries, giving up", seq)
			return
		}
		waitTime := waits[sendAttempts]
		if waitTime > 0 {
			log.Printf("[%d] Waiting %v before retry", seq, waitTime)
			time.Sleep(waitTime)
		}

		// Post notification.
		if err := ns.postPayloadToGCM(payload); err != nil {
			log.Printf("[%d] Could not post notification: %v", seq, err)
			continue
		}

		// Remove sent notification from the pending queue.
		if err := ns.db.Batch(func(tx *bolt.Tx) error {
			messagesBucket := tx.Bucket([]byte("pending_messages"))
			if messagesBucket == nil {
				return errors.New("missing pending_messages bucket")
			}
			if err := messagesBucket.Delete(key); err != nil {
				return fmt.Errorf("error while deleting sent message: %v", err)
			}
			return nil
		}); err != nil {
			// We'll return; I guess we'll try to clean up again whenever the server restarts.
			log.Printf("[%d] Could not remove notification: %v", seq, err)
		}
		return
	}
}

func (ns *notificationService) postPayloadToGCM(payload []byte) error {
	// Set up request.
	values := url.Values{}
	values.Set("restricted_package_name", bnotifyPackageName)
	values.Set("registration_id", ns.registrationID)
	values.Set("data.payload", base64.StdEncoding.EncodeToString(payload))

	req, err := http.NewRequest("POST", gcmSendAddress, strings.NewReader(values.Encode()))
	if err != nil {
		return err
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
	req.Header.Add("Authorization", fmt.Sprintf("key=%s", ns.apiKey))

	// Make request to GCM server.
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// Check for HTTP error code.
	if resp.StatusCode != 200 {
		return fmt.Errorf("GCM HTTP error: %v", resp.Status)
	}

	// Read the first line of the response and figure out if it indicates a GCM-level error.
	bodyReader := bufio.NewReader(resp.Body)
	lineBytes, _, err := bodyReader.ReadLine()
	if err != nil {
		return err
	}
	line := string(lineBytes)
	if strings.HasPrefix(line, "Error=") {
		return fmt.Errorf("GCM error: %v", strings.TrimPrefix(line, "Error="))
	}
	return nil
}

func main() {
	flag.Parse()

	// Read settings.
	settingsBytes, err := ioutil.ReadFile(*settingsFilename)
	if err != nil {
		log.Fatalf("Error reading settings file: %v", err)
	}
	settings := &pb.BNotifySettings{}
	err = proto.UnmarshalText(string(settingsBytes), settings)
	if err != nil {
		log.Fatalf("Error reading settings file: %v", err)
	}

	// Open state database & initialize if need be.
	db, err := bolt.Open(*stateFilename, 0640, &bolt.Options{Timeout: time.Second})
	if err != nil {
		log.Fatalf("Error opening state file: %v", err)
	}
	defer db.Close()

	var serverID []byte
	var pendingSeqs []uint64
	if err := db.Update(func(tx *bolt.Tx) error {
		messagesBucket, err := tx.CreateBucketIfNotExists([]byte("pending_messages"))
		if err != nil {
			return fmt.Errorf("could not create pending_messages bucket: %v", err)
		}
		messagesBucket.ForEach(func(key, _ []byte) error {
			pendingSeqs = append(pendingSeqs, binary.BigEndian.Uint64(key))
			return nil
		})

		settingsBucket, err := tx.CreateBucketIfNotExists([]byte("settings"))
		if err != nil {
			return fmt.Errorf("error creating settings bucket: %v", err)
		}
		if serverID = settingsBucket.Get([]byte("serverID")); serverID == nil {
			serverID = make([]byte, serverIDSize)
			if _, err := rand.Read(serverID); err != nil {
				return fmt.Errorf("error generating server ID: %v", err)
			}
			if err := settingsBucket.Put([]byte("serverID"), serverID); err != nil {
				return fmt.Errorf("error setting server ID: %v", err)
			}
		}
		return nil
	}); err != nil {
		log.Fatalf("Error initializing state file: %v", err)
	}

	// Derive key from password & salt (registration ID).
	key := pbkdf2.Key([]byte(settings.Password), []byte(settings.RegistrationId), pbkdfIterCount, aesKeySize, sha1.New)

	// Initialize cipher based on key.
	blockCipher, err := aes.NewCipher(key)
	if err != nil {
		log.Fatalf("Error initializing block cipher: %v", err)
	}
	gcmCipher, err := cipher.NewGCMWithNonceSize(blockCipher, len(serverID)+binary.Size(uint64(0)))
	if err != nil {
		log.Fatalf("Error initializing GCM cipher: %v", err)
	}

	// Create service, socket, and gRPC server objects.
	service := &notificationService{
		db:             db,
		apiKey:         settings.ApiKey,
		registrationID: settings.RegistrationId,
		gcmCipher:      gcmCipher,
	}
	listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", *port))
	if err != nil {
		log.Fatalf("Error listening on port %d: %v", *port, err)
	}
	defer listener.Close()
	server := grpc.NewServer()
	pb.RegisterNotificationServiceServer(server, service)

	// Begin serving.
	for _, seq := range pendingSeqs {
		go service.sendPayload(seq)
	}
	log.Printf("Listening for requests on port %d", *port)
	server.Serve(listener)
}
<|endoftext|>


<commit_before>package cache

import (
	"fmt"
	"testing"

	"github.com/syou6162/go-active-learning/lib/example"
)

func TestCacheGet(t *testing.T) {
	c, err := NewCache()
	if err != nil {
		t.Error("Cannot connect to redis")
	}
	defer c.Close()

	example := example.NewExample("http://a.hatena.ne.jp", example.POSITIVE)
	c.Client.Del("url:http://a.hatena.ne.jp")
	e, ok := c.GetExample(*example)
	if ok {
		t.Error(fmt.Printf("Cache must not contain %s", example.Url))
	}

	c.AddExample(*example)
	e, ok = c.GetExample(*example)
	if !ok {
		t.Error(fmt.Printf("Cache must return %s", example.Url))
	}
	if example.Url != e.Url {
		t.Error(fmt.Printf("Urls must be same(%s, %s)", example.Url, e.Url))
	}
}

func TestAttachMetaData(t *testing.T) {
	e1 := example.NewExample("http://b.hatena.ne.jp", example.POSITIVE)
	e2 := example.NewExample("http://www.yasuhisay.info", example.NEGATIVE)
	e3 := example.NewExample("https://github.com", example.UNLABELED)
	examples := example.Examples{e1, e2, e3}
	cache, err := NewCache()
	if err != nil {
		t.Error("Cannot connect to redis")
	}
	defer cache.Close()
	cache.AttachMetaData(examples)

	if examples[0].Title == "" {
		t.Errorf("Title must not be empty for %s", examples[0].Url)
	}
	if len(examples[0].Fv) == 0 {
		t.Errorf("Feature vector must not be empty for %s", examples[0].Url)
	}
}
<commit_msg>エラーチェック<commit_after>package cache

import (
	"fmt"
	"testing"

	"github.com/syou6162/go-active-learning/lib/example"
)

func TestCacheGet(t *testing.T) {
	c, err := NewCache()
	if err != nil {
		t.Error("Cannot connect to redis")
	}
	defer c.Close()

	example := example.NewExample("http://a.hatena.ne.jp", example.POSITIVE)
	c.Client.Del("url:http://a.hatena.ne.jp")
	e, ok := c.GetExample(*example)
	if ok {
		t.Error(fmt.Printf("Cache must not contain %s", example.Url))
	}

	if err := c.AddExample(*example); err != nil {
		t.Error(fmt.Printf("Cannot set this url: %s", example.Url))
	}
	e, ok = c.GetExample(*example)
	if !ok {
		t.Error(fmt.Printf("Cache must return %s", example.Url))
	}
	if example.Url != e.Url {
		t.Error(fmt.Printf("Urls must be same(%s, %s)", example.Url, e.Url))
	}
}

func TestAttachMetaData(t *testing.T) {
	e1 := example.NewExample("http://b.hatena.ne.jp", example.POSITIVE)
	e2 := example.NewExample("http://www.yasuhisay.info", example.NEGATIVE)
	e3 := example.NewExample("https://github.com", example.UNLABELED)
	examples := example.Examples{e1, e2, e3}
	cache, err := NewCache()
	if err != nil {
		t.Error("Cannot connect to redis")
	}
	defer cache.Close()
	cache.AttachMetaData(examples)

	if examples[0].Title == "" {
		t.Errorf("Title must not be empty for %s", examples[0].Url)
	}
	if len(examples[0].Fv) == 0 {
		t.Errorf("Feature vector must not be empty for %s", examples[0].Url)
	}
}
<|endoftext|>


<commit_before>package dlog

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"strings"
	"sync"
	"time"
)

const (
	separator = ":" // or ▶
)

// terminal colors
const (
	black uint8 = iota + 30
	red
	green
	yellow
	blue
	purple
	teal
)

// rank
const (
	noRank uint8 = iota
	errorRank
	warnRank
	infoRank
	debugRank
)

var rank = errorRank

// as long as DLOG env is not empty -> show log
func init() {
	switch strings.ToUpper(os.Getenv("DLOG")) {
	case "DEBUG", "*":
		rank = debugRank
	case "INFO":
		rank = infoRank
	case "WARN":
		rank = warnRank
	case "ERROR", "ERR":
		rank = errorRank
	default:
		rank = noRank
	}
}

// Dlog .
type Dlog struct {
	name string

	prevTime   time.Time
	prevTimeMu sync.Mutex

	log    func(uint8, *Log)
	writer io.Writer
	hook   chan<- *Log

	Debug            handler
	Info, Done, Fail handler
	Warn             handler
	Error            handler
}

type handler func(...interface{})

// or should we send it to dev/null ?
func logNull(...interface{}) {}

// Option .
type Option struct {
	Writer io.Writer
	Hook   chan<- *Log
}

// New .
func New(name string, opt *Option) (_dlog *Dlog) {
	// blank option as default
	if opt == nil {
		opt = &Option{}
	}

	// new dlog
	_dlog = &Dlog{
		name: name,

		prevTime: time.Now(),

		writer: opt.Writer,
		hook:   opt.Hook,
	}

	// writer
	if _dlog.writer == nil {
		_dlog.writer = os.Stdout
		_dlog.log = _dlog.write
	} else {
		_dlog.log = _dlog.writeJSON
	}

	// default handler
	_dlog.Debug = logNull
	_dlog.Info, _dlog.Done, _dlog.Fail = logNull, logNull, logNull
	_dlog.Done = logNull
	_dlog.Warn = logNull
	_dlog.Error = logNull

	if rank >= debugRank {
		_dlog.Debug = _dlog.handlerFunc(teal)
	}
	if rank >= infoRank {
		_dlog.Info = _dlog.handlerFunc(blue)
		_dlog.Done = _dlog.handlerFunc(green)
		_dlog.Fail = _dlog.handlerFunc(purple)
	}
	if rank >= warnRank {
		_dlog.Warn = _dlog.handlerFunc(yellow)
	}
	if rank >= errorRank {
		_dlog.Error = _dlog.handlerFunc(red)
	}

	return
}

func (d *Dlog) handlerFunc(color uint8) handler {
	return func(arg ...interface{}) {
		// time
		d.prevTimeMu.Lock()
		now, delta := getDelta(d.prevTime)
		d.prevTime = now
		d.prevTimeMu.Unlock()

		_log := NewLog(d.name, now, delta, arg...)

		// write to writer
		d.log(color, _log)

		// send to hook
		if d.hook != nil {
			d.hook <- _log
		}
	}
}

func (d *Dlog) write(color uint8, _log *Log) {
	timestamp := _log.Timestamp.Format("15:04:05.000")
	prefix := fmt.Sprintf("\033[%vm%v %s #%v %v\033[0m", color, timestamp, _log.Name, _log.Caller, separator)
	delta := fmt.Sprintf("\033[%vm+%s\033[0m", black, humanizeNano(_log.Delta))

	arg := append([]interface{}{prefix}, _log.Data...)
	arg = append(arg, delta)

	// skip err
	fmt.Fprintln(d.writer, arg...)
}

func (d *Dlog) writeJSON(color uint8, _log *Log) {
	jsonStr, err := json.Marshal(_log)
	// skip err
	if err != nil {
		return
	}

	// skip err
	fmt.Fprintln(d.writer, string(jsonStr))
	return
}
<commit_msg>add simple log<commit_after>package dlog

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"strings"
	"sync"
	"time"
)

const (
	separator = ":" // or ▶
)

// terminal colors
const (
	black uint8 = iota + 30
	red
	green
	yellow
	blue
	purple
	teal
)

// rank
const (
	noRank uint8 = iota
	errorRank
	warnRank
	infoRank
	debugRank
)

var rank = errorRank

// as long as DLOG env is not empty -> show log
func init() {
	switch strings.ToUpper(os.Getenv("DLOG")) {
	case "DEBUG", "*":
		rank = debugRank
	case "INFO":
		rank = infoRank
	case "WARN":
		rank = warnRank
	case "ERROR", "ERR":
		rank = errorRank
	default:
		rank = noRank
	}
}

// Dlog .
type Dlog struct {
	name string

	prevTime   time.Time
	prevTimeMu sync.Mutex

	log    func(uint8, *Log)
	writer io.Writer
	hook   chan<- *Log

	Debug            handler
	Info, Done, Fail handler
	Warn             handler
	Error            handler
}

type handler func(...interface{})

// or should we send it to dev/null ?
func logNull(...interface{}) {}

// Option .
type Option struct {
	Writer io.Writer
	Hook   chan<- *Log
	Type   string
}

// New .
func New(name string, opt *Option) (_dlog *Dlog) {
	// blank option as default
	if opt == nil {
		opt = &Option{}
	}

	// new dlog
	_dlog = &Dlog{
		name: name,

		prevTime: time.Now(),

		writer: opt.Writer,
		hook:   opt.Hook,
	}

	// writer
	if _dlog.writer == nil {
		_dlog.writer = os.Stdout
	}

	// log
	switch opt.Type {
	case "json":
		_dlog.log = _dlog.writeJSON
	case "simple":
		_dlog.log = _dlog.writeSimple
	default:
		_dlog.log = _dlog.write
	}

	// default handler
	_dlog.Debug = logNull
	_dlog.Info, _dlog.Done, _dlog.Fail = logNull, logNull, logNull
	_dlog.Done = logNull
	_dlog.Warn = logNull
	_dlog.Error = logNull

	if rank >= debugRank {
		_dlog.Debug = _dlog.handlerFunc(teal)
	}
	if rank >= infoRank {
		_dlog.Info = _dlog.handlerFunc(blue)
		_dlog.Done = _dlog.handlerFunc(green)
		_dlog.Fail = _dlog.handlerFunc(purple)
	}
	if rank >= warnRank {
		_dlog.Warn = _dlog.handlerFunc(yellow)
	}
	if rank >= errorRank {
		_dlog.Error = _dlog.handlerFunc(red)
	}

	return
}

func (d *Dlog) handlerFunc(color uint8) handler {
	return func(arg ...interface{}) {
		// time
		d.prevTimeMu.Lock()
		now, delta := getDelta(d.prevTime)
		d.prevTime = now
		d.prevTimeMu.Unlock()

		_log := NewLog(d.name, now, delta, arg...)

		// write to writer
		d.log(color, _log)

		// send to hook
		if d.hook != nil {
			d.hook <- _log
		}
	}
}

func (d *Dlog) write(color uint8, _log *Log) {
	timestamp := _log.Timestamp.Format("15:04:05.000")
	prefix := fmt.Sprintf("\033[%vm%v %s #%v %v\033[0m", color, timestamp, _log.Name, _log.Caller, separator)
	delta := fmt.Sprintf("\033[%vm+%s\033[0m", black, humanizeNano(_log.Delta))

	arg := append([]interface{}{prefix}, _log.Data...)
	arg = append(arg, delta)

	// skip err
	fmt.Fprintln(d.writer, arg...)
}

// no color, no time, no delta
func (d *Dlog) writeSimple(color uint8, _log *Log) {
	prefix := fmt.Sprintf("%s #%v %v", _log.Name, _log.Caller, separator)

	arg := append([]interface{}{prefix}, _log.Data...)

	// skip err
	fmt.Fprintln(d.writer, arg...)
}

func (d *Dlog) writeJSON(color uint8, _log *Log) {
	jsonStr, err := json.Marshal(_log)
	// skip err
	if err != nil {
		return
	}

	// skip err
	fmt.Fprintln(d.writer, string(jsonStr))
	return
}
<|endoftext|>


<commit_before>package radius

import (
	"bytes"
	"crypto/md5"
	"encoding/binary"
	"errors"
	"math"
	"net"
	"strconv"
	"time"
)

// ErrNoAttribute is returned when an attribute was not found when one was
// expected.
var ErrNoAttribute = errors.New("radius: attribute not found")

// Attribute is a wire encoded RADIUS attribute.
type Attribute []byte

// Integer returns the given attribute as an integer. An error is returned if
// the attribute is not 4 bytes long.
func Integer(a Attribute) (uint32, error) {
	if len(a) != 4 {
		return 0, errors.New("invalid length")
	}
	return binary.BigEndian.Uint32(a), nil
}

// NewInteger creates a new Attribute from the given integer value.
func NewInteger(i uint32) Attribute {
	v := make([]byte, 4)
	binary.BigEndian.PutUint32(v, i)
	return Attribute(v)
}

// String returns the given attribute as a string.
func String(a Attribute) string {
	return string(a)
}

// NewString returns a new Attribute from the given string. An error is returned
// if the string length is greater than 253.
func NewString(s string) (Attribute, error) {
	if len(s) > 253 {
		return nil, errors.New("string too long")
	}
	return Attribute(s), nil
}

// Bytes returns the given Attribute as a byte slice.
func Bytes(a Attribute) []byte {
	b := make([]byte, len(a))
	copy(b, []byte(a))
	return b
}

// NewBytes returns a new Attribute from the given byte slice. An error is
// returned if the slice is longer than 253.
func NewBytes(b []byte) (Attribute, error) {
	if len(b) > 253 {
		return nil, errors.New("value too long")
	}
	a := make(Attribute, len(b))
	copy(a, Attribute(b))
	return a, nil
}

// IPAddr returns the given Attribute as an IPv4 IP address. An error is
// returned if the attribute is not 4 bytes long.
func IPAddr(a Attribute) (net.IP, error) {
	if len(a) != net.IPv4len {
		return nil, errors.New("invalid length")
	}
	b := make([]byte, net.IPv4len)
	copy(b, []byte(a))
	return b, nil
}

// NewIPAddr returns a new Attribute from the given IP address. An error is
// returned if the given address is not an IPv4 address.
func NewIPAddr(a net.IP) (Attribute, error) {
	a = a.To4()
	if a == nil {
		return nil, errors.New("invalid IPv4 address")
	}
	b := make(Attribute, len(a))
	copy(b, Attribute(a))
	return b, nil
}

// IPv6Addr returns the given Attribute as an IPv6 IP address. An error is
// returned if the attribute is not 16 bytes long.
func IPv6Addr(a Attribute) (net.IP, error) {
	if len(a) != net.IPv6len {
		return nil, errors.New("invalid length")
	}
	b := make([]byte, net.IPv6len)
	copy(b, []byte(a))
	return b, nil
}

// NewIPv6Addr returns a new Attribute from the given IP address. An error is
// returned if the given address is not an IPv6 address.
func NewIPv6Addr(a net.IP) (Attribute, error) {
	a = a.To16()
	if a == nil {
		return nil, errors.New("invalid IPv6 address")
	}
	b := make(Attribute, len(a))
	copy(b, Attribute(a))
	return b, nil
}

// IFID returns the given attribute as a 8-byte hardware address. An error is
// return if the attribute is not 8 bytes long.
func IFID(a Attribute) (net.HardwareAddr, error) {
	if len(a) != 8 {
		return nil, errors.New("invalid length")
	}
	ifid := make(net.HardwareAddr, len(a))
	copy(ifid, a)
	return ifid, nil
}

// NewIFID returns a new Attribute from the given hardware address. An error
// is returned if the address is not 8 bytes long.
func NewIFID(addr net.HardwareAddr) (Attribute, error) {
	if len(addr) != 8 {
		return nil, errors.New("invalid length")
	}
	attr := make(Attribute, len(addr))
	copy(attr, addr)
	return attr, nil
}

// UserPassword decrypts the given  "User-Password"-encrypted (as defined in RFC
// 2865) Attribute, and returns the plaintext. An error is returned if the
// attribute length is invalid, the secret is empty, or the requestAuthenticator
// length is invalid.
func UserPassword(a Attribute, secret, requestAuthenticator []byte) ([]byte, error) {
	if len(a) < 16 || len(a) > 128 {
		return nil, errors.New("invalid attribute length (" + strconv.Itoa(len(a)) + ")")
	}
	if len(secret) == 0 {
		return nil, errors.New("empty secret")
	}
	if len(requestAuthenticator) != 16 {
		return nil, errors.New("invalid requestAuthenticator length (" + strconv.Itoa(len(requestAuthenticator)) + ")")
	}

	dec := make([]byte, 0, len(a))

	hash := md5.New()
	hash.Write(secret)
	hash.Write(requestAuthenticator)
	dec = hash.Sum(dec)

	for i, b := range a[:16] {
		dec[i] ^= b
	}

	for i := 16; i < len(a); i += 16 {
		hash.Reset()
		hash.Write(secret)
		hash.Write(a[i-16 : i])
		dec = hash.Sum(dec)

		for j, b := range a[i : i+16] {
			dec[i+j] ^= b
		}
	}

	if i := bytes.IndexByte(dec, 0); i > -1 {
		return dec[:i], nil
	}
	return dec, nil
}

// NewUserPassword returns a new "User-Password"-encrypted attribute from the
// given plaintext, secret, and requestAuthenticator. An error is returned if
// the plaintext is too long, the secret is empty, or the requestAuthenticator
// is an invalid length.
func NewUserPassword(plaintext, secret, requestAuthenticator []byte) (Attribute, error) {
	if len(plaintext) > 128 {
		return nil, errors.New("plaintext longer than 128 characters")
	}
	if len(secret) == 0 {
		return nil, errors.New("empty secret")
	}
	if len(requestAuthenticator) != 16 {
		return nil, errors.New("requestAuthenticator not 16-bytes")
	}

	chunks := len(plaintext) >> 4
	if chunks == 0 {
		chunks = 1
	}

	enc := make([]byte, 0, chunks*16)

	hash := md5.New()
	hash.Write(secret)
	hash.Write(requestAuthenticator)
	enc = hash.Sum(enc)

	for i, b := range plaintext[:16] {
		enc[i] ^= b
	}

	for i := 16; i < len(plaintext); i += 16 {
		hash.Reset()
		hash.Write(secret)
		hash.Write(enc[i-16 : i])
		enc = hash.Sum(enc)

		for j, b := range plaintext[i : i+16] {
			enc[i+j] ^= b
		}
	}

	return enc, nil
}

// Date returns the given Attribute as time.Time. An error is returned if the
// attribute is not 4 bytes long.
func Date(a Attribute) (time.Time, error) {
	if len(a) != 4 {
		return time.Time{}, errors.New("invalid length")
	}
	sec := binary.BigEndian.Uint32([]byte(a))
	return time.Unix(int64(sec), 0), nil
}

// NewDate returns a new Attribute from the given time.Time.
func NewDate(t time.Time) (Attribute, error) {
	unix := t.Unix()
	if unix > math.MaxUint32 {
		return nil, errors.New("time out of range")
	}
	a := make([]byte, 4)
	binary.BigEndian.PutUint32(a, uint32(t.Unix()))
	return a, nil
}

// VendorSpecific returns the vendor ID and value from the given attribute. An
// error is returned if the attribute is less than 5 bytes long.
func VendorSpecific(a Attribute) (vendorID uint32, value Attribute, err error) {
	if len(a) < 5 {
		err = errors.New("invalid length")
		return
	}
	vendorID = binary.BigEndian.Uint32(a[:4])
	value = make([]byte, len(a)-4)
	copy(value, a[4:])
	return
}

// NewVendorSpecific returns a new vendor specific attribute with the given
// vendor ID and value.
func NewVendorSpecific(vendorID uint32, value Attribute) (Attribute, error) {
	if len(value) > 249 {
		return nil, errors.New("value too long")
	}
	a := make([]byte, 4+len(value))
	binary.BigEndian.PutUint32(a, vendorID)
	copy(a[4:], value)
	return a, nil
}

// Integer64 returns the given attribute as an integer. An error is returned if
// the attribute is not 8 bytes long.
func Integer64(a Attribute) (uint64, error) {
	if len(a) != 8 {
		return 0, errors.New("invalid length")
	}
	return binary.BigEndian.Uint64(a), nil
}

// NewInteger64 creates a new Attribute from the given integer value.
func NewInteger64(i uint64) Attribute {
	v := make([]byte, 8)
	binary.BigEndian.PutUint64(v, i)
	return Attribute(v)
}

// TODO: ipv6prefix
<commit_msg>add Tag, NewTag<commit_after>package radius

import (
	"bytes"
	"crypto/md5"
	"encoding/binary"
	"errors"
	"math"
	"net"
	"strconv"
	"time"
)

// ErrNoAttribute is returned when an attribute was not found when one was
// expected.
var ErrNoAttribute = errors.New("radius: attribute not found")

// Attribute is a wire encoded RADIUS attribute.
type Attribute []byte

// Integer returns the given attribute as an integer. An error is returned if
// the attribute is not 4 bytes long.
func Integer(a Attribute) (uint32, error) {
	if len(a) != 4 {
		return 0, errors.New("invalid length")
	}
	return binary.BigEndian.Uint32(a), nil
}

// NewInteger creates a new Attribute from the given integer value.
func NewInteger(i uint32) Attribute {
	v := make([]byte, 4)
	binary.BigEndian.PutUint32(v, i)
	return Attribute(v)
}

// String returns the given attribute as a string.
func String(a Attribute) string {
	return string(a)
}

// NewString returns a new Attribute from the given string. An error is returned
// if the string length is greater than 253.
func NewString(s string) (Attribute, error) {
	if len(s) > 253 {
		return nil, errors.New("string too long")
	}
	return Attribute(s), nil
}

// Bytes returns the given Attribute as a byte slice.
func Bytes(a Attribute) []byte {
	b := make([]byte, len(a))
	copy(b, []byte(a))
	return b
}

// NewBytes returns a new Attribute from the given byte slice. An error is
// returned if the slice is longer than 253.
func NewBytes(b []byte) (Attribute, error) {
	if len(b) > 253 {
		return nil, errors.New("value too long")
	}
	a := make(Attribute, len(b))
	copy(a, Attribute(b))
	return a, nil
}

// IPAddr returns the given Attribute as an IPv4 IP address. An error is
// returned if the attribute is not 4 bytes long.
func IPAddr(a Attribute) (net.IP, error) {
	if len(a) != net.IPv4len {
		return nil, errors.New("invalid length")
	}
	b := make([]byte, net.IPv4len)
	copy(b, []byte(a))
	return b, nil
}

// NewIPAddr returns a new Attribute from the given IP address. An error is
// returned if the given address is not an IPv4 address.
func NewIPAddr(a net.IP) (Attribute, error) {
	a = a.To4()
	if a == nil {
		return nil, errors.New("invalid IPv4 address")
	}
	b := make(Attribute, len(a))
	copy(b, Attribute(a))
	return b, nil
}

// IPv6Addr returns the given Attribute as an IPv6 IP address. An error is
// returned if the attribute is not 16 bytes long.
func IPv6Addr(a Attribute) (net.IP, error) {
	if len(a) != net.IPv6len {
		return nil, errors.New("invalid length")
	}
	b := make([]byte, net.IPv6len)
	copy(b, []byte(a))
	return b, nil
}

// NewIPv6Addr returns a new Attribute from the given IP address. An error is
// returned if the given address is not an IPv6 address.
func NewIPv6Addr(a net.IP) (Attribute, error) {
	a = a.To16()
	if a == nil {
		return nil, errors.New("invalid IPv6 address")
	}
	b := make(Attribute, len(a))
	copy(b, Attribute(a))
	return b, nil
}

// IFID returns the given attribute as a 8-byte hardware address. An error is
// return if the attribute is not 8 bytes long.
func IFID(a Attribute) (net.HardwareAddr, error) {
	if len(a) != 8 {
		return nil, errors.New("invalid length")
	}
	ifid := make(net.HardwareAddr, len(a))
	copy(ifid, a)
	return ifid, nil
}

// NewIFID returns a new Attribute from the given hardware address. An error
// is returned if the address is not 8 bytes long.
func NewIFID(addr net.HardwareAddr) (Attribute, error) {
	if len(addr) != 8 {
		return nil, errors.New("invalid length")
	}
	attr := make(Attribute, len(addr))
	copy(attr, addr)
	return attr, nil
}

// UserPassword decrypts the given  "User-Password"-encrypted (as defined in RFC
// 2865) Attribute, and returns the plaintext. An error is returned if the
// attribute length is invalid, the secret is empty, or the requestAuthenticator
// length is invalid.
func UserPassword(a Attribute, secret, requestAuthenticator []byte) ([]byte, error) {
	if len(a) < 16 || len(a) > 128 {
		return nil, errors.New("invalid attribute length (" + strconv.Itoa(len(a)) + ")")
	}
	if len(secret) == 0 {
		return nil, errors.New("empty secret")
	}
	if len(requestAuthenticator) != 16 {
		return nil, errors.New("invalid requestAuthenticator length (" + strconv.Itoa(len(requestAuthenticator)) + ")")
	}

	dec := make([]byte, 0, len(a))

	hash := md5.New()
	hash.Write(secret)
	hash.Write(requestAuthenticator)
	dec = hash.Sum(dec)

	for i, b := range a[:16] {
		dec[i] ^= b
	}

	for i := 16; i < len(a); i += 16 {
		hash.Reset()
		hash.Write(secret)
		hash.Write(a[i-16 : i])
		dec = hash.Sum(dec)

		for j, b := range a[i : i+16] {
			dec[i+j] ^= b
		}
	}

	if i := bytes.IndexByte(dec, 0); i > -1 {
		return dec[:i], nil
	}
	return dec, nil
}

// NewUserPassword returns a new "User-Password"-encrypted attribute from the
// given plaintext, secret, and requestAuthenticator. An error is returned if
// the plaintext is too long, the secret is empty, or the requestAuthenticator
// is an invalid length.
func NewUserPassword(plaintext, secret, requestAuthenticator []byte) (Attribute, error) {
	if len(plaintext) > 128 {
		return nil, errors.New("plaintext longer than 128 characters")
	}
	if len(secret) == 0 {
		return nil, errors.New("empty secret")
	}
	if len(requestAuthenticator) != 16 {
		return nil, errors.New("requestAuthenticator not 16-bytes")
	}

	chunks := len(plaintext) >> 4
	if chunks == 0 {
		chunks = 1
	}

	enc := make([]byte, 0, chunks*16)

	hash := md5.New()
	hash.Write(secret)
	hash.Write(requestAuthenticator)
	enc = hash.Sum(enc)

	for i, b := range plaintext[:16] {
		enc[i] ^= b
	}

	for i := 16; i < len(plaintext); i += 16 {
		hash.Reset()
		hash.Write(secret)
		hash.Write(enc[i-16 : i])
		enc = hash.Sum(enc)

		for j, b := range plaintext[i : i+16] {
			enc[i+j] ^= b
		}
	}

	return enc, nil
}

// Date returns the given Attribute as time.Time. An error is returned if the
// attribute is not 4 bytes long.
func Date(a Attribute) (time.Time, error) {
	if len(a) != 4 {
		return time.Time{}, errors.New("invalid length")
	}
	sec := binary.BigEndian.Uint32([]byte(a))
	return time.Unix(int64(sec), 0), nil
}

// NewDate returns a new Attribute from the given time.Time.
func NewDate(t time.Time) (Attribute, error) {
	unix := t.Unix()
	if unix > math.MaxUint32 {
		return nil, errors.New("time out of range")
	}
	a := make([]byte, 4)
	binary.BigEndian.PutUint32(a, uint32(t.Unix()))
	return a, nil
}

// VendorSpecific returns the vendor ID and value from the given attribute. An
// error is returned if the attribute is less than 5 bytes long.
func VendorSpecific(a Attribute) (vendorID uint32, value Attribute, err error) {
	if len(a) < 5 {
		err = errors.New("invalid length")
		return
	}
	vendorID = binary.BigEndian.Uint32(a[:4])
	value = make([]byte, len(a)-4)
	copy(value, a[4:])
	return
}

// NewVendorSpecific returns a new vendor specific attribute with the given
// vendor ID and value.
func NewVendorSpecific(vendorID uint32, value Attribute) (Attribute, error) {
	if len(value) > 249 {
		return nil, errors.New("value too long")
	}
	a := make([]byte, 4+len(value))
	binary.BigEndian.PutUint32(a, vendorID)
	copy(a[4:], value)
	return a, nil
}

// Integer64 returns the given attribute as an integer. An error is returned if
// the attribute is not 8 bytes long.
func Integer64(a Attribute) (uint64, error) {
	if len(a) != 8 {
		return 0, errors.New("invalid length")
	}
	return binary.BigEndian.Uint64(a), nil
}

// NewInteger64 creates a new Attribute from the given integer value.
func NewInteger64(i uint64) Attribute {
	v := make([]byte, 8)
	binary.BigEndian.PutUint64(v, i)
	return Attribute(v)
}

// Tag returns the components of a tagged attribute.
func Tag(a Attribute) (tag byte, value Attribute, err error) {
	switch len(a) {
	case 0:
		err = errors.New("invalid length")
	case 1:
		tag = a[0]
	default:
		tag = a[0]
		value = make(Attribute, len(a)-1)
		copy(value, a[1:])
	}
	return
}

// NewTag returns a new tagged attribute.
func NewTag(tag byte, value Attribute) (Attribute, error) {
	if len(value) > 252 {
		return nil, errors.New("invalid value length")
	}
	a := make(Attribute, 1+len(value))
	a[0] = tag
	copy(a[1:], value)
	return a, nil
}

// TODO: ipv6prefix
<|endoftext|>