<commit_before>package schema

import (
	"reflect"
	"testing"

	"github.com/hashicorp/hil/ast"
	"github.com/hashicorp/terraform/config"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/terraform"
)

func TestConfigFieldReader_impl(t *testing.T) {
	var _ FieldReader = new(ConfigFieldReader)
}

func TestConfigFieldReader(t *testing.T) {
	testFieldReader(t, func(s map[string]*Schema) FieldReader {
		return &ConfigFieldReader{
			Schema: s,

			Config: testConfig(t, map[string]interface{}{
				"bool":   true,
				"float":  3.1415,
				"int":    42,
				"string": "string",

				"list": []interface{}{"foo", "bar"},

				"listInt": []interface{}{21, 42},

				"map": map[string]interface{}{
					"foo": "bar",
					"bar": "baz",
				},

				"set": []interface{}{10, 50},
				"setDeep": []interface{}{
					map[string]interface{}{
						"index": 10,
						"value": "foo",
					},
					map[string]interface{}{
						"index": 50,
						"value": "bar",
					},
				},
			}),
		}
	})
}

func TestConfigFieldReader_DefaultHandling(t *testing.T) {
	schema := map[string]*Schema{
		"strWithDefault": &Schema{
			Type:    TypeString,
			Default: "ImADefault",
		},
		"strWithDefaultFunc": &Schema{
			Type: TypeString,
			DefaultFunc: func() (interface{}, error) {
				return "FuncDefault", nil
			},
		},
	}

	cases := map[string]struct {
		Addr   []string
		Result FieldReadResult
		Config *terraform.ResourceConfig
		Err    bool
	}{
		"gets default value when no config set": {
			[]string{"strWithDefault"},
			FieldReadResult{
				Value:    "ImADefault",
				Exists:   true,
				Computed: false,
			},
			testConfig(t, map[string]interface{}{}),
			false,
		},
		"config overrides default value": {
			[]string{"strWithDefault"},
			FieldReadResult{
				Value:    "fromConfig",
				Exists:   true,
				Computed: false,
			},
			testConfig(t, map[string]interface{}{
				"strWithDefault": "fromConfig",
			}),
			false,
		},
		"gets default from function when no config set": {
			[]string{"strWithDefaultFunc"},
			FieldReadResult{
				Value:    "FuncDefault",
				Exists:   true,
				Computed: false,
			},
			testConfig(t, map[string]interface{}{}),
			false,
		},
		"config overrides default function": {
			[]string{"strWithDefaultFunc"},
			FieldReadResult{
				Value:    "fromConfig",
				Exists:   true,
				Computed: false,
			},
			testConfig(t, map[string]interface{}{
				"strWithDefaultFunc": "fromConfig",
			}),
			false,
		},
	}

	for name, tc := range cases {
		r := &ConfigFieldReader{
			Schema: schema,
			Config: tc.Config,
		}
		out, err := r.ReadField(tc.Addr)
		if err != nil != tc.Err {
			t.Fatalf("%s: err: %s", name, err)
		}
		if s, ok := out.Value.(*Set); ok {
			// If it is a set, convert to a list so its more easily checked.
			out.Value = s.List()
		}
		if !reflect.DeepEqual(tc.Result, out) {
			t.Fatalf("%s: bad: %#v", name, out)
		}
	}
}

func TestConfigFieldReader_ComputedMap(t *testing.T) {
	schema := map[string]*Schema{
		"map": &Schema{
			Type:     TypeMap,
			Computed: true,
		},
	}

	cases := map[string]struct {
		Addr   []string
		Result FieldReadResult
		Config *terraform.ResourceConfig
		Err    bool
	}{
		"set, normal": {
			[]string{"map"},
			FieldReadResult{
				Value: map[string]interface{}{
					"foo": "bar",
				},
				Exists:   true,
				Computed: false,
			},
			testConfig(t, map[string]interface{}{
				"map": map[string]interface{}{
					"foo": "bar",
				},
			}),
			false,
		},

		"computed element": {
			[]string{"map"},
			FieldReadResult{
				Exists:   true,
				Computed: true,
			},
			testConfigInterpolate(t, map[string]interface{}{
				"map": map[string]interface{}{
					"foo": "${var.foo}",
				},
			}, map[string]ast.Variable{
				"var.foo": ast.Variable{
					Value: config.UnknownVariableValue,
					Type:  ast.TypeString,
				},
			}),
			false,
		},
	}

	for name, tc := range cases {
		r := &ConfigFieldReader{
			Schema: schema,
			Config: tc.Config,
		}
		out, err := r.ReadField(tc.Addr)
		if err != nil != tc.Err {
			t.Fatalf("%s: err: %s", name, err)
		}
		if s, ok := out.Value.(*Set); ok {
			// If it is a set, convert to the raw map
			out.Value = s.m
			if len(s.m) == 0 {
				out.Value = nil
			}
		}
		if !reflect.DeepEqual(tc.Result, out) {
			t.Fatalf("%s: bad: %#v", name, out)
		}
	}
}

func TestConfigFieldReader_ComputedSet(t *testing.T) {
	schema := map[string]*Schema{
		"strSet": &Schema{
			Type: TypeSet,
			Elem: &Schema{Type: TypeString},
			Set: func(v interface{}) int {
				return hashcode.String(v.(string))
			},
		},
	}

	cases := map[string]struct {
		Addr   []string
		Result FieldReadResult
		Config *terraform.ResourceConfig
		Err    bool
	}{
		"set, normal": {
			[]string{"strSet"},
			FieldReadResult{
				Value: map[string]interface{}{
					"2356372769": "foo",
				},
				Exists:   true,
				Computed: false,
			},
			testConfig(t, map[string]interface{}{
				"strSet": []interface{}{"foo"},
			}),
			false,
		},

		"set, computed element": {
			[]string{"strSet"},
			FieldReadResult{
				Value:    nil,
				Exists:   true,
				Computed: true,
			},
			testConfigInterpolate(t, map[string]interface{}{
				"strSet": []interface{}{"${var.foo}"},
			}, map[string]ast.Variable{
				"var.foo": ast.Variable{
					Value: config.UnknownVariableValue,
					Type:  ast.TypeString,
				},
			}),
			false,
		},

		"set, computed element substring": {
			[]string{"strSet"},
			FieldReadResult{
				Value:    nil,
				Exists:   true,
				Computed: true,
			},
			testConfigInterpolate(t, map[string]interface{}{
				"strSet": []interface{}{"${var.foo}/32"},
			}, map[string]ast.Variable{
				"var.foo": ast.Variable{
					Value: config.UnknownVariableValue,
					Type:  ast.TypeString,
				},
			}),
			false,
		},
	}

	for name, tc := range cases {
		r := &ConfigFieldReader{
			Schema: schema,
			Config: tc.Config,
		}
		out, err := r.ReadField(tc.Addr)
		if err != nil != tc.Err {
			t.Fatalf("%s: err: %s", name, err)
		}
		if s, ok := out.Value.(*Set); ok {
			// If it is a set, convert to the raw map
			out.Value = s.m
			if len(s.m) == 0 {
				out.Value = nil
			}
		}
		if !reflect.DeepEqual(tc.Result, out) {
			t.Fatalf("%s: bad: %#v", name, out)
		}
	}
}

func testConfig(
	t *testing.T, raw map[string]interface{}) *terraform.ResourceConfig {
	return testConfigInterpolate(t, raw, nil)
}

func testConfigInterpolate(
	t *testing.T,
	raw map[string]interface{},
	vs map[string]ast.Variable) *terraform.ResourceConfig {
	rc, err := config.NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	if len(vs) > 0 {
		if err := rc.Interpolate(vs); err != nil {
			t.Fatalf("err: %s", err)
		}
	}

	return terraform.NewResourceConfig(rc)
}
<commit_msg>Use built-in schema.HashString.<commit_after>package schema

import (
	"reflect"
	"testing"

	"github.com/hashicorp/hil/ast"
	"github.com/hashicorp/terraform/config"
	"github.com/hashicorp/terraform/terraform"
)

func TestConfigFieldReader_impl(t *testing.T) {
	var _ FieldReader = new(ConfigFieldReader)
}

func TestConfigFieldReader(t *testing.T) {
	testFieldReader(t, func(s map[string]*Schema) FieldReader {
		return &ConfigFieldReader{
			Schema: s,

			Config: testConfig(t, map[string]interface{}{
				"bool":   true,
				"float":  3.1415,
				"int":    42,
				"string": "string",

				"list": []interface{}{"foo", "bar"},

				"listInt": []interface{}{21, 42},

				"map": map[string]interface{}{
					"foo": "bar",
					"bar": "baz",
				},

				"set": []interface{}{10, 50},
				"setDeep": []interface{}{
					map[string]interface{}{
						"index": 10,
						"value": "foo",
					},
					map[string]interface{}{
						"index": 50,
						"value": "bar",
					},
				},
			}),
		}
	})
}

func TestConfigFieldReader_DefaultHandling(t *testing.T) {
	schema := map[string]*Schema{
		"strWithDefault": &Schema{
			Type:    TypeString,
			Default: "ImADefault",
		},
		"strWithDefaultFunc": &Schema{
			Type: TypeString,
			DefaultFunc: func() (interface{}, error) {
				return "FuncDefault", nil
			},
		},
	}

	cases := map[string]struct {
		Addr   []string
		Result FieldReadResult
		Config *terraform.ResourceConfig
		Err    bool
	}{
		"gets default value when no config set": {
			[]string{"strWithDefault"},
			FieldReadResult{
				Value:    "ImADefault",
				Exists:   true,
				Computed: false,
			},
			testConfig(t, map[string]interface{}{}),
			false,
		},
		"config overrides default value": {
			[]string{"strWithDefault"},
			FieldReadResult{
				Value:    "fromConfig",
				Exists:   true,
				Computed: false,
			},
			testConfig(t, map[string]interface{}{
				"strWithDefault": "fromConfig",
			}),
			false,
		},
		"gets default from function when no config set": {
			[]string{"strWithDefaultFunc"},
			FieldReadResult{
				Value:    "FuncDefault",
				Exists:   true,
				Computed: false,
			},
			testConfig(t, map[string]interface{}{}),
			false,
		},
		"config overrides default function": {
			[]string{"strWithDefaultFunc"},
			FieldReadResult{
				Value:    "fromConfig",
				Exists:   true,
				Computed: false,
			},
			testConfig(t, map[string]interface{}{
				"strWithDefaultFunc": "fromConfig",
			}),
			false,
		},
	}

	for name, tc := range cases {
		r := &ConfigFieldReader{
			Schema: schema,
			Config: tc.Config,
		}
		out, err := r.ReadField(tc.Addr)
		if err != nil != tc.Err {
			t.Fatalf("%s: err: %s", name, err)
		}
		if s, ok := out.Value.(*Set); ok {
			// If it is a set, convert to a list so its more easily checked.
			out.Value = s.List()
		}
		if !reflect.DeepEqual(tc.Result, out) {
			t.Fatalf("%s: bad: %#v", name, out)
		}
	}
}

func TestConfigFieldReader_ComputedMap(t *testing.T) {
	schema := map[string]*Schema{
		"map": &Schema{
			Type:     TypeMap,
			Computed: true,
		},
	}

	cases := map[string]struct {
		Addr   []string
		Result FieldReadResult
		Config *terraform.ResourceConfig
		Err    bool
	}{
		"set, normal": {
			[]string{"map"},
			FieldReadResult{
				Value: map[string]interface{}{
					"foo": "bar",
				},
				Exists:   true,
				Computed: false,
			},
			testConfig(t, map[string]interface{}{
				"map": map[string]interface{}{
					"foo": "bar",
				},
			}),
			false,
		},

		"computed element": {
			[]string{"map"},
			FieldReadResult{
				Exists:   true,
				Computed: true,
			},
			testConfigInterpolate(t, map[string]interface{}{
				"map": map[string]interface{}{
					"foo": "${var.foo}",
				},
			}, map[string]ast.Variable{
				"var.foo": ast.Variable{
					Value: config.UnknownVariableValue,
					Type:  ast.TypeString,
				},
			}),
			false,
		},
	}

	for name, tc := range cases {
		r := &ConfigFieldReader{
			Schema: schema,
			Config: tc.Config,
		}
		out, err := r.ReadField(tc.Addr)
		if err != nil != tc.Err {
			t.Fatalf("%s: err: %s", name, err)
		}
		if s, ok := out.Value.(*Set); ok {
			// If it is a set, convert to the raw map
			out.Value = s.m
			if len(s.m) == 0 {
				out.Value = nil
			}
		}
		if !reflect.DeepEqual(tc.Result, out) {
			t.Fatalf("%s: bad: %#v", name, out)
		}
	}
}

