<commit_before>package timespec

import (
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type Timespec struct {
	time      *Time
	date      *Date
	increment *Increment
}

type Date struct {
	month      time.Month
	day        int
	year       int
	isToday    bool
	isTomorrow bool
}

func (d *Date) IsNull() bool {
	return d.year == 0 && d.month == 0 && d.day == 0
}

type Time struct {
	hours        int
	minutes      int
	timezoneName string
	isNow        bool
}

type IncrementType int

const (
	IncrementMinutes IncrementType = iota
	IncrementHours
	IncrementDays
	IncrementWeeks
	IncrementMonths
	IncrementYears
)

type Increment struct {
	count int
	unit  IncrementType
}

var (
	monthNames = []*regexp.Regexp{
		regexp.MustCompile("Jan(uary)?"),
		regexp.MustCompile("Feb(ruary)?"),
		regexp.MustCompile("Mar(ch)?"),
		regexp.MustCompile("Apr(il)?"),
		regexp.MustCompile("May"),
		regexp.MustCompile("June?"),
		regexp.MustCompile("July?"),
		regexp.MustCompile("Aug(ust)?"),
		regexp.MustCompile("Sep(tember)?"),
		regexp.MustCompile("Oct(ober)?"),
		regexp.MustCompile("Nov(ember)?"),
		regexp.MustCompile("Dec(ember)?"),
	}
	dayNames = []*regexp.Regexp{
		regexp.MustCompile("Mon(day)?"),
		regexp.MustCompile("Tue(sday)?"),
		regexp.MustCompile("Wed(nesday)?"),
		regexp.MustCompile("Thu(rsday)?"),
		regexp.MustCompile("Fri(day)?"),
		regexp.MustCompile("Sat(urday)?"),
		regexp.MustCompile("Sun(day)?"),
	}
	periodNames = []*regexp.Regexp{
		regexp.MustCompile("minutes?"),
		regexp.MustCompile("hours?"),
		regexp.MustCompile("days?"),
		regexp.MustCompile("weeks?"),
		regexp.MustCompile("months?"),
		regexp.MustCompile("years?"),
	}
)

type charclass func(r byte) bool

func isdigit(r byte) bool {
	return r >= '0' && r <= '9'
}

func isspace(r byte) bool {
	return r == ' ' || r == '\n' || r == '\t'
}

func not(class charclass) charclass {
	return func(r byte) bool {
		return !class(r)
	}
}

func skip(in io.ByteScanner, class charclass) byte {
	c, err := in.ReadByte()
	if c == 0 {
		return c
	}
	for class(c) && err == nil {
		c, err = in.ReadByte()
	}

	in.UnreadByte()

	return c
}

func expect(in io.ByteScanner, out *[]byte, class charclass) (byte, bool) {
	c, _ := in.ReadByte()

	if !class(c) {
		in.UnreadByte()
		return c, false
	} else {
		*out = append(*out, c)
		return c, true
	}
}

func expectBytes(in io.ByteScanner, s []byte) (string, bool) {
	buf := []byte{}

	for _, expected := range s {
		c, _ := in.ReadByte()
		if c != expected {
			return string(buf), false
		} else {
			buf = append(buf, c)
		}
	}

	return "", true
}

func any(in io.ByteScanner, out *[]byte, class charclass) {
	c, err := in.ReadByte()
	if err != nil {
		return
	}

	for class(c) && err == nil {
		*out = append(*out, c)
		c, err = in.ReadByte()
	}

	in.UnreadByte()
}

func expectN(n int, in io.ByteScanner, out *[]byte, class charclass) (byte, bool) {
	var c byte

	for i := 0; i < n; i++ {
		c, ok := expect(in, out, class)
		if !ok {
			return c, false
		}
	}

	return c, true
}

func parseTimespec(in io.ByteScanner, spec *Timespec) error {
	c, _ := in.ReadByte()
	in.UnreadByte()
	if c == 0 {
		return fmt.Errorf("parseTimespec: unexpected EOF")
	}

	if c == 'n' {
		actual, ok := expectBytes(in, []byte("now"))
		if !ok {
			return fmt.Errorf("parseTimespec: expected %q, got %q", "now", actual)
		}

		spec.time.isNow = true
		return parseIncrement(in, spec.increment)
	}

	err := parseTime(in, spec.time)
	if err != nil {
		return err
	}

	err = parseDate(in, spec.date)
	if err != nil {
		spec.date = &Date{}
	}

	err = parseIncrement(in, spec.increment)
	if err != nil {
		spec.increment = &Increment{}
	}

	spec.date.isToday = spec.date.IsNull()

	return nil
}

func parseIncrement(in io.ByteScanner, incr *Increment) error {
	skip(in, isspace)
	c, _ := in.ReadByte()

	if c == 0 {
		return nil
	}

	if c == 'n' {
		in.UnreadByte()
		actual, ok := expectBytes(in, []byte("next"))
		if !ok {
			return fmt.Errorf("parseIncrement: Expected \"next\", got %q", actual)
		}

		incr.count = 1
	} else if c == '+' {
		buf := []byte{}
		skip(in, isspace)
		any(in, &buf, isdigit)
		count, err := strconv.ParseInt(string(buf), 10, 0)
		if err != nil {
			return fmt.Errorf("parseIncrement: %s", err)
		}

		incr.count = int(count)
	} else {
		return fmt.Errorf("parseIncrement: Expected '+', got '%c'", c)
	}

	buf := []byte{}
	skip(in, isspace)
	any(in, &buf, not(isspace))

	period := findPeriod(buf)
	if period == -1 {
		return fmt.Errorf("parsePeriod: Invalid period: %q", buf)
	}

	incr.unit = IncrementType(period)

	return nil
}

func findPeriod(buf []byte) int {
	return findInRegexpList(periodNames, buf)
}

func parseDate(in io.ByteScanner, date *Date) error {
	c, _ := in.ReadByte()
	in.UnreadByte()

	if c == 0 {
		return fmt.Errorf("parseDate: unexpected EOF")
	}

	buf := []byte{}
	c = skip(in, isspace)
	if c == '+' || c == 'n' {
		return nil
	}

	any(in, &buf, not(isspace))

	if string(buf) == "today" {
		date.isToday = true
		return nil
	}

	if string(buf) == "tomorrow" {
		date.isTomorrow = true
		return nil
	}

	day := findDayOfWeek(buf)
	if day != -1 {
		date.day = day
		return nil
	}

	month := findMonth(buf)
	if month == -1 {
		return fmt.Errorf("parseDate: Invalid month name: %q", buf)
	}

	date.month = time.Month(month)

	return parseMonth(in, date)
}

func parseMonth(in io.ByteScanner, date *Date) error {
	buf := []byte{}
	skip(in, isspace)
	c, ok := expectN(2, in, &buf, isdigit)
	if !ok {
		return fmt.Errorf("parseMonth: Expected 2 digits, got: %q", buf)
	}

	day, err := strconv.Atoi(string(buf))
	if err != nil {
		return fmt.Errorf("parseMonth: Invalid day numer: %s", buf)
	}

	date.day = day

	skip(in, isspace)
	c, _ = in.ReadByte()
	if c == ',' {
		return parseYear(in, date)
	} else {
		in.UnreadByte()
	}

	return nil
}

func parseYear(in io.ByteScanner, date *Date) error {
	buf := []byte{}

	skip(in, isspace)
	expectN(4, in, &buf, isdigit)

	year, err := strconv.ParseInt(string(buf), 10, 0)
	if err != nil {
		return fmt.Errorf("parseYear: Invalid year format: %q", buf)
	}

	date.year = int(year)

	return nil
}

func findInRegexpList(list []*regexp.Regexp, buf []byte) int {
	for index, re := range list {
		if re.Match(buf) {
			return index
		}
	}

	return -1
}

func findMonth(buf []byte) int {
	index := findInRegexpList(monthNames, buf)
	if index != -1 {
		return index + 1
	} else {
		return -1
	}
}

func findDayOfWeek(buf []byte) int {
	return findInRegexpList(dayNames, buf)
}

func parseTime(in io.ByteScanner, time *Time) error {
	c, _ := in.ReadByte()
	in.UnreadByte()

	if isdigit(c) {
		return parseClock(in, time)
	} else if c == 'n' {
		return parseNoon(in, time)
	} else if c == 'm' {
		return parseMidnight(in, time)
	}

	return fmt.Errorf("parseTime: Unexpected character %c", c)
}

func parseClock(in io.ByteScanner, time *Time) error {
	c, _ := in.ReadByte()
	buf := []byte{c}

	c, _ = in.ReadByte()

	if c != 0 {
		if !isdigit(c) {
			in.UnreadByte()
			skip(in, isspace)
		} else {
			buf = append(buf, c)
		}
	}

	hours, err := strconv.Atoi(string(buf))
	if err != nil {
		return fmt.Errorf("parseClock: invalid number format: %s", buf)
	}

	if hours > 23 {
		return fmt.Errorf("parseClock: invalid hours: %d", hours)
	}

	time.hours = hours

	c, _ = in.ReadByte()
	in.UnreadByte()

	if c == 0 {
		return fmt.Errorf("parseClock: Unexpected EOF")
	}

	if isdigit(c) || c == ':' {
		if err := parseMinute(in, time); err != nil {
			return err
		}
	}

	c = skip(in, isspace)

	if c != 0 && strings.IndexByte("aApP", c) != -1 {
		if err := parseAmPm(in, time); err != nil {
			return err
		}
	}

	parseTimeZone(in, time)

	return nil
}

func parseMinute(in io.ByteScanner, time *Time) error {
	c, _ := in.ReadByte()

	if c == 0 {
		return nil
	}

	if c != ':' && !isdigit(c) {
		return fmt.Errorf("parseMinute: Expected ':' or digit, got '%c'", c)
	} else if isdigit(c) {
		in.UnreadByte()
	} else if c != ':' {
		return nil
	}

	buf := []byte{}
	if c, ok := expectN(2, in, &buf, isdigit); !ok {
		return fmt.Errorf("parseMinute: Expected digit, got '%c'", c)
	}
	minutes, err := strconv.Atoi(string(buf))
	if err != nil {
		return fmt.Errorf("parseMinute: %s", err)
	}

	if minutes >= 60 {
		return fmt.Errorf("parseMinute: Invalid minutes: %d", minutes)
	}

	time.minutes = minutes

	return nil
}

func parseTimeZone(in io.ByteScanner, time *Time) error {
	c := skip(in, isspace)
	// only UTC (case insensitive) is a valid timezone
	if c != 'u' && c != 'U' {
		return nil
	}

	buf := []byte{}

	expectN(3, in, &buf, not(isspace))

	timezone := strings.ToUpper(string(buf))

	if timezone != "UTC" {
		return fmt.Errorf("parseTimeZone: Invalid timezone: %q", buf)
	}

	time.timezoneName = timezone

	return nil
}

func parseAmPm(in io.ByteScanner, time *Time) error {
	c, err := in.ReadByte()
	buf := []byte{c}

	c, err = in.ReadByte()
	if err != nil {
		return fmt.Errorf("parseAmPm: %s", err)
	}

	if c != 'm' && c != 'M' {
		return fmt.Errorf("parseAmPm: Expected 'm', got %c", c)
	} else {
		buf = append(buf, c)
	}

	if strings.ToLower(string(buf)) == "pm" {
		time.hours = (time.hours % 12) + 12
	}

	return nil
}

func parseNoon(in io.ByteScanner, time *Time) error {
	s, ok := expectBytes(in, []byte("noon"))
	if !ok {
		return fmt.Errorf("parseNoon: Expected %q, got %q", "noon", s)
	}

	time.hours = 12

	return nil
}

func parseMidnight(in io.ByteScanner, time *Time) error {
	s, ok := expectBytes(in, []byte("midnight"))
	if !ok {
		return fmt.Errorf("parseMidnight: Expected %q, got %q", "midnight", s)
	}

	return nil
}
<commit_msg>Remove useless allocations.<commit_after>package timespec

import (
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type Timespec struct {
	time      *Time
	date      *Date
	increment *Increment
}

type Date struct {
	month      time.Month
	day        int
	year       int
	isToday    bool
	isTomorrow bool
}

func (d *Date) IsNull() bool {
	return d.year == 0 && d.month == 0 && d.day == 0
}

type Time struct {
	hours        int
	minutes      int
	timezoneName string
	isNow        bool
}

type IncrementType int

const (
	IncrementMinutes IncrementType = iota
	IncrementHours
	IncrementDays
	IncrementWeeks
	IncrementMonths
	IncrementYears
)

type Increment struct {
	count int
	unit  IncrementType
}

var (
	monthNames = []*regexp.Regexp{
		regexp.MustCompile("Jan(uary)?"),
		regexp.MustCompile("Feb(ruary)?"),
		regexp.MustCompile("Mar(ch)?"),
		regexp.MustCompile("Apr(il)?"),
		regexp.MustCompile("May"),
		regexp.MustCompile("June?"),
		regexp.MustCompile("July?"),
		regexp.MustCompile("Aug(ust)?"),
		regexp.MustCompile("Sep(tember)?"),
		regexp.MustCompile("Oct(ober)?"),
		regexp.MustCompile("Nov(ember)?"),
		regexp.MustCompile("Dec(ember)?"),
	}
	dayNames = []*regexp.Regexp{
		regexp.MustCompile("Mon(day)?"),
		regexp.MustCompile("Tue(sday)?"),
		regexp.MustCompile("Wed(nesday)?"),
		regexp.MustCompile("Thu(rsday)?"),
		regexp.MustCompile("Fri(day)?"),
		regexp.MustCompile("Sat(urday)?"),
		regexp.MustCompile("Sun(day)?"),
	}
	periodNames = []*regexp.Regexp{
		regexp.MustCompile("minutes?"),
		regexp.MustCompile("hours?"),
		regexp.MustCompile("days?"),
		regexp.MustCompile("weeks?"),
		regexp.MustCompile("months?"),
		regexp.MustCompile("years?"),
	}
)

type charclass func(r byte) bool

func isdigit(r byte) bool {
	return r >= '0' && r <= '9'
}

func isspace(r byte) bool {
	return r == ' ' || r == '\n' || r == '\t'
}

func nospace(r byte) bool {
	return !isspace(r)
}

func skip(in io.ByteScanner, class charclass) byte {
	c, err := in.ReadByte()
	if c == 0 {
		return c
	}
	for class(c) && err == nil {
		c, err = in.ReadByte()
	}

	in.UnreadByte()

	return c
}

func expect(in io.ByteScanner, out *[]byte, class charclass) (byte, bool) {
	c, _ := in.ReadByte()

	if !class(c) {
		in.UnreadByte()
		return c, false
	} else {
		*out = append(*out, c)
		return c, true
	}
}

func expectBytes(in io.ByteScanner, s []byte) (string, bool) {
	buf := []byte{}

	for _, expected := range s {
		c, _ := in.ReadByte()
		if c != expected {
			return string(buf), false
		} else {
			buf = append(buf, c)
		}
	}

	return "", true
}

func any(in io.ByteScanner, out *[]byte, class charclass) {
	c, err := in.ReadByte()
	if err != nil {
		return
	}

	for class(c) && err == nil {
		*out = append(*out, c)
		c, err = in.ReadByte()
	}

	in.UnreadByte()
}

func expectN(n int, in io.ByteScanner, out *[]byte, class charclass) (byte, bool) {
	var c byte

	for i := 0; i < n; i++ {
		c, ok := expect(in, out, class)
		if !ok {
			return c, false
		}
	}

	return c, true
}

func parseTimespec(in io.ByteScanner, spec *Timespec) error {
	c, _ := in.ReadByte()
	in.UnreadByte()
	if c == 0 {
		return fmt.Errorf("parseTimespec: unexpected EOF")
	}

	if c == 'n' {
		actual, ok := expectBytes(in, []byte("now"))
		if !ok {
			return fmt.Errorf("parseTimespec: expected %q, got %q", "now", actual)
		}

		spec.time.isNow = true
		return parseIncrement(in, spec.increment)
	}

	err := parseTime(in, spec.time)
	if err != nil {
		return err
	}

	err = parseDate(in, spec.date)
	if err != nil {
		spec.date = &Date{}
	}

	err = parseIncrement(in, spec.increment)
	if err != nil {
		spec.increment = &Increment{}
	}

	spec.date.isToday = spec.date.IsNull()

	return nil
}

func parseIncrement(in io.ByteScanner, incr *Increment) error {
	skip(in, isspace)
	c, _ := in.ReadByte()

	if c == 0 {
		return nil
	}

	if c == 'n' {
		in.UnreadByte()
		actual, ok := expectBytes(in, []byte("next"))
		if !ok {
			return fmt.Errorf("parseIncrement: Expected \"next\", got %q", actual)
		}

		incr.count = 1
	} else if c == '+' {
		buf := []byte{}
		skip(in, isspace)
		any(in, &buf, isdigit)
		count, err := strconv.ParseInt(string(buf), 10, 0)
		if err != nil {
			return fmt.Errorf("parseIncrement: %s", err)
		}

		incr.count = int(count)
	} else {
		return fmt.Errorf("parseIncrement: Expected '+', got '%c'", c)
	}

	buf := []byte{}
	skip(in, isspace)
	any(in, &buf, nospace)

	period := findPeriod(buf)
	if period == -1 {
		return fmt.Errorf("parsePeriod: Invalid period: %q", buf)
	}

	incr.unit = IncrementType(period)

	return nil
}

func findPeriod(buf []byte) int {
	return findInRegexpList(periodNames, buf)
}

func parseDate(in io.ByteScanner, date *Date) error {
	c, _ := in.ReadByte()
	in.UnreadByte()

	if c == 0 {
		return fmt.Errorf("parseDate: unexpected EOF")
	}

	buf := []byte{}
	c = skip(in, isspace)
	if c == '+' || c == 'n' {
		return nil
	}

	any(in, &buf, nospace)

	if string(buf) == "today" {
		date.isToday = true
		return nil
	}

	if string(buf) == "tomorrow" {
		date.isTomorrow = true
		return nil
	}

	day := findDayOfWeek(buf)
	if day != -1 {
		date.day = day
		return nil
	}

	month := findMonth(buf)
	if month == -1 {
		return fmt.Errorf("parseDate: Invalid month name: %q", buf)
	}

	date.month = time.Month(month)

	return parseMonth(in, date)
}

func parseMonth(in io.ByteScanner, date *Date) error {
	buf := []byte{}
	skip(in, isspace)
	c, ok := expectN(2, in, &buf, isdigit)
	if !ok {
		return fmt.Errorf("parseMonth: Expected 2 digits, got: %q", buf)
	}

	day, err := strconv.Atoi(string(buf))
	if err != nil {
		return fmt.Errorf("parseMonth: Invalid day numer: %s", buf)
	}

	date.day = day

	skip(in, isspace)
	c, _ = in.ReadByte()
	if c == ',' {
		return parseYear(in, date)
	} else {
		in.UnreadByte()
	}

	return nil
}

func parseYear(in io.ByteScanner, date *Date) error {
	buf := []byte{}

	skip(in, isspace)
	expectN(4, in, &buf, isdigit)

	year, err := strconv.ParseInt(string(buf), 10, 0)
	if err != nil {
		return fmt.Errorf("parseYear: Invalid year format: %q", buf)
	}

	date.year = int(year)

	return nil
}

func findInRegexpList(list []*regexp.Regexp, buf []byte) int {
	for index, re := range list {
		if re.Match(buf) {
			return index
		}
	}

	return -1
}

func findMonth(buf []byte) int {
	index := findInRegexpList(monthNames, buf)
	if index != -1 {
		return index + 1
	} else {
		return -1
	}
}

func findDayOfWeek(buf []byte) int {
	return findInRegexpList(dayNames, buf)
}

func parseTime(in io.ByteScanner, time *Time) error {
	c, _ := in.ReadByte()
	in.UnreadByte()

	if isdigit(c) {
		return parseClock(in, time)
	} else if c == 'n' {
		return parseNoon(in, time)
	} else if c == 'm' {
		return parseMidnight(in, time)
	}

	return fmt.Errorf("parseTime: Unexpected character %c", c)
}

func parseClock(in io.ByteScanner, time *Time) error {
	c, _ := in.ReadByte()
	buf := []byte{c}

	c, _ = in.ReadByte()

	if c != 0 {
		if !isdigit(c) {
			in.UnreadByte()
			skip(in, isspace)
		} else {
			buf = append(buf, c)
		}
	}

	hours, err := strconv.Atoi(string(buf))
	if err != nil {
		return fmt.Errorf("parseClock: invalid number format: %s", buf)
	}

	if hours > 23 {
		return fmt.Errorf("parseClock: invalid hours: %d", hours)
	}

	time.hours = hours

	c, _ = in.ReadByte()
	in.UnreadByte()

	if c == 0 {
		return fmt.Errorf("parseClock: Unexpected EOF")
	}

	if isdigit(c) || c == ':' {
		if err := parseMinute(in, time); err != nil {
			return err
		}
	}

	c = skip(in, isspace)

	if c != 0 && strings.IndexByte("aApP", c) != -1 {
		if err := parseAmPm(in, time); err != nil {
			return err
		}
	}

	parseTimeZone(in, time)

	return nil
}

func parseMinute(in io.ByteScanner, time *Time) error {
	c, _ := in.ReadByte()

	if c == 0 {
		return nil
	}

	if c != ':' && !isdigit(c) {
		return fmt.Errorf("parseMinute: Expected ':' or digit, got '%c'", c)
	} else if isdigit(c) {
		in.UnreadByte()
	} else if c != ':' {
		return nil
	}

	buf := []byte{}
	if c, ok := expectN(2, in, &buf, isdigit); !ok {
		return fmt.Errorf("parseMinute: Expected digit, got '%c'", c)
	}
	minutes, err := strconv.Atoi(string(buf))
	if err != nil {
		return fmt.Errorf("parseMinute: %s", err)
	}

	if minutes >= 60 {
		return fmt.Errorf("parseMinute: Invalid minutes: %d", minutes)
	}

	time.minutes = minutes

	return nil
}

func parseTimeZone(in io.ByteScanner, time *Time) error {
	c := skip(in, isspace)
	// only UTC (case insensitive) is a valid timezone
	if c != 'u' && c != 'U' {
		return nil
	}

	buf := []byte{}

	expectN(3, in, &buf, nospace)

	timezone := strings.ToUpper(string(buf))

	if timezone != "UTC" {
		return fmt.Errorf("parseTimeZone: Invalid timezone: %q", buf)
	}

	time.timezoneName = timezone

	return nil
}

func parseAmPm(in io.ByteScanner, time *Time) error {
	c, err := in.ReadByte()
	buf := []byte{c}

	c, err = in.ReadByte()
	if err != nil {
		return fmt.Errorf("parseAmPm: %s", err)
	}

	if c != 'm' && c != 'M' {
		return fmt.Errorf("parseAmPm: Expected 'm', got %c", c)
	} else {
		buf = append(buf, c)
	}

	if strings.ToLower(string(buf)) == "pm" {
		time.hours = (time.hours % 12) + 12
	}

	return nil
}

func parseNoon(in io.ByteScanner, time *Time) error {
	s, ok := expectBytes(in, []byte("noon"))
	if !ok {
		return fmt.Errorf("parseNoon: Expected %q, got %q", "noon", s)
	}

	time.hours = 12

	return nil
}

func parseMidnight(in io.ByteScanner, time *Time) error {
	s, ok := expectBytes(in, []byte("midnight"))
	if !ok {
		return fmt.Errorf("parseMidnight: Expected %q, got %q", "midnight", s)
	}

	return nil
}
<|endoftext|>


<commit_before>// (c) Copyright IBM Corp. 2021
// (c) Copyright Instana Inc. 2020

// Package instalambda provides Instana tracing instrumentation for
// AWS Lambda functions
package instalambda

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-lambda-go/lambdacontext"
	instana "github.com/instana/go-sensor"
	"github.com/opentracing/opentracing-go"
	otlog "github.com/opentracing/opentracing-go/log"
)

const (
	awsLambdaFlushMaxRetries  = 5
	awsLambdaFlushRetryPeriod = 50 * time.Millisecond
	awsLambdaTimeoutThreshold = 100 * time.Millisecond
)

var errHandlerTimedOut = errors.New("handler has timed out")

type wrappedHandler struct {
	lambda.Handler

	sensor      *instana.Sensor
	onColdStart sync.Once
}

// NewHandler creates a new instrumented handler that can be used with `lambda.StartHandler()` from a handler function
func NewHandler(handlerFunc interface{}, sensor *instana.Sensor) *wrappedHandler {
	return WrapHandler(lambda.NewHandler(handlerFunc), sensor)
}

// WrapHandler instruments a lambda.Handler to trace the invokations with Instana
func WrapHandler(h lambda.Handler, sensor *instana.Sensor) *wrappedHandler {
	return &wrappedHandler{
		Handler: h,
		sensor:  sensor,
	}
}

// Invoke is a handler function for a wrapped handler
func (h *wrappedHandler) Invoke(ctx context.Context, payload []byte) ([]byte, error) {
	lc, ok := lambdacontext.FromContext(ctx)
	if !ok {
		return h.Handler.Invoke(ctx, payload)
	}

	opts := append([]opentracing.StartSpanOption{opentracing.Tags{
		"lambda.arn":     lc.InvokedFunctionArn + ":" + lambdacontext.FunctionVersion,
		"lambda.name":    lambdacontext.FunctionName,
		"lambda.version": lambdacontext.FunctionVersion,
	}}, h.triggerEventSpanOptions(payload, lc.ClientContext)...)

	sp := h.sensor.Tracer().StartSpan("aws.lambda.entry", opts...)
	defer func() {
		sp.Finish()

		// ensure that all collected data has been sent before the invocation is finished
		h.flushAgent(awsLambdaFlushRetryPeriod, awsLambdaFlushMaxRetries)
	}()

	h.onColdStart.Do(func() {
		sp.SetTag("lambda.coldStart", true)
	})

	done := make(chan struct{})
	timeoutChannel := make(<-chan time.Time)

	originalDeadline, deadlineDefined := ctx.Deadline()

	if deadlineDefined {
		deadline := originalDeadline.Add(-awsLambdaTimeoutThreshold)
		timeoutChannel = time.After(time.Until(deadline))
	}

	var (
		resp []byte
		err  error
	)

	go func() {
		resp, err = h.Handler.Invoke(instana.ContextWithSpan(ctx, sp), payload)
		if err != nil {
			sp.LogFields(otlog.Error(err))
		}

		close(done)
	}()

	select {
	case <-done:
	case <-timeoutChannel:
		h.sensor.Logger().Debug("lambda handler has timed out")

		remainingTime := time.Until(originalDeadline).Truncate(time.Millisecond)

		sp.SetTag("lambda.msleft", int64(remainingTime)/1e6) // cast time.Duration to int64 for compatibility with older Go versions
		sp.SetTag("lambda.error", fmt.Sprintf(`The Lambda function was still running when only %s were left, it might have timed out.`, remainingTime))

		sp.LogFields(otlog.Error(errHandlerTimedOut))
	}

	return resp, err
}

func (h *wrappedHandler) flushAgent(retryPeriod time.Duration, maxRetries int) {
	if tr, ok := h.sensor.Tracer().(instana.Tracer); ok {
		var i int
		for {
			if err := tr.Flush(context.Background()); err != nil {
				if err == instana.ErrAgentNotReady && i < maxRetries {
					i++
					time.Sleep(retryPeriod)
					continue
				}

				h.sensor.Logger().Error("failed to send traces:", err)
			}

			break
		}
	}
}

func (h *wrappedHandler) triggerEventSpanOptions(payload []byte, lcc lambdacontext.ClientContext) []opentracing.StartSpanOption {
	switch detectTriggerEventType(payload) {
	case apiGatewayEventType:
		var v events.APIGatewayProxyRequest
		if err := json.Unmarshal(payload, &v); err != nil {
			h.sensor.Logger().Warn("failed to unmarshal API Gateway event payload: ", err)
			return []opentracing.StartSpanOption{opentracing.Tags{}}
		}

		opts := []opentracing.StartSpanOption{h.extractAPIGatewayTriggerTags(v)}
		if parentCtx, ok := h.extractParentContext(v.Headers); ok {
			opts = append(opts, opentracing.ChildOf(parentCtx))
		}

		return opts
	case apiGatewayV2EventType:
		var v events.APIGatewayV2HTTPRequest
		if err := json.Unmarshal(payload, &v); err != nil {
			h.sensor.Logger().Warn("failed to unmarshal API Gateway v2.0 event payload: ", err)
			return []opentracing.StartSpanOption{opentracing.Tags{}}
		}

		opts := []opentracing.StartSpanOption{h.extractAPIGatewayV2TriggerTags(v)}
		if parentCtx, ok := h.extractParentContext(v.Headers); ok {
			opts = append(opts, opentracing.ChildOf(parentCtx))
		}

		return opts
	case albEventType:
		var v events.ALBTargetGroupRequest
		if err := json.Unmarshal(payload, &v); err != nil {
			h.sensor.Logger().Warn("failed to unmarshal ALB event payload: ", err)
			return []opentracing.StartSpanOption{opentracing.Tags{}}
		}

		opts := []opentracing.StartSpanOption{h.extractALBTriggerTags(v)}
		if parentCtx, ok := h.extractParentContext(v.Headers); ok {
			opts = append(opts, opentracing.ChildOf(parentCtx))
		}

		return opts
	case cloudWatchEventType:
		var v events.CloudWatchEvent
		if err := json.Unmarshal(payload, &v); err != nil {
			h.sensor.Logger().Warn("failed to unmarshal CloudWatch event payload: ", err)
			return []opentracing.StartSpanOption{opentracing.Tags{}}
		}

		return []opentracing.StartSpanOption{h.extractCloudWatchTriggerTags(v)}
	case cloudWatchLogsEventType:
		var v events.CloudwatchLogsEvent
		if err := json.Unmarshal(payload, &v); err != nil {
			h.sensor.Logger().Warn("failed to unmarshal CloudWatch Logs event payload: ", err)
			return []opentracing.StartSpanOption{opentracing.Tags{}}
		}

		return []opentracing.StartSpanOption{h.extractCloudWatchLogsTriggerTags(v)}
	case s3EventType:
		var v events.S3Event
		if err := json.Unmarshal(payload, &v); err != nil {
			h.sensor.Logger().Warn("failed to unmarshal S3 event payload: ", err)
			return []opentracing.StartSpanOption{opentracing.Tags{}}
		}

		return []opentracing.StartSpanOption{h.extractS3TriggerTags(v)}
	case sqsEventType:
		var v events.SQSEvent
		if err := json.Unmarshal(payload, &v); err != nil {
			h.sensor.Logger().Warn("failed to unmarshal SQS event payload: ", err)
			return []opentracing.StartSpanOption{opentracing.Tags{}}
		}

		return []opentracing.StartSpanOption{h.extractSQSTriggerTags(v)}
	case invokeRequestType:
		tags := opentracing.Tags{
			"lambda.trigger": "aws:lambda.invoke",
		}

		opts := []opentracing.StartSpanOption{tags}
		if parentCtx, ok := h.extractParentContext(lcc.Custom); ok {
			opts = append(opts, opentracing.ChildOf(parentCtx))
		}
		return opts

	default:
		h.sensor.Logger().Info("unsupported AWS Lambda trigger event type, the entry span will include generic tags only")
		return []opentracing.StartSpanOption{opentracing.Tags{}}
	}
}

func (h *wrappedHandler) extractParentContext(headers map[string]string) (opentracing.SpanContext, bool) {
	hdrs := http.Header{}
	for k, v := range headers {
		hdrs.Set(k, v)
	}

	switch parentCtx, err := h.sensor.Tracer().Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(hdrs)); err {
	case nil:
		return parentCtx, true
	case opentracing.ErrSpanContextNotFound:
		h.sensor.Logger().Debug("lambda invoke event did not provide trace context")
	case opentracing.ErrUnsupportedFormat:
		h.sensor.Logger().Info("lambda invoke event provided trace context in unsupported format")
	default:
		h.sensor.Logger().Warn("failed to extract span context from the lambda invoke event:", err)
	}

	return nil, false
}

