<commit_before>package nv

import (
	"fmt"
	"reflect"
	"strings"
	"testing"
	"time"
)

func check_array(t *testing.T, field string, value interface{}, l int, fn func([]string)) {
	expecteds := strings.Split(field, ";")
	if len(expecteds) < 2 {
		t.Fatal("field does not seem to be a list of expected values:", field)
	}
	if len(expecteds) != l {
		t.Fatal("length mismatch between expected and decoded arrays, expected:",
			len(expecteds), "decoded:", l)
	}
	fn(expecteds)
}

func check_boolean(t *testing.T, field string, value interface{}) {
	exp := false
	_, err := fmt.Sscanf(field, "%t", &exp)
	if err != nil {
		t.Fatal(err)
	}
	got := bool(value.(Boolean))
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_boolean_value(t *testing.T, field string, value interface{}) {
	exp := false
	_, err := fmt.Sscanf(field, "%t", &exp)
	if err != nil {
		t.Fatal(err)
	}
	got := value.(bool)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_boolean_array(t *testing.T, field string, value interface{}) {
	arr := value.([]bool)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_boolean_value(t, expecteds[i], arr[i])
		}
	})
}

func check_byte(t *testing.T, field string, value interface{}) {
	d := int(0)
	_, err := fmt.Sscanf(field, "%d", &d)
	if err != nil {
		t.Fatal(err)
	}
	exp := byte(d)
	got := value.(byte)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_byte_array(t *testing.T, field string, value interface{}) {
	arr := value.([]byte)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_byte(t, expecteds[i], arr[i])
		}
	})
}

func check_double(t *testing.T, field string, value interface{}) {
	exp := float64(0)
	// is a hack, but good enough
	_, err := fmt.Sscanf(field, "%f", &exp)
	if err != nil {
		t.Fatal(err)
	}

	got := value.(float64)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}

func check_hrtime(t *testing.T, field string, value interface{}) {
	d := int64(0)
	_, err := fmt.Sscanf(field, "%d", &d)
	if err != nil {
		t.Fatal(err)
	}
	exp := time.Duration(d)
	got := value.(time.Duration)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}

func check_int8(t *testing.T, field string, value interface{}) {
	exp := int8(0)
	_, err := fmt.Sscanf(field, "%d", &exp)
	if err != nil {
		t.Fatal(err)
	}
	got := value.(int8)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_int8_array(t *testing.T, field string, value interface{}) {
	arr := value.([]int8)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_int8(t, expecteds[i], arr[i])
		}
	})
}

func check_int16(t *testing.T, field string, value interface{}) {
	exp := int16(0)
	_, err := fmt.Sscanf(field, "%d", &exp)
	if err != nil {
		t.Fatal(err)
	}
	got := value.(int16)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_int16_array(t *testing.T, field string, value interface{}) {
	arr := value.([]int16)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_int16(t, expecteds[i], arr[i])
		}
	})
}

func check_int32(t *testing.T, field string, value interface{}) {
	exp := int32(0)
	_, err := fmt.Sscanf(field, "%d", &exp)
	if err != nil {
		t.Fatal(err)
	}
	got := value.(int32)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_int32_array(t *testing.T, field string, value interface{}) {
	arr := value.([]int32)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_int32(t, expecteds[i], arr[i])
		}
	})
}

func check_int64(t *testing.T, field string, value interface{}) {
	exp := int64(0)
	_, err := fmt.Sscanf(field, "%d", &exp)
	if err != nil {
		t.Fatal(err)
	}
	got := value.(int64)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_int64_array(t *testing.T, field string, value interface{}) {
	arr := value.([]int64)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_int64(t, expecteds[i], arr[i])
		}
	})
}

func check_uint8(t *testing.T, field string, value interface{}) {
	exp := uint8(0)
	_, err := fmt.Sscanf(field, "%d", &exp)
	if err != nil {
		t.Fatal(err)
	}
	got := value.(uint8)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_uint8_array(t *testing.T, field string, value interface{}) {
	arr := value.([]uint8)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_uint8(t, expecteds[i], arr[i])
		}
	})
}

func check_uint16(t *testing.T, field string, value interface{}) {
	exp := uint16(0)
	_, err := fmt.Sscanf(field, "%d", &exp)
	if err != nil {
		t.Fatal(err)
	}
	got := value.(uint16)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_uint16_array(t *testing.T, field string, value interface{}) {
	arr := value.([]uint16)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_uint16(t, expecteds[i], arr[i])
		}
	})
}

func check_uint32(t *testing.T, field string, value interface{}) {
	exp := uint32(0)
	_, err := fmt.Sscanf(field, "%d", &exp)
	if err != nil {
		t.Fatal(err)
	}
	got := value.(uint32)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_uint32_array(t *testing.T, field string, value interface{}) {
	arr := value.([]uint32)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_uint32(t, expecteds[i], arr[i])
		}
	})
}

func check_uint64(t *testing.T, field string, value interface{}) {
	exp := uint64(0)
	_, err := fmt.Sscanf(field, "%d", &exp)
	if err != nil {
		t.Fatal(err)
	}
	got := value.(uint64)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_uint64_array(t *testing.T, field string, value interface{}) {
	arr := value.([]uint64)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_uint64(t, expecteds[i], arr[i])
		}
	})
}

func check_nvlist(t *testing.T, field string, value interface{}) {
	list := value.(map[string]interface{})
	for f, v := range list {
		checkers[reflect.TypeOf(v).String()](t, f, v)
	}
}
func check_nvlist_array(t *testing.T, field string, value interface{}) {
	arr := value.([]map[string]interface{})
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_nvlist(t, expecteds[i], arr[i])
		}
	})
}

func check_string(t *testing.T, field string, value interface{}) {
	exp := field
	got := value.(string)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_string_array(t *testing.T, field string, value interface{}) {
	arr := value.([]string)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_string(t, expecteds[i], arr[i])
		}
	})
}

var checkers map[string]func(*testing.T, string, interface{})

func init() {
	checkers = map[string]func(*testing.T, string, interface{}){
		"nv.Boolean":                check_boolean,
		"bool":                      check_boolean_value,
		"byte":                      check_byte,
		"int16":                     check_int16,
		"uint16":                    check_uint16,
		"int32":                     check_int32,
		"uint32":                    check_uint32,
		"int64":                     check_int64,
		"uint64":                    check_uint64,
		"string":                    check_string,
		"[]byte":                    check_byte_array,
		"[]int16":                   check_int16_array,
		"[]uint16":                  check_uint16_array,
		"[]int32":                   check_int32_array,
		"[]uint32":                  check_uint32_array,
		"[]int64":                   check_int64_array,
		"[]uint64":                  check_uint64_array,
		"[]string":                  check_string_array,
		"time.Duration":             check_hrtime,
		"map[string]interface {}":   check_nvlist,
		"[]map[string]interface {}": check_nvlist_array,
		"int8":    check_int8,
		"uint8":   check_uint8,
		"[]bool":  check_boolean_array,
		"[]int8":  check_int8_array,
		"[]uint8": check_uint8_array,
		"float64": check_double,
	}
}

func struct2map(s interface{}) map[string]interface{} {
	m := map[string]interface{}{}
	val := reflect.ValueOf(s).Elem()
	for name, index := range fieldIndexMap(val) {
		m[name] = val.Field(index).Interface()
	}
	return m
}

func assertFields(t *testing.T, name string, m map[string]interface{}) {
	for field, value := range m {
		valueType := reflect.TypeOf(value).String()
		fn, ok := checkers[valueType]
		if !ok {
			t.Fatal(name, "unknown type:", valueType)
		}
		fn(t, field, value)
	}
}

//go:generate make -C _test ../known_good_data_test.go
func TestDecodeGood(t *testing.T) {
	for _, test := range good {
		t.Log(test.name)

		m := map[string]interface{}{}
		err := Decode(test.payload, &m)
		if err != nil {
			t.Fatal(test.name, "decode as map failed:", err)
		}

		assertFields(t, test.name, m)

		s := test.ptr()
		err = Decode(test.payload, s)
		if err != nil {
			t.Fatal(test.name, "decode as struct failed:", err)
		}

		m = struct2map(s)
		if len(m) != reflect.ValueOf(s).Elem().NumField() {
			t.Fatalf("incorrect number of fields, got: %d %+v want: %d %+v\n",
				len(m), m, reflect.ValueOf(s).Elem().NumField(), s)
		}
		assertFields(t, test.name, m)
	}
}

func TestDecodeBad(t *testing.T) {
	for _, test := range decode_bad {
		t.Log(test.err)

		m := map[string]interface{}{}
		err := Decode(test.payload, &m)
		if err == nil {
			t.Fatalf("expected an error, wanted:|%s| payload:|%v|\n",
				test.err, test.payload)
		}
		if test.err != err.Error() {
			t.Fatalf("error mismatch, want:|%s| got:|%s| payload:|%v|\n",
				test.err, err.Error(), test.payload)
		}
	}
}
<commit_msg>nv: manual test to increase coverage<commit_after>package nv

import (
	"fmt"
	"reflect"
	"strings"
	"testing"
	"time"
)

func check_array(t *testing.T, field string, value interface{}, l int, fn func([]string)) {
	expecteds := strings.Split(field, ";")
	if len(expecteds) < 2 {
		t.Fatal("field does not seem to be a list of expected values:", field)
	}
	if len(expecteds) != l {
		t.Fatal("length mismatch between expected and decoded arrays, expected:",
			len(expecteds), "decoded:", l)
	}
	fn(expecteds)
}

func check_boolean(t *testing.T, field string, value interface{}) {
	exp := false
	_, err := fmt.Sscanf(field, "%t", &exp)
	if err != nil {
		t.Fatal(err)
	}
	got := bool(value.(Boolean))
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_boolean_value(t *testing.T, field string, value interface{}) {
	exp := false
	_, err := fmt.Sscanf(field, "%t", &exp)
	if err != nil {
		t.Fatal(err)
	}
	got := value.(bool)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_boolean_array(t *testing.T, field string, value interface{}) {
	arr := value.([]bool)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_boolean_value(t, expecteds[i], arr[i])
		}
	})
}

func check_byte(t *testing.T, field string, value interface{}) {
	d := int(0)
	_, err := fmt.Sscanf(field, "%d", &d)
	if err != nil {
		t.Fatal(err)
	}
	exp := byte(d)
	got := value.(byte)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_byte_array(t *testing.T, field string, value interface{}) {
	arr := value.([]byte)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_byte(t, expecteds[i], arr[i])
		}
	})
}

func check_double(t *testing.T, field string, value interface{}) {
	exp := float64(0)
	// is a hack, but good enough
	_, err := fmt.Sscanf(field, "%f", &exp)
	if err != nil {
		t.Fatal(err)
	}

	got := value.(float64)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}

func check_hrtime(t *testing.T, field string, value interface{}) {
	d := int64(0)
	_, err := fmt.Sscanf(field, "%d", &d)
	if err != nil {
		t.Fatal(err)
	}
	exp := time.Duration(d)
	got := value.(time.Duration)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}

func check_int8(t *testing.T, field string, value interface{}) {
	exp := int8(0)
	_, err := fmt.Sscanf(field, "%d", &exp)
	if err != nil {
		t.Fatal(err)
	}
	got := value.(int8)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_int8_array(t *testing.T, field string, value interface{}) {
	arr := value.([]int8)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_int8(t, expecteds[i], arr[i])
		}
	})
}

func check_int16(t *testing.T, field string, value interface{}) {
	exp := int16(0)
	_, err := fmt.Sscanf(field, "%d", &exp)
	if err != nil {
		t.Fatal(err)
	}
	got := value.(int16)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_int16_array(t *testing.T, field string, value interface{}) {
	arr := value.([]int16)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_int16(t, expecteds[i], arr[i])
		}
	})
}

func check_int32(t *testing.T, field string, value interface{}) {
	exp := int32(0)
	_, err := fmt.Sscanf(field, "%d", &exp)
	if err != nil {
		t.Fatal(err)
	}
	got := value.(int32)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_int32_array(t *testing.T, field string, value interface{}) {
	arr := value.([]int32)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_int32(t, expecteds[i], arr[i])
		}
	})
}

func check_int64(t *testing.T, field string, value interface{}) {
	exp := int64(0)
	_, err := fmt.Sscanf(field, "%d", &exp)
	if err != nil {
		t.Fatal(err)
	}
	got := value.(int64)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_int64_array(t *testing.T, field string, value interface{}) {
	arr := value.([]int64)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_int64(t, expecteds[i], arr[i])
		}
	})
}

func check_uint8(t *testing.T, field string, value interface{}) {
	exp := uint8(0)
	_, err := fmt.Sscanf(field, "%d", &exp)
	if err != nil {
		t.Fatal(err)
	}
	got := value.(uint8)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_uint8_array(t *testing.T, field string, value interface{}) {
	arr := value.([]uint8)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_uint8(t, expecteds[i], arr[i])
		}
	})
}