func TestConfigFieldReader_ComputedSet(t *testing.T) {
	schema := map[string]*Schema{
		"strSet": &Schema{
			Type: TypeSet,
			Elem: &Schema{Type: TypeString},
			Set:  HashString,
		},
	}

	cases := map[string]struct {
		Addr   []string
		Result FieldReadResult
		Config *terraform.ResourceConfig
		Err    bool
	}{
		"set, normal": {
			[]string{"strSet"},
			FieldReadResult{
				Value: map[string]interface{}{
					"2356372769": "foo",
				},
				Exists:   true,
				Computed: false,
			},
			testConfig(t, map[string]interface{}{
				"strSet": []interface{}{"foo"},
			}),
			false,
		},

		"set, computed element": {
			[]string{"strSet"},
			FieldReadResult{
				Value:    nil,
				Exists:   true,
				Computed: true,
			},
			testConfigInterpolate(t, map[string]interface{}{
				"strSet": []interface{}{"${var.foo}"},
			}, map[string]ast.Variable{
				"var.foo": ast.Variable{
					Value: config.UnknownVariableValue,
					Type:  ast.TypeString,
				},
			}),
			false,
		},

		"set, computed element substring": {
			[]string{"strSet"},
			FieldReadResult{
				Value:    nil,
				Exists:   true,
				Computed: true,
			},
			testConfigInterpolate(t, map[string]interface{}{
				"strSet": []interface{}{"${var.foo}/32"},
			}, map[string]ast.Variable{
				"var.foo": ast.Variable{
					Value: config.UnknownVariableValue,
					Type:  ast.TypeString,
				},
			}),
			false,
		},
	}

	for name, tc := range cases {
		r := &ConfigFieldReader{
			Schema: schema,
			Config: tc.Config,
		}
		out, err := r.ReadField(tc.Addr)
		if err != nil != tc.Err {
			t.Fatalf("%s: err: %s", name, err)
		}
		if s, ok := out.Value.(*Set); ok {
			// If it is a set, convert to the raw map
			out.Value = s.m
			if len(s.m) == 0 {
				out.Value = nil
			}
		}
		if !reflect.DeepEqual(tc.Result, out) {
			t.Fatalf("%s: bad: %#v", name, out)
		}
	}
}

func testConfig(
	t *testing.T, raw map[string]interface{}) *terraform.ResourceConfig {
	return testConfigInterpolate(t, raw, nil)
}

func testConfigInterpolate(
	t *testing.T,
	raw map[string]interface{},
	vs map[string]ast.Variable) *terraform.ResourceConfig {
	rc, err := config.NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	if len(vs) > 0 {
		if err := rc.Interpolate(vs); err != nil {
			t.Fatalf("err: %s", err)
		}
	}

	return terraform.NewResourceConfig(rc)
}
<|endoftext|>


<commit_before>package connection

import (
	"bytes"
	crand "crypto/rand"
	"crypto/sha256"
	"encoding/binary"
	"errors"
	"io"
	"net"
	"time"

	log "github.com/sirupsen/logrus"
	"golang.org/x/crypto/nacl/box"
	"golang.org/x/crypto/nacl/secretbox"
	"golang.org/x/crypto/ripemd160"

	"github.com/tendermint/go-crypto"
	wire "github.com/tendermint/go-wire"
	cmn "github.com/tendermint/tmlibs/common"
)

const (
	dataLenSize     = 2 // uint16 to describe the length, is <= dataMaxSize
	dataMaxSize     = 1024
	totalFrameSize  = dataMaxSize + dataLenSize
	sealedFrameSize = totalFrameSize + secretbox.Overhead
	authSigMsgSize  = (32 + 1) + (64 + 1) // fixed size (length prefixed) byte arrays
)

type authSigMessage struct {
	Key crypto.PubKey
	Sig crypto.Signature
}

// SecretConnection implements net.Conn
type SecretConnection struct {
	conn       io.ReadWriteCloser
	recvBuffer []byte
	recvNonce  *[24]byte
	sendNonce  *[24]byte
	remPubKey  crypto.PubKeyEd25519
	shrSecret  *[32]byte // shared secret
}

// MakeSecretConnection performs handshake and returns a new authenticated SecretConnection.
func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey crypto.PrivKeyEd25519) (*SecretConnection, error) {
	locPubKey := locPrivKey.PubKey().Unwrap().(crypto.PubKeyEd25519)

	// Generate ephemeral keys for perfect forward secrecy.
	locEphPub, locEphPriv := genEphKeys()

	// Write local ephemeral pubkey and receive one too.
	// NOTE: every 32-byte string is accepted as a Curve25519 public key
	// (see DJB's Curve25519 paper: http://cr.yp.to/ecdh/curve25519-20060209.pdf)
	remEphPub, err := shareEphPubKey(conn, locEphPub)
	if err != nil {
		return nil, err
	}

	// Compute common shared secret.
	shrSecret := computeSharedSecret(remEphPub, locEphPriv)

	// Sort by lexical order.
	loEphPub, hiEphPub := sort32(locEphPub, remEphPub)

	// Generate nonces to use for secretbox.
	recvNonce, sendNonce := genNonces(loEphPub, hiEphPub, locEphPub == loEphPub)

	// Generate common challenge to sign.
	challenge := genChallenge(loEphPub, hiEphPub)

	// Construct SecretConnection.
	sc := &SecretConnection{
		conn:       conn,
		recvBuffer: nil,
		recvNonce:  recvNonce,
		sendNonce:  sendNonce,
		shrSecret:  shrSecret,
	}

	// Sign the challenge bytes for authentication.
	locSignature := signChallenge(challenge, locPrivKey)

	// Share (in secret) each other's pubkey & challenge signature
	authSigMsg, err := shareAuthSignature(sc, locPubKey, locSignature)
	if err != nil {
		return nil, err
	}
	remPubKey, remSignature := authSigMsg.Key, authSigMsg.Sig
	if !remPubKey.VerifyBytes(challenge[:], remSignature) {
		return nil, errors.New("Challenge verification failed")
	}

	sc.remPubKey = remPubKey.Unwrap().(crypto.PubKeyEd25519)
	return sc, nil
}

// CONTRACT: data smaller than dataMaxSize is read atomically.
func (sc *SecretConnection) Read(data []byte) (n int, err error) {
	if 0 < len(sc.recvBuffer) {
		n_ := copy(data, sc.recvBuffer)
		sc.recvBuffer = sc.recvBuffer[n_:]
		return
	}

	sealedFrame := make([]byte, sealedFrameSize)
	if _, err = io.ReadFull(sc.conn, sealedFrame); err != nil {
		return
	}

	// decrypt the frame
	frame := make([]byte, totalFrameSize)
	if _, ok := secretbox.Open(frame[:0], sealedFrame, sc.recvNonce, sc.shrSecret); !ok {
		return n, errors.New("Failed to decrypt SecretConnection")
	}

	incr2Nonce(sc.recvNonce)
	chunkLength := binary.BigEndian.Uint16(frame) // read the first two bytes
	if chunkLength > dataMaxSize {
		return 0, errors.New("chunkLength is greater than dataMaxSize")
	}

	chunk := frame[dataLenSize : dataLenSize+chunkLength]
	n = copy(data, chunk)
	sc.recvBuffer = chunk[n:]
	return
}

// RemotePubKey returns authenticated remote pubkey
func (sc *SecretConnection) RemotePubKey() crypto.PubKeyEd25519 {
	return sc.remPubKey
}

// Writes encrypted frames of `sealedFrameSize`
// CONTRACT: data smaller than dataMaxSize is read atomically.
func (sc *SecretConnection) Write(data []byte) (n int, err error) {
	for 0 < len(data) {
		var chunk []byte
		frame := make([]byte, totalFrameSize)
		if dataMaxSize < len(data) {
			chunk = data[:dataMaxSize]
			data = data[dataMaxSize:]
		} else {
			chunk = data
			data = nil
		}
		binary.BigEndian.PutUint16(frame, uint16(len(chunk)))
		copy(frame[dataLenSize:], chunk)

		// encrypt the frame
		sealedFrame := make([]byte, sealedFrameSize)
		secretbox.Seal(sealedFrame[:0], frame, sc.sendNonce, sc.shrSecret)
		incr2Nonce(sc.sendNonce)

		if _, err := sc.conn.Write(sealedFrame); err != nil {
			return n, err
		}

		n += len(chunk)
	}
	return
}

// Close implements net.Conn
func (sc *SecretConnection) Close() error { return sc.conn.Close() }

// LocalAddr implements net.Conn
func (sc *SecretConnection) LocalAddr() net.Addr { return sc.conn.(net.Conn).LocalAddr() }

// RemoteAddr implements net.Conn
func (sc *SecretConnection) RemoteAddr() net.Addr { return sc.conn.(net.Conn).RemoteAddr() }

// SetDeadline implements net.Conn
func (sc *SecretConnection) SetDeadline(t time.Time) error { return sc.conn.(net.Conn).SetDeadline(t) }

// SetReadDeadline implements net.Conn
func (sc *SecretConnection) SetReadDeadline(t time.Time) error {
	return sc.conn.(net.Conn).SetReadDeadline(t)
}

// SetWriteDeadline implements net.Conn
func (sc *SecretConnection) SetWriteDeadline(t time.Time) error {
	return sc.conn.(net.Conn).SetWriteDeadline(t)
}

func computeSharedSecret(remPubKey, locPrivKey *[32]byte) (shrSecret *[32]byte) {
	shrSecret = new([32]byte)
	box.Precompute(shrSecret, remPubKey, locPrivKey)
	return
}

func genChallenge(loPubKey, hiPubKey *[32]byte) (challenge *[32]byte) {
	return hash32(append(loPubKey[:], hiPubKey[:]...))
}

// increment nonce big-endian by 2 with wraparound.
func incr2Nonce(nonce *[24]byte) {
	incrNonce(nonce)
	incrNonce(nonce)
}

// increment nonce big-endian by 1 with wraparound.
func incrNonce(nonce *[24]byte) {
	for i := 23; 0 <= i; i-- {
		nonce[i]++
		if nonce[i] != 0 {
			return
		}
	}
}

func genEphKeys() (ephPub, ephPriv *[32]byte) {
	var err error
	ephPub, ephPriv, err = box.GenerateKey(crand.Reader)
	if err != nil {
		log.Panic("Could not generate ephemeral keypairs")
	}
	return
}

func genNonces(loPubKey, hiPubKey *[32]byte, locIsLo bool) (*[24]byte, *[24]byte) {
	nonce1 := hash24(append(loPubKey[:], hiPubKey[:]...))
	nonce2 := new([24]byte)
	copy(nonce2[:], nonce1[:])
	nonce2[len(nonce2)-1] ^= 0x01
	if locIsLo {
		return nonce1, nonce2
	}
	return nonce2, nonce1
}

func signChallenge(challenge *[32]byte, locPrivKey crypto.PrivKeyEd25519) (signature crypto.SignatureEd25519) {
	signature = locPrivKey.Sign(challenge[:]).Unwrap().(crypto.SignatureEd25519)
	return
}