func (h *wrappedHandler) extractAPIGatewayTriggerTags(evt events.APIGatewayProxyRequest) opentracing.Tags {
	tags := opentracing.Tags{
		"lambda.trigger": "aws:api.gateway",
		"http.method":    evt.HTTPMethod,
		"http.url":       evt.Path,
		"http.path_tpl":  evt.Resource,
		"http.params":    h.sanitizeHTTPParams(evt.QueryStringParameters, evt.MultiValueQueryStringParameters).Encode(),
	}

	if headers := h.collectHTTPHeaders(evt.Headers, evt.MultiValueHeaders); len(headers) > 0 {
		tags["http.header"] = headers
	}

	return tags
}

func (h *wrappedHandler) extractAPIGatewayV2TriggerTags(evt events.APIGatewayV2HTTPRequest) opentracing.Tags {
	routeKeyPath := evt.RouteKey
	// Strip any trailing HTTP request method
	if i := strings.Index(routeKeyPath, " "); i >= 0 {
		routeKeyPath = evt.RouteKey[i+1:]
	}

	tags := opentracing.Tags{
		"lambda.trigger": "aws:api.gateway",
		"http.method":    evt.RequestContext.HTTP.Method,
		"http.url":       evt.RequestContext.HTTP.Path,
		"http.path_tpl":  routeKeyPath,
		"http.params":    h.sanitizeHTTPParams(evt.QueryStringParameters, nil).Encode(),
	}

	if headers := h.collectHTTPHeaders(evt.Headers, nil); len(headers) > 0 {
		tags["http.header"] = headers
	}

	return tags
}