func check_uint16(t *testing.T, field string, value interface{}) {
	exp := uint16(0)
	_, err := fmt.Sscanf(field, "%d", &exp)
	if err != nil {
		t.Fatal(err)
	}
	got := value.(uint16)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_uint16_array(t *testing.T, field string, value interface{}) {
	arr := value.([]uint16)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_uint16(t, expecteds[i], arr[i])
		}
	})
}

func check_uint32(t *testing.T, field string, value interface{}) {
	exp := uint32(0)
	_, err := fmt.Sscanf(field, "%d", &exp)
	if err != nil {
		t.Fatal(err)
	}
	got := value.(uint32)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_uint32_array(t *testing.T, field string, value interface{}) {
	arr := value.([]uint32)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_uint32(t, expecteds[i], arr[i])
		}
	})
}

func check_uint64(t *testing.T, field string, value interface{}) {
	exp := uint64(0)
	_, err := fmt.Sscanf(field, "%d", &exp)
	if err != nil {
		t.Fatal(err)
	}
	got := value.(uint64)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_uint64_array(t *testing.T, field string, value interface{}) {
	arr := value.([]uint64)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_uint64(t, expecteds[i], arr[i])
		}
	})
}

func check_nvlist(t *testing.T, field string, value interface{}) {
	list := value.(map[string]interface{})
	for f, v := range list {
		checkers[reflect.TypeOf(v).String()](t, f, v)
	}
}
func check_nvlist_array(t *testing.T, field string, value interface{}) {
	arr := value.([]map[string]interface{})
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_nvlist(t, expecteds[i], arr[i])
		}
	})
}

func check_string(t *testing.T, field string, value interface{}) {
	exp := field
	got := value.(string)
	if got != exp {
		t.Fatal("expected:", exp, "got:", got)
	}
}
func check_string_array(t *testing.T, field string, value interface{}) {
	arr := value.([]string)
	check_array(t, field, value, len(arr), func(expecteds []string) {
		for i := range arr {
			check_string(t, expecteds[i], arr[i])
		}
	})
}

var checkers map[string]func(*testing.T, string, interface{})

func init() {
	checkers = map[string]func(*testing.T, string, interface{}){
		"nv.Boolean":                check_boolean,
		"bool":                      check_boolean_value,
		"byte":                      check_byte,
		"int16":                     check_int16,
		"uint16":                    check_uint16,
		"int32":                     check_int32,
		"uint32":                    check_uint32,
		"int64":                     check_int64,
		"uint64":                    check_uint64,
		"string":                    check_string,
		"[]byte":                    check_byte_array,
		"[]int16":                   check_int16_array,
		"[]uint16":                  check_uint16_array,
		"[]int32":                   check_int32_array,
		"[]uint32":                  check_uint32_array,
		"[]int64":                   check_int64_array,
		"[]uint64":                  check_uint64_array,
		"[]string":                  check_string_array,
		"time.Duration":             check_hrtime,
		"map[string]interface {}":   check_nvlist,
		"[]map[string]interface {}": check_nvlist_array,
		"int8":    check_int8,
		"uint8":   check_uint8,
		"[]bool":  check_boolean_array,
		"[]int8":  check_int8_array,
		"[]uint8": check_uint8_array,
		"float64": check_double,
	}
}

func struct2map(s interface{}) map[string]interface{} {
	m := map[string]interface{}{}
	val := reflect.ValueOf(s).Elem()
	for name, index := range fieldIndexMap(val) {
		m[name] = val.Field(index).Interface()
	}
	return m
}

func assertFields(t *testing.T, name string, m map[string]interface{}) {
	for field, value := range m {
		valueType := reflect.TypeOf(value).String()
		fn, ok := checkers[valueType]
		if !ok {
			t.Fatal(name, "unknown type:", valueType)
		}
		fn(t, field, value)
	}
}

//go:generate make -C _test ../known_good_data_test.go
func TestDecodeGood(t *testing.T) {
	for _, test := range good {
		t.Log(test.name)

		m := map[string]interface{}{}
		err := Decode(test.payload, &m)
		if err != nil {
			t.Fatal(test.name, "decode as map failed:", err)
		}

		assertFields(t, test.name, m)

		s := test.ptr()
		err = Decode(test.payload, s)
		if err != nil {
			t.Fatal(test.name, "decode as struct failed:", err)
		}

		m = struct2map(s)
		if len(m) != reflect.ValueOf(s).Elem().NumField() {
			t.Fatalf("incorrect number of fields, got: %d %+v want: %d %+v\n",
				len(m), m, reflect.ValueOf(s).Elem().NumField(), s)
		}
		assertFields(t, test.name, m)
	}
}

func TestDecodeBad(t *testing.T) {
	for _, test := range decode_bad {
		t.Log(test.err)

		m := map[string]interface{}{}
		err := Decode(test.payload, &m)
		if err == nil {
			t.Fatalf("expected an error, wanted:|%s| payload:|%v|\n",
				test.err, test.payload)
		}
		if test.err != err.Error() {
			t.Fatalf("error mismatch, want:|%s| got:|%s| payload:|%v|\n",
				test.err, err.Error(), test.payload)
		}
	}
}

func TestDecodeBadArgs(t *testing.T) {
	bad_args := []struct {
		arg interface{}
		err string
	}{
		{
			arg: struct{}{},
			err: "cannot decode into non-pointer: reflect.Value",
		},
	}
	for _, test := range bad_args {
		err := Decode([]byte(enc_dec_name_typ), test.arg)
		if err == nil {
			t.Fatalf("expected an error, wanted:|%s|\n", test.err)
		}
		if test.err != err.Error() {
			t.Fatalf("error mismatch, want:|%s| got:|%s|\n",
				test.err, err.Error())
		}
	}
}
<|endoftext|>


<commit_before>package vcsclient

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"os"
	"path"

	"sort"

	"golang.org/x/tools/godoc/vfs"
	"sourcegraph.com/sourcegraph/go-vcs/vcs"
	"sourcegraph.com/sqs/pbtypes"
)

type FileSystem interface {
	vfs.FileSystem
	Get(path string) (*TreeEntry, error)
}

type repositoryFS struct {
	at   vcs.CommitID
	repo *repository
}

var _ FileSystem = &repositoryFS{}

func (fs *repositoryFS) Open(name string) (vfs.ReadSeekCloser, error) {
	e, err := fs.Get(name)
	if err != nil {
		return nil, err
	}

	return nopCloser{bytes.NewReader(e.Contents)}, nil
}

func (fs *repositoryFS) Lstat(path string) (os.FileInfo, error) {
	e, err := fs.Get(path)
	if err != nil {
		return nil, err
	}

	return e.Stat()
}

func (fs *repositoryFS) Stat(path string) (os.FileInfo, error) {
	// TODO(sqs): follow symlinks (as Stat specification requires)
	e, err := fs.Get(path)
	if err != nil {
		return nil, err
	}

	return e.Stat()
}

func (fs *repositoryFS) ReadDir(path string) ([]os.FileInfo, error) {
	e, err := fs.Get(path)
	if err != nil {
		return nil, err
	}

	fis := make([]os.FileInfo, len(e.Entries))
	for i, e := range e.Entries {
		fis[i], err = e.Stat()
		if err != nil {
			return nil, err
		}
	}

	return fis, nil
}

func (fs *repositoryFS) String() string {
	return fmt.Sprintf("repository %s commit %s (client)", fs.repo.repoPath, fs.at)
}

// Get returns the whole TreeEntry struct for a tree entry.
func (fs *repositoryFS) Get(path string) (*TreeEntry, error) {
	url, err := fs.url(path, nil)
	if err != nil {
		return nil, err
	}

	req, err := fs.repo.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, err
	}

	var entry *TreeEntry
	_, err = fs.repo.client.Do(req, &entry)
	if err != nil {
		return nil, err
	}

	return entry, nil
}

// FileWithRange is returned by GetFileWithOptions and includes the
// returned file's TreeEntry as well as the actual range of lines and
// bytes returned (based on the GetFileOptions parameters). That is,
// if Start/EndLine are set in GetFileOptions, this struct's
// Start/EndByte will be set to the actual start and end bytes of
// those specified lines, and so on for the other fields in
// GetFileOptions.
type FileWithRange struct {
	*TreeEntry
	FileRange // range of actual returned tree entry contents within file
}

// GetFileWithOptions gets a file and allows additional configuration
// of the range to return, etc.
func (fs *repositoryFS) GetFileWithOptions(path string, opt GetFileOptions) (*FileWithRange, error) {
	url, err := fs.url(path, opt)
	if err != nil {
		return nil, err
	}

	req, err := fs.repo.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, err
	}

	var file *FileWithRange
	_, err = fs.repo.client.Do(req, &file)
	if err != nil {
		return nil, err
	}

	return file, nil
}

// A FileGetter is a repository FileSystem that can get files with
// extended range options (GetFileWithOptions).
//
// It's generally more efficient to use the client's implementation of
// the GetFileWithOptions method instead of calling the
// vcsclient.GetFileWithOptions func because the former causes only
// the requested range to be sent over the network, while the latter
// requests the whole file and narrows the range on the client side.
type FileGetter interface {
	GetFileWithOptions(path string, opt GetFileOptions) (*FileWithRange, error)
}

// GetFileWithOptions gets a file and observes the options specified
// in opt. If fs implements FileGetter, fs.GetFileWithOptions is
// called; otherwise the options are applied on the client side after
// fetching the whole file.
func GetFileWithOptions(fs vfs.FileSystem, path string, opt GetFileOptions) (*FileWithRange, error) {
	if fg, ok := fs.(FileGetter); ok {
		return fg.GetFileWithOptions(path, opt)
	}

	fi, err := fs.Lstat(path)
	if err != nil {
		return nil, err
	}

	e := newTreeEntry(fi)
	fwr := FileWithRange{TreeEntry: e}

	if fi.Mode().IsDir() {
		ee, err := readDir(fs, path, int(opt.RecurseSingleSubfolderLimit), true)
		if err != nil {
			return nil, err
		}
		sort.Sort(TreeEntriesByTypeByName(ee))
		e.Entries = ee
	} else if fi.Mode().IsRegular() {
		f, err := fs.Open(path)
		if err != nil {
			return nil, err
		}
		defer f.Close()

		contents, err := ioutil.ReadAll(f)
		if err != nil {
			return nil, err
		}

		e.Contents = contents

		if empty := (GetFileOptions{}); opt != empty {
			fr, _, err := ComputeFileRange(contents, opt)
			if err != nil {
				return nil, err
			}

			// Trim to only requested range.
			e.Contents = e.Contents[fr.StartByte:fr.EndByte]
			fwr.FileRange = *fr
		}
	}

	return &fwr, nil
}

// readDir uses the passed vfs.FileSystem to read from starting at the base path.
// If recurseSingleSubfolderLimit is non-zero, it will descend and include
// sub-folders with a single sub-folder inside. It will only inspect up to
// recurseSingleSubfolderLimit sub-folders. first should always be set to
// true, other values are used internally.
func readDir(fs vfs.FileSystem, base string, recurseSingleSubfolderLimit int, first bool) ([]*TreeEntry, error) {
	entries, err := fs.ReadDir(base)
	if err != nil {
		return nil, err
	}
	if recurseSingleSubfolderLimit > 0 && !first && !singleSubDir(entries) {
		return nil, nil
	}
	te := make([]*TreeEntry, len(entries))
	dirCount := 0
	for i, fi := range entries {
		te[i] = newTreeEntry(fi)
		if fi.Mode().IsDir() && dirCount < recurseSingleSubfolderLimit {
			dirCount++
			ee, err := readDir(fs, path.Join(base, fi.Name()), recurseSingleSubfolderLimit, false)
			if err != nil {
				return nil, err
			}
			te[i].Entries = ee
		}
	}
	return te, nil
}

func singleSubDir(entries []os.FileInfo) bool {
	return len(entries) == 1 && entries[0].IsDir()
}

func newTreeEntry(fi os.FileInfo) *TreeEntry {
	e := &TreeEntry{
		Name:    fi.Name(),
		Size:    fi.Size(),
		ModTime: pbtypes.NewTimestamp(fi.ModTime()),
	}
	if fi.Mode().IsDir() {
		e.Type = DirEntry
	} else if fi.Mode().IsRegular() {
		e.Type = FileEntry
	} else if fi.Mode()&os.ModeSymlink != 0 {
		e.Type = SymlinkEntry
	}
	return e
}

// url generates the URL to RouteRepoTreeEntry for the given path (all other
// route vars are taken from repositoryFS fields).
func (fs *repositoryFS) url(path string, opt interface{}) (*url.URL, error) {
	return fs.repo.url(RouteRepoTreeEntry, map[string]string{
		"CommitID": string(fs.at),
		"Path":     path,
	}, opt)
}

type nopCloser struct {
	io.ReadSeeker
}

func (nc nopCloser) Close() error { return nil }
<commit_msg>perf: Concurrently recurse readDir<commit_after>package vcsclient

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"os"
	"path"
	"sync"

	"sort"

	"golang.org/x/tools/godoc/vfs"
	"sourcegraph.com/sourcegraph/go-vcs/vcs"
	"sourcegraph.com/sqs/pbtypes"
)