func shareAuthSignature(sc *SecretConnection, pubKey crypto.PubKeyEd25519, signature crypto.SignatureEd25519) (*authSigMessage, error) {
	var recvMsg authSigMessage
	var err1, err2 error

	cmn.Parallel(
		func() {
			msgBytes := wire.BinaryBytes(authSigMessage{pubKey.Wrap(), signature.Wrap()})
			_, err1 = sc.Write(msgBytes)
		},
		func() {
			readBuffer := make([]byte, authSigMsgSize)
			_, err2 = io.ReadFull(sc, readBuffer)
			if err2 != nil {
				return
			}
			n := int(0) // not used.
			recvMsg = wire.ReadBinary(authSigMessage{}, bytes.NewBuffer(readBuffer), authSigMsgSize, &n, &err2).(authSigMessage)
		},
	)

	if err1 != nil {
		return nil, err1
	}
	if err2 != nil {
		return nil, err2
	}
	return &recvMsg, nil
}

func shareEphPubKey(conn io.ReadWriteCloser, locEphPub *[32]byte) (remEphPub *[32]byte, err error) {
	var err1, err2 error

	cmn.Parallel(
		func() {
			_, err1 = conn.Write(locEphPub[:])
		},
		func() {
			remEphPub = new([32]byte)
			_, err2 = io.ReadFull(conn, remEphPub[:])
		},
	)

	if err1 != nil {
		return nil, err1
	}
	if err2 != nil {
		return nil, err2
	}
	return remEphPub, nil
}

func sort32(foo, bar *[32]byte) (*[32]byte, *[32]byte) {
	if bytes.Compare(foo[:], bar[:]) < 0 {
		return foo, bar
	}
	return bar, foo
}

// sha256
func hash32(input []byte) (res *[32]byte) {
	hasher := sha256.New()
	hasher.Write(input) // does not error
	resSlice := hasher.Sum(nil)
	res = new([32]byte)
	copy(res[:], resSlice)
	return
}

// We only fill in the first 20 bytes with ripemd160
func hash24(input []byte) (res *[24]byte) {
	hasher := ripemd160.New()
	hasher.Write(input) // does not error
	resSlice := hasher.Sum(nil)
	res = new([24]byte)
	copy(res[:], resSlice)
	return
}
<commit_msg>Only allow ed25519 pubkeys when connecting (#1789)<commit_after>package connection

import (
	"bytes"
	crand "crypto/rand"
	"crypto/sha256"
	"encoding/binary"
	"errors"
	"io"
	"net"
	"time"

	log "github.com/sirupsen/logrus"
	"golang.org/x/crypto/nacl/box"
	"golang.org/x/crypto/nacl/secretbox"
	"golang.org/x/crypto/ripemd160"

	"github.com/tendermint/go-crypto"
	wire "github.com/tendermint/go-wire"
	cmn "github.com/tendermint/tmlibs/common"
)

const (
	dataLenSize     = 2 // uint16 to describe the length, is <= dataMaxSize
	dataMaxSize     = 1024
	totalFrameSize  = dataMaxSize + dataLenSize
	sealedFrameSize = totalFrameSize + secretbox.Overhead
	authSigMsgSize  = (32 + 1) + (64 + 1) // fixed size (length prefixed) byte arrays
)

type authSigMessage struct {
	Key crypto.PubKey
	Sig crypto.Signature
}

// SecretConnection implements net.Conn
type SecretConnection struct {
	conn       io.ReadWriteCloser
	recvBuffer []byte
	recvNonce  *[24]byte
	sendNonce  *[24]byte
	remPubKey  crypto.PubKeyEd25519
	shrSecret  *[32]byte // shared secret
}

// MakeSecretConnection performs handshake and returns a new authenticated SecretConnection.
func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey crypto.PrivKeyEd25519) (*SecretConnection, error) {
	locPubKey := locPrivKey.PubKey().Unwrap().(crypto.PubKeyEd25519)

	// Generate ephemeral keys for perfect forward secrecy.
	locEphPub, locEphPriv := genEphKeys()

	// Write local ephemeral pubkey and receive one too.
	// NOTE: every 32-byte string is accepted as a Curve25519 public key
	// (see DJB's Curve25519 paper: http://cr.yp.to/ecdh/curve25519-20060209.pdf)
	remEphPub, err := shareEphPubKey(conn, locEphPub)
	if err != nil {
		return nil, err
	}

	// Compute common shared secret.
	shrSecret := computeSharedSecret(remEphPub, locEphPriv)

	// Sort by lexical order.
	loEphPub, hiEphPub := sort32(locEphPub, remEphPub)

	// Generate nonces to use for secretbox.
	recvNonce, sendNonce := genNonces(loEphPub, hiEphPub, locEphPub == loEphPub)

	// Generate common challenge to sign.
	challenge := genChallenge(loEphPub, hiEphPub)

	// Construct SecretConnection.
	sc := &SecretConnection{
		conn:       conn,
		recvBuffer: nil,
		recvNonce:  recvNonce,
		sendNonce:  sendNonce,
		shrSecret:  shrSecret,
	}

	// Sign the challenge bytes for authentication.
	locSignature := signChallenge(challenge, locPrivKey)

	// Share (in secret) each other's pubkey & challenge signature
	authSigMsg, err := shareAuthSignature(sc, locPubKey, locSignature)
	if err != nil {
		return nil, err
	}

	remPubKey, remSignature := authSigMsg.Key, authSigMsg.Sig
	if _, ok := remPubKey.PubKeyInner.(crypto.PubKeyEd25519); !ok {
		return nil, errors.New("peer sent a nil public key")
	}

	if !remPubKey.VerifyBytes(challenge[:], remSignature) {
		return nil, errors.New("Challenge verification failed")
	}

	sc.remPubKey = remPubKey.Unwrap().(crypto.PubKeyEd25519)
	return sc, nil
}

// CONTRACT: data smaller than dataMaxSize is read atomically.
func (sc *SecretConnection) Read(data []byte) (n int, err error) {
	if 0 < len(sc.recvBuffer) {
		n_ := copy(data, sc.recvBuffer)
		sc.recvBuffer = sc.recvBuffer[n_:]
		return
	}

	sealedFrame := make([]byte, sealedFrameSize)
	if _, err = io.ReadFull(sc.conn, sealedFrame); err != nil {
		return
	}

	// decrypt the frame
	frame := make([]byte, totalFrameSize)
	if _, ok := secretbox.Open(frame[:0], sealedFrame, sc.recvNonce, sc.shrSecret); !ok {
		return n, errors.New("Failed to decrypt SecretConnection")
	}

	incr2Nonce(sc.recvNonce)
	chunkLength := binary.BigEndian.Uint16(frame) // read the first two bytes
	if chunkLength > dataMaxSize {
		return 0, errors.New("chunkLength is greater than dataMaxSize")
	}

	chunk := frame[dataLenSize : dataLenSize+chunkLength]
	n = copy(data, chunk)
	sc.recvBuffer = chunk[n:]
	return
}

// RemotePubKey returns authenticated remote pubkey
func (sc *SecretConnection) RemotePubKey() crypto.PubKeyEd25519 {
	return sc.remPubKey
}

// Writes encrypted frames of `sealedFrameSize`
// CONTRACT: data smaller than dataMaxSize is read atomically.
func (sc *SecretConnection) Write(data []byte) (n int, err error) {
	for 0 < len(data) {
		var chunk []byte
		frame := make([]byte, totalFrameSize)
		if dataMaxSize < len(data) {
			chunk = data[:dataMaxSize]
			data = data[dataMaxSize:]
		} else {
			chunk = data
			data = nil
		}
		binary.BigEndian.PutUint16(frame, uint16(len(chunk)))
		copy(frame[dataLenSize:], chunk)

		// encrypt the frame
		sealedFrame := make([]byte, sealedFrameSize)
		secretbox.Seal(sealedFrame[:0], frame, sc.sendNonce, sc.shrSecret)
		incr2Nonce(sc.sendNonce)

		if _, err := sc.conn.Write(sealedFrame); err != nil {
			return n, err
		}

		n += len(chunk)
	}
	return
}

// Close implements net.Conn
func (sc *SecretConnection) Close() error { return sc.conn.Close() }

// LocalAddr implements net.Conn
func (sc *SecretConnection) LocalAddr() net.Addr { return sc.conn.(net.Conn).LocalAddr() }

// RemoteAddr implements net.Conn
func (sc *SecretConnection) RemoteAddr() net.Addr { return sc.conn.(net.Conn).RemoteAddr() }

// SetDeadline implements net.Conn
func (sc *SecretConnection) SetDeadline(t time.Time) error { return sc.conn.(net.Conn).SetDeadline(t) }

// SetReadDeadline implements net.Conn
func (sc *SecretConnection) SetReadDeadline(t time.Time) error {
	return sc.conn.(net.Conn).SetReadDeadline(t)
}

// SetWriteDeadline implements net.Conn
func (sc *SecretConnection) SetWriteDeadline(t time.Time) error {
	return sc.conn.(net.Conn).SetWriteDeadline(t)
}

func computeSharedSecret(remPubKey, locPrivKey *[32]byte) (shrSecret *[32]byte) {
	shrSecret = new([32]byte)
	box.Precompute(shrSecret, remPubKey, locPrivKey)
	return
}

func genChallenge(loPubKey, hiPubKey *[32]byte) (challenge *[32]byte) {
	return hash32(append(loPubKey[:], hiPubKey[:]...))
}

// increment nonce big-endian by 2 with wraparound.
func incr2Nonce(nonce *[24]byte) {
	incrNonce(nonce)
	incrNonce(nonce)
}

// increment nonce big-endian by 1 with wraparound.
func incrNonce(nonce *[24]byte) {
	for i := 23; 0 <= i; i-- {
		nonce[i]++
		if nonce[i] != 0 {
			return
		}
	}
}

func genEphKeys() (ephPub, ephPriv *[32]byte) {
	var err error
	ephPub, ephPriv, err = box.GenerateKey(crand.Reader)
	if err != nil {
		log.Panic("Could not generate ephemeral keypairs")
	}
	return
}

func genNonces(loPubKey, hiPubKey *[32]byte, locIsLo bool) (*[24]byte, *[24]byte) {
	nonce1 := hash24(append(loPubKey[:], hiPubKey[:]...))
	nonce2 := new([24]byte)
	copy(nonce2[:], nonce1[:])
	nonce2[len(nonce2)-1] ^= 0x01
	if locIsLo {
		return nonce1, nonce2
	}
	return nonce2, nonce1
}

func signChallenge(challenge *[32]byte, locPrivKey crypto.PrivKeyEd25519) (signature crypto.SignatureEd25519) {
	signature = locPrivKey.Sign(challenge[:]).Unwrap().(crypto.SignatureEd25519)
	return
}

func shareAuthSignature(sc *SecretConnection, pubKey crypto.PubKeyEd25519, signature crypto.SignatureEd25519) (*authSigMessage, error) {
	var recvMsg authSigMessage
	var err1, err2 error

	cmn.Parallel(
		func() {
			msgBytes := wire.BinaryBytes(authSigMessage{pubKey.Wrap(), signature.Wrap()})
			_, err1 = sc.Write(msgBytes)
		},
		func() {
			readBuffer := make([]byte, authSigMsgSize)
			_, err2 = io.ReadFull(sc, readBuffer)
			if err2 != nil {
				return
			}
			n := int(0) // not used.
			recvMsg = wire.ReadBinary(authSigMessage{}, bytes.NewBuffer(readBuffer), authSigMsgSize, &n, &err2).(authSigMessage)
		},
	)

	if err1 != nil {
		return nil, err1
	}
	if err2 != nil {
		return nil, err2
	}
	return &recvMsg, nil
}

func shareEphPubKey(conn io.ReadWriteCloser, locEphPub *[32]byte) (remEphPub *[32]byte, err error) {
	var err1, err2 error

	cmn.Parallel(
		func() {
			_, err1 = conn.Write(locEphPub[:])
		},
		func() {
			remEphPub = new([32]byte)
			_, err2 = io.ReadFull(conn, remEphPub[:])
		},
	)

	if err1 != nil {
		return nil, err1
	}
	if err2 != nil {
		return nil, err2
	}
	return remEphPub, nil
}