func (h *wrappedHandler) extractALBTriggerTags(evt events.ALBTargetGroupRequest) opentracing.Tags {
	tags := opentracing.Tags{
		"lambda.trigger": "aws:application.load.balancer",
		"http.method":    evt.HTTPMethod,
		"http.url":       evt.Path,
		"http.params":    h.sanitizeHTTPParams(evt.QueryStringParameters, evt.MultiValueQueryStringParameters).Encode(),
	}

	if headers := h.collectHTTPHeaders(evt.Headers, evt.MultiValueHeaders); len(headers) > 0 {
		tags["http.header"] = headers
	}

	return tags
}

func (h *wrappedHandler) extractCloudWatchTriggerTags(evt events.CloudWatchEvent) opentracing.Tags {
	return opentracing.Tags{
		"lambda.trigger":              "aws:cloudwatch.events",
		"cloudwatch.events.id":        evt.ID,
		"cloudwatch.events.resources": evt.Resources,
	}
}

func (h *wrappedHandler) extractCloudWatchLogsTriggerTags(evt events.CloudwatchLogsEvent) opentracing.Tags {
	logs, err := evt.AWSLogs.Parse()
	if err != nil {
		return opentracing.Tags{
			"lambda.trigger":                "aws:cloudwatch.logs",
			"cloudwatch.logs.decodingError": err,
		}
	}

	var events []string
	for _, event := range logs.LogEvents {
		events = append(events, event.Message)
	}

	return opentracing.Tags{
		"lambda.trigger":         "aws:cloudwatch.logs",
		"cloudwatch.logs.group":  logs.LogGroup,
		"cloudwatch.logs.stream": logs.LogStream,
		"cloudwatch.logs.events": events,
	}
}

func (h *wrappedHandler) extractS3TriggerTags(evt events.S3Event) opentracing.Tags {
	var events []instana.AWSS3EventTags
	for _, rec := range evt.Records {
		events = append(events, instana.AWSS3EventTags{
			Name:   rec.EventName,
			Bucket: rec.S3.Bucket.Name,
			Object: rec.S3.Object.Key,
		})
	}

	return opentracing.Tags{
		"lambda.trigger": "aws:s3",
		"s3.events":      events,
	}
}

func (h *wrappedHandler) extractSQSTriggerTags(evt events.SQSEvent) opentracing.Tags {
	var msgs []instana.AWSSQSMessageTags
	for _, rec := range evt.Records {
		msgs = append(msgs, instana.AWSSQSMessageTags{
			Queue: rec.EventSourceARN,
		})
	}

	return opentracing.Tags{
		"lambda.trigger": "aws:sqs",
		"sqs.messages":   msgs,
	}
}

func (h *wrappedHandler) sanitizeHTTPParams(
	queryStringParams map[string]string,
	multiValueQueryStringParams map[string][]string,
) url.Values {
	secretMatcher := instana.DefaultSecretsMatcher()
	if tr, ok := h.sensor.Tracer().(instana.Tracer); ok {
		secretMatcher = tr.Options().Secrets
	}

	params := url.Values{}

	for k, v := range queryStringParams {
		if secretMatcher.Match(k) {
			v = "<redacted>"
		}
		params.Set(k, v)
	}

	for k, vv := range multiValueQueryStringParams {
		isSecret := secretMatcher.Match(k)
		for _, v := range vv {
			if isSecret {
				v = "<redacted>"
			}
			params.Add(k, v)
		}
	}

	return params
}

func (h *wrappedHandler) collectHTTPHeaders(headers map[string]string, multiValueHeaders map[string][]string) map[string]string {
	var collectableHeaders []string
	if tr, ok := h.sensor.Tracer().(instana.Tracer); ok {
		collectableHeaders = tr.Options().CollectableHTTPHeaders
	}

	if len(collectableHeaders) == 0 {
		return nil
	}

	// Normalize header names first by bringing them to the canonical MIME format to avoid missing headers because of mismatching case
	hdrs := http.Header{}
	for k, v := range headers {
		hdrs.Set(k, v)
	}

	for k, vv := range multiValueHeaders {
		for _, v := range vv {
			hdrs.Add(k, v)
		}
	}

	collected := make(map[string]string)
	for _, k := range collectableHeaders {
		if v := hdrs.Get(k); v != "" {
			collected[k] = v
		}
	}

	return collected
}
<commit_msg>Use context.Context to detect Lambda timeouts<commit_after>// (c) Copyright IBM Corp. 2021
// (c) Copyright Instana Inc. 2020

// Package instalambda provides Instana tracing instrumentation for
// AWS Lambda functions
package instalambda

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-lambda-go/lambdacontext"
	instana "github.com/instana/go-sensor"
	"github.com/opentracing/opentracing-go"
	otlog "github.com/opentracing/opentracing-go/log"
)

const (
	awsLambdaFlushMaxRetries  = 5
	awsLambdaFlushRetryPeriod = 50 * time.Millisecond
	awsLambdaTimeoutThreshold = 100 * time.Millisecond
)

var errHandlerTimedOut = errors.New("handler has timed out")

type wrappedHandler struct {
	lambda.Handler

	sensor      *instana.Sensor
	onColdStart sync.Once
}

// NewHandler creates a new instrumented handler that can be used with `lambda.StartHandler()` from a handler function
func NewHandler(handlerFunc interface{}, sensor *instana.Sensor) *wrappedHandler {
	return WrapHandler(lambda.NewHandler(handlerFunc), sensor)
}

// WrapHandler instruments a lambda.Handler to trace the invokations with Instana
func WrapHandler(h lambda.Handler, sensor *instana.Sensor) *wrappedHandler {
	return &wrappedHandler{
		Handler: h,
		sensor:  sensor,
	}
}

// Invoke is a handler function for a wrapped handler
func (h *wrappedHandler) Invoke(ctx context.Context, payload []byte) ([]byte, error) {
	lc, ok := lambdacontext.FromContext(ctx)
	if !ok {
		return h.Handler.Invoke(ctx, payload)
	}

	opts := append([]opentracing.StartSpanOption{opentracing.Tags{
		"lambda.arn":     lc.InvokedFunctionArn + ":" + lambdacontext.FunctionVersion,
		"lambda.name":    lambdacontext.FunctionName,
		"lambda.version": lambdacontext.FunctionVersion,
	}}, h.triggerEventSpanOptions(payload, lc.ClientContext)...)

	sp := h.sensor.Tracer().StartSpan("aws.lambda.entry", opts...)
	defer func() {
		sp.Finish()

		// ensure that all collected data has been sent before the invocation is finished
		h.flushAgent(awsLambdaFlushRetryPeriod, awsLambdaFlushMaxRetries)
	}()

	h.onColdStart.Do(func() {
		sp.SetTag("lambda.coldStart", true)
	})

	traceCtx := ctx
	done := make(chan struct{})

	originalDeadline, deadlineDefined := ctx.Deadline()
	if deadlineDefined {
		var cancel context.CancelFunc

		// add a leeway for the agent client to flush data
		traceCtx, cancel = context.WithDeadline(ctx, originalDeadline.Add(-awsLambdaTimeoutThreshold))
		defer cancel()
	}

	var (
		resp []byte
		err  error
	)

	go func() {
		resp, err = h.Handler.Invoke(instana.ContextWithSpan(ctx, sp), payload)
		if err != nil {
			sp.LogFields(otlog.Error(err))
		}

		close(done)
	}()

	select {
	case <-done:
	case <-traceCtx.Done():
		if ctx.Err() == context.DeadlineExceeded {
			h.sensor.Logger().Debug("lambda handler has timed out")

			remainingTime := time.Until(originalDeadline).Truncate(time.Millisecond)

			sp.SetTag("lambda.msleft", int64(remainingTime)/1e6) // cast time.Duration to int64 for compatibility with older Go versions
			sp.SetTag("lambda.error", fmt.Sprintf(`The Lambda function was still running when only %s were left, it might have timed out.`, remainingTime))

			sp.LogFields(otlog.Error(errHandlerTimedOut))
		}
	}

	return resp, err
}