type FileSystem interface {
	vfs.FileSystem
	Get(path string) (*TreeEntry, error)
}

type repositoryFS struct {
	at   vcs.CommitID
	repo *repository
}

var _ FileSystem = &repositoryFS{}

func (fs *repositoryFS) Open(name string) (vfs.ReadSeekCloser, error) {
	e, err := fs.Get(name)
	if err != nil {
		return nil, err
	}

	return nopCloser{bytes.NewReader(e.Contents)}, nil
}

func (fs *repositoryFS) Lstat(path string) (os.FileInfo, error) {
	e, err := fs.Get(path)
	if err != nil {
		return nil, err
	}

	return e.Stat()
}

func (fs *repositoryFS) Stat(path string) (os.FileInfo, error) {
	// TODO(sqs): follow symlinks (as Stat specification requires)
	e, err := fs.Get(path)
	if err != nil {
		return nil, err
	}

	return e.Stat()
}

func (fs *repositoryFS) ReadDir(path string) ([]os.FileInfo, error) {
	e, err := fs.Get(path)
	if err != nil {
		return nil, err
	}

	fis := make([]os.FileInfo, len(e.Entries))
	for i, e := range e.Entries {
		fis[i], err = e.Stat()
		if err != nil {
			return nil, err
		}
	}

	return fis, nil
}

func (fs *repositoryFS) String() string {
	return fmt.Sprintf("repository %s commit %s (client)", fs.repo.repoPath, fs.at)
}

// Get returns the whole TreeEntry struct for a tree entry.
func (fs *repositoryFS) Get(path string) (*TreeEntry, error) {
	url, err := fs.url(path, nil)
	if err != nil {
		return nil, err
	}

	req, err := fs.repo.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, err
	}

	var entry *TreeEntry
	_, err = fs.repo.client.Do(req, &entry)
	if err != nil {
		return nil, err
	}

	return entry, nil
}

// FileWithRange is returned by GetFileWithOptions and includes the
// returned file's TreeEntry as well as the actual range of lines and
// bytes returned (based on the GetFileOptions parameters). That is,
// if Start/EndLine are set in GetFileOptions, this struct's
// Start/EndByte will be set to the actual start and end bytes of
// those specified lines, and so on for the other fields in
// GetFileOptions.
type FileWithRange struct {
	*TreeEntry
	FileRange // range of actual returned tree entry contents within file
}

// GetFileWithOptions gets a file and allows additional configuration
// of the range to return, etc.
func (fs *repositoryFS) GetFileWithOptions(path string, opt GetFileOptions) (*FileWithRange, error) {
	url, err := fs.url(path, opt)
	if err != nil {
		return nil, err
	}

	req, err := fs.repo.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, err
	}

	var file *FileWithRange
	_, err = fs.repo.client.Do(req, &file)
	if err != nil {
		return nil, err
	}

	return file, nil
}

// A FileGetter is a repository FileSystem that can get files with
// extended range options (GetFileWithOptions).
//
// It's generally more efficient to use the client's implementation of
// the GetFileWithOptions method instead of calling the
// vcsclient.GetFileWithOptions func because the former causes only
// the requested range to be sent over the network, while the latter
// requests the whole file and narrows the range on the client side.
type FileGetter interface {
	GetFileWithOptions(path string, opt GetFileOptions) (*FileWithRange, error)
}

// GetFileWithOptions gets a file and observes the options specified
// in opt. If fs implements FileGetter, fs.GetFileWithOptions is
// called; otherwise the options are applied on the client side after
// fetching the whole file.
func GetFileWithOptions(fs vfs.FileSystem, path string, opt GetFileOptions) (*FileWithRange, error) {
	if fg, ok := fs.(FileGetter); ok {
		return fg.GetFileWithOptions(path, opt)
	}

	fi, err := fs.Lstat(path)
	if err != nil {
		return nil, err
	}

	e := newTreeEntry(fi)
	fwr := FileWithRange{TreeEntry: e}

	if fi.Mode().IsDir() {
		ee, err := readDir(fs, path, int(opt.RecurseSingleSubfolderLimit), true)
		if err != nil {
			return nil, err
		}
		sort.Sort(TreeEntriesByTypeByName(ee))
		e.Entries = ee
	} else if fi.Mode().IsRegular() {
		f, err := fs.Open(path)
		if err != nil {
			return nil, err
		}
		defer f.Close()

		contents, err := ioutil.ReadAll(f)
		if err != nil {
			return nil, err
		}

		e.Contents = contents

		if empty := (GetFileOptions{}); opt != empty {
			fr, _, err := ComputeFileRange(contents, opt)
			if err != nil {
				return nil, err
			}

			// Trim to only requested range.
			e.Contents = e.Contents[fr.StartByte:fr.EndByte]
			fwr.FileRange = *fr
		}
	}

	return &fwr, nil
}

// readDir uses the passed vfs.FileSystem to read from starting at the base path.
// If recurseSingleSubfolderLimit is non-zero, it will descend and include
// sub-folders with a single sub-folder inside. It will only inspect up to
// recurseSingleSubfolderLimit sub-folders. first should always be set to
// true, other values are used internally.
func readDir(fs vfs.FileSystem, base string, recurseSingleSubfolderLimit int, first bool) ([]*TreeEntry, error) {
	entries, err := fs.ReadDir(base)
	if err != nil {
		return nil, err
	}
	if recurseSingleSubfolderLimit > 0 && !first && !singleSubDir(entries) {
		return nil, nil
	}
	var (
		wg         sync.WaitGroup
		recurseErr error
		dirCount   = 0
		te         = make([]*TreeEntry, len(entries))
	)
	for i, fi := range entries {
		te[i] = newTreeEntry(fi)
		if fi.Mode().IsDir() && dirCount < recurseSingleSubfolderLimit {
			dirCount++
			i, name := i, fi.Name()
			wg.Add(1)
			go func() {
				defer wg.Done()
				ee, err := readDir(fs, path.Join(base, name), recurseSingleSubfolderLimit, false)
				if err != nil {
					recurseErr = err
					return
				}
				te[i].Entries = ee
			}()
		}
	}
	wg.Wait()
	if recurseErr != nil {
		return nil, recurseErr
	}
	return te, nil
}

func singleSubDir(entries []os.FileInfo) bool {
	return len(entries) == 1 && entries[0].IsDir()
}

func newTreeEntry(fi os.FileInfo) *TreeEntry {
	e := &TreeEntry{
		Name:    fi.Name(),
		Size:    fi.Size(),
		ModTime: pbtypes.NewTimestamp(fi.ModTime()),
	}
	if fi.Mode().IsDir() {
		e.Type = DirEntry
	} else if fi.Mode().IsRegular() {
		e.Type = FileEntry
	} else if fi.Mode()&os.ModeSymlink != 0 {
		e.Type = SymlinkEntry
	}
	return e
}

// url generates the URL to RouteRepoTreeEntry for the given path (all other
// route vars are taken from repositoryFS fields).
func (fs *repositoryFS) url(path string, opt interface{}) (*url.URL, error) {
	return fs.repo.url(RouteRepoTreeEntry, map[string]string{
		"CommitID": string(fs.at),
		"Path":     path,
	}, opt)
}

type nopCloser struct {
	io.ReadSeeker
}

func (nc nopCloser) Close() error { return nil }
<|endoftext|>


<commit_before>package common

import (
	"log"
	"os"
	"strconv"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/request"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/packer/helper/multistep"
)

// StateRefreshFunc is a function type used for StateChangeConf that is
// responsible for refreshing the item being watched for a state change.
//
// It returns three results. `result` is any object that will be returned
// as the final object after waiting for state change. This allows you to
// return the final updated object, for example an EC2 instance after refreshing
// it.
//
// `state` is the latest state of that object. And `err` is any error that
// may have happened while refreshing the state.
type StateRefreshFunc func() (result interface{}, state string, err error)

// StateChangeConf is the configuration struct used for `WaitForState`.
type StateChangeConf struct {
	Pending   []string
	Refresh   StateRefreshFunc
	StepState multistep.StateBag
	Target    string
}

// Following are wrapper functions that use Packer's environment-variables to
// determing retry logic, then call the AWS SDK's built-in waiters.

func WaitUntilAMIAvailable(ctx aws.Context, conn *ec2.EC2, imageId string) error {
	imageInput := ec2.DescribeImagesInput{
		ImageIds: []*string{&imageId},
	}

	waitOpts := getWaiterOptions()
	if len(waitOpts) == 0 {
		// Bump this default to 30 minutes because the aws default
		// of ten minutes doesn't work for some of our long-running copies.
		waitOpts = append(waitOpts, request.WithWaiterMaxAttempts(120))
	}
	err := conn.WaitUntilImageAvailableWithContext(
		ctx,
		&imageInput,
		waitOpts...)
	return err
}

func WaitUntilInstanceTerminated(ctx aws.Context, conn *ec2.EC2, instanceId string) error {

	instanceInput := ec2.DescribeInstancesInput{
		InstanceIds: []*string{&instanceId},
	}

	err := conn.WaitUntilInstanceTerminatedWithContext(
		ctx,
		&instanceInput,
		getWaiterOptions()...)
	return err
}

// This function works for both requesting and cancelling spot instances.
func WaitUntilSpotRequestFulfilled(ctx aws.Context, conn *ec2.EC2, spotRequestId string) error {
	spotRequestInput := ec2.DescribeSpotInstanceRequestsInput{
		SpotInstanceRequestIds: []*string{&spotRequestId},
	}

	err := conn.WaitUntilSpotInstanceRequestFulfilledWithContext(
		ctx,
		&spotRequestInput,
		getWaiterOptions()...)
	return err
}

func WaitUntilVolumeAvailable(ctx aws.Context, conn *ec2.EC2, volumeId string) error {
	volumeInput := ec2.DescribeVolumesInput{
		VolumeIds: []*string{&volumeId},
	}

	err := conn.WaitUntilVolumeAvailableWithContext(
		ctx,
		&volumeInput,
		getWaiterOptions()...)
	return err
}

func WaitUntilSnapshotDone(ctx aws.Context, conn *ec2.EC2, snapshotID string) error {
	snapInput := ec2.DescribeSnapshotsInput{
		SnapshotIds: []*string{&snapshotID},
	}

	err := conn.WaitUntilSnapshotCompletedWithContext(
		ctx,
		&snapInput,
		getWaiterOptions()...)
	return err
}

// Wrappers for our custom AWS waiters

func WaitUntilVolumeAttached(ctx aws.Context, conn *ec2.EC2, volumeId string) error {
	volumeInput := ec2.DescribeVolumesInput{
		VolumeIds: []*string{&volumeId},
	}

	err := WaitForVolumeToBeAttached(conn,
		ctx,
		&volumeInput,
		getWaiterOptions()...)
	return err
}

func WaitUntilVolumeDetached(ctx aws.Context, conn *ec2.EC2, volumeId string) error {
	volumeInput := ec2.DescribeVolumesInput{
		VolumeIds: []*string{&volumeId},
	}

	err := WaitForVolumeToBeDetached(conn,
		ctx,
		&volumeInput,
		getWaiterOptions()...)
	return err
}

func WaitUntilImageImported(ctx aws.Context, conn *ec2.EC2, taskID string) error {
	importInput := ec2.DescribeImportImageTasksInput{
		ImportTaskIds: []*string{&taskID},
	}

	err := WaitForImageToBeImported(conn,
		ctx,
		&importInput,
		getWaiterOptions()...)
	return err
}

// Custom waiters using AWS's request.Waiter

func WaitForVolumeToBeAttached(c *ec2.EC2, ctx aws.Context, input *ec2.DescribeVolumesInput, opts ...request.WaiterOption) error {
	w := request.Waiter{
		Name:        "DescribeVolumes",
		MaxAttempts: 40,
		Delay:       request.ConstantWaiterDelay(5 * time.Second),
		Acceptors: []request.WaiterAcceptor{
			{
				State:    request.SuccessWaiterState,
				Matcher:  request.PathAllWaiterMatch,
				Argument: "Volumes[].Attachments[].State",
				Expected: "attached",
			},
		},
		Logger: c.Config.Logger,
		NewRequest: func(opts []request.Option) (*request.Request, error) {
			var inCpy *ec2.DescribeVolumesInput
			if input != nil {
				tmp := *input
				inCpy = &tmp
			}
			req, _ := c.DescribeVolumesRequest(inCpy)
			req.SetContext(ctx)
			req.ApplyOptions(opts...)
			return req, nil
		},
	}
	w.ApplyOptions(opts...)

	return w.WaitWithContext(ctx)
}

func WaitForVolumeToBeDetached(c *ec2.EC2, ctx aws.Context, input *ec2.DescribeVolumesInput, opts ...request.WaiterOption) error {
	w := request.Waiter{
		Name:        "DescribeVolumes",
		MaxAttempts: 40,
		Delay:       request.ConstantWaiterDelay(5 * time.Second),
		Acceptors: []request.WaiterAcceptor{
			{
				State:    request.SuccessWaiterState,
				Matcher:  request.PathAllWaiterMatch,
				Argument: "length(Volumes[].Attachments[]) == `0`",
				Expected: true,
			},
		},
		Logger: c.Config.Logger,
		NewRequest: func(opts []request.Option) (*request.Request, error) {
			var inCpy *ec2.DescribeVolumesInput
			if input != nil {
				tmp := *input
				inCpy = &tmp
			}
			req, _ := c.DescribeVolumesRequest(inCpy)
			req.SetContext(ctx)
			req.ApplyOptions(opts...)
			return req, nil
		},
	}
	w.ApplyOptions(opts...)

	return w.WaitWithContext(ctx)
}