func sort32(foo, bar *[32]byte) (*[32]byte, *[32]byte) {
	if bytes.Compare(foo[:], bar[:]) < 0 {
		return foo, bar
	}
	return bar, foo
}

// sha256
func hash32(input []byte) (res *[32]byte) {
	hasher := sha256.New()
	hasher.Write(input) // does not error
	resSlice := hasher.Sum(nil)
	res = new([32]byte)
	copy(res[:], resSlice)
	return
}

// We only fill in the first 20 bytes with ripemd160
func hash24(input []byte) (res *[24]byte) {
	hasher := ripemd160.New()
	hasher.Write(input) // does not error
	resSlice := hasher.Sum(nil)
	res = new([24]byte)
	copy(res[:], resSlice)
	return
}
<|endoftext|>


<commit_before>package device

import (
	"fmt"
	"io/ioutil"
	"strconv"
	"strings"

	"github.com/lxc/lxd/lxd/device/config"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/shared"
)

type infinibandSRIOV struct {
	deviceCommon
}

// validateConfig checks the supplied config for correctness.
func (d *infinibandSRIOV) validateConfig() error {
	if d.instance.Type() != instance.TypeContainer {
		return ErrUnsupportedDevType
	}

	requiredFields := []string{"parent"}
	optionalFields := []string{
		"name",
		"mtu",
		"hwaddr",
	}
	err := config.ValidateDevice(nicValidationRules(requiredFields, optionalFields), d.config)
	if err != nil {
		return err
	}

	return nil
}

// validateEnvironment checks the runtime environment for correctness.
func (d *infinibandSRIOV) validateEnvironment() error {
	if d.config["name"] == "" {
		return fmt.Errorf("Requires name property to start")
	}

	if !shared.PathExists(fmt.Sprintf("/sys/class/net/%s", d.config["parent"])) {
		return fmt.Errorf("Parent device '%s' doesn't exist", d.config["parent"])
	}

	return nil
}

// Start is run when the device is added to a running instance or instance is starting up.
func (d *infinibandSRIOV) Start() (*RunConfig, error) {
	err := d.validateEnvironment()
	if err != nil {
		return nil, err
	}

	saveData := make(map[string]string)

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

	reservedDevices, err := instanceGetReservedDevices(d.state, d.config)
	if err != nil {
		return nil, err
	}

	vfDev, err := d.findFreeVirtualFunction(reservedDevices)
	if err != nil {
		return nil, err
	}

	saveData["host_name"] = vfDev
	ifDev, ok := devices[saveData["host_name"]]
	if !ok {
		return nil, fmt.Errorf("Specified infiniband device \"%s\" not found", saveData["host_name"])
	}

	// Record hwaddr and mtu before potentially modifying them.
	err = networkSnapshotPhysicalNic(saveData["host_name"], saveData)
	if err != nil {
		return nil, err
	}

	// Set the MAC address.
	if d.config["hwaddr"] != "" {
		_, err := shared.RunCommand("ip", "link", "set", "dev", saveData["host_name"], "address", d.config["hwaddr"])
		if err != nil {
			return nil, fmt.Errorf("Failed to set the MAC address: %s", err)
		}
	}

	// Set the MTU.
	if d.config["mtu"] != "" {
		_, err := shared.RunCommand("ip", "link", "set", "dev", saveData["host_name"], "mtu", d.config["mtu"])
		if err != nil {
			return nil, fmt.Errorf("Failed to set the MTU: %s", err)
		}
	}

	runConf := RunConfig{}

	// Configure runConf with infiniband setup instructions.
	err = infinibandAddDevices(d.state, d.instance.DevicesPath(), d.name, &ifDev, &runConf)
	if err != nil {
		return nil, err
	}

	err = d.volatileSet(saveData)
	if err != nil {
		return nil, err
	}

	runConf.NetworkInterface = []RunConfigItem{
		{Key: "name", Value: d.config["name"]},
		{Key: "type", Value: "phys"},
		{Key: "flags", Value: "up"},
		{Key: "link", Value: saveData["host_name"]},
	}

	return &runConf, nil
}

// findFreeVirtualFunction looks on the specified parent device for an unused virtual function.
// Returns the name of the interface if found, error if not.
func (d *infinibandSRIOV) findFreeVirtualFunction(reservedDevices map[string]struct{}) (string, error) {
	sriovNumVFs := fmt.Sprintf("/sys/class/net/%s/device/sriov_numvfs", d.config["parent"])
	sriovTotalVFs := fmt.Sprintf("/sys/class/net/%s/device/sriov_totalvfs", d.config["parent"])

	// Verify that this is indeed a SR-IOV enabled device.
	if !shared.PathExists(sriovTotalVFs) {
		return "", fmt.Errorf("Parent device '%s' doesn't support SR-IOV", d.config["parent"])
	}

	// Get parent dev_port and dev_id values.
	pfDevPort, err := ioutil.ReadFile(fmt.Sprintf("/sys/class/net/%s/dev_port", d.config["parent"]))
	if err != nil {
		return "", err
	}

	pfDevID, err := ioutil.ReadFile(fmt.Sprintf("/sys/class/net/%s/dev_id", d.config["parent"]))
	if err != nil {
		return "", err
	}

	// Get number of currently enabled VFs.
	sriovNumVfsBuf, err := ioutil.ReadFile(sriovNumVFs)
	if err != nil {
		return "", err
	}
	sriovNumVfsStr := strings.TrimSpace(string(sriovNumVfsBuf))
	sriovNum, err := strconv.Atoi(sriovNumVfsStr)
	if err != nil {
		return "", err
	}

	// Check if any VFs are already enabled.
	nicName := ""
	for i := 0; i < sriovNum; i++ {
		if !shared.PathExists(fmt.Sprintf("/sys/class/net/%s/device/virtfn%d/net", d.config["parent"], i)) {
			continue
		}

		// Check if VF is already in use.
		empty, err := shared.PathIsEmpty(fmt.Sprintf("/sys/class/net/%s/device/virtfn%d/net", d.config["parent"], i))
		if err != nil {
			return "", err
		}
		if empty {
			continue
		}

		vfListPath := fmt.Sprintf("/sys/class/net/%s/device/virtfn%d/net", d.config["parent"], i)
		nicName, err = NetworkSRIOVGetFreeVFInterface(reservedDevices, vfListPath, pfDevID, pfDevPort)
		if err != nil {
			return "", err
		}

		// Found a free VF.
		if nicName != "" {
			break
		}
	}

	if nicName == "" {
		return "", fmt.Errorf("All virtual functions on parent device are already in use")
	}

	return nicName, nil
}

// Stop is run when the device is removed from the instance.
func (d *infinibandSRIOV) Stop() (*RunConfig, error) {
	v := d.volatileGet()
	runConf := RunConfig{
		PostHooks:        []func() error{d.postStop},
		NetworkInterface: []RunConfigItem{{Key: "link", Value: v["host_name"]}},
	}

	err := unixDeviceRemove(d.instance.DevicesPath(), IBDevPrefix, d.name, "", &runConf)
	if err != nil {
		return nil, err
	}

	return &runConf, nil
}