func (h *wrappedHandler) flushAgent(retryPeriod time.Duration, maxRetries int) {
	if tr, ok := h.sensor.Tracer().(instana.Tracer); ok {
		var i int
		for {
			if err := tr.Flush(context.Background()); err != nil {
				if err == instana.ErrAgentNotReady && i < maxRetries {
					i++
					time.Sleep(retryPeriod)
					continue
				}

				h.sensor.Logger().Error("failed to send traces:", err)
			}

			break
		}
	}
}

func (h *wrappedHandler) triggerEventSpanOptions(payload []byte, lcc lambdacontext.ClientContext) []opentracing.StartSpanOption {
	switch detectTriggerEventType(payload) {
	case apiGatewayEventType:
		var v events.APIGatewayProxyRequest
		if err := json.Unmarshal(payload, &v); err != nil {
			h.sensor.Logger().Warn("failed to unmarshal API Gateway event payload: ", err)
			return []opentracing.StartSpanOption{opentracing.Tags{}}
		}

		opts := []opentracing.StartSpanOption{h.extractAPIGatewayTriggerTags(v)}
		if parentCtx, ok := h.extractParentContext(v.Headers); ok {
			opts = append(opts, opentracing.ChildOf(parentCtx))
		}

		return opts
	case apiGatewayV2EventType:
		var v events.APIGatewayV2HTTPRequest
		if err := json.Unmarshal(payload, &v); err != nil {
			h.sensor.Logger().Warn("failed to unmarshal API Gateway v2.0 event payload: ", err)
			return []opentracing.StartSpanOption{opentracing.Tags{}}
		}

		opts := []opentracing.StartSpanOption{h.extractAPIGatewayV2TriggerTags(v)}
		if parentCtx, ok := h.extractParentContext(v.Headers); ok {
			opts = append(opts, opentracing.ChildOf(parentCtx))
		}

		return opts
	case albEventType:
		var v events.ALBTargetGroupRequest
		if err := json.Unmarshal(payload, &v); err != nil {
			h.sensor.Logger().Warn("failed to unmarshal ALB event payload: ", err)
			return []opentracing.StartSpanOption{opentracing.Tags{}}
		}

		opts := []opentracing.StartSpanOption{h.extractALBTriggerTags(v)}
		if parentCtx, ok := h.extractParentContext(v.Headers); ok {
			opts = append(opts, opentracing.ChildOf(parentCtx))
		}

		return opts
	case cloudWatchEventType:
		var v events.CloudWatchEvent
		if err := json.Unmarshal(payload, &v); err != nil {
			h.sensor.Logger().Warn("failed to unmarshal CloudWatch event payload: ", err)
			return []opentracing.StartSpanOption{opentracing.Tags{}}
		}

		return []opentracing.StartSpanOption{h.extractCloudWatchTriggerTags(v)}
	case cloudWatchLogsEventType:
		var v events.CloudwatchLogsEvent
		if err := json.Unmarshal(payload, &v); err != nil {
			h.sensor.Logger().Warn("failed to unmarshal CloudWatch Logs event payload: ", err)
			return []opentracing.StartSpanOption{opentracing.Tags{}}
		}

		return []opentracing.StartSpanOption{h.extractCloudWatchLogsTriggerTags(v)}
	case s3EventType:
		var v events.S3Event
		if err := json.Unmarshal(payload, &v); err != nil {
			h.sensor.Logger().Warn("failed to unmarshal S3 event payload: ", err)
			return []opentracing.StartSpanOption{opentracing.Tags{}}
		}

		return []opentracing.StartSpanOption{h.extractS3TriggerTags(v)}
	case sqsEventType:
		var v events.SQSEvent
		if err := json.Unmarshal(payload, &v); err != nil {
			h.sensor.Logger().Warn("failed to unmarshal SQS event payload: ", err)
			return []opentracing.StartSpanOption{opentracing.Tags{}}
		}

		return []opentracing.StartSpanOption{h.extractSQSTriggerTags(v)}
	case invokeRequestType:
		tags := opentracing.Tags{
			"lambda.trigger": "aws:lambda.invoke",
		}

		opts := []opentracing.StartSpanOption{tags}
		if parentCtx, ok := h.extractParentContext(lcc.Custom); ok {
			opts = append(opts, opentracing.ChildOf(parentCtx))
		}
		return opts

	default:
		h.sensor.Logger().Info("unsupported AWS Lambda trigger event type, the entry span will include generic tags only")
		return []opentracing.StartSpanOption{opentracing.Tags{}}
	}
}

func (h *wrappedHandler) extractParentContext(headers map[string]string) (opentracing.SpanContext, bool) {
	hdrs := http.Header{}
	for k, v := range headers {
		hdrs.Set(k, v)
	}

	switch parentCtx, err := h.sensor.Tracer().Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(hdrs)); err {
	case nil:
		return parentCtx, true
	case opentracing.ErrSpanContextNotFound:
		h.sensor.Logger().Debug("lambda invoke event did not provide trace context")
	case opentracing.ErrUnsupportedFormat:
		h.sensor.Logger().Info("lambda invoke event provided trace context in unsupported format")
	default:
		h.sensor.Logger().Warn("failed to extract span context from the lambda invoke event:", err)
	}

	return nil, false
}

func (h *wrappedHandler) extractAPIGatewayTriggerTags(evt events.APIGatewayProxyRequest) opentracing.Tags {
	tags := opentracing.Tags{
		"lambda.trigger": "aws:api.gateway",
		"http.method":    evt.HTTPMethod,
		"http.url":       evt.Path,
		"http.path_tpl":  evt.Resource,
		"http.params":    h.sanitizeHTTPParams(evt.QueryStringParameters, evt.MultiValueQueryStringParameters).Encode(),
	}

	if headers := h.collectHTTPHeaders(evt.Headers, evt.MultiValueHeaders); len(headers) > 0 {
		tags["http.header"] = headers
	}

	return tags
}

func (h *wrappedHandler) extractAPIGatewayV2TriggerTags(evt events.APIGatewayV2HTTPRequest) opentracing.Tags {
	routeKeyPath := evt.RouteKey
	// Strip any trailing HTTP request method
	if i := strings.Index(routeKeyPath, " "); i >= 0 {
		routeKeyPath = evt.RouteKey[i+1:]
	}

	tags := opentracing.Tags{
		"lambda.trigger": "aws:api.gateway",
		"http.method":    evt.RequestContext.HTTP.Method,
		"http.url":       evt.RequestContext.HTTP.Path,
		"http.path_tpl":  routeKeyPath,
		"http.params":    h.sanitizeHTTPParams(evt.QueryStringParameters, nil).Encode(),
	}

	if headers := h.collectHTTPHeaders(evt.Headers, nil); len(headers) > 0 {
		tags["http.header"] = headers
	}

	return tags
}

func (h *wrappedHandler) extractALBTriggerTags(evt events.ALBTargetGroupRequest) opentracing.Tags {
	tags := opentracing.Tags{
		"lambda.trigger": "aws:application.load.balancer",
		"http.method":    evt.HTTPMethod,
		"http.url":       evt.Path,
		"http.params":    h.sanitizeHTTPParams(evt.QueryStringParameters, evt.MultiValueQueryStringParameters).Encode(),
	}

	if headers := h.collectHTTPHeaders(evt.Headers, evt.MultiValueHeaders); len(headers) > 0 {
		tags["http.header"] = headers
	}

	return tags
}

func (h *wrappedHandler) extractCloudWatchTriggerTags(evt events.CloudWatchEvent) opentracing.Tags {
	return opentracing.Tags{
		"lambda.trigger":              "aws:cloudwatch.events",
		"cloudwatch.events.id":        evt.ID,
		"cloudwatch.events.resources": evt.Resources,
	}
}

func (h *wrappedHandler) extractCloudWatchLogsTriggerTags(evt events.CloudwatchLogsEvent) opentracing.Tags {
	logs, err := evt.AWSLogs.Parse()
	if err != nil {
		return opentracing.Tags{
			"lambda.trigger":                "aws:cloudwatch.logs",
			"cloudwatch.logs.decodingError": err,
		}
	}

	var events []string
	for _, event := range logs.LogEvents {
		events = append(events, event.Message)
	}

	return opentracing.Tags{
		"lambda.trigger":         "aws:cloudwatch.logs",
		"cloudwatch.logs.group":  logs.LogGroup,
		"cloudwatch.logs.stream": logs.LogStream,
		"cloudwatch.logs.events": events,
	}
}

func (h *wrappedHandler) extractS3TriggerTags(evt events.S3Event) opentracing.Tags {
	var events []instana.AWSS3EventTags
	for _, rec := range evt.Records {
		events = append(events, instana.AWSS3EventTags{
			Name:   rec.EventName,
			Bucket: rec.S3.Bucket.Name,
			Object: rec.S3.Object.Key,
		})
	}

	return opentracing.Tags{
		"lambda.trigger": "aws:s3",
		"s3.events":      events,
	}
}

func (h *wrappedHandler) extractSQSTriggerTags(evt events.SQSEvent) opentracing.Tags {
	var msgs []instana.AWSSQSMessageTags
	for _, rec := range evt.Records {
		msgs = append(msgs, instana.AWSSQSMessageTags{
			Queue: rec.EventSourceARN,
		})
	}

	return opentracing.Tags{
		"lambda.trigger": "aws:sqs",
		"sqs.messages":   msgs,
	}
}

func (h *wrappedHandler) sanitizeHTTPParams(
	queryStringParams map[string]string,
	multiValueQueryStringParams map[string][]string,
) url.Values {
	secretMatcher := instana.DefaultSecretsMatcher()
	if tr, ok := h.sensor.Tracer().(instana.Tracer); ok {
		secretMatcher = tr.Options().Secrets
	}

	params := url.Values{}

	for k, v := range queryStringParams {
		if secretMatcher.Match(k) {
			v = "<redacted>"
		}
		params.Set(k, v)
	}

	for k, vv := range multiValueQueryStringParams {
		isSecret := secretMatcher.Match(k)
		for _, v := range vv {
			if isSecret {
				v = "<redacted>"
			}
			params.Add(k, v)
		}
	}

	return params
}