func WaitForImageToBeImported(c *ec2.EC2, ctx aws.Context, input *ec2.DescribeImportImageTasksInput, opts ...request.WaiterOption) error {
	w := request.Waiter{
		Name:        "DescribeImages",
		MaxAttempts: 300,
		Delay:       request.ConstantWaiterDelay(5 * time.Second),
		Acceptors: []request.WaiterAcceptor{
			{
				State:    request.SuccessWaiterState,
				Matcher:  request.PathAllWaiterMatch,
				Argument: "ImportImageTasks[].Status",
				Expected: "completed",
			},
		},
		Logger: c.Config.Logger,
		NewRequest: func(opts []request.Option) (*request.Request, error) {
			var inCpy *ec2.DescribeImportImageTasksInput
			if input != nil {
				tmp := *input
				inCpy = &tmp
			}
			req, _ := c.DescribeImportImageTasksRequest(inCpy)
			req.SetContext(ctx)
			req.ApplyOptions(opts...)
			return req, nil
		},
	}
	w.ApplyOptions(opts...)

	return w.WaitWithContext(ctx)
}

// This helper function uses the environment variables AWS_TIMEOUT_SECONDS and
// AWS_POLL_DELAY_SECONDS to generate waiter options that can be passed into any
// request.Waiter function. These options will control how many times the waiter
// will retry the request, as well as how long to wait between the retries.

// DEFAULTING BEHAVIOR:
// if AWS_POLL_DELAY_SECONDS is set but the others are not, Packer will set this
// poll delay and use the waiter-specific default

// if AWS_TIMEOUT_SECONDS is set but AWS_MAX_ATTEMPTS is not, Packer will use
// AWS_TIMEOUT_SECONDS and _either_ AWS_POLL_DELAY_SECONDS _or_ 2 if the user has not set AWS_POLL_DELAY_SECONDS, to determine a max number of attempts to make.

// if AWS_TIMEOUT_SECONDS, _and_ AWS_MAX_ATTEMPTS are both set,
// AWS_TIMEOUT_SECONDS will be ignored.

// if AWS_MAX_ATTEMPTS is set but AWS_POLL_DELAY_SECONDS is not, then we will
// use waiter-specific defaults.

type envInfo struct {
	envKey     string
	Val        int
	overridden bool
}

type overridableWaitVars struct {
	awsPollDelaySeconds envInfo
	awsMaxAttempts      envInfo
	awsTimeoutSeconds   envInfo
}

func getWaiterOptions() []request.WaiterOption {
	envOverrides := getEnvOverrides()
	waitOpts := applyEnvOverrides(envOverrides)
	return waitOpts
}

func getOverride(varInfo envInfo) envInfo {
	override := os.Getenv(varInfo.envKey)
	if override != "" {
		n, err := strconv.Atoi(override)
		if err != nil {
			log.Printf("Invalid %s '%s', using default", varInfo.envKey, override)
		} else {
			varInfo.overridden = true
			varInfo.Val = n
		}
	}

	return varInfo
}
func getEnvOverrides() overridableWaitVars {
	// Load env vars from environment.
	envValues := overridableWaitVars{
		envInfo{"AWS_POLL_DELAY_SECONDS", 0, false},
		envInfo{"AWS_MAX_ATTEMPTS", 0, false},
		envInfo{"AWS_TIMEOUT_SECONDS", 0, false},
	}

	envValues.awsMaxAttempts = getOverride(envValues.awsMaxAttempts)
	envValues.awsPollDelaySeconds = getOverride(envValues.awsPollDelaySeconds)
	envValues.awsTimeoutSeconds = getOverride(envValues.awsTimeoutSeconds)

	return envValues
}

func applyEnvOverrides(envOverrides overridableWaitVars) []request.WaiterOption {
	waitOpts := make([]request.WaiterOption, 0)
	// If user has set poll delay seconds, overwrite it. If user has NOT,
	// default to a poll delay of 2 seconds
	if envOverrides.awsPollDelaySeconds.overridden {
		delaySeconds := request.ConstantWaiterDelay(time.Duration(envOverrides.awsPollDelaySeconds.Val) * time.Second)
		waitOpts = append(waitOpts, request.WithWaiterDelay(delaySeconds))
	}

	// If user has set max attempts, overwrite it. If user hasn't set max
	// attempts, default to whatever the waiter has set as a default.
	if envOverrides.awsMaxAttempts.overridden {
		waitOpts = append(waitOpts, request.WithWaiterMaxAttempts(envOverrides.awsMaxAttempts.Val))
	}

	if envOverrides.awsMaxAttempts.overridden && envOverrides.awsTimeoutSeconds.overridden {
		log.Printf("WARNING: AWS_MAX_ATTEMPTS and AWS_TIMEOUT_SECONDS are" +
			" both set. Packer will be using AWS_MAX_ATTEMPTS and discarding " +
			"AWS_TIMEOUT_SECONDS. If you have not set AWS_POLL_DELAY_SECONDS, " +
			"Packer will default to a 2 second poll delay.")
	} else if envOverrides.awsTimeoutSeconds.overridden {
		log.Printf("DEPRECATION WARNING: env var AWS_TIMEOUT_SECONDS is " +
			"deprecated in favor of AWS_MAX_ATTEMPTS. If you have not " +
			"explicitly set AWS_POLL_DELAY_SECONDS, we are defaulting to a " +
			"poll delay of 2 seconds, regardless of the AWS waiter's default.")
		maxAttempts := envOverrides.awsTimeoutSeconds.Val / envOverrides.awsPollDelaySeconds.Val
		// override the delay so we can get the timeout right
		if !envOverrides.awsPollDelaySeconds.overridden {
			delaySeconds := request.ConstantWaiterDelay(time.Duration(envOverrides.awsPollDelaySeconds.Val) * time.Second)
			waitOpts = append(waitOpts, request.WithWaiterDelay(delaySeconds))
		}
		waitOpts = append(waitOpts, request.WithWaiterMaxAttempts(maxAttempts))
	}
	if len(waitOpts) == 0 {
		log.Printf("No AWS timeout and polling overrides have been set. " +
			"Packer will default to waiter-specific delays and timeouts. If you would " +
			"like to customize the length of time between retries and max " +
			"number of retries you may do so by setting the environment " +
			"variables AWS_POLL_DELAY_SECONDS and AWS_MAX_ATTEMPTS to your " +
			"desired values.")
	}

	return waitOpts
}
<commit_msg>reset poll delay seconds default to 2 because it can actuall be used in one part of the conditional tree for applyEnvOverrides<commit_after>package common

import (
	"log"
	"os"
	"strconv"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/request"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/packer/helper/multistep"
)

// StateRefreshFunc is a function type used for StateChangeConf that is
// responsible for refreshing the item being watched for a state change.
//
// It returns three results. `result` is any object that will be returned
// as the final object after waiting for state change. This allows you to
// return the final updated object, for example an EC2 instance after refreshing
// it.
//
// `state` is the latest state of that object. And `err` is any error that
// may have happened while refreshing the state.
type StateRefreshFunc func() (result interface{}, state string, err error)

// StateChangeConf is the configuration struct used for `WaitForState`.
type StateChangeConf struct {
	Pending   []string
	Refresh   StateRefreshFunc
	StepState multistep.StateBag
	Target    string
}

// Following are wrapper functions that use Packer's environment-variables to
// determing retry logic, then call the AWS SDK's built-in waiters.

func WaitUntilAMIAvailable(ctx aws.Context, conn *ec2.EC2, imageId string) error {
	imageInput := ec2.DescribeImagesInput{
		ImageIds: []*string{&imageId},
	}

	waitOpts := getWaiterOptions()
	if len(waitOpts) == 0 {
		// Bump this default to 30 minutes because the aws default
		// of ten minutes doesn't work for some of our long-running copies.
		waitOpts = append(waitOpts, request.WithWaiterMaxAttempts(120))
	}
	err := conn.WaitUntilImageAvailableWithContext(
		ctx,
		&imageInput,
		waitOpts...)
	return err
}

func WaitUntilInstanceTerminated(ctx aws.Context, conn *ec2.EC2, instanceId string) error {

	instanceInput := ec2.DescribeInstancesInput{
		InstanceIds: []*string{&instanceId},
	}

	err := conn.WaitUntilInstanceTerminatedWithContext(
		ctx,
		&instanceInput,
		getWaiterOptions()...)
	return err
}

// This function works for both requesting and cancelling spot instances.
func WaitUntilSpotRequestFulfilled(ctx aws.Context, conn *ec2.EC2, spotRequestId string) error {
	spotRequestInput := ec2.DescribeSpotInstanceRequestsInput{
		SpotInstanceRequestIds: []*string{&spotRequestId},
	}

	err := conn.WaitUntilSpotInstanceRequestFulfilledWithContext(
		ctx,
		&spotRequestInput,
		getWaiterOptions()...)
	return err
}

func WaitUntilVolumeAvailable(ctx aws.Context, conn *ec2.EC2, volumeId string) error {
	volumeInput := ec2.DescribeVolumesInput{
		VolumeIds: []*string{&volumeId},
	}

	err := conn.WaitUntilVolumeAvailableWithContext(
		ctx,
		&volumeInput,
		getWaiterOptions()...)
	return err
}

func WaitUntilSnapshotDone(ctx aws.Context, conn *ec2.EC2, snapshotID string) error {
	snapInput := ec2.DescribeSnapshotsInput{
		SnapshotIds: []*string{&snapshotID},
	}

	err := conn.WaitUntilSnapshotCompletedWithContext(
		ctx,
		&snapInput,
		getWaiterOptions()...)
	return err
}

// Wrappers for our custom AWS waiters

func WaitUntilVolumeAttached(ctx aws.Context, conn *ec2.EC2, volumeId string) error {
	volumeInput := ec2.DescribeVolumesInput{
		VolumeIds: []*string{&volumeId},
	}

	err := WaitForVolumeToBeAttached(conn,
		ctx,
		&volumeInput,
		getWaiterOptions()...)
	return err
}

func WaitUntilVolumeDetached(ctx aws.Context, conn *ec2.EC2, volumeId string) error {
	volumeInput := ec2.DescribeVolumesInput{
		VolumeIds: []*string{&volumeId},
	}

	err := WaitForVolumeToBeDetached(conn,
		ctx,
		&volumeInput,
		getWaiterOptions()...)
	return err
}

func WaitUntilImageImported(ctx aws.Context, conn *ec2.EC2, taskID string) error {
	importInput := ec2.DescribeImportImageTasksInput{
		ImportTaskIds: []*string{&taskID},
	}

	err := WaitForImageToBeImported(conn,
		ctx,
		&importInput,
		getWaiterOptions()...)
	return err
}

// Custom waiters using AWS's request.Waiter

func WaitForVolumeToBeAttached(c *ec2.EC2, ctx aws.Context, input *ec2.DescribeVolumesInput, opts ...request.WaiterOption) error {
	w := request.Waiter{
		Name:        "DescribeVolumes",
		MaxAttempts: 40,
		Delay:       request.ConstantWaiterDelay(5 * time.Second),
		Acceptors: []request.WaiterAcceptor{
			{
				State:    request.SuccessWaiterState,
				Matcher:  request.PathAllWaiterMatch,
				Argument: "Volumes[].Attachments[].State",
				Expected: "attached",
			},
		},
		Logger: c.Config.Logger,
		NewRequest: func(opts []request.Option) (*request.Request, error) {
			var inCpy *ec2.DescribeVolumesInput
			if input != nil {
				tmp := *input
				inCpy = &tmp
			}
			req, _ := c.DescribeVolumesRequest(inCpy)
			req.SetContext(ctx)
			req.ApplyOptions(opts...)
			return req, nil
		},
	}
	w.ApplyOptions(opts...)

	return w.WaitWithContext(ctx)
}

func WaitForVolumeToBeDetached(c *ec2.EC2, ctx aws.Context, input *ec2.DescribeVolumesInput, opts ...request.WaiterOption) error {
	w := request.Waiter{
		Name:        "DescribeVolumes",
		MaxAttempts: 40,
		Delay:       request.ConstantWaiterDelay(5 * time.Second),
		Acceptors: []request.WaiterAcceptor{
			{
				State:    request.SuccessWaiterState,
				Matcher:  request.PathAllWaiterMatch,
				Argument: "length(Volumes[].Attachments[]) == `0`",
				Expected: true,
			},
		},
		Logger: c.Config.Logger,
		NewRequest: func(opts []request.Option) (*request.Request, error) {
			var inCpy *ec2.DescribeVolumesInput
			if input != nil {
				tmp := *input
				inCpy = &tmp
			}
			req, _ := c.DescribeVolumesRequest(inCpy)
			req.SetContext(ctx)
			req.ApplyOptions(opts...)
			return req, nil
		},
	}
	w.ApplyOptions(opts...)

	return w.WaitWithContext(ctx)
}