// postStop is run after the device is removed from the instance.
func (d *infinibandSRIOV) postStop() error {
	defer d.volatileSet(map[string]string{
		"host_name":         "",
		"last_state.hwaddr": "",
		"last_state.mtu":    "",
	})

	// Remove infiniband host files for this device.
	err := unixDeviceDeleteFiles(d.state, d.instance.DevicesPath(), IBDevPrefix, d.name, "")
	if err != nil {
		return fmt.Errorf("Failed to delete files for device '%s': %v", d.name, err)
	}

	// Restore hwaddr and mtu.
	v := d.volatileGet()
	if v["host_name"] != "" {
		err := networkRestorePhysicalNic(v["host_name"], v)
		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>device/infiniband/sriov: Switches to use resources package for device probing<commit_after>package device

import (
	"fmt"

	"github.com/lxc/lxd/lxd/device/config"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/resources"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
)

type infinibandSRIOV struct {
	deviceCommon
}

// validateConfig checks the supplied config for correctness.
func (d *infinibandSRIOV) validateConfig() error {
	if d.instance.Type() != instance.TypeContainer {
		return ErrUnsupportedDevType
	}

	requiredFields := []string{"parent"}
	optionalFields := []string{
		"name",
		"mtu",
		"hwaddr",
	}
	err := config.ValidateDevice(nicValidationRules(requiredFields, optionalFields), d.config)
	if err != nil {
		return err
	}

	return nil
}

// validateEnvironment checks the runtime environment for correctness.
func (d *infinibandSRIOV) validateEnvironment() error {
	if d.config["name"] == "" {
		return fmt.Errorf("Requires name property to start")
	}

	if !shared.PathExists(fmt.Sprintf("/sys/class/net/%s", d.config["parent"])) {
		return fmt.Errorf("Parent device '%s' doesn't exist", d.config["parent"])
	}

	return nil
}

// Start is run when the device is added to a running instance or instance is starting up.
func (d *infinibandSRIOV) Start() (*RunConfig, error) {
	err := d.validateEnvironment()
	if err != nil {
		return nil, err
	}

	saveData := make(map[string]string)

	// Load network interface info.
	nics, err := resources.GetNetwork()
	if err != nil {
		return nil, err
	}

	// Filter the network interfaces to just infiniband devices related to parent.
	ibDevs := infinibandDevices(nics, d.config["parent"])

	// We don't count the parent as an available VF.
	delete(ibDevs, d.config["parent"])

	// Load any interfaces already allocated to other devices.
	reservedDevices, err := instanceGetReservedDevices(d.state, d.config)
	if err != nil {
		return nil, err
	}

	// Remove reserved devices from available list.
	for k := range reservedDevices {
		delete(ibDevs, k)
	}

	if len(ibDevs) < 1 {
		return nil, fmt.Errorf("All virtual functions on parent device are already in use")
	}

	// Get first VF device that is free.
	var vfDev *api.ResourcesNetworkCardPort
	for _, v := range ibDevs {
		vfDev = v
		break
	}

	saveData["host_name"] = vfDev.ID

	// Record hwaddr and mtu before potentially modifying them.
	err = networkSnapshotPhysicalNic(saveData["host_name"], saveData)
	if err != nil {
		return nil, err
	}

	// Set the MAC address.
	if d.config["hwaddr"] != "" {
		_, err := shared.RunCommand("ip", "link", "set", "dev", saveData["host_name"], "address", d.config["hwaddr"])
		if err != nil {
			return nil, fmt.Errorf("Failed to set the MAC address: %s", err)
		}
	}

	// Set the MTU.
	if d.config["mtu"] != "" {
		_, err := shared.RunCommand("ip", "link", "set", "dev", saveData["host_name"], "mtu", d.config["mtu"])
		if err != nil {
			return nil, fmt.Errorf("Failed to set the MTU: %s", err)
		}
	}

	runConf := RunConfig{}

	// Configure runConf with infiniband setup instructions.
	err = infinibandAddDevices(d.state, d.instance.DevicesPath(), d.name, vfDev, &runConf)
	if err != nil {
		return nil, err
	}

	err = d.volatileSet(saveData)
	if err != nil {
		return nil, err
	}

	runConf.NetworkInterface = []RunConfigItem{
		{Key: "name", Value: d.config["name"]},
		{Key: "type", Value: "phys"},
		{Key: "flags", Value: "up"},
		{Key: "link", Value: saveData["host_name"]},
	}

	return &runConf, nil
}

// Stop is run when the device is removed from the instance.
func (d *infinibandSRIOV) Stop() (*RunConfig, error) {
	v := d.volatileGet()
	runConf := RunConfig{
		PostHooks:        []func() error{d.postStop},
		NetworkInterface: []RunConfigItem{{Key: "link", Value: v["host_name"]}},
	}

	err := unixDeviceRemove(d.instance.DevicesPath(), IBDevPrefix, d.name, "", &runConf)
	if err != nil {
		return nil, err
	}

	return &runConf, nil
}

// postStop is run after the device is removed from the instance.
func (d *infinibandSRIOV) postStop() error {
	defer d.volatileSet(map[string]string{
		"host_name":         "",
		"last_state.hwaddr": "",
		"last_state.mtu":    "",
	})

	// Remove infiniband host files for this device.
	err := unixDeviceDeleteFiles(d.state, d.instance.DevicesPath(), IBDevPrefix, d.name, "")
	if err != nil {
		return fmt.Errorf("Failed to delete files for device '%s': %v", d.name, err)
	}

	// Restore hwaddr and mtu.
	v := d.volatileGet()
	if v["host_name"] != "" {
		err := networkRestorePhysicalNic(v["host_name"], v)
		if err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"io"
	"log"
	"net/http"
)

type Event struct {
	Id     string `json:"id"`
	Status string `json:"status"`
}

type Config struct {
	Hostname string
}

type NetworkSettings struct {
	IpAddress   string
	PortMapping map[string]map[string]string
}

type Container struct {
	Id              string
	Image           string
	Config          *Config
	NetworkSettings *NetworkSettings
}

func inspectContainer(id string, c http.Client) *Container {
	// Use the container id to fetch the container json from the Remote API
	// http://docs.docker.io/en/latest/api/docker_remote_api_v1.4/#inspect-a-container
	res, err := c.Get("http://localhost:4243/containers/" + id + "/json")
	if err != nil {
		log.Println(err)
		return nil
	}
	defer res.Body.Close()

	if res.StatusCode == http.StatusOK {
		d := json.NewDecoder(res.Body)

		var container Container
		if err = d.Decode(&container); err != nil {
			log.Fatal(err)
		}
		return &container
	}
	return nil
}

func notify(container *Container) {
	settings := container.NetworkSettings

	if settings != nil && settings.PortMapping != nil {
		// I only care about Tcp ports but you can also view Udp mappings
		if ports, ok := settings.PortMapping["Tcp"]; ok {

			log.Printf("Ip address allocated for: %s", container.Id)

			// Log the public and private port mappings
			for privatePort, publicPort := range ports {
				// I am just writing to stdout but you can use this information to update hipache, redis, etc...
				log.Printf("%s -> %s", privatePort, publicPort)
			}
		}
	}
}

func main() {
	c := http.Client{}
	res, err := c.Get("http://localhost:4243/events")
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()

	// Read the streaming json from the events endpoint
	// http://docs.docker.io/en/latest/api/docker_remote_api_v1.3/#monitor-docker-s-events
	d := json.NewDecoder(res.Body)
	for {
		var event Event
		if err := d.Decode(&event); err != nil {
			if err == io.EOF {
				break
			}
			log.Fatal(err)
		}
		if event.Status == "start" {
			// We only want to inspect the container if it has started
			if container := inspectContainer(event.Id, c); container != nil {
				notify(container)
			}
		}
	}
}
<commit_msg>dump events; add supervisor/kato config<commit_after>package main

import (
	"encoding/json"
	"io"
	"log"
	"net/http"
)

type Event struct {
	Id     string `json:"id"`
	Status string `json:"status"`
	From   string `json:"from"`
	Time   int64  `json:"time"`
}

func main() {
	c := http.Client{}
	res, err := c.Get("http://localhost:4243/events")
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()

	// Read the streaming json from the events endpoint
	// http://docs.docker.io/en/latest/api/docker_remote_api_v1.3/#monitor-docker-s-events
	d := json.NewDecoder(res.Body)
	for {
		var event Event
		if err := d.Decode(&event); err != nil {
			if err == io.EOF {
				break
			}
			log.Fatal(err)
		}
		log.Printf("Event: %+v", event)
	}
}
<|endoftext|>


<commit_before>package resource

type ComponentType int

// ComponentPackageMap is an alias to a map[string]string. It is used to store
// component names to relate to which package they belong to. For example
// the instantclient package consists of: instantclient-sqlplus, instantclient-basic
// etc. So I want a reference to know which component belongs to which package
// to aid in determining which package to pull from
type ComponentPackageMap map[string]ComponentType

const (
	APEX ComponentType = iota
	DB
	INSTANT_CLIENT
	JAVA
	ORDS
	SQLCL
	SQLDEV
)

var (
	ComponentMap = ComponentPackageMap{
		"apex": APEX,
		"db":   DB,
		"instantclient-basic":      INSTANT_CLIENT,
		"instantclient-basic-lite": INSTANT_CLIENT,
		"instantclient-jdbc":       INSTANT_CLIENT,
		"instantclient-odbc":       INSTANT_CLIENT,
		"instantclient-sdk":        INSTANT_CLIENT,
		"instantclient-sqlplus":    INSTANT_CLIENT,
		"instantclient-wrc":        INSTANT_CLIENT,
		"java-jdk":                 JAVA,
		"java-jre":                 JAVA,
		"ords":                     ORDS,
		"sqlcl":                    SQLCL,
		"sqldev":                   SQLDEV,
	}
)
<commit_msg>Add sqldev-jdk to component map<commit_after>package resource

type ComponentType int

// ComponentPackageMap is an alias to a map[string]string. It is used to store
// component names to relate to which package they belong to. For example
// the instantclient package consists of: instantclient-sqlplus, instantclient-basic
// etc. So I want a reference to know which component belongs to which package
// to aid in determining which package to pull from
type ComponentPackageMap map[string]ComponentType

const (
	APEX ComponentType = iota
	DB
	INSTANT_CLIENT
	JAVA
	ORDS
	SQLCL
	SQLDEV
)

var (
	ComponentMap = ComponentPackageMap{
		"apex": APEX,
		"db":   DB,
		"instantclient-basic":      INSTANT_CLIENT,
		"instantclient-basic-lite": INSTANT_CLIENT,
		"instantclient-jdbc":       INSTANT_CLIENT,
		"instantclient-odbc":       INSTANT_CLIENT,
		"instantclient-sdk":        INSTANT_CLIENT,
		"instantclient-sqlplus":    INSTANT_CLIENT,
		"instantclient-wrc":        INSTANT_CLIENT,
		"java-jdk":                 JAVA,
		"java-jre":                 JAVA,
		"ords":                     ORDS,
		"sqlcl":                    SQLCL,
		"sqldev":                   SQLDEV,
		"sqldev-jdk":               SQLDEV,
	}
)
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/go-ini/ini"
	"os"
	"path/filepath"
	"strings"
	"encoding/json"
)

var usage string = `
A tool designed to compare Zend Server's "etc" directories - all .ini files, directive by directive.

Usage:
       comparETC <output mode> <old etc path> <new etc path>

"output mode" should be one of:
  help            - print these usage instructions
  human           - human-readable output
  csv             - CSV output with field names
  json            - JSON output
  html            - HTML output
  `
var result string
var iniObject *ini.File

func main() {
	flag.Parse()
	mode := flag.Arg(0)

	switch mode {
	default:
		result = usage
	case "human":
		result = produceHuman()
	case "json":
		result = produceJson()
	case "html":
		result = produceHtml()
	case "csv":
		result = produceCsv()
	}
	fmt.Printf("%s\n", result)

}

func iniList(path string, f os.FileInfo, err error) error {
	if strings.HasSuffix(path, ".ini") {
		_ = iniObject.Append(path)
	}
	return nil
}

func mergeStringMaps(m1 map[string]string, m2 map[string]string) map[string]string {
	for key, value := range m2 {
		m1[key] = value
	}
	return m1
}

func getDirectories () (string, string) {
	old := flag.Arg(1)
	new := flag.Arg(2)
	return old, new
}

func parseDir (dir string) map[string]string {
	iniObject = ini.Empty()
	_ = filepath.Walk(dir, iniList)
	sections := iniObject.SectionStrings()
	KVpairs := make(map[string]string, 1000)
	for _, section := range sections {
		KVpairs = mergeStringMaps(KVpairs, iniObject.Section(section).KeysHash())
	}
	return KVpairs
}

func getDiff () map[string]map[string]string {
	oldEtc, newEtc := getDirectories()
	var oldCfg = make(map[string]string, 1000)
	var newCfg = make(map[string]string, 1000)
	var diffCfg = make(map[string]map[string]string, 1000)

	oldCfg = parseDir(oldEtc)
	newCfg = parseDir(newEtc)

	for kN, vN := range newCfg {
		if vO, ok := oldCfg[kN]; ok {
			// directive from New exists in Old
			if vO != vN {
				diffCfg[kN] = map[string]string{"old":vO, "new":vN}
			}
			delete(oldCfg, kN)

		} else {
			// directive from New doesn't exist in Old
			diffCfg[kN] = map[string]string{"old":"__undefined__", "new":vN}
		}
	}

	// directives from Old that don't exist in New
	for kO, vO := range oldCfg {
		diffCfg[kO] = map[string]string{"old":vO, "new":"__undefined__"}
	}

	return diffCfg
}

func produceHuman() string {
	data := getDiff()
	var formatted string
	for key, values := range data {
		o, _ := values["old"]
		n, _ := values["new"]
		formatted = formatted + fmt.Sprintf("%s:\n      old:  %s\n      new:  %s\n\n", key, o, n)
	}
	return formatted
}
func produceJson() string {
	data := getDiff()
	jList, _ := json.Marshal(data)
	return fmt.Sprintf("%s", jList)
}
func produceHtml() string {
	//fmt.Printf("|%-60s|%-6s|\n", "foo00000000000", "b")
	return "Not implemented yet"
}
func produceCsv() string {
	data := getDiff()
	var formatted string = "\"directive\",\"old_value\",\"new_value\"\n"
	for key, values := range data {
		o, _ := values["old"]
		n, _ := values["new"]
		formatted = formatted + fmt.Sprintf("\"%s\",\"%s\",\"%s\"\n", key, o, n)
	}
	return formatted
}
<commit_msg>Update zs-etcCompare.go<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/go-ini/ini"
	"os"
	"path/filepath"
	"strings"
	"encoding/json"
)

var usage string = `
A tool designed to compare Zend Server's "etc" directories - all .ini files, directive by directive.

Usage:
       zs-etcCompare <output mode> <old etc path> <new etc path>

"output mode" should be one of:
  help            - print these usage instructions
  human           - human-readable output
  csv             - CSV output with field names
  json            - JSON output
  html            - HTML output
  `
var result string
var iniObject *ini.File

func main() {
	flag.Parse()
	mode := flag.Arg(0)

	switch mode {
	default:
		result = usage
	case "human":
		result = produceHuman()
	case "json":
		result = produceJson()
	case "html":
		result = produceHtml()
	case "csv":
		result = produceCsv()
	}
	fmt.Printf("%s\n", result)

}

func iniList(path string, f os.FileInfo, err error) error {
	if strings.HasSuffix(path, ".ini") {
		_ = iniObject.Append(path)
	}
	return nil
}

func mergeStringMaps(m1 map[string]string, m2 map[string]string) map[string]string {
	for key, value := range m2 {
		m1[key] = value
	}
	return m1
}

func getDirectories () (string, string) {
	old := flag.Arg(1)
	new := flag.Arg(2)
	return old, new
}

func parseDir (dir string) map[string]string {
	iniObject = ini.Empty()
	_ = filepath.Walk(dir, iniList)
	sections := iniObject.SectionStrings()
	KVpairs := make(map[string]string, 1000)
	for _, section := range sections {
		KVpairs = mergeStringMaps(KVpairs, iniObject.Section(section).KeysHash())
	}
	return KVpairs
}

func getDiff () map[string]map[string]string {
	oldEtc, newEtc := getDirectories()
	var oldCfg = make(map[string]string, 1000)
	var newCfg = make(map[string]string, 1000)
	var diffCfg = make(map[string]map[string]string, 1000)

	oldCfg = parseDir(oldEtc)
	newCfg = parseDir(newEtc)

	for kN, vN := range newCfg {
		if vO, ok := oldCfg[kN]; ok {
			// directive from New exists in Old
			if vO != vN {
				diffCfg[kN] = map[string]string{"old":vO, "new":vN}
			}
			delete(oldCfg, kN)

		} else {
			// directive from New doesn't exist in Old
			diffCfg[kN] = map[string]string{"old":"__undefined__", "new":vN}
		}
	}

	// directives from Old that don't exist in New
	for kO, vO := range oldCfg {
		diffCfg[kO] = map[string]string{"old":vO, "new":"__undefined__"}
	}

	return diffCfg
}

func produceHuman() string {
	data := getDiff()
	var formatted string
	for key, values := range data {
		o, _ := values["old"]
		n, _ := values["new"]
		formatted = formatted + fmt.Sprintf("%s:\n      old:  %s\n      new:  %s\n\n", key, o, n)
	}
	return formatted
}
func produceJson() string {
	data := getDiff()
	jList, _ := json.Marshal(data)
	return fmt.Sprintf("%s", jList)
}
func produceHtml() string {
	//fmt.Printf("|%-60s|%-6s|\n", "foo00000000000", "b")
	return "Not implemented yet"
}
func produceCsv() string {
	data := getDiff()
	var formatted string = "\"directive\",\"old_value\",\"new_value\"\n"
	for key, values := range data {
		o, _ := values["old"]
		n, _ := values["new"]
		formatted = formatted + fmt.Sprintf("\"%s\",\"%s\",\"%s\"\n", key, o, n)
	}
	return formatted
}
<|endoftext|>


<commit_before>package sshbytestream

import (
	"fmt"
	"github.com/zrepl/zrepl/util"
	"io"
	"os"
)

type Error struct {
	Stderr  []byte
	WaitErr error
}

func (e Error) Error() string {
	return fmt.Sprintf("ssh command failed with error: %v. stderr:\n%s\n", e.WaitErr, e.Stderr)
}

type SSHTransport struct {
	Host         string
	User         string
	Port         uint16
	IdentityFile string
	SSHCommand   string
	Options      []string
}

var SSHCommand string = "ssh"

func Incoming() (wc io.ReadWriteCloser, err error) {
	// derivce ReadWriteCloser from stdin & stdout
	return IncomingReadWriteCloser{}, nil
}

type IncomingReadWriteCloser struct{}

func (f IncomingReadWriteCloser) Read(p []byte) (n int, err error) {
	return os.Stdin.Read(p)
}

func (f IncomingReadWriteCloser) Write(p []byte) (n int, err error) {
	return os.Stdout.Write(p)
}

func (f IncomingReadWriteCloser) Close() (err error) {
	if err = os.Stdin.Close(); err != nil {
		return
	}
	if err = os.Stdout.Close(); err != nil {
		return
	}
	return
}

func Outgoing(remote SSHTransport) (c *util.IOCommand, err error) {

	sshArgs := make([]string, 0, 2*len(remote.Options)+4)
	sshArgs = append(sshArgs,
		"-p", fmt.Sprintf("%d", remote.Port),
		"-q",
		"-i", remote.IdentityFile,
		"-o", "BatchMode=yes",
	)
	for _, option := range remote.Options {
		sshArgs = append(sshArgs, "-o", option)
	}
	sshArgs = append(sshArgs, fmt.Sprintf("%s@%s", remote.User, remote.Host))

	var sshCommand = SSHCommand
	if len(remote.SSHCommand) > 0 {
		sshCommand = SSHCommand
	}

	if c, err = util.NewIOCommand(sshCommand, sshArgs, util.IOCommandStderrBufSize); err != nil {
		return
	}

	// Clear environment of cmd, ssh shall not rely on SSH_AUTH_SOCK, etc.
	c.Cmd.Env = []string{}

	err = c.Start()
	return
}
<commit_msg>sshbytestream: default ServerAliveInterval<commit_after>package sshbytestream

import (
	"fmt"
	"github.com/zrepl/zrepl/util"
	"io"
	"os"
)

type Error struct {
	Stderr  []byte
	WaitErr error
}

func (e Error) Error() string {
	return fmt.Sprintf("ssh command failed with error: %v. stderr:\n%s\n", e.WaitErr, e.Stderr)
}

type SSHTransport struct {
	Host         string
	User         string
	Port         uint16
	IdentityFile string
	SSHCommand   string
	Options      []string
}

var SSHCommand string = "ssh"
var SSHServerAliveInterval uint = 60

func Incoming() (wc io.ReadWriteCloser, err error) {
	// derivce ReadWriteCloser from stdin & stdout
	return IncomingReadWriteCloser{}, nil
}

type IncomingReadWriteCloser struct{}

func (f IncomingReadWriteCloser) Read(p []byte) (n int, err error) {
	return os.Stdin.Read(p)
}

func (f IncomingReadWriteCloser) Write(p []byte) (n int, err error) {
	return os.Stdout.Write(p)
}

func (f IncomingReadWriteCloser) Close() (err error) {
	if err = os.Stdin.Close(); err != nil {
		return
	}
	if err = os.Stdout.Close(); err != nil {
		return
	}
	return
}

func Outgoing(remote SSHTransport) (c *util.IOCommand, err error) {

	sshArgs := make([]string, 0, 2*len(remote.Options)+4)
	sshArgs = append(sshArgs,
		"-p", fmt.Sprintf("%d", remote.Port),
		"-q",
		"-i", remote.IdentityFile,
		"-o", "BatchMode=yes",
		"-o", fmt.Sprintf("ServerAliveInterval=%d", SSHServerAliveInterval),
	)
	for _, option := range remote.Options {
		sshArgs = append(sshArgs, "-o", option)
	}
	sshArgs = append(sshArgs, fmt.Sprintf("%s@%s", remote.User, remote.Host))

	var sshCommand = SSHCommand
	if len(remote.SSHCommand) > 0 {
		sshCommand = SSHCommand
	}

	if c, err = util.NewIOCommand(sshCommand, sshArgs, util.IOCommandStderrBufSize); err != nil {
		return
	}

	// Clear environment of cmd, ssh shall not rely on SSH_AUTH_SOCK, etc.
	c.Cmd.Env = []string{}

	err = c.Start()
	return
}
<|endoftext|>


<commit_before>// Copyright 2015 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package stack

import (
	"bytes"
	"fmt"
	"go/ast"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"

	"github.com/maruel/ut"
)

func overrideEnv(env []string, key, value string) []string {
	prefix := key + "="
	for i, e := range env {
		if strings.HasPrefix(e, prefix) {
			env[i] = value
			return env
		}
	}
	return append(env, prefix+value)
}

func getCrash(t *testing.T, content string) (string, []byte) {
	name, err := ioutil.TempDir("", "panicparse")
	ut.AssertEqual(t, nil, err)
	defer os.RemoveAll(name)
	main := filepath.Join(name, "main.go")
	ut.AssertEqual(t, nil, ioutil.WriteFile(main, []byte(content), 0500))
	cmd := exec.Command("go", "run", main)
	// Use the Go 1.4 compatible format.
	cmd.Env = overrideEnv(os.Environ(), "GOTRACEBACK", "2")
	out, _ := cmd.CombinedOutput()
	return main, out
}

func TestAugment(t *testing.T) {
	extra := &bytes.Buffer{}
	main, content := getCrash(t, mainSource)
	goroutines, err := ParseDump(bytes.NewBuffer(content), extra)
	ut.AssertEqual(t, nil, err)
	// On go1.4, there's one less space.
	actual := extra.String()
	if actual != "panic: ooh\n\nexit status 2\n" && actual != "panic: ooh\nexit status 2\n" {
		t.Fatalf("Unexpected panic output:\n%#v", actual)
	}
	// The number of goroutine alive depends on the runtime environment. It
	// doesn't matter as only the crashing thread is of importance.
	ut.AssertEqual(t, true, len(goroutines) >= 1)

	// Preload content so no disk I/O is done.
	c := &cache{files: map[string][]byte{main: []byte(mainSource)}}
	c.augmentGoroutine(&goroutines[0])
	pointer := uint64(0xfffffffff)
	pointerStr := fmt.Sprintf("0x%x", pointer)
	expected := Stack{
		Calls: []Call{
			{
				SourcePath: filepath.Join(goroot, "src", "runtime", "panic.go"),
				Func:       Function{"panic"},
				Args:       Args{Values: []Arg{{Value: pointer}, {Value: pointer}}},
			},
			{
				Func: Function{"main.S.f1"},
			},
			{
				Func: Function{"main.(*S).f2"},
				Args: Args{
					Values:    []Arg{{Value: pointer}},
					Processed: []string{"*S(" + pointerStr + ")"},
				},
			},
			{
				Func: Function{"main.f3"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 3}, {Value: 1}},
					Processed: []string{"string(" + pointerStr + ", len=3)", "1"},
				},
			},
			{
				Func: Function{"main.f4"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 3}},
					Processed: []string{"string(" + pointerStr + ", len=3)"},
				},
			},
			{
				Func: Function{"main.f5"},
				Args: Args{
					Values:    []Arg{{}, {}, {}, {}, {}, {}, {}, {}, {}, {}},
					Processed: []string{"0", "0", "0", "0", "0", "0", "0", "0", "0", "interface{}(0x0)"},
					Elided:    true,
				},
			},
			{
				Func: Function{"main.f6"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: pointer}},
					Processed: []string{"error(" + pointerStr + ")"},
				},
			},
			{
				Func: Function{"main.f7"},
				Args: Args{
					Values:    []Arg{{}, {}},
					Processed: []string{"error(0x0)"},
				},
			},
			{
				Func: Function{"main.f8"},
				Args: Args{
					Values:    []Arg{{Value: 0x3fe0000000000000}, {Value: 0x40000000}},
					Processed: []string{"0.5", "2"},
				},
			},
			{
				Func: Function{"main.f9"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 5}, {Value: 7}},
					Processed: []string{"[]int(" + pointerStr + " len=5 cap=7)"},
				},
			},
			{
				Func: Function{"main.f10"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 5}, {Value: 7}},
					Processed: []string{"[]interface{}(" + pointerStr + " len=5 cap=7)"},
				},
			},
			{
				Func: Function{"main.f11"},
				Args: Args{
					Values:    []Arg{{}},
					Processed: []string{"func(0x0)"},
				},
			},
			{
				Func: Function{"main.f12"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 2}, {Value: 2}},
					Processed: []string{"func(" + pointerStr + ")", "func(0x2)"},
				},
			},
			{
				Func: Function{"main.f13"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 2}},
					Processed: []string{"string(" + pointerStr + ", len=2)"},
				},
			},
			{
				Func: Function{"main.main"},
			},
		},
	}
	s := goroutines[0].Signature.Stack
	// On Travis, runtime.GOROOT() != what is dumped when running a command via
	// "go run". E.g. GOROOT() were "/usr/local/go" yet the path output via a
	// subcommand is "/home/travis/.gimme/versions/go1.4.linux.amd64". Kidding
	// me, right?
	ut.AssertEqual(t, true, strings.HasSuffix(s.Calls[0].SourcePath, "panic.go"))
	s.Calls[0].SourcePath = expected.Calls[0].SourcePath
	// Zap out the panic() call, since its signature changed between go1.4 and
	// go1.5, it used to be runtime.gopanic().
	ut.AssertEqual(t, true, strings.HasSuffix(s.Calls[0].Func.Raw, "panic"))
	s.Calls[0].Func = expected.Calls[0].Func

	// Zap out pointers.
	for i := range s.Calls {
		if i >= len(expected.Calls) {
			// When using GOTRACEBACK=2, it'll include runtime.main() and
			// runtime.goexit(). Ignore these since they could be changed in a future
			// version.
			s.Calls = s.Calls[:len(expected.Calls)]
			break
		}
		if i > 0 {
			ut.AssertEqual(t, true, s.Calls[i].Line > s.Calls[i-1].Line)
		}
		s.Calls[i].Line = 0
		for j := range s.Calls[i].Args.Values {
			if j >= len(expected.Calls[i].Args.Values) {
				break
			}
			if expected.Calls[i].Args.Values[j].Value == pointer {
				// Replace the pointer value.
				ut.AssertEqual(t, false, s.Calls[i].Args.Values[j].Value == 0)
				old := fmt.Sprintf("0x%x", s.Calls[i].Args.Values[j].Value)
				s.Calls[i].Args.Values[j].Value = pointer
				for k := range s.Calls[i].Args.Processed {
					s.Calls[i].Args.Processed[k] = strings.Replace(s.Calls[i].Args.Processed[k], old, pointerStr, -1)
				}
			}
		}
		if expected.Calls[i].SourcePath == "" {
			expected.Calls[i].SourcePath = main
		}
	}
	// Zap out panic() exact line number.
	s.Calls[0].Line = 0
	ut.AssertEqual(t, expected, s)
}