func (h *wrappedHandler) collectHTTPHeaders(headers map[string]string, multiValueHeaders map[string][]string) map[string]string {
	var collectableHeaders []string
	if tr, ok := h.sensor.Tracer().(instana.Tracer); ok {
		collectableHeaders = tr.Options().CollectableHTTPHeaders
	}

	if len(collectableHeaders) == 0 {
		return nil
	}

	// Normalize header names first by bringing them to the canonical MIME format to avoid missing headers because of mismatching case
	hdrs := http.Header{}
	for k, v := range headers {
		hdrs.Set(k, v)
	}

	for k, vv := range multiValueHeaders {
		for _, v := range vv {
			hdrs.Add(k, v)
		}
	}

	collected := make(map[string]string)
	for _, k := range collectableHeaders {
		if v := hdrs.Get(k); v != "" {
			collected[k] = v
		}
	}

	return collected
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2019 Tigera, Inc. All rights reserved.

// 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 commands

import (
	"fmt"
	"strings"

	"github.com/docopt/docopt-go"
	"github.com/projectcalico/calicoctl/calicoctl/commands/constants"
	log "github.com/sirupsen/logrus"
)

func Patch(args []string) error {
	doc := constants.DatastoreIntro + `Usage:
  calicoctl patch <KIND> <NAME> --patch=<PATCH> [--type=<TYPE>] [--config=<CONFIG>] [--namespace=<NS>]

Examples:
  # Partially update a node using a strategic merge patch.
  calicoctl patch node node-0 --patch '{"spec":{"unschedulable":true}}'

  # Partially update a node using a json merge patch.
  calicoctl patch node node-0 --patch '{"spec":{"unschedulable":true}}' --type json

Options:
  -h --help                  Show this screen.
  -p --patch=<PATCH>         Spec to use to patch the resource.  If set
                             to "-" loads from stdin.
  -t --type=<TYPE>           Format of patch type:
                                json        JSON Patch, RFC 6902 (not yet implemented)
                                merge       JSON Merge Patch, RFC 7386 (not yet implemented)
                                strategic   Strategic merge patch (default)
  -c --config=<CONFIG>       Path to the file containing connection
                             configuration in YAML or JSON format.
                             [default: ` + constants.DefaultConfigPath + `]
  -n --namespace=<NS>        Namespace of the resource.
                             Only applicable to NetworkPolicy, NetworkSet, and WorkloadEndpoint.
                             Uses the default namespace if not specified.

Description:
  The patch command is used to patch a specific resource by type and identifiers in place.
  Currently, only JSON format is accepted.

  Valid resource types are:

    * bgpConfiguration
    * bgpPeer
    * felixConfiguration
    * globalNetworkPolicy
    * globalNetworkSet
    * hostEndpoint
    * ipPool
    * networkPolicy
    * networkSet
    * node
    * profile
    * workloadEndpoint

  The resource type is case insensitive and may be pluralized.

  Attempting to patch a resource that does not exists is treated as a
  terminating error unless the --skip-not-exists flag is set.  If this flag is
  set, resources that do not exist are skipped.

  When patching resources by type, only a single type may be specified at a
  time.  The name is required along with any and other identifiers required to
  uniquely identify a resource of the specified type.
`

	parsedArgs, err := docopt.Parse(doc, args, true, "", false, false)
	if err != nil {
		return fmt.Errorf("Invalid option: 'calicoctl %s'. Use flag '--help' to read about a specific subcommand.", strings.Join(args, " "))
	}
	if len(parsedArgs) == 0 {
		return nil
	}

	results := executeConfigCommand(parsedArgs, actionPatch)
	log.Infof("results: %+v", results)

	if results.numResources == 0 {
		return fmt.Errorf("No resources specified")
	} else if results.err == nil && results.numHandled > 0 {
		fmt.Printf("Successfully patched %d '%s' resource\n", results.numHandled, results.singleKind)
	} else if results.err != nil {
		return fmt.Errorf("Hit error: %v", results.err)
	}

	if len(results.resErrs) > 0 {
		var errStr string
		for _, err := range results.resErrs {
			errStr += fmt.Sprintf("Failed to patch '%s' resource: %v\n", results.singleKind, err)
		}
		return fmt.Errorf(errStr)
	}

	return nil
}
<commit_msg>Update patch docs to use real spec (#2060)<commit_after>// Copyright (c) 2016-2019 Tigera, Inc. All rights reserved.

// 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 commands

import (
	"fmt"
	"strings"

	"github.com/docopt/docopt-go"
	"github.com/projectcalico/calicoctl/calicoctl/commands/constants"
	log "github.com/sirupsen/logrus"
)

func Patch(args []string) error {
	doc := constants.DatastoreIntro + `Usage:
  calicoctl patch <KIND> <NAME> --patch=<PATCH> [--type=<TYPE>] [--config=<CONFIG>] [--namespace=<NS>]

Examples:
  # Partially update a node using a strategic merge patch.
  calicoctl patch node node-0 --patch '{"spec":{"bgp": {"routeReflectorClusterID": "CLUSTER_ID"}}}'

  # Partially update a node using a json merge patch.
  calicoctl patch node node-0 --patch '{"spec":{"bgp": {"routeReflectorClusterID": "CLUSTER_ID"}}}' --type json

Options:
  -h --help                  Show this screen.
  -p --patch=<PATCH>         Spec to use to patch the resource.
  -t --type=<TYPE>           Format of patch type:
                                strategic   Strategic merge patch (default)
                                json        JSON Patch, RFC 6902 (not yet implemented)
                                merge       JSON Merge Patch, RFC 7386 (not yet implemented)
  -c --config=<CONFIG>       Path to the file containing connection
                             configuration in YAML or JSON format.
                             [default: ` + constants.DefaultConfigPath + `]
  -n --namespace=<NS>        Namespace of the resource.
                             Only applicable to NetworkPolicy, NetworkSet, and WorkloadEndpoint.
                             Uses the default namespace if not specified.

Description:
  The patch command is used to patch a specific resource by type and identifiers in place.
  Currently, only JSON format is accepted.

  Valid resource types are:

    * bgpConfiguration
    * bgpPeer
    * felixConfiguration
    * globalNetworkPolicy
    * globalNetworkSet
    * hostEndpoint
    * ipPool
    * networkPolicy
    * networkSet
    * node
    * profile
    * workloadEndpoint

  The resource type is case insensitive and may be pluralized.

  Attempting to patch a resource that does not exists is treated as a
  terminating error unless the --skip-not-exists flag is set.  If this flag is
  set, resources that do not exist are skipped.

  When patching resources by type, only a single type may be specified at a
  time.  The name is required along with any and other identifiers required to
  uniquely identify a resource of the specified type.
`

	parsedArgs, err := docopt.Parse(doc, args, true, "", false, false)
	if err != nil {
		return fmt.Errorf("Invalid option: 'calicoctl %s'. Use flag '--help' to read about a specific subcommand.", strings.Join(args, " "))
	}
	if len(parsedArgs) == 0 {
		return nil
	}

	results := executeConfigCommand(parsedArgs, actionPatch)
	log.Infof("results: %+v", results)

	if results.numResources == 0 {
		return fmt.Errorf("No resources specified")
	} else if results.err == nil && results.numHandled > 0 {
		fmt.Printf("Successfully patched %d '%s' resource\n", results.numHandled, results.singleKind)
	} else if results.err != nil {
		return fmt.Errorf("Hit error: %v", results.err)
	}

	if len(results.resErrs) > 0 {
		var errStr string
		for _, err := range results.resErrs {
			errStr += fmt.Sprintf("Failed to patch '%s' resource: %v\n", results.singleKind, err)
		}
		return fmt.Errorf(errStr)
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, Inc.
//
// 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 tspi

// #include <trousers/tss.h>
import "C"
import (
	"bufio"
	"crypto/sha1"
	"encoding/binary"
	"io"
	"os"
	"unsafe"

	"github.com/google/go-tspi/tspiconst"
)

// TPM is a TSS TPM object
type TPM struct {
	handle  C.TSS_HTPM
	context C.TSS_HCONTEXT
}

// GetEventLog returns an array of structures representing the contents of the
// TSS event log
func (tpm *TPM) GetEventLog() ([]tspiconst.Log, error) {
	var count C.UINT32
	var events *C.TSS_PCR_EVENT
	var event C.TSS_PCR_EVENT
	var log []tspiconst.Log

	f, err := os.Open("/sys/kernel/security/tpm0/binary_bios_measurements")
	if err != nil {
		return nil, err
	}

	firmware_events := bufio.NewReader(f)

	for {
		var entry tspiconst.Log
		var datalen int32
		err := binary.Read(firmware_events, binary.LittleEndian, &entry.Pcr)
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}
		err = binary.Read(firmware_events, binary.LittleEndian, &entry.Eventtype)
		if err != nil {
			return nil, err
		}
		err = binary.Read(firmware_events, binary.LittleEndian, &entry.PcrValue)
		if err != nil {
			return nil, err
		}
		err = binary.Read(firmware_events, binary.LittleEndian, &datalen)
		if err != nil {
			return nil, err
		}
		data := make([]byte, datalen)
		err = binary.Read(firmware_events, binary.LittleEndian, &data)
		if err != nil {
			return nil, err
		}
		entry.Event = data[:]
		log = append(log, entry)
	}

	err = tspiError(C.Tspi_TPM_GetEventLog(tpm.handle, &count, &events))
	if err != nil {
		return nil, err
	}

	if count == 0 {
		return log, nil
	}

	length := count * C.UINT32(unsafe.Sizeof(event))
	slice := (*[1 << 30]C.TSS_PCR_EVENT)(unsafe.Pointer(events))[:length:length]

	for i := 0; i < int(count); i++ {
		var entry tspiconst.Log
		entry.Pcr = int32(slice[i].ulPcrIndex)
		entry.Eventtype = int32(slice[i].eventType)
		copy(entry.PcrValue[:], C.GoBytes(unsafe.Pointer(slice[i].rgbPcrValue), C.int(slice[i].ulPcrValueLength)))
		entry.Event = C.GoBytes(unsafe.Pointer(slice[i].rgbEvent), C.int(slice[i].ulEventLength))
		log = append(log, entry)
	}
	C.Tspi_Context_FreeMemory(tpm.context, (*C.BYTE)(unsafe.Pointer(events)))
	return log, nil
}

// ExtendPCR extends a pcr. If event is nil, data must be pre-hashed with
// SHA1. If event is not nil, event is used to populate the TSS event
// log. If both data and event are provided, both will be used to create the
// extend hash.
func (tpm *TPM) ExtendPCR(pcr int, data []byte, eventtype int, event []byte) error {
	var outlen C.UINT32
	var pcrval *C.BYTE
	var eventstruct C.TSS_PCR_EVENT
	var err error

	shasum := sha1.Sum(data)

	if event != nil {
		var pcrdata *C.BYTE
		var pcrdatalen C.UINT32

		eventstruct.versionInfo.bMajor = 1
		eventstruct.versionInfo.bMinor = 2
		eventstruct.versionInfo.bRevMajor = 1
		eventstruct.versionInfo.bRevMinor = 0
		eventstruct.ulPcrIndex = C.UINT32(pcr)
		eventstruct.rgbPcrValue = (*C.BYTE)(&shasum[0])
		eventstruct.eventType = C.TSS_EVENTTYPE(eventtype)
		eventstruct.ulEventLength = C.UINT32(len(event))
		eventstruct.rgbEvent = (*C.BYTE)(&event[0])

		if data == nil || len(data) == 0 {
			pcrdata = nil
			pcrdatalen = C.UINT32(0)
		} else {
			pcrdata = (*C.BYTE)(&data[0])
			pcrdatalen = C.UINT32(len(data))
		}

		err = tspiError(C.Tspi_TPM_PcrExtend(tpm.handle, C.UINT32(pcr), pcrdatalen, pcrdata, &eventstruct, &outlen, &pcrval))
	} else {
		err = tspiError(C.Tspi_TPM_PcrExtend(tpm.handle, C.UINT32(pcr), C.UINT32(len(shasum)), (*C.BYTE)(&shasum[0]), nil, &outlen, &pcrval))
	}

	C.Tspi_Context_FreeMemory(tpm.context, pcrval)

	return err
}

//GetQuote takes an encrypted key blob representing the AIK, a set of PCRs
//and a challenge and returns a blob containing a hash of the PCR hashes and
//the challenge, and a validation blob signed by the AIK.
func (tpm *TPM) GetQuote(aik *Key, pcrs *PCRs, challenge []byte) ([]byte, []byte, error) {
	var validation C.TSS_VALIDATION
	challangeHash := sha1.Sum(challenge[:])

	validation.ulExternalDataLength = sha1.Size
	validation.rgbExternalData = (*C.BYTE)(&challangeHash[0])
	err := tspiError(C.Tspi_TPM_Quote(tpm.handle, aik.handle, pcrs.handle, &validation))

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

	data := C.GoBytes(unsafe.Pointer(validation.rgbData), (C.int)(validation.ulDataLength))
	validationOutput := C.GoBytes(unsafe.Pointer(validation.rgbValidationData), (C.int)(validation.ulValidationDataLength))

	C.Tspi_Context_FreeMemory(tpm.context, validation.rgbData)
	C.Tspi_Context_FreeMemory(tpm.context, validation.rgbValidationData)

	return data, validationOutput, nil
}

// ActivateIdentity accepts an encrypted key blob representing the AIK and
// two blobs representing the asymmetric and symmetric challenges associated
// with the AIK. If the TPM is able to decrypt the challenges and the
// challenges correspond to the AIK, the TPM will return the original
// challenge secret.
func (tpm *TPM) ActivateIdentity(aik *Key, asymblob []byte, symblob []byte) (secret []byte, err error) {
	var creds *C.BYTE
	var credlen C.UINT32

	err = tspiError(C.Tspi_TPM_ActivateIdentity(tpm.handle, aik.handle, (C.UINT32)(len(asymblob)), (*C.BYTE)(&asymblob[0]), (C.UINT32)(len(symblob)), (*C.BYTE)(&symblob[0]), &credlen, &creds))

	if err != nil {
		return nil, err
	}

	plaintext := C.GoBytes(unsafe.Pointer(creds), (C.int)(credlen))

	C.Tspi_Context_FreeMemory(tpm.context, creds)

	return plaintext, nil
}

// GetPolicy returns the TSS policy associated with the TPM.
func (tpm *TPM) GetPolicy(poltype int) (*Policy, error) {
	var policyHandle C.TSS_HPOLICY
	err := tspiError(C.Tspi_GetPolicyObject((C.TSS_HOBJECT)(tpm.handle), (C.TSS_FLAG)(poltype), &policyHandle))
	return &Policy{handle: policyHandle, context: tpm.context}, err
}

// TakeOwnership transitions a TPM from unowned state to owned, installing the
// encrypted key blob as the SRK.
func (tpm *TPM) TakeOwnership(srk *Key) error {
	err := tspiError(C.Tspi_TPM_TakeOwnership(tpm.handle, srk.handle, 0))
	return err
}

// AssignPolicy assigns a TSS policy to the TPM.
func (tpm *TPM) AssignPolicy(policy *Policy) error {
	err := tspiError(C.Tspi_Policy_AssignToObject(policy.handle, (C.TSS_HOBJECT)(tpm.handle)))
	return err
}

// CollateIdentityRequest creates a signing request for the provided AIKq
func (tpm *TPM) CollateIdentityRequest(srk *Key, pubkey *Key, aik *Key) ([]byte, error) {
	var certLen C.UINT32
	var cCertReq *C.BYTE
	err := tspiError(C.Tspi_TPM_CollateIdentityRequest(tpm.handle, srk.handle, pubkey.handle, 0, nil, aik.handle, C.TSS_ALG_AES, &certLen, &cCertReq))
	certReq := C.GoBytes(unsafe.Pointer(cCertReq), (C.int)(certLen))
	C.Tspi_Context_FreeMemory(tpm.context, cCertReq)
	return certReq, err
}

// Close closes the TPM object.
func (tpm *TPM) Close() error {
	err := tspiError(C.Tspi_Context_CloseObject(tpm.context, tpm.handle))
	return err
}
<commit_msg>Fix to GetQuote<commit_after>// Copyright 2015 CoreOS, Inc.
//
// 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 tspi

// #include <trousers/tss.h>
import "C"
import (
	"bufio"
	"crypto/sha1"
	"encoding/binary"
	"io"
	"os"
	"unsafe"

	"github.com/google/go-tspi/tspiconst"
)

// TPM is a TSS TPM object
type TPM struct {
	handle  C.TSS_HTPM
	context C.TSS_HCONTEXT
}

// GetEventLog returns an array of structures representing the contents of the
// TSS event log
func (tpm *TPM) GetEventLog() ([]tspiconst.Log, error) {
	var count C.UINT32
	var events *C.TSS_PCR_EVENT
	var event C.TSS_PCR_EVENT
	var log []tspiconst.Log

	f, err := os.Open("/sys/kernel/security/tpm0/binary_bios_measurements")
	if err != nil {
		return nil, err
	}

	firmware_events := bufio.NewReader(f)

	for {
		var entry tspiconst.Log
		var datalen int32
		err := binary.Read(firmware_events, binary.LittleEndian, &entry.Pcr)
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}
		err = binary.Read(firmware_events, binary.LittleEndian, &entry.Eventtype)
		if err != nil {
			return nil, err
		}
		err = binary.Read(firmware_events, binary.LittleEndian, &entry.PcrValue)
		if err != nil {
			return nil, err
		}
		err = binary.Read(firmware_events, binary.LittleEndian, &datalen)
		if err != nil {
			return nil, err
		}
		data := make([]byte, datalen)
		err = binary.Read(firmware_events, binary.LittleEndian, &data)
		if err != nil {
			return nil, err
		}
		entry.Event = data[:]
		log = append(log, entry)
	}

	err = tspiError(C.Tspi_TPM_GetEventLog(tpm.handle, &count, &events))
	if err != nil {
		return nil, err
	}

	if count == 0 {
		return log, nil
	}

	length := count * C.UINT32(unsafe.Sizeof(event))
	slice := (*[1 << 30]C.TSS_PCR_EVENT)(unsafe.Pointer(events))[:length:length]

	for i := 0; i < int(count); i++ {
		var entry tspiconst.Log
		entry.Pcr = int32(slice[i].ulPcrIndex)
		entry.Eventtype = int32(slice[i].eventType)
		copy(entry.PcrValue[:], C.GoBytes(unsafe.Pointer(slice[i].rgbPcrValue), C.int(slice[i].ulPcrValueLength)))
		entry.Event = C.GoBytes(unsafe.Pointer(slice[i].rgbEvent), C.int(slice[i].ulEventLength))
		log = append(log, entry)
	}
	C.Tspi_Context_FreeMemory(tpm.context, (*C.BYTE)(unsafe.Pointer(events)))
	return log, nil
}

// ExtendPCR extends a pcr. If event is nil, data must be pre-hashed with
// SHA1. If event is not nil, event is used to populate the TSS event
// log. If both data and event are provided, both will be used to create the
// extend hash.
func (tpm *TPM) ExtendPCR(pcr int, data []byte, eventtype int, event []byte) error {
	var outlen C.UINT32
	var pcrval *C.BYTE
	var eventstruct C.TSS_PCR_EVENT
	var err error

	shasum := sha1.Sum(data)

	if event != nil {
		var pcrdata *C.BYTE
		var pcrdatalen C.UINT32

		eventstruct.versionInfo.bMajor = 1
		eventstruct.versionInfo.bMinor = 2
		eventstruct.versionInfo.bRevMajor = 1
		eventstruct.versionInfo.bRevMinor = 0
		eventstruct.ulPcrIndex = C.UINT32(pcr)
		eventstruct.rgbPcrValue = (*C.BYTE)(&shasum[0])
		eventstruct.eventType = C.TSS_EVENTTYPE(eventtype)
		eventstruct.ulEventLength = C.UINT32(len(event))
		eventstruct.rgbEvent = (*C.BYTE)(&event[0])

		if data == nil || len(data) == 0 {
			pcrdata = nil
			pcrdatalen = C.UINT32(0)
		} else {
			pcrdata = (*C.BYTE)(&data[0])
			pcrdatalen = C.UINT32(len(data))
		}

		err = tspiError(C.Tspi_TPM_PcrExtend(tpm.handle, C.UINT32(pcr), pcrdatalen, pcrdata, &eventstruct, &outlen, &pcrval))
	} else {
		err = tspiError(C.Tspi_TPM_PcrExtend(tpm.handle, C.UINT32(pcr), C.UINT32(len(shasum)), (*C.BYTE)(&shasum[0]), nil, &outlen, &pcrval))
	}

	C.Tspi_Context_FreeMemory(tpm.context, pcrval)

	return err
}

//GetQuote takes an encrypted key blob representing the AIK, a set of PCRs
//and a challenge and returns a blob containing a hash of the PCR hashes and
//the challenge, and a validation blob signed by the AIK.
func (tpm *TPM) GetQuote(aik *Key, pcrs *PCRs, challenge []byte) ([]byte, []byte, error) {
	var validation C.TSS_VALIDATION
	challangeHash := sha1.Sum(challenge[:])

	validation.ulExternalDataLength = sha1.Size
	validation.rgbExternalData = (*C.BYTE)(C.CBytes(challangeHash[:]))
	defer C.free(unsafe.Pointer(validation.rgbExternalData))
	err := tspiError(C.Tspi_TPM_Quote(tpm.handle, aik.handle, pcrs.handle, &validation))

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

	data := C.GoBytes(unsafe.Pointer(validation.rgbData), (C.int)(validation.ulDataLength))
	validationOutput := C.GoBytes(unsafe.Pointer(validation.rgbValidationData), (C.int)(validation.ulValidationDataLength))

	C.Tspi_Context_FreeMemory(tpm.context, validation.rgbData)
	C.Tspi_Context_FreeMemory(tpm.context, validation.rgbValidationData)

	return data, validationOutput, nil
}

// ActivateIdentity accepts an encrypted key blob representing the AIK and
// two blobs representing the asymmetric and symmetric challenges associated
// with the AIK. If the TPM is able to decrypt the challenges and the
// challenges correspond to the AIK, the TPM will return the original
// challenge secret.
func (tpm *TPM) ActivateIdentity(aik *Key, asymblob []byte, symblob []byte) (secret []byte, err error) {
	var creds *C.BYTE
	var credlen C.UINT32

	err = tspiError(C.Tspi_TPM_ActivateIdentity(tpm.handle, aik.handle, (C.UINT32)(len(asymblob)), (*C.BYTE)(&asymblob[0]), (C.UINT32)(len(symblob)), (*C.BYTE)(&symblob[0]), &credlen, &creds))

	if err != nil {
		return nil, err
	}

	plaintext := C.GoBytes(unsafe.Pointer(creds), (C.int)(credlen))

	C.Tspi_Context_FreeMemory(tpm.context, creds)

	return plaintext, nil
}

// GetPolicy returns the TSS policy associated with the TPM.
func (tpm *TPM) GetPolicy(poltype int) (*Policy, error) {
	var policyHandle C.TSS_HPOLICY
	err := tspiError(C.Tspi_GetPolicyObject((C.TSS_HOBJECT)(tpm.handle), (C.TSS_FLAG)(poltype), &policyHandle))
	return &Policy{handle: policyHandle, context: tpm.context}, err
}

// TakeOwnership transitions a TPM from unowned state to owned, installing the
// encrypted key blob as the SRK.
func (tpm *TPM) TakeOwnership(srk *Key) error {
	err := tspiError(C.Tspi_TPM_TakeOwnership(tpm.handle, srk.handle, 0))
	return err
}

// AssignPolicy assigns a TSS policy to the TPM.
func (tpm *TPM) AssignPolicy(policy *Policy) error {
	err := tspiError(C.Tspi_Policy_AssignToObject(policy.handle, (C.TSS_HOBJECT)(tpm.handle)))
	return err
}

// CollateIdentityRequest creates a signing request for the provided AIKq
func (tpm *TPM) CollateIdentityRequest(srk *Key, pubkey *Key, aik *Key) ([]byte, error) {
	var certLen C.UINT32
	var cCertReq *C.BYTE
	err := tspiError(C.Tspi_TPM_CollateIdentityRequest(tpm.handle, srk.handle, pubkey.handle, 0, nil, aik.handle, C.TSS_ALG_AES, &certLen, &cCertReq))
	certReq := C.GoBytes(unsafe.Pointer(cCertReq), (C.int)(certLen))
	C.Tspi_Context_FreeMemory(tpm.context, cCertReq)
	return certReq, err
}

// Close closes the TPM object.
func (tpm *TPM) Close() error {
	err := tspiError(C.Tspi_Context_CloseObject(tpm.context, tpm.handle))
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"os/user"
	"path/filepath"

	"code.google.com/p/go.crypto/bcrypt"
)

var (
	Decrypt = decrypt
)

func Encrypt(passwd string) (hash string, err error) {
	hashBytes, err := bcrypt.GenerateFromPassword([]byte(passwd), bcrypt.DefaultCost)
	hash = string(hashBytes)
	return
}

func decrypt(hash, passwd []byte) error {
	return bcrypt.CompareHashAndPassword(hash, passwd)
}

func MkLogger(fname string) *log.Logger {
	f, err := os.Create(fname)
	if err != nil {
		panic("MkLogger: " + err.Error())
	}
	return log.New(f, "", log.Ldate|log.Ltime|log.Lshortfile)
}

func GetHomeDir() (string, error) {
	usr, err := user.Current()
	if err != nil {
		return "", err
	}
	return usr.HomeDir, nil
}

func FileExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func PathToFullPath(path string) string {
	if filepath.IsAbs(path) {
		return path
	}
	cwd, err := os.Getwd()
	if err != nil {
		return filepath.Clean(path)
	}
	return filepath.Join(cwd, path)
}

func Basedir() string {
	basedir, _ := filepath.Split(PathToFullPath(os.Args[0]))
	return basedir
}
<commit_msg>Add a func to insert a test user into a clean DB<commit_after>package main

import (
	"database/sql"
	"log"
	"os"
	"os/user"
	"path/filepath"

	"code.google.com/p/go.crypto/bcrypt"
)

var (
	Decrypt = decrypt
)

func Encrypt(passwd string) (hash string, err error) {
	hashBytes, err := bcrypt.GenerateFromPassword([]byte(passwd), bcrypt.DefaultCost)
	hash = string(hashBytes)
	return
}

func decrypt(hash, passwd []byte) error {
	return bcrypt.CompareHashAndPassword(hash, passwd)
}

func insertTestAuthor(db *sql.DB, uname, passwd, fullname, email, www string) error {
	passwdHash, err := Encrypt(passwd)
	if err != nil {
		return err
	}
	stmt, _ := db.Prepare(`insert into author
		(disp_name, passwd, full_name, email, www)
		values ($1, $2, $3, $4, $5)`)
	defer stmt.Close()
	stmt.Exec(uname, passwdHash, fullname, email, www)
	return nil
}

func MkLogger(fname string) *log.Logger {
	f, err := os.Create(fname)
	if err != nil {
		panic("MkLogger: " + err.Error())
	}
	return log.New(f, "", log.Ldate|log.Ltime|log.Lshortfile)
}

func GetHomeDir() (string, error) {
	usr, err := user.Current()
	if err != nil {
		return "", err
	}
	return usr.HomeDir, nil
}

func FileExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func PathToFullPath(path string) string {
	if filepath.IsAbs(path) {
		return path
	}
	cwd, err := os.Getwd()
	if err != nil {
		return filepath.Clean(path)
	}
	return filepath.Join(cwd, path)
}

func Basedir() string {
	basedir, _ := filepath.Split(PathToFullPath(os.Args[0]))
	return basedir
}
<|endoftext|>


<commit_before>package grab

import (
	"context"
	"io"
	"sync/atomic"
)

type transfer struct {
	n   int64 // must be 64bit aligned on 386
	ctx context.Context
	lim RateLimiter
	w   io.Writer
	r   io.Reader
	b   []byte
}

func newTransfer(ctx context.Context, lim RateLimiter, dst io.Writer, src io.Reader, buf []byte) *transfer {
	return &transfer{
		ctx: ctx,
		lim: lim,
		w:   dst,
		r:   src,
		b:   buf,
	}
}

// copy behaves similarly to io.CopyBuffer except that it checks for cancelation
// of the given context.Context and reports progress in a thread-safe manner.
func (c *transfer) copy() (written int64, err error) {
	if c.b == nil {
		c.b = make([]byte, 32*1024)
	}
	for {
		select {
		case <-c.ctx.Done():
			err = c.ctx.Err()
			return
		default:
			// keep working
		}
		if c.lim != nil {
			err = c.lim.WaitN(c.ctx, len(c.b))
			if err != nil {
				return
			}
		}
		nr, er := c.r.Read(c.b)
		if nr > 0 {
			nw, ew := c.w.Write(c.b[0:nr])
			if nw > 0 {
				written += int64(nw)
				atomic.StoreInt64(&c.n, written)
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = io.ErrShortWrite
				break
			}
		}
		if er != nil {
			if er != io.EOF {
				err = er
			}
			break
		}
	}
	return written, err
}

// N returns the number of bytes transferred.
func (c *transfer) N() (n int64) {
	if c == nil {
		return 0
	}
	n = atomic.LoadInt64(&c.n)
	return
}
<commit_msg>Fixed rate limiting for transfers that underrun the buffer. Fixes #58<commit_after>package grab

import (
	"context"
	"io"
	"sync/atomic"
)

type transfer struct {
	n   int64 // must be 64bit aligned on 386
	ctx context.Context
	lim RateLimiter
	w   io.Writer
	r   io.Reader
	b   []byte
}

func newTransfer(ctx context.Context, lim RateLimiter, dst io.Writer, src io.Reader, buf []byte) *transfer {
	return &transfer{
		ctx: ctx,
		lim: lim,
		w:   dst,
		r:   src,
		b:   buf,
	}
}

// copy behaves similarly to io.CopyBuffer except that it checks for cancelation
// of the given context.Context and reports progress in a thread-safe manner.
func (c *transfer) copy() (written int64, err error) {
	if c.b == nil {
		c.b = make([]byte, 32*1024)
	}
	for {
		select {
		case <-c.ctx.Done():
			err = c.ctx.Err()
			return
		default:
			// keep working
		}
		nr, er := c.r.Read(c.b)
		if nr > 0 {
			nw, ew := c.w.Write(c.b[0:nr])
			if nw > 0 {
				written += int64(nw)
				atomic.StoreInt64(&c.n, written)
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = io.ErrShortWrite
				break
			}
			// wait for rate limiter
			if c.lim != nil {
				err = c.lim.WaitN(c.ctx, nr)
				if err != nil {
					return
				}
			}
		}
		if er != nil {
			if er != io.EOF {
				err = er
			}
			break
		}
	}
	return written, err
}

// N returns the number of bytes transferred.
func (c *transfer) N() (n int64) {
	if c == nil {
		return 0
	}
	n = atomic.LoadInt64(&c.n)
	return
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"os"
	"os/user"
	"path"

	"github.com/go-ini/ini"
)

const (
	rcFilename        = ".arigatorc"
	publicKeyFilename = "public_key.json"
	caBundleFilename  = "ca_bundle.pem"
	registryURI       = "https://registry.arigato.sh"
)

type preferences struct {
	Core core `ini:"core"`
}

type core struct {
	PublicKeyFile string `ini:"public_key_file"`
	CABundleFile  string `ini:"ca_bundle_file"`
	RegistryURI   string `ini:"registry_uri"`
}

func newPreferences() (*preferences, error) {
	cwd, err := os.Getwd()
	if err != nil {
		return nil, fmt.Errorf("Could not determine cwd: %s", err)
	}

	defaultKeyPath := path.Join(cwd, publicKeyFilename)
	defaultBundlePath := path.Join(cwd, caBundleFilename)
	prefs := &preferences{
		Core: core{
			PublicKeyFile: defaultKeyPath,
			CABundleFile:  defaultBundlePath,
			RegistryURI:   registryURI,
		},
	}

	u, err := user.Current()
	if err != nil {
		return nil, err
	}

	filePath := path.Join(u.HomeDir, rcFilename)
	_, err = os.Stat(filePath)
	if os.IsNotExist(err) {
		return prefs, nil
	}

	if err != nil {
		return nil, err
	}

	err = ini.MapTo(prefs, filePath)
	if err != nil {
		return nil, err
	}

	return prefs, nil
}
<commit_msg>Fix lookup location for keys and certs<commit_after>package config

import (
	"fmt"
	"os"
	"os/user"
	"path"

	"github.com/go-ini/ini"
	"github.com/kardianos/osext"
)

const (
	rcFilename        = ".arigatorc"
	publicKeyFilename = "public_key.json"
	caBundleFilename  = "ca_bundle.pem"
	registryURI       = "https://registry.arigato.sh"
)

type preferences struct {
	Core core `ini:"core"`
}

type core struct {
	PublicKeyFile string `ini:"public_key_file"`
	CABundleFile  string `ini:"ca_bundle_file"`
	RegistryURI   string `ini:"registry_uri"`
}

func newPreferences() (*preferences, error) {
	dir, err := osext.ExecutableFolder()
	if err != nil {
		return nil, fmt.Errorf("Could not determine executable location: %s", err)
	}

	// certs and keys live in the root of the node package
	defaultKeyPath := path.Join(dir, "..", publicKeyFilename)
	defaultBundlePath := path.Join(dir, "..", caBundleFilename)
	prefs := &preferences{
		Core: core{
			PublicKeyFile: defaultKeyPath,
			CABundleFile:  defaultBundlePath,
			RegistryURI:   registryURI,
		},
	}

	u, err := user.Current()
	if err != nil {
		return nil, err
	}

	filePath := path.Join(u.HomeDir, rcFilename)
	_, err = os.Stat(filePath)
	if os.IsNotExist(err) {
		return prefs, nil
	}

	if err != nil {
		return nil, err
	}

	err = ini.MapTo(prefs, filePath)
	if err != nil {
		return nil, err
	}

	return prefs, nil
}
<|endoftext|>


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

package pendingheap

import (
	"go.uber.org/yarpc/api/peer"
	"go.uber.org/yarpc/yarpcconfig"
)

// Spec returns a configuration specification for the round-robin peer list
// implementation, making it possible to select the least recently chosen peer
// with transports that use outbound peer list configuration (like HTTP).
//
//  cfg := yarpcconfig.New()
//  cfg.MustRegisterPeerList(pendingheap.Spec())
//
// This enables the round-robin peer list:
//
//  outbounds:
//    otherservice:
//      unary:
//        http:
//          url: https://host:port/rpc
//          fewest-pending-requests:
//            peers:
//              - 127.0.0.1:8080
//              - 127.0.0.1:8081
func Spec() yarpcconfig.PeerListSpec {
	return yarpcconfig.PeerListSpec{
		Name: "fewest-pending-requests",
		BuildPeerList: func(c struct{}, t peer.Transport, k *yarpcconfig.Kit) (peer.ChooserList, error) {
			return New(t), nil
		},
	}
}
<commit_msg>Remove round-robin references in pendingheap documentation (#1503)<commit_after>// Copyright (c) 2018 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package pendingheap

import (
	"go.uber.org/yarpc/api/peer"
	"go.uber.org/yarpc/yarpcconfig"
)

// Spec returns a configuration specification for the pending heap peer list
// implementation, making it possible to select the least recently chosen peer
// with transports that use outbound peer list configuration (like HTTP).
//
//  cfg := yarpcconfig.New()
//  cfg.MustRegisterPeerList(pendingheap.Spec())
//
// This enables the pending heap peer list:
//
//  outbounds:
//    otherservice:
//      unary:
//        http:
//          url: https://host:port/rpc
//          fewest-pending-requests:
//            peers:
//              - 127.0.0.1:8080
//              - 127.0.0.1:8081
func Spec() yarpcconfig.PeerListSpec {
	return yarpcconfig.PeerListSpec{
		Name: "fewest-pending-requests",
		BuildPeerList: func(c struct{}, t peer.Transport, k *yarpcconfig.Kit) (peer.ChooserList, error) {
			return New(t), nil
		},
	}
}
<|endoftext|>


<commit_before>package build

import (
	"bytes"
	"context"
	"io"
	"io/ioutil"
	"strings"
	"testing"

	"github.com/docker/docker/api/types"
	dclient "github.com/docker/docker/client"
	"github.com/docker/docker/integration/internal/container"
	"github.com/docker/docker/pkg/stdcopy"
	"github.com/docker/docker/testutil/daemon"
	"github.com/docker/docker/testutil/fakecontext"
	"gotest.tools/assert"
	is "gotest.tools/assert/cmp"
	"gotest.tools/skip"
)

func TestBuildSquashParent(t *testing.T) {
	skip.If(t, testEnv.DaemonInfo.OSType == "windows")

	var client dclient.APIClient
	if !testEnv.DaemonInfo.ExperimentalBuild {
		skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")

		d := daemon.New(t, daemon.WithExperimental)
		d.StartWithBusybox(t)
		defer d.Stop(t)
		client = d.NewClientT(t)
	} else {
		client = testEnv.APIClient()
	}

	dockerfile := `
		FROM busybox
		RUN echo hello > /hello
		RUN echo world >> /hello
		RUN echo hello > /remove_me
		ENV HELLO world
		RUN rm /remove_me
		`

	// build and get the ID that we can use later for history comparison
	ctx := context.Background()
	source := fakecontext.New(t, "", fakecontext.WithDockerfile(dockerfile))
	defer source.Close()

	name := "test"
	resp, err := client.ImageBuild(ctx,
		source.AsTarReader(t),
		types.ImageBuildOptions{
			Remove:      true,
			ForceRemove: true,
			Tags:        []string{name},
		})
	assert.NilError(t, err)
	_, err = io.Copy(ioutil.Discard, resp.Body)
	resp.Body.Close()
	assert.NilError(t, err)

	inspect, _, err := client.ImageInspectWithRaw(ctx, name)
	assert.NilError(t, err)
	origID := inspect.ID

	// build with squash
	resp, err = client.ImageBuild(ctx,
		source.AsTarReader(t),
		types.ImageBuildOptions{
			Remove:      true,
			ForceRemove: true,
			Squash:      true,
			Tags:        []string{name},
		})
	assert.NilError(t, err)
	_, err = io.Copy(ioutil.Discard, resp.Body)
	resp.Body.Close()
	assert.NilError(t, err)

	cid := container.Run(ctx, t, client,
		container.WithImage(name),
		container.WithCmd("/bin/sh", "-c", "cat /hello"),
	)
	reader, err := client.ContainerLogs(ctx, cid, types.ContainerLogsOptions{
		ShowStdout: true,
	})
	assert.NilError(t, err)

	actualStdout := new(bytes.Buffer)
	actualStderr := ioutil.Discard
	_, err = stdcopy.StdCopy(actualStdout, actualStderr, reader)
	assert.NilError(t, err)
	assert.Check(t, is.Equal(strings.TrimSpace(actualStdout.String()), "hello\nworld"))

	container.Run(ctx, t, client,
		container.WithImage(name),
		container.WithCmd("/bin/sh", "-c", "[ ! -f /remove_me ]"),
	)
	container.Run(ctx, t, client,
		container.WithImage(name),
		container.WithCmd("/bin/sh", "-c", `[ "$(echo $HELLO)" == "world" ]`),
	)

	origHistory, err := client.ImageHistory(ctx, origID)
	assert.NilError(t, err)
	testHistory, err := client.ImageHistory(ctx, name)
	assert.NilError(t, err)

	inspect, _, err = client.ImageInspectWithRaw(ctx, name)
	assert.NilError(t, err)
	assert.Check(t, is.Len(testHistory, len(origHistory)+1))
	assert.Check(t, is.Len(inspect.RootFS.Layers, 2))
}
<commit_msg>TestBuildSquashParent: fix non-standard comparisson<commit_after>package build

import (
	"bytes"
	"context"
	"io"
	"io/ioutil"
	"strings"
	"testing"

	"github.com/docker/docker/api/types"
	dclient "github.com/docker/docker/client"
	"github.com/docker/docker/integration/internal/container"
	"github.com/docker/docker/pkg/stdcopy"
	"github.com/docker/docker/testutil/daemon"
	"github.com/docker/docker/testutil/fakecontext"
	"gotest.tools/assert"
	is "gotest.tools/assert/cmp"
	"gotest.tools/skip"
)

func TestBuildSquashParent(t *testing.T) {
	skip.If(t, testEnv.DaemonInfo.OSType == "windows")

	var client dclient.APIClient
	if !testEnv.DaemonInfo.ExperimentalBuild {
		skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")

		d := daemon.New(t, daemon.WithExperimental)
		d.StartWithBusybox(t)
		defer d.Stop(t)
		client = d.NewClientT(t)
	} else {
		client = testEnv.APIClient()
	}

	dockerfile := `
		FROM busybox
		RUN echo hello > /hello
		RUN echo world >> /hello
		RUN echo hello > /remove_me
		ENV HELLO world
		RUN rm /remove_me
		`

	// build and get the ID that we can use later for history comparison
	ctx := context.Background()
	source := fakecontext.New(t, "", fakecontext.WithDockerfile(dockerfile))
	defer source.Close()

	name := "test"
	resp, err := client.ImageBuild(ctx,
		source.AsTarReader(t),
		types.ImageBuildOptions{
			Remove:      true,
			ForceRemove: true,
			Tags:        []string{name},
		})
	assert.NilError(t, err)
	_, err = io.Copy(ioutil.Discard, resp.Body)
	resp.Body.Close()
	assert.NilError(t, err)

	inspect, _, err := client.ImageInspectWithRaw(ctx, name)
	assert.NilError(t, err)
	origID := inspect.ID

	// build with squash
	resp, err = client.ImageBuild(ctx,
		source.AsTarReader(t),
		types.ImageBuildOptions{
			Remove:      true,
			ForceRemove: true,
			Squash:      true,
			Tags:        []string{name},
		})
	assert.NilError(t, err)
	_, err = io.Copy(ioutil.Discard, resp.Body)
	resp.Body.Close()
	assert.NilError(t, err)

	cid := container.Run(ctx, t, client,
		container.WithImage(name),
		container.WithCmd("/bin/sh", "-c", "cat /hello"),
	)
	reader, err := client.ContainerLogs(ctx, cid, types.ContainerLogsOptions{
		ShowStdout: true,
	})
	assert.NilError(t, err)

	actualStdout := new(bytes.Buffer)
	actualStderr := ioutil.Discard
	_, err = stdcopy.StdCopy(actualStdout, actualStderr, reader)
	assert.NilError(t, err)
	assert.Check(t, is.Equal(strings.TrimSpace(actualStdout.String()), "hello\nworld"))

	container.Run(ctx, t, client,
		container.WithImage(name),
		container.WithCmd("/bin/sh", "-c", "[ ! -f /remove_me ]"),
	)
	container.Run(ctx, t, client,
		container.WithImage(name),
		container.WithCmd("/bin/sh", "-c", `[ "$(echo $HELLO)" = "world" ]`),
	)

	origHistory, err := client.ImageHistory(ctx, origID)
	assert.NilError(t, err)
	testHistory, err := client.ImageHistory(ctx, name)
	assert.NilError(t, err)

	inspect, _, err = client.ImageInspectWithRaw(ctx, name)
	assert.NilError(t, err)
	assert.Check(t, is.Len(testHistory, len(origHistory)+1))
	assert.Check(t, is.Len(inspect.RootFS.Layers, 2))
}
<|endoftext|>


<commit_before>package test

import (
	"io/ioutil"
	"log"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/opentable/sous/config"
	"github.com/opentable/sous/graph"
	"github.com/opentable/sous/lib"
	"github.com/opentable/sous/server"
	"github.com/samsalisbury/psyringe"
	"github.com/samsalisbury/semv"
)

func buildManifest(cluster, repo, version string) *sous.Manifest {
	m := sous.Manifest{Source: sous.SourceLocation{Repo: repo}}
	m.Deployments = make(sous.DeploySpecs)
	m.Deployments[cluster] = sous.DeploySpec{Version: semv.MustParse(version)}
	return &m
}

func TestWriteState(t *testing.T) {
	log.SetFlags(log.Flags() | log.Lshortfile)
	steadyManifest := buildManifest("test-cluster", "github.com/opentable/steady", "1.2.3")
	diesManifest := buildManifest("test-cluster", "github.com/opentable/dies", "133.56.987431")
	changesManifest := buildManifest("test-cluster", "github.com/opentable/changes", "0.17.19")
	newManifest := buildManifest("test-cluster", "github.com/opentable/new", "0.0.1")

	state := &sous.State{}
	state.Defs.Clusters = make(sous.Clusters)
	state.Defs.Clusters["test-cluster"] = &sous.Cluster{Name: "test-cluster"}

	state.Manifests = sous.NewManifests()
	state.Manifests.Add(steadyManifest)
	state.Manifests.Add(diesManifest)
	state.Manifests.Add(changesManifest)

	sm := sous.DummyStateManager{State: state}
	smm, err := sm.ReadState()
	if err != nil {
		t.Fatal("State manager double is broken", err)
	}
	if smm.Manifests.Len() <= 0 {
		t.Fatal("State manager double is empty")
	}
	gf := func() server.Injector {
		di := psyringe.New()
		di.Add(sous.NewLogSet(os.Stderr, os.Stderr, ioutil.Discard))
		graph.AddInternals(di)
		di.Add(
			func() graph.LocalStateReader { return graph.LocalStateReader{sm} },
			func() graph.LocalStateWriter { return graph.LocalStateWriter{sm} },
		)
		di.Add(&config.Verbosity{})
		return di
	}

	ts := httptest.NewServer(server.SousRouteMap.BuildRouter(gf))
	defer ts.Close()

	hsm, err := sous.NewHTTPStateManager(ts.URL)
	if err != nil {
		t.Fatal(err)
	}

	ls, err := hsm.ReadState()
	if err != nil {
		t.Error(err)
	}
	log.Printf("%#v", ls)
	if ls.Manifests.Len() != state.Manifests.Len() {
		t.Errorf("Local state has %d manifests to remote's %d", ls.Manifests.Len(), state.Manifests.Len())
	}

	ls.Manifests.Remove(diesManifest.ID())
	ls.Manifests.Add(newManifest)
	ch, there := ls.Manifests.Get(changesManifest.ID())
	if !there {
		t.Fatal("Changed manifest not in local manifests!")
	}
	chd := ch.Deployments["test-cluster"]
	chd.Version = semv.MustParse("0.18.0")
	ch.Deployments["test-cluster"] = chd
	ls.Manifests.Remove(ch.ID())
	ls.Manifests.Add(ch)
	log.Printf("%#v", ls)
	err = hsm.WriteState(ls)
	if err != nil {
		t.Fatalf("%+v", err)
	}

	state, err = sm.ReadState()

	if ls.Manifests.Len() != state.Manifests.Len() {
		t.Errorf("After write, local state has %d manifests to remote's %d", ls.Manifests.Len(), state.Manifests.Len())
	}

	d, there := state.Manifests.Get(diesManifest.ID())
	if there {
		t.Errorf("Removed manifest still in server's state: %#v", d)
	}
	_, there = state.Manifests.Get(steadyManifest.ID())
	if !there {
		t.Errorf("Untouched manifest not in server's state")
	}
	_, there = state.Manifests.Get(newManifest.ID())
	if !there {
		t.Errorf("Added manifest not in server's state")
	}
	c, there := state.Manifests.Get(changesManifest.ID())
	if !there {
		t.Errorf("Changed manifest missing from server's state")
	}
	if c.Deployments["test-cluster"].Version.String() != "0.18.0" {
		t.Errorf("Server's version of changed state should be '0.18.0' was %v", c.Deployments["test-cluster"].Version)
	}
}
<commit_msg>integration: fix package name + build tag<commit_after>// +build integration

package integration

import (
	"io/ioutil"
	"log"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/opentable/sous/config"
	"github.com/opentable/sous/graph"
	"github.com/opentable/sous/lib"
	"github.com/opentable/sous/server"
	"github.com/samsalisbury/psyringe"
	"github.com/samsalisbury/semv"
)

func buildManifest(cluster, repo, version string) *sous.Manifest {
	m := sous.Manifest{Source: sous.SourceLocation{Repo: repo}}
	m.Deployments = make(sous.DeploySpecs)
	m.Deployments[cluster] = sous.DeploySpec{Version: semv.MustParse(version)}
	return &m
}

func TestWriteState(t *testing.T) {
	log.SetFlags(log.Flags() | log.Lshortfile)
	steadyManifest := buildManifest("test-cluster", "github.com/opentable/steady", "1.2.3")
	diesManifest := buildManifest("test-cluster", "github.com/opentable/dies", "133.56.987431")
	changesManifest := buildManifest("test-cluster", "github.com/opentable/changes", "0.17.19")
	newManifest := buildManifest("test-cluster", "github.com/opentable/new", "0.0.1")

	state := &sous.State{}
	state.Defs.Clusters = make(sous.Clusters)
	state.Defs.Clusters["test-cluster"] = &sous.Cluster{Name: "test-cluster"}

	state.Manifests = sous.NewManifests()
	state.Manifests.Add(steadyManifest)
	state.Manifests.Add(diesManifest)
	state.Manifests.Add(changesManifest)

	sm := sous.DummyStateManager{State: state}
	smm, err := sm.ReadState()
	if err != nil {
		t.Fatal("State manager double is broken", err)
	}
	if smm.Manifests.Len() <= 0 {
		t.Fatal("State manager double is empty")
	}
	gf := func() server.Injector {
		di := psyringe.New()
		di.Add(sous.NewLogSet(os.Stderr, os.Stderr, ioutil.Discard))
		graph.AddInternals(di)
		di.Add(
			func() graph.LocalStateReader { return graph.LocalStateReader{sm} },
			func() graph.LocalStateWriter { return graph.LocalStateWriter{sm} },
		)
		di.Add(&config.Verbosity{})
		return di
	}

	ts := httptest.NewServer(server.SousRouteMap.BuildRouter(gf))
	defer ts.Close()

	hsm, err := sous.NewHTTPStateManager(ts.URL)
	if err != nil {
		t.Fatal(err)
	}

	ls, err := hsm.ReadState()
	if err != nil {
		t.Error(err)
	}
	log.Printf("%#v", ls)
	if ls.Manifests.Len() != state.Manifests.Len() {
		t.Errorf("Local state has %d manifests to remote's %d", ls.Manifests.Len(), state.Manifests.Len())
	}

	ls.Manifests.Remove(diesManifest.ID())
	ls.Manifests.Add(newManifest)
	ch, there := ls.Manifests.Get(changesManifest.ID())
	if !there {
		t.Fatal("Changed manifest not in local manifests!")
	}
	chd := ch.Deployments["test-cluster"]
	chd.Version = semv.MustParse("0.18.0")
	ch.Deployments["test-cluster"] = chd
	ls.Manifests.Remove(ch.ID())
	ls.Manifests.Add(ch)
	log.Printf("%#v", ls)
	err = hsm.WriteState(ls)
	if err != nil {
		t.Fatalf("%+v", err)
	}

	state, err = sm.ReadState()

	if ls.Manifests.Len() != state.Manifests.Len() {
		t.Errorf("After write, local state has %d manifests to remote's %d", ls.Manifests.Len(), state.Manifests.Len())
	}

	d, there := state.Manifests.Get(diesManifest.ID())
	if there {
		t.Errorf("Removed manifest still in server's state: %#v", d)
	}
	_, there = state.Manifests.Get(steadyManifest.ID())
	if !there {
		t.Errorf("Untouched manifest not in server's state")
	}
	_, there = state.Manifests.Get(newManifest.ID())
	if !there {
		t.Errorf("Added manifest not in server's state")
	}
	c, there := state.Manifests.Get(changesManifest.ID())
	if !there {
		t.Errorf("Changed manifest missing from server's state")
	}
	if c.Deployments["test-cluster"].Version.String() != "0.18.0" {
		t.Errorf("Server's version of changed state should be '0.18.0' was %v", c.Deployments["test-cluster"].Version)
	}
}
<|endoftext|>


<commit_before>/**
Copyright (c) 2015 The ConnectorDB Contributors (see AUTHORS)
Licensed under the MIT license.
**/
package website

//Router returns a fully formed Gorilla router given an optional prefix
import (
	"connectordb"
	"net/http"

	"github.com/gorilla/mux"
)

// Router handles the website
func Router(db *connectordb.Database, r *mux.Router) (*mux.Router, error) {
	if r == nil {
		r = mux.NewRouter()
	}

	err := LoadFiles()
	if err != nil {
		return nil, err
	}

	//Allow for the application to match /path and /path/ to the same place.
	r.StrictSlash(true)

	//The app and web prefixes are served directly from the correct directories
	www := "/" + WWWPrefix
	r.PathPrefix(www).Handler(http.StripPrefix(www, http.FileServer(http.Dir(WWWPath))))
	app := "/" + AppPrefix
	r.PathPrefix(app).Handler(http.StripPrefix(app, http.FileServer(http.Dir(AppPath))))

	//Handle the favicon
	r.Handle("/favicon.ico", http.RedirectHandler(www+"/favicon.ico", http.StatusMovedPermanently))
	r.Handle("/robots.txt", http.RedirectHandler(www+"/robots.txt", http.StatusMovedPermanently))

	// These functions are actually fairly standard for login/logout across different apps
	// so we make them work the same way here
	r.Handle("/logout", http.HandlerFunc(LogoutHandler)).Methods("GET")
	r.Handle("/login", Authenticator(WWWLogin, Login, db)).Methods("GET")

	//Now load the user/device/stream paths
	r.HandleFunc("/", Authenticator(WWWIndex, Index, db)).Methods("GET")
	r.HandleFunc("/{user}", Authenticator(WWWLogin, User, db)).Methods("GET")
	r.HandleFunc("/{user}/{device}", Authenticator(WWWLogin, Device, db)).Methods("GET")
	r.HandleFunc("/{user}/{device}/{stream}", Authenticator(WWWLogin, Stream, db)).Methods("GET")

	return r, nil
}
<commit_msg>Added Sitemap url to www/sitemap.xml<commit_after>/**
Copyright (c) 2015 The ConnectorDB Contributors (see AUTHORS)
Licensed under the MIT license.
**/
package website

//Router returns a fully formed Gorilla router given an optional prefix
import (
	"connectordb"
	"net/http"

	"github.com/gorilla/mux"
)

// Router handles the website
func Router(db *connectordb.Database, r *mux.Router) (*mux.Router, error) {
	if r == nil {
		r = mux.NewRouter()
	}

	err := LoadFiles()
	if err != nil {
		return nil, err
	}

	//Allow for the application to match /path and /path/ to the same place.
	r.StrictSlash(true)

	//The app and web prefixes are served directly from the correct directories
	www := "/" + WWWPrefix
	r.PathPrefix(www).Handler(http.StripPrefix(www, http.FileServer(http.Dir(WWWPath))))
	app := "/" + AppPrefix
	r.PathPrefix(app).Handler(http.StripPrefix(app, http.FileServer(http.Dir(AppPath))))

	//Handle the favicon
	r.Handle("/favicon.ico", http.RedirectHandler(www+"/favicon.ico", http.StatusMovedPermanently))
	r.Handle("/robots.txt", http.RedirectHandler(www+"/robots.txt", http.StatusMovedPermanently))
	r.Handle("/sitemap.xml", http.RedirectHandler(www+"/sitemap.xml", http.StatusMovedPermanently))

	// These functions are actually fairly standard for login/logout across different apps
	// so we make them work the same way here
	r.Handle("/logout", http.HandlerFunc(LogoutHandler)).Methods("GET")
	r.Handle("/login", Authenticator(WWWLogin, Login, db)).Methods("GET")

	//Now load the user/device/stream paths
	r.HandleFunc("/", Authenticator(WWWIndex, Index, db)).Methods("GET")
	r.HandleFunc("/{user}", Authenticator(WWWLogin, User, db)).Methods("GET")
	r.HandleFunc("/{user}/{device}", Authenticator(WWWLogin, Device, db)).Methods("GET")
	r.HandleFunc("/{user}/{device}/{stream}", Authenticator(WWWLogin, Stream, db)).Methods("GET")

	return r, nil
}
<|endoftext|>