func WaitForImageToBeImported(c *ec2.EC2, ctx aws.Context, input *ec2.DescribeImportImageTasksInput, opts ...request.WaiterOption) error {
	w := request.Waiter{
		Name:        "DescribeImages",
		MaxAttempts: 300,
		Delay:       request.ConstantWaiterDelay(5 * time.Second),
		Acceptors: []request.WaiterAcceptor{
			{
				State:    request.SuccessWaiterState,
				Matcher:  request.PathAllWaiterMatch,
				Argument: "ImportImageTasks[].Status",
				Expected: "completed",
			},
		},
		Logger: c.Config.Logger,
		NewRequest: func(opts []request.Option) (*request.Request, error) {
			var inCpy *ec2.DescribeImportImageTasksInput
			if input != nil {
				tmp := *input
				inCpy = &tmp
			}
			req, _ := c.DescribeImportImageTasksRequest(inCpy)
			req.SetContext(ctx)
			req.ApplyOptions(opts...)
			return req, nil
		},
	}
	w.ApplyOptions(opts...)

	return w.WaitWithContext(ctx)
}

// This helper function uses the environment variables AWS_TIMEOUT_SECONDS and
// AWS_POLL_DELAY_SECONDS to generate waiter options that can be passed into any
// request.Waiter function. These options will control how many times the waiter
// will retry the request, as well as how long to wait between the retries.

// DEFAULTING BEHAVIOR:
// if AWS_POLL_DELAY_SECONDS is set but the others are not, Packer will set this
// poll delay and use the waiter-specific default

// if AWS_TIMEOUT_SECONDS is set but AWS_MAX_ATTEMPTS is not, Packer will use
// AWS_TIMEOUT_SECONDS and _either_ AWS_POLL_DELAY_SECONDS _or_ 2 if the user has not set AWS_POLL_DELAY_SECONDS, to determine a max number of attempts to make.

// if AWS_TIMEOUT_SECONDS, _and_ AWS_MAX_ATTEMPTS are both set,
// AWS_TIMEOUT_SECONDS will be ignored.

// if AWS_MAX_ATTEMPTS is set but AWS_POLL_DELAY_SECONDS is not, then we will
// use waiter-specific defaults.

type envInfo struct {
	envKey     string
	Val        int
	overridden bool
}

type overridableWaitVars struct {
	awsPollDelaySeconds envInfo
	awsMaxAttempts      envInfo
	awsTimeoutSeconds   envInfo
}

func getWaiterOptions() []request.WaiterOption {
	envOverrides := getEnvOverrides()
	waitOpts := applyEnvOverrides(envOverrides)
	return waitOpts
}

func getOverride(varInfo envInfo) envInfo {
	override := os.Getenv(varInfo.envKey)
	if override != "" {
		n, err := strconv.Atoi(override)
		if err != nil {
			log.Printf("Invalid %s '%s', using default", varInfo.envKey, override)
		} else {
			varInfo.overridden = true
			varInfo.Val = n
		}
	}

	return varInfo
}
func getEnvOverrides() overridableWaitVars {
	// Load env vars from environment.
	envValues := overridableWaitVars{
		envInfo{"AWS_POLL_DELAY_SECONDS", 2, false},
		envInfo{"AWS_MAX_ATTEMPTS", 0, false},
		envInfo{"AWS_TIMEOUT_SECONDS", 0, false},
	}

	envValues.awsMaxAttempts = getOverride(envValues.awsMaxAttempts)
	envValues.awsPollDelaySeconds = getOverride(envValues.awsPollDelaySeconds)
	envValues.awsTimeoutSeconds = getOverride(envValues.awsTimeoutSeconds)

	return envValues
}

func applyEnvOverrides(envOverrides overridableWaitVars) []request.WaiterOption {
	waitOpts := make([]request.WaiterOption, 0)
	// If user has set poll delay seconds, overwrite it. If user has NOT,
	// default to a poll delay of 2 seconds
	if envOverrides.awsPollDelaySeconds.overridden {
		delaySeconds := request.ConstantWaiterDelay(time.Duration(envOverrides.awsPollDelaySeconds.Val) * time.Second)
		waitOpts = append(waitOpts, request.WithWaiterDelay(delaySeconds))
	}

	// If user has set max attempts, overwrite it. If user hasn't set max
	// attempts, default to whatever the waiter has set as a default.
	if envOverrides.awsMaxAttempts.overridden {
		waitOpts = append(waitOpts, request.WithWaiterMaxAttempts(envOverrides.awsMaxAttempts.Val))
	}

	if envOverrides.awsMaxAttempts.overridden && envOverrides.awsTimeoutSeconds.overridden {
		log.Printf("WARNING: AWS_MAX_ATTEMPTS and AWS_TIMEOUT_SECONDS are" +
			" both set. Packer will be using AWS_MAX_ATTEMPTS and discarding " +
			"AWS_TIMEOUT_SECONDS. If you have not set AWS_POLL_DELAY_SECONDS, " +
			"Packer will default to a 2 second poll delay.")
	} else if envOverrides.awsTimeoutSeconds.overridden {
		log.Printf("DEPRECATION WARNING: env var AWS_TIMEOUT_SECONDS is " +
			"deprecated in favor of AWS_MAX_ATTEMPTS. If you have not " +
			"explicitly set AWS_POLL_DELAY_SECONDS, we are defaulting to a " +
			"poll delay of 2 seconds, regardless of the AWS waiter's default.")
		maxAttempts := envOverrides.awsTimeoutSeconds.Val / envOverrides.awsPollDelaySeconds.Val
		// override the delay so we can get the timeout right
		if !envOverrides.awsPollDelaySeconds.overridden {
			delaySeconds := request.ConstantWaiterDelay(time.Duration(envOverrides.awsPollDelaySeconds.Val) * time.Second)
			waitOpts = append(waitOpts, request.WithWaiterDelay(delaySeconds))
		}
		waitOpts = append(waitOpts, request.WithWaiterMaxAttempts(maxAttempts))
	}
	if len(waitOpts) == 0 {
		log.Printf("No AWS timeout and polling overrides have been set. " +
			"Packer will default to waiter-specific delays and timeouts. If you would " +
			"like to customize the length of time between retries and max " +
			"number of retries you may do so by setting the environment " +
			"variables AWS_POLL_DELAY_SECONDS and AWS_MAX_ATTEMPTS to your " +
			"desired values.")
	}

	return waitOpts
}
<|endoftext|>


<commit_before>package hcsshim

import (
	"errors"
	"fmt"
	"syscall"
)

var (
	// ErrComputeSystemDoesNotExist is an error encountered when the container being operated on no longer exists
	ErrComputeSystemDoesNotExist = syscall.Errno(0xc037010e)

	// ErrElementNotFound is an error encountered when the object being referenced does not exist
	ErrElementNotFound = syscall.Errno(0x490)

	// ErrHandleClose is an error encountered when the handle generating the notification being waited on has been closed
	ErrHandleClose = errors.New("hcsshim: the handle generating this notification has been closed")

	// ErrAlreadyClosed is an error encountered when using a handle that has been closed by the Close method
	ErrAlreadyClosed = errors.New("hcsshim: the handle has already been closed")

	// ErrInvalidNotificationType is an error encountered when an invalid notification type is used
	ErrInvalidNotificationType = errors.New("hcsshim: invalid notification type")

	// ErrInvalidProcessState is an error encountered when the process is not in a valid state for the requested operation
	ErrInvalidProcessState = errors.New("the process is in an invalid state for the attempted operation")

	// ErrTimeout is an error encountered when waiting on a notification times out
	ErrTimeout = errors.New("hcsshim: timeout waiting for notification")

	// ErrUnexpectedContainerExit is the error encountered when a container exits while waiting for
	// a different expected notification
	ErrUnexpectedContainerExit = errors.New("unexpected container exit")

	// ErrUnexpectedProcessAbort is the error encountered when communication with the compute service
	// is lost while waiting for a notification
	ErrUnexpectedProcessAbort = errors.New("lost communication with compute service")

	// ErrUnexpectedValue is an error encountered when hcs returns an invalid value
	ErrUnexpectedValue = errors.New("unexpected value returned from hcs")

	// ErrVmcomputeAlreadyStopped is an error encountered when a shutdown or terminate request is made on a stopped container
	ErrVmcomputeAlreadyStopped = syscall.Errno(0xc0370110)

	// ErrVmcomputeOperationPending is an error encountered when the operation is being completed asynchronously
	ErrVmcomputeOperationPending = syscall.Errno(0xC0370103)

	// ErrVmcomputeOperationInvalidState is an error encountered when the compute system is not in a valid state for the requested operation
	ErrVmcomputeOperationInvalidState = syscall.Errno(0xc0370105)

	// ErrProcNotFound is an error encountered when the the process cannot be found
	ErrProcNotFound = syscall.Errno(0x7f)
)

// ProcessError is an error encountered in HCS during an operation on a Process object
type ProcessError struct {
	Process   *process
	Operation string
	ExtraInfo string
	Err       error
}

// ContainerError is an error encountered in HCS during an operation on a Container object
type ContainerError struct {
	Container *container
	Operation string
	ExtraInfo string
	Err       error
}

func (e *ContainerError) Error() string {
	if e == nil {
		return "<nil>"
	}

	if e.Container == nil {
		return "unexpected nil container for error: " + e.Err.Error()
	}

	s := "container " + e.Container.id

	if e.Operation != "" {
		s += " encountered an error during " + e.Operation
	}

	switch e.Err.(type) {
	case nil:
		break
	case syscall.Errno:
		s += fmt.Sprintf(": failure in a Windows system call: %s (0x%x)", e.Err, win32FromError(e.Err))
	default:
		s += fmt.Sprintf(": %s", e.Err.Error())
	}

	if e.ExtraInfo != "" {
		s += " extra info: " + e.ExtraInfo
	}

	return s
}

func makeContainerError(container *container, operation string, extraInfo string, err error) error {
	// Don't double wrap errors
	if _, ok := err.(*ContainerError); ok {
		return err
	}
	containerError := &ContainerError{Container: container, Operation: operation, ExtraInfo: extraInfo, Err: err}
	return containerError
}

func (e *ProcessError) Error() string {
	if e == nil {
		return "<nil>"
	}

	if e.Process == nil {
		return "Unexpected nil process for error: " + e.Err.Error()
	}

	s := fmt.Sprintf("process %d", e.Process.processID)

	if e.Process.container != nil {
		s += " in container " + e.Process.container.id
	}

	if e.Operation != "" {
		s += " encountered an error during " + e.Operation
	}

	switch e.Err.(type) {
	case nil:
		break
	case syscall.Errno:
		s += fmt.Sprintf(": failure in a Windows system call: %s (0x%x)", e.Err, win32FromError(e.Err))
	default:
		s += fmt.Sprintf(": %s", e.Err.Error())
	}

	return s
}

func makeProcessError(process *process, operation string, extraInfo string, err error) error {
	// Don't double wrap errors
	if _, ok := err.(*ProcessError); ok {
		return err
	}
	processError := &ProcessError{Process: process, Operation: operation, ExtraInfo: extraInfo, Err: err}
	return processError
}

// IsNotExist checks if an error is caused by the Container or Process not existing.
// Note: Currently, ErrElementNotFound can mean that a Process has either
// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist
// will currently return true when the error is ErrElementNotFound or ErrProcNotFound.
func IsNotExist(err error) bool {
	err = getInnerError(err)
	return err == ErrComputeSystemDoesNotExist ||
		err == ErrElementNotFound ||
		err == ErrProcNotFound
}

// IsAlreadyClosed checks if an error is caused by the Container or Process having been
// already closed by a call to the Close() method.
func IsAlreadyClosed(err error) bool {
	err = getInnerError(err)
	return err == ErrAlreadyClosed
}

// IsPending returns a boolean indicating whether the error is that
// the requested operation is being completed in the background.
func IsPending(err error) bool {
	err = getInnerError(err)
	return err == ErrVmcomputeOperationPending
}

// IsTimeout returns a boolean indicating whether the error is caused by
// a timeout waiting for the operation to complete.
func IsTimeout(err error) bool {
	err = getInnerError(err)
	return err == ErrTimeout
}

// IsAlreadyStopped returns a boolean indicating whether the error is caused by
// a Container or Process being already stopped.
// Note: Currently, ErrElementNotFound can mean that a Process has either
// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist
// will currently return true when the error is ErrElementNotFound or ErrProcNotFound.
func IsAlreadyStopped(err error) bool {
	err = getInnerError(err)
	return err == ErrVmcomputeAlreadyStopped ||
		err == ErrElementNotFound ||
		err == ErrProcNotFound
}

func getInnerError(err error) error {
	switch pe := err.(type) {
	case nil:
		return nil
	case *ContainerError:
		err = pe.Err
	case *ProcessError:
		err = pe.Err
	}
	return err
}
<commit_msg>Add 'Access is denied' error<commit_after>package hcsshim