func TestAugmentDummy(t *testing.T) {
	goroutines := []Goroutine{
		{
			Signature: Signature{
				Stack: Stack{
					Calls: []Call{{SourcePath: "missing.go"}},
				},
			},
		},
	}
	Augment(goroutines)
}

func TestLoad(t *testing.T) {
	c := &cache{
		files:  map[string][]byte{"bad.go": []byte("bad content")},
		parsed: map[string]*parsedFile{},
	}
	c.load("foo.asm")
	c.load("bad.go")
	c.load("doesnt_exist.go")
	ut.AssertEqual(t, 3, len(c.parsed))
	ut.AssertEqual(t, (*parsedFile)(nil), c.parsed["foo.asm"])
	ut.AssertEqual(t, (*parsedFile)(nil), c.parsed["bad.go"])
	ut.AssertEqual(t, (*parsedFile)(nil), c.parsed["doesnt_exist.go"])
	ut.AssertEqual(t, (*ast.FuncDecl)(nil), c.getFuncAST(&Call{SourcePath: "other"}))
}

const mainSource = `// Exercises most code paths in processCall().

package main

import "errors"

type S struct {
}

func (s S) f1() {
	panic("ooh")
}

func (s *S) f2() {
	s.f1()
}

func f3(s string, i int) {
	(&S{}).f2()
}

func f4(s string) {
	f3(s, 1)
}

func f5(s1, s2, s3, s4, s5, s6, s7, s8, s9 int, s10 interface{}) {
	f4("ooh")
}

func f6(err error) {
	f5(0, 0, 0, 0, 0, 0, 0, 0, 0, nil)
}

func f7(error) {
	f6(errors.New("Ooh"))
}

func f8(a float64, b float32) {
	f7(nil)
}

func f9(a []int) {
	f8(0.5, 2)
}

func f10(a []interface{}) {
	f9(make([]int, 5, 7))
}

func f11(a func()) {
	f10(make([]interface{}, 5, 7))
}

func f12(a ...func()) {
	f11(nil)
}

func f13(s string) {
	// This asserts that a local function definition is not picked up by accident.
	a := func(i int) int {
		return 1 + i
	}
	_ = a(3)
	f12(nil, nil)
}

func main() {
	f13("yo")
}
`
<commit_msg>Change the floating point value in expectation.<commit_after>// Copyright 2015 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package stack