import (
	"errors"
	"fmt"
	"syscall"
)

var (
	// ErrComputeSystemDoesNotExist is an error encountered when the container being operated on no longer exists
	ErrComputeSystemDoesNotExist = syscall.Errno(0xc037010e)

	// ErrElementNotFound is an error encountered when the object being referenced does not exist
	ErrElementNotFound = syscall.Errno(0x490)

	// ErrHandleClose is an error encountered when the handle generating the notification being waited on has been closed
	ErrHandleClose = errors.New("hcsshim: the handle generating this notification has been closed")

	// ErrAlreadyClosed is an error encountered when using a handle that has been closed by the Close method
	ErrAlreadyClosed = errors.New("hcsshim: the handle has already been closed")

	// ErrInvalidNotificationType is an error encountered when an invalid notification type is used
	ErrInvalidNotificationType = errors.New("hcsshim: invalid notification type")

	// ErrInvalidProcessState is an error encountered when the process is not in a valid state for the requested operation
	ErrInvalidProcessState = errors.New("the process is in an invalid state for the attempted operation")

	// ErrTimeout is an error encountered when waiting on a notification times out
	ErrTimeout = errors.New("hcsshim: timeout waiting for notification")

	// ErrUnexpectedContainerExit is the error encountered when a container exits while waiting for
	// a different expected notification
	ErrUnexpectedContainerExit = errors.New("unexpected container exit")

	// ErrUnexpectedProcessAbort is the error encountered when communication with the compute service
	// is lost while waiting for a notification
	ErrUnexpectedProcessAbort = errors.New("lost communication with compute service")

	// ErrUnexpectedValue is an error encountered when hcs returns an invalid value
	ErrUnexpectedValue = errors.New("unexpected value returned from hcs")

	// ErrVmcomputeAlreadyStopped is an error encountered when a shutdown or terminate request is made on a stopped container
	ErrVmcomputeAlreadyStopped = syscall.Errno(0xc0370110)

	// ErrVmcomputeOperationPending is an error encountered when the operation is being completed asynchronously
	ErrVmcomputeOperationPending = syscall.Errno(0xC0370103)

	// ErrVmcomputeOperationInvalidState is an error encountered when the compute system is not in a valid state for the requested operation
	ErrVmcomputeOperationInvalidState = syscall.Errno(0xc0370105)

	// ErrProcNotFound is an error encountered when the the process cannot be found
	ErrProcNotFound = syscall.Errno(0x7f)

	// ErrVmcomputeOperationAccessIsDenied is an error which can be encountered when enumerating compute systems in RS1/RS2
	// builds when the underlying silo might be in the process of terminating. HCS was fixed in RS3.
	ErrVmcomputeOperationAccessIsDenied = syscall.Errno(0x5)
)

// ProcessError is an error encountered in HCS during an operation on a Process object
type ProcessError struct {
	Process   *process
	Operation string
	ExtraInfo string
	Err       error
}

// ContainerError is an error encountered in HCS during an operation on a Container object
type ContainerError struct {
	Container *container
	Operation string
	ExtraInfo string
	Err       error
}

func (e *ContainerError) Error() string {
	if e == nil {
		return "<nil>"
	}

	if e.Container == nil {
		return "unexpected nil container for error: " + e.Err.Error()
	}

	s := "container " + e.Container.id

	if e.Operation != "" {
		s += " encountered an error during " + e.Operation
	}

	switch e.Err.(type) {
	case nil:
		break
	case syscall.Errno:
		s += fmt.Sprintf(": failure in a Windows system call: %s (0x%x)", e.Err, win32FromError(e.Err))
	default:
		s += fmt.Sprintf(": %s", e.Err.Error())
	}

	if e.ExtraInfo != "" {
		s += " extra info: " + e.ExtraInfo
	}

	return s
}

func makeContainerError(container *container, operation string, extraInfo string, err error) error {
	// Don't double wrap errors
	if _, ok := err.(*ContainerError); ok {
		return err
	}
	containerError := &ContainerError{Container: container, Operation: operation, ExtraInfo: extraInfo, Err: err}
	return containerError
}

func (e *ProcessError) Error() string {
	if e == nil {
		return "<nil>"
	}

	if e.Process == nil {
		return "Unexpected nil process for error: " + e.Err.Error()
	}

	s := fmt.Sprintf("process %d", e.Process.processID)

	if e.Process.container != nil {
		s += " in container " + e.Process.container.id
	}

	if e.Operation != "" {
		s += " encountered an error during " + e.Operation
	}

	switch e.Err.(type) {
	case nil:
		break
	case syscall.Errno:
		s += fmt.Sprintf(": failure in a Windows system call: %s (0x%x)", e.Err, win32FromError(e.Err))
	default:
		s += fmt.Sprintf(": %s", e.Err.Error())
	}

	return s
}

func makeProcessError(process *process, operation string, extraInfo string, err error) error {
	// Don't double wrap errors
	if _, ok := err.(*ProcessError); ok {
		return err
	}
	processError := &ProcessError{Process: process, Operation: operation, ExtraInfo: extraInfo, Err: err}
	return processError
}

// IsNotExist checks if an error is caused by the Container or Process not existing.
// Note: Currently, ErrElementNotFound can mean that a Process has either
// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist
// will currently return true when the error is ErrElementNotFound or ErrProcNotFound.
func IsNotExist(err error) bool {
	err = getInnerError(err)
	return err == ErrComputeSystemDoesNotExist ||
		err == ErrElementNotFound ||
		err == ErrProcNotFound
}

// IsAlreadyClosed checks if an error is caused by the Container or Process having been
// already closed by a call to the Close() method.
func IsAlreadyClosed(err error) bool {
	err = getInnerError(err)
	return err == ErrAlreadyClosed
}

// IsPending returns a boolean indicating whether the error is that
// the requested operation is being completed in the background.
func IsPending(err error) bool {
	err = getInnerError(err)
	return err == ErrVmcomputeOperationPending
}

// IsTimeout returns a boolean indicating whether the error is caused by
// a timeout waiting for the operation to complete.
func IsTimeout(err error) bool {
	err = getInnerError(err)
	return err == ErrTimeout
}

// IsAlreadyStopped returns a boolean indicating whether the error is caused by
// a Container or Process being already stopped.
// Note: Currently, ErrElementNotFound can mean that a Process has either
// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist
// will currently return true when the error is ErrElementNotFound or ErrProcNotFound.
func IsAlreadyStopped(err error) bool {
	err = getInnerError(err)
	return err == ErrVmcomputeAlreadyStopped ||
		err == ErrElementNotFound ||
		err == ErrProcNotFound
}

func getInnerError(err error) error {
	switch pe := err.(type) {
	case nil:
		return nil
	case *ContainerError:
		err = pe.Err
	case *ProcessError:
		err = pe.Err
	}
	return err
}
<|endoftext|>


<commit_before>package errors

const (
	Internal     = 500
	BadReq       = 400
	NotFound     = 404
	Unauthorized = 401
	Forbidden    = 403
)

var EnableDevMsg = false

type Errors struct {
	HttpStatus int    `json:"-"`
	ErrMessage string `json:"message,omitempty"`
	ErrType    string `json:"error,omitempty"`
	ErrCode    string `json:"code,omitempty"`
	ErrDevMsg  string `json:"dev_message,omitempty"`
}

func (err *Errors) Error() string {
	return err.ErrMessage
}

func New(msg string) *Errors {
	return &Errors{
		ErrMessage: msg,
	}
}

func (err *Errors) Http(i int) *Errors {
	err.HttpStatus = i

	return err
}

func (err *Errors) Type(s string) *Errors {
	err.ErrType = s

	return err
}

func (err *Errors) Code(s string) *Errors {
	err.ErrCode = s

	return err
}

func (err *Errors) DevMsg(s string) *Errors {
	err.ErrDevMsg = s

	return err
}

func IsStatus(status int, err error) bool {
	if errs, ok := err.(*Errors); ok {
		if status == errs.HttpStatus {
			return true
		}
	}

	return false
}

func Panic(err error) {
	if err != nil {
		panic(err)
	}
}
<commit_msg>update<commit_after>package errors

import (
	"errors"
	"fmt"
)

const (
	Internal     = 500
	BadReq       = 400
	NotFound     = 404
	Unauthorized = 401
	Forbidden    = 403
)

func New(s string) error {
	return errors.New(s)
}

func Newf(format string, v ...interface{}) error {
	return fmt.Errorf(format, v...)
}

type Option func(*Errors)

func NewMsg(opt ...Option) error {
	err := &Errors{}

	for i := 0; i < len(opt); i++ {
		opt[i](err)
	}

	return err
}

func Http(i int) Option {
	return func(err *Errors) {
		err.HttpStatus = i
	}
}

func Msg(s string) Option {
	return func(err *Errors) {
		err.ErrMessage = s
	}
}

func Msgf(format string, v ...interface{}) Option {
	return func(err *Errors) {
		err.ErrMessage = fmt.Sprintf(format, v...)
	}
}

func Type(s string) Option {
	return func(err *Errors) {
		err.ErrType = s
	}
}

func Code(s string) Option {
	return func(err *Errors) {
		err.ErrCode = s
	}
}

func Dev(s string) Option {
	return func(err *Errors) {
		err.ErrMessage = s
	}
}

type Errors struct {
	HttpStatus int    `json:"-"`
	ErrMessage string `json:"message,omitempty"`
	ErrType    string `json:"error,omitempty"`
	ErrCode    string `json:"code,omitempty"`
	ErrDevMsg  string `json:"dev_message,omitempty"`
}

func (err *Errors) Error() string {
	return err.ErrMessage
}

func (err *Errors) Http(i int) *Errors {
	err.HttpStatus = i

	return err
}

func (err *Errors) Type(s string) *Errors {
	err.ErrType = s

	return err
}

func (err *Errors) Code(s string) *Errors {
	err.ErrCode = s

	return err
}

func (err *Errors) Dev(s string) *Errors {
	err.ErrDevMsg = s

	return err
}

func IsStatus(status int, err error) bool {
	if errs, ok := err.(*Errors); ok {
		if status == errs.HttpStatus {
			return true
		}
	}

	return false
}

func Panic(err error) {
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package hostqueue

import (
	"html/template"
	"net/http"
	"regexp"

	"appengine"

	"github.com/gorilla/mux"
)

type Status struct {
	Name  string `json:"name"`
	Hosts []Host `json:"hosts"`
	Next  int    `json:"next"`
}

/* group/status/{groupUUID}
   Respond with the group name, current queue, who is up next in that queue, and if this week has a host yet.
   I'm not sure what this app is going to be yet, api with spa? or server & client code
*/
func DisplayGroupStatus(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)
	pathVars := mux.Vars(r)

	uuid := pathVars["uuid"]
	ctx.Infof("UUID: %s", uuid)

	if isValidUUID((uuid)) {
		group, err := GetGroupByUUID(ctx, uuid)
		ctx.Infof("group: %v", group)
		if err != nil {
			panic(err)
		}

		status := convertToStatus(group)
		w.Header().Set("Content-Type", "text/html; charset=utf-8")

		var tpl = template.Must(template.ParseGlob("templates/*.html"))
		if err := tpl.ExecuteTemplate(w, "status.html", status); err != nil {
			ctx.Infof("%v", err)
		}

	} else {
		w.Write([]byte("Invalid group"))
	}
}

func convertToStatus(group Group) Status {
	var status Status

	status.Name = group.GroupName
	status.Next = group.Next
	status.Hosts = group.Hosts

	return status
}

func isValidUUID(text string) bool {
	r := regexp.MustCompile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$")
	return r.MatchString(text)
}
<commit_msg>Consolidated status creation.<commit_after>package hostqueue

import (
	"html/template"
	"net/http"
	"regexp"

	"appengine"

	"github.com/gorilla/mux"
)

type Status struct {
	Name  string `json:"name"`
	Hosts []Host `json:"hosts"`
	Next  int    `json:"next"`
}

/* group/status/{groupUUID}
   Respond with the group name, current queue, who is up next in that queue, and if this week has a host yet.
   I'm not sure what this app is going to be yet, api with spa? or server & client code
*/
func DisplayGroupStatus(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)
	pathVars := mux.Vars(r)

	uuid := pathVars["uuid"]
	ctx.Infof("UUID: %s", uuid)

	if isValidUUID((uuid)) {
		group, err := GetGroupByUUID(ctx, uuid)
		ctx.Infof("group: %v", group)
		if err != nil {
			panic(err)
		}

		status := convertToStatus(group)
		w.Header().Set("Content-Type", "text/html; charset=utf-8")

		var tpl = template.Must(template.ParseGlob("templates/*.html"))
		if err := tpl.ExecuteTemplate(w, "status.html", status); err != nil {
			ctx.Infof("%v", err)
		}

	} else {
		w.Write([]byte("Invalid group"))
	}
}

func convertToStatus(group Group) Status {
	return Status{
		Name: group.GroupName,
		Next: group.Next,
		Hosts: group.Hosts,
		}
}

func isValidUUID(text string) bool {
	r := regexp.MustCompile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$")
	return r.MatchString(text)
}
<|endoftext|>


<commit_before>/*
Copyright 2015 Steven Labrum

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

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

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

package CheckIt

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"net/http"
	"path/filepath"
	"strconv"
	"strings"
	"text/template"
)

var outputs = [10]*CompileOut{}
var boxes = []*BoxStruct{}
var interfaces = []Box{}
var page = Page{}
var aboutPage = Page{}
var about = AboutStruct{}
var configuration = &Config{}

var (
	httpListen = flag.String("http", "127.0.0.1:3999", "host:port to listen on")
	htmlOutput = flag.Bool("html", false, "render program output as HTML")
)

func baseCase(w http.ResponseWriter, r *http.Request) {

	headTemp.Execute(w, nil)
	openBodyTemp.Execute(w, nil)
	pageStartTemp.Execute(w, page)

	for key := range boxes {
		boxTemp.Execute(w, boxes[key])
	}

	pageCloseTemp.Execute(w, nil)
	htmlCloseTemp.Execute(w, nil)

}

/*  FrontPage is an HTTP handler that displays the basecase
unless a stored page is being loaded.
*/
func FrontPage(w http.ResponseWriter, r *http.Request) {
	title := r.URL.Path[len("/"):]

	if len(title) < 1 {
		baseCase(w, r)
	} else {
		title := r.URL.Path[len("/"):]
		title = configuration.Path + title
		pageNames, _ := filepath.Glob(title + "/*.page")
		boxNames, _ := filepath.Glob(title + "/*.box")

		fmt.Println(len(pageNames))
		fmt.Println("hello")

		if pageNames == nil || boxNames == nil {
			http.Redirect(w, r, "/", http.StatusFound)
		} else {

			pageName := pageNames[0]
			fmt.Println(boxNames)

			headTemp.Execute(w, nil)
			openBodyTemp.Execute(w, nil)

			p := ReadPage(configuration.Path + pageName)
			pageStartTemp.Execute(w, p)

			boxes = []*BoxStruct{}
			for key := range boxNames {
				boxP := ReadBox(configuration.Path + boxNames[key])
				boxes = append(boxes, boxP)
				boxTemp.Execute(w, boxP)
			}

			pageCloseTemp.Execute(w, nil)
			htmlCloseTemp.Execute(w, nil)
		}
	}
}

func AboutPage(w http.ResponseWriter, r *http.Request) {

	headTemp.Execute(w, nil)
	openBodyTemp.Execute(w, nil)
	pageStartTemp.Execute(w, aboutPage)
	aboutTemp.Execute(w, about)
	pageCloseTemp.Execute(w, nil)
	htmlCloseTemp.Execute(w, nil)

}

var outputText = `<pre>{{printf "%s" . |html}}</pre>`
var output = template.Must(template.New("output").Parse(outputText))
var shareText = `{{printf "%s" . |html}}`
var shareOutput = template.Must(template.New("shareOutput").Parse(shareText))

// Compile is an HTTP handler that reads Source code from the request,
// runs the program (returning any errors),
// and sends the program's output as the HTTP response.
func PipeCompile(w http.ResponseWriter, req *http.Request) {

	title := req.URL.Path[len("/pipeile/"):]

	fmt.Println(title)
	str := strings.Split(title, "/")
	title = str[0]

	position, _ := strconv.Atoi(str[1])

	body := new(bytes.Buffer)

	if _, err := body.ReadFrom(req.Body); err != nil {
		return
	}

	fmt.Println(position)

	updateBody(boxes, title, body.String())

	out, err := InterfaceRun(interfaces[position-1], body.Bytes(), title)
	compOut := CompileOut{Out: out, Error: err}

	outputs[position-1] = &compOut

	if err != nil {
		w.WriteHeader(404)
		output.Execute(w, out)
	} else if *htmlOutput {
		w.Write(out)
	} else {
		output.Execute(w, out)
	}
}

func sharHandler(w http.ResponseWriter, r *http.Request) {
	out := Share()
	fmt.Println("PATH :/ " + configuration.Path)
	Save(configuration.Path, out)
	shareOutput.Execute(w, out)
}

func initConfig(config *Config) {
	configuration = config
	page.Heading = config.Heading
	page.SubHeading = config.SubHeading
	about.Text = config.About
	about.SecondaryText = config.AboutSide
	aboutPage.Heading = "About"
	aboutPage.SubHeading = ""
}

func initBoxes(boxs ...Box) {

	for key := range boxs {

		var box = BoxStruct{}

		box.Id = strconv.Itoa(key)
		box.Position = strconv.Itoa(key + 1)
		box.Total = len(boxs)
		box.Lang = boxs[key].Syntax()
		box.Body = boxs[key].Default()
		box.Head = "Heading"
		box.SubHead = "Subhead"
		box.Text = boxs[key].Help()

		boxes = append(boxes, &box)

		interfaces = append(interfaces, boxs[key])

	}
}

func Serve(config *Config, boxs ...Box) (err error) {
	initConfig(config)
	initBoxes(boxs...)

	fmt.Println("cool beans")
	http.HandleFunc("/share/", sharHandler)
	http.HandleFunc("/about", AboutPage)
	http.HandleFunc("/", FrontPage)
	http.HandleFunc("/compile/", PipeCompile)
	http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("css"))))
	http.Handle("/fonts/", http.StripPrefix("/fonts/", http.FileServer(http.Dir("fonts"))))
	http.Handle("/js/", http.StripPrefix("/js", http.FileServer(http.Dir("js"))))
	http.ListenAndServe(":"+configuration.Port, nil)

	err = errors.New("Server crashed")
	return err
}
<commit_msg>more pathing<commit_after>/*
Copyright 2015 Steven Labrum

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

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

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

package CheckIt

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"net/http"
	"path/filepath"
	"strconv"
	"strings"
	"text/template"
)

var outputs = [10]*CompileOut{}
var boxes = []*BoxStruct{}
var interfaces = []Box{}
var page = Page{}
var aboutPage = Page{}
var about = AboutStruct{}
var configuration = &Config{}

var (
	httpListen = flag.String("http", "127.0.0.1:3999", "host:port to listen on")
	htmlOutput = flag.Bool("html", false, "render program output as HTML")
)

func baseCase(w http.ResponseWriter, r *http.Request) {

	headTemp.Execute(w, nil)
	openBodyTemp.Execute(w, nil)
	pageStartTemp.Execute(w, page)

	for key := range boxes {
		boxTemp.Execute(w, boxes[key])
	}

	pageCloseTemp.Execute(w, nil)
	htmlCloseTemp.Execute(w, nil)

}

/*  FrontPage is an HTTP handler that displays the basecase
unless a stored page is being loaded.
*/
func FrontPage(w http.ResponseWriter, r *http.Request) {
	title := r.URL.Path[len("/"):]

	if len(title) < 1 {
		baseCase(w, r)
	} else {
		title := r.URL.Path[len("/"):]
		title = configuration.Path + title
		pageNames, _ := filepath.Glob(title + "/*.page")
		boxNames, _ := filepath.Glob(title + "/*.box")

		fmt.Println(len(pageNames))
		fmt.Println("hello")

		if pageNames == nil || boxNames == nil {
			http.Redirect(w, r, "/", http.StatusFound)
		} else {

			pageName := pageNames[0]
			fmt.Println(boxNames)

			headTemp.Execute(w, nil)
			openBodyTemp.Execute(w, nil)

			p := ReadPage(configuration.Path + pageName)
			pageStartTemp.Execute(w, p)

			boxes = []*BoxStruct{}
			for key := range boxNames {
				boxP := ReadBox(configuration.Path + boxNames[key])
				boxes = append(boxes, boxP)
				boxTemp.Execute(w, boxP)
			}

			pageCloseTemp.Execute(w, nil)
			htmlCloseTemp.Execute(w, nil)
		}
	}
}

func AboutPage(w http.ResponseWriter, r *http.Request) {

	headTemp.Execute(w, nil)
	openBodyTemp.Execute(w, nil)
	pageStartTemp.Execute(w, aboutPage)
	aboutTemp.Execute(w, about)
	pageCloseTemp.Execute(w, nil)
	htmlCloseTemp.Execute(w, nil)

}

var outputText = `<pre>{{printf "%s" . |html}}</pre>`
var output = template.Must(template.New("output").Parse(outputText))
var shareText = `{{printf "%s" . |html}}`
var shareOutput = template.Must(template.New("shareOutput").Parse(shareText))

// Compile is an HTTP handler that reads Source code from the request,
// runs the program (returning any errors),
// and sends the program's output as the HTTP response.
func PipeCompile(w http.ResponseWriter, req *http.Request) {

	title := req.URL.Path[len("/pipeile/"):]

	fmt.Println(title)
	str := strings.Split(title, "/")
	title = str[0]

	position, _ := strconv.Atoi(str[1])

	body := new(bytes.Buffer)

	if _, err := body.ReadFrom(req.Body); err != nil {
		return
	}

	fmt.Println(position)

	updateBody(boxes, title, body.String())

	out, err := InterfaceRun(interfaces[position-1], body.Bytes(), title)
	compOut := CompileOut{Out: out, Error: err}

	outputs[position-1] = &compOut

	if err != nil {
		w.WriteHeader(404)
		output.Execute(w, out)
	} else if *htmlOutput {
		w.Write(out)
	} else {
		output.Execute(w, out)
	}
}

func sharHandler(w http.ResponseWriter, r *http.Request) {
	out := Share()
	fmt.Println("PATH :/ " + configuration.Path)
	Save(configuration.Path+"/", out)
	shareOutput.Execute(w, out)
}

func initConfig(config *Config) {
	configuration = config
	page.Heading = config.Heading
	page.SubHeading = config.SubHeading
	about.Text = config.About
	about.SecondaryText = config.AboutSide
	aboutPage.Heading = "About"
	aboutPage.SubHeading = ""
}

func initBoxes(boxs ...Box) {

	for key := range boxs {

		var box = BoxStruct{}

		box.Id = strconv.Itoa(key)
		box.Position = strconv.Itoa(key + 1)
		box.Total = len(boxs)
		box.Lang = boxs[key].Syntax()
		box.Body = boxs[key].Default()
		box.Head = "Heading"
		box.SubHead = "Subhead"
		box.Text = boxs[key].Help()

		boxes = append(boxes, &box)

		interfaces = append(interfaces, boxs[key])

	}
}

func Serve(config *Config, boxs ...Box) (err error) {
	initConfig(config)
	initBoxes(boxs...)

	fmt.Println("cool beans")
	http.HandleFunc("/share/", sharHandler)
	http.HandleFunc("/about", AboutPage)
	http.HandleFunc("/", FrontPage)
	http.HandleFunc("/compile/", PipeCompile)
	http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("css"))))
	http.Handle("/fonts/", http.StripPrefix("/fonts/", http.FileServer(http.Dir("fonts"))))
	http.Handle("/js/", http.StripPrefix("/js", http.FileServer(http.Dir("js"))))
	http.ListenAndServe(":"+configuration.Port, nil)

	err = errors.New("Server crashed")
	return err
}
<|endoftext|>


<commit_before>package grid

import (
	"fmt"
	"io"
	"log"
	"mime"
	"os"
)

// ExportService handles communication with the Export related
// methods of the GRiD API.
//
// GRiD API docs: https://github.com/CRREL/GRiD-API/blob/v0.0/composed_api.rst#get-export-details
type ExportService struct {
	client *Client
}

type File struct {
	URL  string `json:"url"`
	Pk   int    `json:"pk"`
	Name string `json:"name"`
}

type ExportDetail struct {
	ExportFiles []File `json:"exportfiles"`
}

func (s *ExportService) ListByPk(pk int) ([]File, *Response, error) {
	url := fmt.Sprintf("api/v0/export/%v/", pk)

	req, err := s.client.NewRequest("GET", url, nil)

	exportDetail := new(ExportDetail)
	resp, err := s.client.Do(req, exportDetail)
	return exportDetail.ExportFiles, resp, err
}

func (s *ExportService) DownloadByPk(pk int) (*Response, error) {
	url := fmt.Sprintf("export/download/file/%v/", pk)

	req, err := s.client.NewRequest("GET", url, nil)

	var foo interface{}
	resp, err := s.client.Do(req, foo)

	cd := resp.Header.Get("Content-Disposition")
	_, params, err := mime.ParseMediaType(cd)
	fname := params["filename"]
	file, err := os.Create(fname)
	defer file.Close()

	numBytes, err := io.Copy(file, resp.Body)
	log.Println("Downloaded", numBytes, "bytes to", fname)
	return resp, err
}
<commit_msg>Defer response body closing<commit_after>package grid

import (
	"fmt"
	"io"
	"log"
	"mime"
	"os"
)

// ExportService handles communication with the Export related
// methods of the GRiD API.
//
// GRiD API docs: https://github.com/CRREL/GRiD-API/blob/v0.0/composed_api.rst#get-export-details
type ExportService struct {
	client *Client
}

type File struct {
	URL  string `json:"url"`
	Pk   int    `json:"pk"`
	Name string `json:"name"`
}