import (
	"bytes"
	"fmt"
	"go/ast"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"

	"github.com/maruel/ut"
)

func overrideEnv(env []string, key, value string) []string {
	prefix := key + "="
	for i, e := range env {
		if strings.HasPrefix(e, prefix) {
			env[i] = value
			return env
		}
	}
	return append(env, prefix+value)
}

func getCrash(t *testing.T, content string) (string, []byte) {
	name, err := ioutil.TempDir("", "panicparse")
	ut.AssertEqual(t, nil, err)
	defer os.RemoveAll(name)
	main := filepath.Join(name, "main.go")
	ut.AssertEqual(t, nil, ioutil.WriteFile(main, []byte(content), 0500))
	cmd := exec.Command("go", "run", main)
	// Use the Go 1.4 compatible format.
	cmd.Env = overrideEnv(os.Environ(), "GOTRACEBACK", "2")
	out, _ := cmd.CombinedOutput()
	return main, out
}

func TestAugment(t *testing.T) {
	extra := &bytes.Buffer{}
	main, content := getCrash(t, mainSource)
	goroutines, err := ParseDump(bytes.NewBuffer(content), extra)
	ut.AssertEqual(t, nil, err)
	// On go1.4, there's one less space.
	actual := extra.String()
	if actual != "panic: ooh\n\nexit status 2\n" && actual != "panic: ooh\nexit status 2\n" {
		t.Fatalf("Unexpected panic output:\n%#v", actual)
	}
	// The number of goroutine alive depends on the runtime environment. It
	// doesn't matter as only the crashing thread is of importance.
	ut.AssertEqual(t, true, len(goroutines) >= 1)

	// Preload content so no disk I/O is done.
	c := &cache{files: map[string][]byte{main: []byte(mainSource)}}
	c.augmentGoroutine(&goroutines[0])
	pointer := uint64(0xfffffffff)
	pointerStr := fmt.Sprintf("0x%x", pointer)
	expected := Stack{
		Calls: []Call{
			{
				SourcePath: filepath.Join(goroot, "src", "runtime", "panic.go"),
				Func:       Function{"panic"},
				Args:       Args{Values: []Arg{{Value: pointer}, {Value: pointer}}},
			},
			{
				Func: Function{"main.S.f1"},
			},
			{
				Func: Function{"main.(*S).f2"},
				Args: Args{
					Values:    []Arg{{Value: pointer}},
					Processed: []string{"*S(" + pointerStr + ")"},
				},
			},
			{
				Func: Function{"main.f3"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 3}, {Value: 1}},
					Processed: []string{"string(" + pointerStr + ", len=3)", "1"},
				},
			},
			{
				Func: Function{"main.f4"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 3}},
					Processed: []string{"string(" + pointerStr + ", len=3)"},
				},
			},
			{
				Func: Function{"main.f5"},
				Args: Args{
					Values:    []Arg{{}, {}, {}, {}, {}, {}, {}, {}, {}, {}},
					Processed: []string{"0", "0", "0", "0", "0", "0", "0", "0", "0", "interface{}(0x0)"},
					Elided:    true,
				},
			},
			{
				Func: Function{"main.f6"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: pointer}},
					Processed: []string{"error(" + pointerStr + ")"},
				},
			},
			{
				Func: Function{"main.f7"},
				Args: Args{
					Values:    []Arg{{}, {}},
					Processed: []string{"error(0x0)"},
				},
			},
			{
				Func: Function{"main.f8"},
				Args: Args{
					Values:    []Arg{{Value: 0x3fe0000000000000}, {Value: 0xc440066666}},
					Processed: []string{"0.5", "2.1"},
				},
			},
			{
				Func: Function{"main.f9"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 5}, {Value: 7}},
					Processed: []string{"[]int(" + pointerStr + " len=5 cap=7)"},
				},
			},
			{
				Func: Function{"main.f10"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 5}, {Value: 7}},
					Processed: []string{"[]interface{}(" + pointerStr + " len=5 cap=7)"},
				},
			},
			{
				Func: Function{"main.f11"},
				Args: Args{
					Values:    []Arg{{}},
					Processed: []string{"func(0x0)"},
				},
			},
			{
				Func: Function{"main.f12"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 2}, {Value: 2}},
					Processed: []string{"func(" + pointerStr + ")", "func(0x2)"},
				},
			},
			{
				Func: Function{"main.f13"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 2}},
					Processed: []string{"string(" + pointerStr + ", len=2)"},
				},
			},
			{
				Func: Function{"main.main"},
			},
		},
	}
	s := goroutines[0].Signature.Stack
	// On Travis, runtime.GOROOT() != what is dumped when running a command via
	// "go run". E.g. GOROOT() were "/usr/local/go" yet the path output via a
	// subcommand is "/home/travis/.gimme/versions/go1.4.linux.amd64". Kidding
	// me, right?
	ut.AssertEqual(t, true, strings.HasSuffix(s.Calls[0].SourcePath, "panic.go"))
	s.Calls[0].SourcePath = expected.Calls[0].SourcePath
	// Zap out the panic() call, since its signature changed between go1.4 and
	// go1.5, it used to be runtime.gopanic().
	ut.AssertEqual(t, true, strings.HasSuffix(s.Calls[0].Func.Raw, "panic"))
	s.Calls[0].Func = expected.Calls[0].Func

	// Zap out pointers.
	for i := range s.Calls {
		if i >= len(expected.Calls) {
			// When using GOTRACEBACK=2, it'll include runtime.main() and
			// runtime.goexit(). Ignore these since they could be changed in a future
			// version.
			s.Calls = s.Calls[:len(expected.Calls)]
			break
		}
		if i > 0 {
			ut.AssertEqual(t, true, s.Calls[i].Line > s.Calls[i-1].Line)
		}
		s.Calls[i].Line = 0
		for j := range s.Calls[i].Args.Values {
			if j >= len(expected.Calls[i].Args.Values) {
				break
			}
			if expected.Calls[i].Args.Values[j].Value == pointer {
				// Replace the pointer value.
				ut.AssertEqual(t, false, s.Calls[i].Args.Values[j].Value == 0)
				old := fmt.Sprintf("0x%x", s.Calls[i].Args.Values[j].Value)
				s.Calls[i].Args.Values[j].Value = pointer
				for k := range s.Calls[i].Args.Processed {
					s.Calls[i].Args.Processed[k] = strings.Replace(s.Calls[i].Args.Processed[k], old, pointerStr, -1)
				}
			}
		}
		if expected.Calls[i].SourcePath == "" {
			expected.Calls[i].SourcePath = main
		}
	}
	// Zap out panic() exact line number.
	s.Calls[0].Line = 0
	ut.AssertEqual(t, expected, s)
}

func TestAugmentDummy(t *testing.T) {
	goroutines := []Goroutine{
		{
			Signature: Signature{
				Stack: Stack{
					Calls: []Call{{SourcePath: "missing.go"}},
				},
			},
		},
	}
	Augment(goroutines)
}

func TestLoad(t *testing.T) {
	c := &cache{
		files:  map[string][]byte{"bad.go": []byte("bad content")},
		parsed: map[string]*parsedFile{},
	}
	c.load("foo.asm")
	c.load("bad.go")
	c.load("doesnt_exist.go")
	ut.AssertEqual(t, 3, len(c.parsed))
	ut.AssertEqual(t, (*parsedFile)(nil), c.parsed["foo.asm"])
	ut.AssertEqual(t, (*parsedFile)(nil), c.parsed["bad.go"])
	ut.AssertEqual(t, (*parsedFile)(nil), c.parsed["doesnt_exist.go"])
	ut.AssertEqual(t, (*ast.FuncDecl)(nil), c.getFuncAST(&Call{SourcePath: "other"}))
}

const mainSource = `// Exercises most code paths in processCall().

package main

import "errors"

type S struct {
}

func (s S) f1() {
	panic("ooh")
}

func (s *S) f2() {
	s.f1()
}

func f3(s string, i int) {
	(&S{}).f2()
}

func f4(s string) {
	f3(s, 1)
}

func f5(s1, s2, s3, s4, s5, s6, s7, s8, s9 int, s10 interface{}) {
	f4("ooh")
}

func f6(err error) {
	f5(0, 0, 0, 0, 0, 0, 0, 0, 0, nil)
}

func f7(error) {
	f6(errors.New("Ooh"))
}

func f8(a float64, b float32) {
	f7(nil)
}

func f9(a []int) {
	f8(0.5, 2.1)
}

func f10(a []interface{}) {
	f9(make([]int, 5, 7))
}

func f11(a func()) {
	f10(make([]interface{}, 5, 7))
}

func f12(a ...func()) {
	f11(nil)
}

func f13(s string) {
	// This asserts that a local function definition is not picked up by accident.
	a := func(i int) int {
		return 1 + i
	}
	_ = a(3)
	f12(nil, nil)
}

func main() {
	f13("yo")
}
`
<|endoftext|>