type ExportDetail struct {
	ExportFiles []File `json:"exportfiles"`
}

func (s *ExportService) ListByPk(pk int) ([]File, *Response, error) {
	url := fmt.Sprintf("api/v0/export/%v/", pk)

	req, err := s.client.NewRequest("GET", url, nil)

	exportDetail := new(ExportDetail)
	resp, err := s.client.Do(req, exportDetail)
	return exportDetail.ExportFiles, resp, err
}

func (s *ExportService) DownloadByPk(pk int) (*Response, error) {
	url := fmt.Sprintf("export/download/file/%v/", pk)

	req, err := s.client.NewRequest("GET", url, nil)

	var foo interface{}
	resp, err := s.client.Do(req, foo)
	defer resp.Body.Close()

	cd := resp.Header.Get("Content-Disposition")
	_, params, err := mime.ParseMediaType(cd)
	fname := params["filename"]
	file, err := os.Create(fname)
	defer file.Close()

	numBytes, err := io.Copy(file, resp.Body)
	log.Println("Downloaded", numBytes, "bytes to", fname)
	return resp, err
}
<|endoftext|>


<commit_before>// Package fernet generates and verifies HMAC-based authentication tokens. It
// also encrypts data, so it can be used to transmit secure messages over the
// wire.
//
// For more information and background, see the original Fernet project at
// https://github.com/hgmnz/fernet.
package fernet

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/sha256"
	"crypto/subtle"
	"encoding/base64"
	"encoding/binary"
	"errors"
	"time"
)

const maxClockSkew = 60 * time.Second

var encoding = base64.URLEncoding

func gen(src, iv []byte, ts time.Time, k *Key) ([]byte, error) {
	if *k == (Key{}) {
		return nil, errors.New("fernet: zero key")
	}
	var msg, tok bytes.Buffer
	binary.Write(&msg, binary.BigEndian, ts.Unix())
	msg.Write(iv)
	p := pad(src, aes.BlockSize)
	bc, _ := aes.NewCipher(k.cryptBytes())
	cipher.NewCBCEncrypter(bc, iv).CryptBlocks(p, p)
	msg.Write(p)
	tok.Write(genhmac(msg.Bytes(), k.signBytes()))
	tok.Write(msg.Bytes())
	return b64enc(tok.Bytes()), nil
}

func verify(p []byte, ttl time.Duration, now time.Time, k *Key) []byte {
	if *k == (Key{}) {
		return nil
	}
	tok := b64dec(make([]byte, encoding.DecodedLen(len(p))), p)
	r := bytes.NewBuffer(tok)
	var h struct {
		HMAC     [sha256.Size]byte
		IssuedAt int64
		IV       [aes.BlockSize]byte
	}
	err := binary.Read(r, binary.BigEndian, &h)
	if err != nil {
		return nil
	}
	if subtle.ConstantTimeCompare(h.HMAC[:], genhmac(tok[len(h.HMAC):], k.signBytes())) != 1 {
		return nil
	}
	ts := time.Unix(h.IssuedAt, 0)
	if now.After(ts.Add(ttl)) || ts.After(now.Add(maxClockSkew)) {
		return nil
	}
	msg := r.Bytes()
	if len(msg)%aes.BlockSize != 0 {
		return nil
	}
	bc, _ := aes.NewCipher(k.cryptBytes())
	cipher.NewCBCDecrypter(bc, h.IV[:]).CryptBlocks(msg, msg)
	return unpad(msg)
}

// Pads p to a multiple of k using PKCS #7 standard block padding.
// See http://tools.ietf.org/html/rfc5652#section-6.3.
func pad(p []byte, k int) []byte {
	q := make([]byte, len(p)/k*k+k)
	copy(q, p)
	c := byte(len(q) - len(p))
	for i := len(p); i < len(q); i++ {
		q[i] = c
	}
	return q
}

// Removes PKCS #7 standard block padding from p.
// See http://tools.ietf.org/html/rfc5652#section-6.3.
// This function is the inverse of pad.
func unpad(p []byte) []byte {
	c := p[len(p)-1]
	for i := len(p) - int(c); i < len(p); i++ {
		if i < 0 || p[i] != c {
			return nil
		}
	}
	return p[:len(p)-int(c)]
}

func b64enc(src []byte) []byte {
	dst := make([]byte, encoding.EncodedLen(len(src)))
	encoding.Encode(dst, src)
	return dst
}

func b64dec(dst, src []byte) []byte {
	if encoding.DecodedLen(len(src)) > len(dst) {
		return nil
	}
	n, err := encoding.Decode(dst, src)
	if err != nil {
		return nil
	}
	return dst[:n]
}

func genhmac(p, k []byte) []byte {
	h := hmac.New(sha256.New, k)
	h.Write(p)
	return h.Sum(nil)
}
<commit_msg>simplify<commit_after>// Package fernet generates and verifies HMAC-based authentication tokens. It
// also encrypts data, so it can be used to transmit secure messages over the
// wire.
//
// For more information and background, see the original Fernet project at
// https://github.com/hgmnz/fernet.
package fernet

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/sha256"
	"crypto/subtle"
	"encoding/base64"
	"encoding/binary"
	"errors"
	"time"
)

const maxClockSkew = 60 * time.Second

var encoding = base64.URLEncoding

func gen(src, iv []byte, ts time.Time, k *Key) ([]byte, error) {
	if *k == (Key{}) {
		return nil, errors.New("fernet: zero key")
	}
	var msg, tok bytes.Buffer
	binary.Write(&msg, binary.BigEndian, ts.Unix())
	msg.Write(iv)
	p := pad(src, aes.BlockSize)
	bc, _ := aes.NewCipher(k.cryptBytes())
	cipher.NewCBCEncrypter(bc, iv).CryptBlocks(p, p)
	msg.Write(p)
	tok.Write(genhmac(msg.Bytes(), k.signBytes()))
	tok.Write(msg.Bytes())
	return b64enc(tok.Bytes()), nil
}

func verify(p []byte, ttl time.Duration, now time.Time, k *Key) []byte {
	if *k == (Key{}) {
		return nil
	}
	tok := b64dec(p)
	r := bytes.NewBuffer(tok)
	var h struct {
		HMAC     [sha256.Size]byte
		IssuedAt int64
		IV       [aes.BlockSize]byte
	}
	err := binary.Read(r, binary.BigEndian, &h)
	if err != nil {
		return nil
	}
	if subtle.ConstantTimeCompare(h.HMAC[:], genhmac(tok[len(h.HMAC):], k.signBytes())) != 1 {
		return nil
	}
	ts := time.Unix(h.IssuedAt, 0)
	if now.After(ts.Add(ttl)) || ts.After(now.Add(maxClockSkew)) {
		return nil
	}
	msg := r.Bytes()
	if len(msg)%aes.BlockSize != 0 {
		return nil
	}
	bc, _ := aes.NewCipher(k.cryptBytes())
	cipher.NewCBCDecrypter(bc, h.IV[:]).CryptBlocks(msg, msg)
	return unpad(msg)
}

// Pads p to a multiple of k using PKCS #7 standard block padding.
// See http://tools.ietf.org/html/rfc5652#section-6.3.
func pad(p []byte, k int) []byte {
	q := make([]byte, len(p)/k*k+k)
	copy(q, p)
	c := byte(len(q) - len(p))
	for i := len(p); i < len(q); i++ {
		q[i] = c
	}
	return q
}

// Removes PKCS #7 standard block padding from p.
// See http://tools.ietf.org/html/rfc5652#section-6.3.
// This function is the inverse of pad.
func unpad(p []byte) []byte {
	c := p[len(p)-1]
	for i := len(p) - int(c); i < len(p); i++ {
		if i < 0 || p[i] != c {
			return nil
		}
	}
	return p[:len(p)-int(c)]
}

func b64enc(src []byte) []byte {
	dst := make([]byte, encoding.EncodedLen(len(src)))
	encoding.Encode(dst, src)
	return dst
}

func b64dec(src []byte) []byte {
	dst := make([]byte, encoding.DecodedLen(len(src)))
	n, err := encoding.Decode(dst, src)
	if err != nil {
		return nil
	}
	return dst[:n]
}

func genhmac(p, k []byte) []byte {
	h := hmac.New(sha256.New, k)
	h.Write(p)
	return h.Sum(nil)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	dockerclient "github.com/fsouza/go-dockerclient"
)

func newDocker(opt *containerOption) *docker {
	return &docker{opt: opt, client: client()}
}

type docker struct {
	opt    *containerOption
	client *dockerclient.Client
}

type containerOption struct {
	image      string
	env        []string
	cmd        []string
	volumes    []string
	workingDir string
}

func (d docker) run() (int, error) {
	con, err := d.createContainer()
	if err != nil {
		return 0, err
	}

	err = d.startContainer(con.ID)
	if err != nil {
		return 0, err
	}

	status, err := d.waitContainer(con.ID)
	if err != nil {
		return 0, err
	}

	err = d.removeContainer(con.ID)
	if err != nil {
		return 0, err
	}
	return status, nil
}

func (d docker) createContainer() (*dockerclient.Container, error) {
	volumes := map[string]struct{}{}
	for _, v := range d.opt.volumes {
		volumes[v] = struct{}{}
	}
	return d.client.CreateContainer(dockerclient.CreateContainerOptions{
		Config: &dockerclient.Config{
			Image:      d.opt.image,
			Env:        d.opt.env,
			Cmd:        d.opt.cmd,
			Volumes:    volumes,
			WorkingDir: d.opt.workingDir,
		},
		HostConfig: &dockerclient.HostConfig{
			Binds: d.opt.volumes,
		},
	})
}

func (d docker) startContainer(id string) error {
	return d.client.StartContainer(id, nil)
}

func (d docker) waitContainer(id string) (int, error) {
	return d.client.WaitContainer(id)
}

func (d docker) removeContainer(id string) error {
	return d.client.RemoveContainer(
		dockerclient.RemoveContainerOptions{ID: id},
	)
}

func (d docker) inspectContainer(id string) (*dockerclient.Container, error) {
	return d.client.InspectContainer(id)
}

func client() *dockerclient.Client {
	endpoint := os.Getenv("DOCKER_HOST")
	path := os.Getenv("DOCKER_CERT_PATH")
	ca := fmt.Sprintf("%s/ca.pem", path)
	cert := fmt.Sprintf("%s/cert.pem", path)
	key := fmt.Sprintf("%s/key.pem", path)
	client, _ := dockerclient.NewTLSClient(endpoint, cert, key, ca)
	return client
}
<commit_msg>Added docker client for socket.<commit_after>package main

import (
	"fmt"
	"os"

	dockerclient "github.com/fsouza/go-dockerclient"
)

func newDocker(opt *containerOption) *docker {
	return &docker{opt: opt, client: socketClient()}
}

type docker struct {
	opt    *containerOption
	client *dockerclient.Client
}

type containerOption struct {
	image      string
	env        []string
	cmd        []string
	volumes    []string
	workingDir string
}

func (d docker) run() (int, error) {
	con, err := d.createContainer()
	if err != nil {
		return 0, err
	}

	err = d.startContainer(con.ID)
	if err != nil {
		return 0, err
	}

	status, err := d.waitContainer(con.ID)
	if err != nil {
		return 0, err
	}

	err = d.removeContainer(con.ID)
	if err != nil {
		return 0, err
	}
	return status, nil
}

func (d docker) createContainer() (*dockerclient.Container, error) {
	volumes := map[string]struct{}{}
	for _, v := range d.opt.volumes {
		volumes[v] = struct{}{}
	}
	return d.client.CreateContainer(dockerclient.CreateContainerOptions{
		Config: &dockerclient.Config{
			Image:      d.opt.image,
			Env:        d.opt.env,
			Cmd:        d.opt.cmd,
			Volumes:    volumes,
			WorkingDir: d.opt.workingDir,
		},
		HostConfig: &dockerclient.HostConfig{
			Binds: d.opt.volumes,
		},
	})
}

func (d docker) startContainer(id string) error {
	return d.client.StartContainer(id, nil)
}

func (d docker) waitContainer(id string) (int, error) {
	return d.client.WaitContainer(id)
}

func (d docker) removeContainer(id string) error {
	return d.client.RemoveContainer(
		dockerclient.RemoveContainerOptions{ID: id},
	)
}

func (d docker) inspectContainer(id string) (*dockerclient.Container, error) {
	return d.client.InspectContainer(id)
}

func socketClient() *dockerclient.Client {
	endpoint := "unix:///tmp/docker.sock"
	client, _ := dockerclient.NewClient(endpoint)
	return client
}

func tlsClient() *dockerclient.Client {
	endpoint := os.Getenv("DOCKER_HOST")
	path := os.Getenv("DOCKER_CERT_PATH")
	ca := fmt.Sprintf("%s/ca.pem", path)
	cert := fmt.Sprintf("%s/cert.pem", path)
	key := fmt.Sprintf("%s/key.pem", path)
	client, _ := dockerclient.NewTLSClient(endpoint, cert, key, ca)
	return client
}
<|endoftext|>