<commit_before>/*
Copyright 2021 The Kubernetes Authors.

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

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

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

package server

/*
We make an attempt here to identify the events that take place during
lifecycle of the apiserver.

We also identify each event with a name so we can refer to it.

Events:
- ShutdownInitiated: KILL signal received
- AfterShutdownDelayDuration: shutdown delay duration has passed
- InFlightRequestsDrained: all in flight request(s) have been drained
- HasBeenReady is signaled when the readyz endpoint succeeds for the first time

The following is a sequence of shutdown events that we expect to see with
  'ShutdownSendRetryAfter' = false:

T0: ShutdownInitiated: KILL signal received
	- /readyz starts returning red
    - run pre shutdown hooks

T0+70s: AfterShutdownDelayDuration: shutdown delay duration has passed
	- the default value of 'ShutdownDelayDuration' is '70s'
	- it's time to initiate shutdown of the HTTP Server, server.Shutdown is invoked
	- as a consequene, the Close function has is called for all listeners
 	- the HTTP Server stops listening immediately
	- any new request arriving on a new TCP socket is denied with
      a network error similar to 'connection refused'
    - the HTTP Server waits gracefully for existing requests to complete
      up to '60s' (dictated by ShutdownTimeout)
	- active long running requests will receive a GOAWAY.

T0+70s: HTTPServerStoppedListening:
	- this event is signaled when the HTTP Server has stopped listening
      which is immediately after server.Shutdown has been invoked

T0 + 70s + up-to 60s: InFlightRequestsDrained: existing in flight requests have been drained
	- long running requests are outside of this scope
	- up-to 60s: the default value of 'ShutdownTimeout' is 60s, this means that
      any request in flight has a hard timeout of 60s.
	- it's time to call 'Shutdown' on the audit events since all
	  in flight request(s) have drained.

The following is a sequence of shutdown events that we expect to see with
  'ShutdownSendRetryAfter' = true:

T0: ShutdownInitiated: KILL signal received
	- /readyz starts returning red
    - run pre shutdown hooks

T0+70s: AfterShutdownDelayDuration: shutdown delay duration has passed
	- the default value of 'ShutdownDelayDuration' is '70s'
	- the HTTP Server will continue to listen
	- the apiserver is not accepting new request(s)
		- it includes new request(s) on a new or an existing TCP connection
		- new request(s) arriving after this point are replied with a 429
      	  and the  response headers: 'Retry-After: 1` and 'Connection: close'
	- note: these new request(s) will not show up in audit logs

T0 + 70s + up to 60s: InFlightRequestsDrained: existing in flight requests have been drained
	- long running requests are outside of this scope
	- up to 60s: the default value of 'ShutdownTimeout' is 60s, this means that
      any request in flight has a hard timeout of 60s.
	- server.Shutdown is called, the HTTP Server stops listening immediately
    - the HTTP Server waits gracefully for existing requests to complete
      up to '2s' (it's hard coded right now)
*/

// lifecycleSignal encapsulates a named apiserver event
type lifecycleSignal interface {
	// Signal signals the event, indicating that the event has occurred.
	// Signal is idempotent, once signaled the event stays signaled and
	// it immediately unblocks any goroutine waiting for this event.
	Signal()

	// Signaled returns a channel that is closed when the underlying event
	// has been signaled. Successive calls to Signaled return the same value.
	Signaled() <-chan struct{}

	// Name returns the name of the signal, useful for logging.
	Name() string
}

// lifecycleSignals provides an abstraction of the events that
// transpire during the lifecycle of the apiserver. This abstraction makes it easy
// for us to write unit tests that can verify expected graceful termination behavior.
//
// GenericAPIServer can use these to either:
//  - signal that a particular termination event has transpired
//  - wait for a designated termination event to transpire and do some action.
type lifecycleSignals struct {
	// ShutdownInitiated event is signaled when an apiserver shutdown has been initiated.
	// It is signaled when the `stopCh` provided by the main goroutine
	// receives a KILL signal and is closed as a consequence.
	ShutdownInitiated lifecycleSignal

	// AfterShutdownDelayDuration event is signaled as soon as ShutdownDelayDuration
	// has elapsed since the ShutdownInitiated event.
	// ShutdownDelayDuration allows the apiserver to delay shutdown for some time.
	AfterShutdownDelayDuration lifecycleSignal

	// InFlightRequestsDrained event is signaled when the existing requests
	// in flight have completed. This is used as signal to shut down the audit backends
	InFlightRequestsDrained lifecycleSignal

	// HTTPServerStoppedListening termination event is signaled when the
	// HTTP Server has stopped listening to the underlying socket.
	HTTPServerStoppedListening lifecycleSignal

	// HasBeenReady is signaled when the readyz endpoint succeeds for the first time.
	HasBeenReady lifecycleSignal
}

// newLifecycleSignals returns an instance of lifecycleSignals interface to be used
// to coordinate lifecycle of the apiserver
func newLifecycleSignals() lifecycleSignals {
	return lifecycleSignals{
		ShutdownInitiated:          newNamedChannelWrapper("ShutdownInitiated"),
		AfterShutdownDelayDuration: newNamedChannelWrapper("AfterShutdownDelayDuration"),
		InFlightRequestsDrained:    newNamedChannelWrapper("InFlightRequestsDrained"),
		HTTPServerStoppedListening: newNamedChannelWrapper("HTTPServerStoppedListening"),
		HasBeenReady:               newNamedChannelWrapper("HasBeenReady"),
	}
}

func newNamedChannelWrapper(name string) lifecycleSignal {
	return &namedChannelWrapper{
		name: name,
		ch:   make(chan struct{}),
	}
}

type namedChannelWrapper struct {
	name string
	ch   chan struct{}
}

func (e *namedChannelWrapper) Signal() {
	select {
	case <-e.ch:
		// already closed, don't close again.
	default:
		close(e.ch)
	}
}

func (e *namedChannelWrapper) Signaled() <-chan struct{} {
	return e.ch
}

func (e *namedChannelWrapper) Name() string {
	return e.name
}
<commit_msg>make lifecycle signal thread safe<commit_after>/*
Copyright 2021 The Kubernetes Authors.

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

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

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

package server

import (
	"sync"
)

/*
We make an attempt here to identify the events that take place during
lifecycle of the apiserver.

We also identify each event with a name so we can refer to it.

Events:
- ShutdownInitiated: KILL signal received
- AfterShutdownDelayDuration: shutdown delay duration has passed
- InFlightRequestsDrained: all in flight request(s) have been drained
- HasBeenReady is signaled when the readyz endpoint succeeds for the first time

The following is a sequence of shutdown events that we expect to see with
  'ShutdownSendRetryAfter' = false:

T0: ShutdownInitiated: KILL signal received
	- /readyz starts returning red
    - run pre shutdown hooks

T0+70s: AfterShutdownDelayDuration: shutdown delay duration has passed
	- the default value of 'ShutdownDelayDuration' is '70s'
	- it's time to initiate shutdown of the HTTP Server, server.Shutdown is invoked
	- as a consequene, the Close function has is called for all listeners
 	- the HTTP Server stops listening immediately
	- any new request arriving on a new TCP socket is denied with
      a network error similar to 'connection refused'
    - the HTTP Server waits gracefully for existing requests to complete
      up to '60s' (dictated by ShutdownTimeout)
	- active long running requests will receive a GOAWAY.

T0+70s: HTTPServerStoppedListening:
	- this event is signaled when the HTTP Server has stopped listening
      which is immediately after server.Shutdown has been invoked

T0 + 70s + up-to 60s: InFlightRequestsDrained: existing in flight requests have been drained
	- long running requests are outside of this scope
	- up-to 60s: the default value of 'ShutdownTimeout' is 60s, this means that
      any request in flight has a hard timeout of 60s.
	- it's time to call 'Shutdown' on the audit events since all
	  in flight request(s) have drained.

The following is a sequence of shutdown events that we expect to see with
  'ShutdownSendRetryAfter' = true:

T0: ShutdownInitiated: KILL signal received
	- /readyz starts returning red
    - run pre shutdown hooks

T0+70s: AfterShutdownDelayDuration: shutdown delay duration has passed
	- the default value of 'ShutdownDelayDuration' is '70s'
	- the HTTP Server will continue to listen
	- the apiserver is not accepting new request(s)
		- it includes new request(s) on a new or an existing TCP connection
		- new request(s) arriving after this point are replied with a 429
      	  and the  response headers: 'Retry-After: 1` and 'Connection: close'
	- note: these new request(s) will not show up in audit logs

T0 + 70s + up to 60s: InFlightRequestsDrained: existing in flight requests have been drained
	- long running requests are outside of this scope
	- up to 60s: the default value of 'ShutdownTimeout' is 60s, this means that
      any request in flight has a hard timeout of 60s.
	- server.Shutdown is called, the HTTP Server stops listening immediately
    - the HTTP Server waits gracefully for existing requests to complete
      up to '2s' (it's hard coded right now)
*/

// lifecycleSignal encapsulates a named apiserver event
type lifecycleSignal interface {
	// Signal signals the event, indicating that the event has occurred.
	// Signal is idempotent, once signaled the event stays signaled and
	// it immediately unblocks any goroutine waiting for this event.
	Signal()

	// Signaled returns a channel that is closed when the underlying event
	// has been signaled. Successive calls to Signaled return the same value.
	Signaled() <-chan struct{}

	// Name returns the name of the signal, useful for logging.
	Name() string
}

// lifecycleSignals provides an abstraction of the events that
// transpire during the lifecycle of the apiserver. This abstraction makes it easy
// for us to write unit tests that can verify expected graceful termination behavior.
//
// GenericAPIServer can use these to either:
//  - signal that a particular termination event has transpired
//  - wait for a designated termination event to transpire and do some action.
type lifecycleSignals struct {
	// ShutdownInitiated event is signaled when an apiserver shutdown has been initiated.
	// It is signaled when the `stopCh` provided by the main goroutine
	// receives a KILL signal and is closed as a consequence.
	ShutdownInitiated lifecycleSignal

	// AfterShutdownDelayDuration event is signaled as soon as ShutdownDelayDuration
	// has elapsed since the ShutdownInitiated event.
	// ShutdownDelayDuration allows the apiserver to delay shutdown for some time.
	AfterShutdownDelayDuration lifecycleSignal

	// InFlightRequestsDrained event is signaled when the existing requests
	// in flight have completed. This is used as signal to shut down the audit backends
	InFlightRequestsDrained lifecycleSignal

	// HTTPServerStoppedListening termination event is signaled when the
	// HTTP Server has stopped listening to the underlying socket.
	HTTPServerStoppedListening lifecycleSignal

	// HasBeenReady is signaled when the readyz endpoint succeeds for the first time.
	HasBeenReady lifecycleSignal
}

// newLifecycleSignals returns an instance of lifecycleSignals interface to be used
// to coordinate lifecycle of the apiserver
func newLifecycleSignals() lifecycleSignals {
	return lifecycleSignals{
		ShutdownInitiated:          newNamedChannelWrapper("ShutdownInitiated"),
		AfterShutdownDelayDuration: newNamedChannelWrapper("AfterShutdownDelayDuration"),
		InFlightRequestsDrained:    newNamedChannelWrapper("InFlightRequestsDrained"),
		HTTPServerStoppedListening: newNamedChannelWrapper("HTTPServerStoppedListening"),
		HasBeenReady:               newNamedChannelWrapper("HasBeenReady"),
	}
}

func newNamedChannelWrapper(name string) lifecycleSignal {
	return &namedChannelWrapper{
		name: name,
		once: sync.Once{},
		ch:   make(chan struct{}),
	}
}

type namedChannelWrapper struct {
	name string
	once sync.Once
	ch   chan struct{}
}

func (e *namedChannelWrapper) Signal() {
	e.once.Do(func() {
		close(e.ch)
	})
}

func (e *namedChannelWrapper) Signaled() <-chan struct{} {
	return e.ch
}

func (e *namedChannelWrapper) Name() string {
	return e.name
}
<|endoftext|>