<commit_before>package slave

import (
	"fmt"
	"github.com/KIT-MAMID/mamid/msp"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	replSetStartup    = 0
	replSetPrimary    = 1
	replSetSecondary  = 2
	replSetRecovering = 3
	replSetUnknown    = 6
)

type replSetState int

type MongodConfigurator interface {
	MongodConfiguration(p msp.PortNumber) (msp.Mongod, *msp.Error)
	ApplyMongodConfiguration(m msp.Mongod) *msp.Error
}

type ConcreteMongodConfigurator struct {
	dial                      func(url string) (*mgo.Session, error)
	MongodSoftShutdownTimeout time.Duration
}

func (c *ConcreteMongodConfigurator) connect(port msp.PortNumber) (*mgo.Session, *msp.Error) {
	sess, err := c.dial(fmt.Sprintf("mongodb://127.0.0.1:%d/?connect=direct", port))

	/*
		mgo.SetDebug(true)

		var aLogger *log.Logger
		aLogger = log.New(os.Stderr, "", log.LstdFlags)
		mgo.SetLogger(aLogger)
	*/

	if err != nil {
		return nil, &msp.Error{
			Identifier:      msp.SlaveConnectMongodError,
			Description:     fmt.Sprintf("Establishing a connection to mongod instance on port %d failed", port),
			LongDescription: fmt.Sprintf("ConcreteMongodConfigurator.dial() failed with: %s", err),
		}
	}
	sess.SetMode(mgo.Monotonic, true)

	return sess, nil
}

func (c *ConcreteMongodConfigurator) fetchConfiguration(sess *mgo.Session, port msp.PortNumber) (msp.Mongod, *msp.Error, replSetState) {
	running := bson.M{}
	if err := sess.Run("isMaster", &running); err != nil {
		return msp.Mongod{}, &msp.Error{
			Identifier:      msp.SlaveGetMongodStatusError,
			Description:     fmt.Sprintf("Getting master information from mongod instance on port %d failed", port),
			LongDescription: fmt.Sprintf("mgo/Session.Run(\"isMaster\") failed with\n%s", err.Error()),
		}, replSetUnknown
	}

	if _, exists := running["setName"]; !exists {
		return msp.Mongod{
			Port:                    port,
			ReplicaSetName:          "",
			ReplicaSetMembers:       nil,
			ShardingConfigServer:    false,
			StatusError:             nil,
			LastEstablishStateError: nil,
			State: msp.MongodStateNotRunning,
		}, nil, replSetStartup
	}

	status := bson.M{}
	if err := sess.Run("replSetGetStatus", &status); err != nil {
		return msp.Mongod{}, &msp.Error{
			Identifier:      msp.SlaveGetMongodStatusError,
			Description:     fmt.Sprintf("Getting replica set status information from mongod instance on port %d failed", port),
			LongDescription: fmt.Sprintf("mgo/Session.Run(\"replSetGetStatus\") failed with\n%s", err.Error()),
		}, replSetUnknown
	}

	members := make([]msp.HostPort, len(status["members"].([]interface{})))
	for k, member := range status["members"].([]interface{}) {
		pair := strings.Split(member.(bson.M)["name"].(string), ":")
		remotePort, _ := strconv.Atoi(pair[1])
		members[k] = msp.HostPort{pair[0], msp.PortNumber(remotePort)}
	}

	var state msp.MongodState
	if replSetState(status["myState"].(int)) == replSetRecovering {
		state = msp.MongodStateRecovering
	} else {
		state = msp.MongodStateRunning
	}

	return msp.Mongod{
		Port:                    port,
		ReplicaSetName:          status["set"].(string),
		ReplicaSetMembers:       members,
		ShardingConfigServer:    false,
		StatusError:             nil,
		LastEstablishStateError: nil,
		State: state,
	}, nil, replSetState(status["myState"].(int))
}

func (c *ConcreteMongodConfigurator) MongodConfiguration(port msp.PortNumber) (msp.Mongod, *msp.Error) {
	sess, err := c.connect(port)
	if err != nil {
		return msp.Mongod{}, err
	}
	defer sess.Close()

	mongod, err, _ := c.fetchConfiguration(sess, port)
	return mongod, err
}

type mongodMembers []msp.HostPort

func (m mongodMembers) Len() int {
	return len(m)
}
func (m mongodMembers) Less(i, j int) bool {
	diff := m[i].Port - m[j].Port
	if diff < 0 {
		return true
	}
	if diff > 0 {
		return false
	}
	return m[i].Hostname < m[j].Hostname
}
func (m mongodMembers) Swap(i, j int) {
	m[i], m[j] = m[j], m[i]
}

func (c *ConcreteMongodConfigurator) ApplyMongodConfiguration(m msp.Mongod) *msp.Error {
	sess, err := c.connect(m.Port)
	defer sess.Close()
	if err != nil {
		return err
	}

	current, err, state := c.fetchConfiguration(sess, m.Port)
	if err != nil {
		return err
	}

	sort.Sort(mongodMembers(current.ReplicaSetMembers))
	sort.Sort(mongodMembers(m.ReplicaSetMembers))

	if m.State == msp.MongodStateDestroyed {
		var result interface{}
		sess.Run(bson.D{{"shutdown", 1}, {"timeoutSecs", int64(c.MongodSoftShutdownTimeout.Seconds())}}, result)
		return nil // shutdown never errors ... We'll just try to force kill the process after another timeout
	}

	if m.State == msp.MongodStateRunning {
		if state == replSetStartup {
			var result interface{}
			err := sess.Run("replSetInitiate", &result)
			if err != nil {
				return &msp.Error{
					Identifier:      msp.SlaveReplicaSetInitError,
					Description:     fmt.Sprintf("Replica set %s could not be initiated on instance on port %d", m.ReplicaSetName, m.Port),
					LongDescription: fmt.Sprintf("Command replSetInitiate failed with\n%s", err.Error()),
				}
			}
		}

		members := make([]bson.M, len(m.ReplicaSetMembers))
		for k, member := range m.ReplicaSetMembers {
			members[k] = bson.M{"_id": k, "host": fmt.Sprintf("%s:%d", member.Hostname, member.Port)}
		}

		var result interface{}
		cmd := bson.D{{"replSetReconfig", bson.M{"_id": m.ReplicaSetName, "version": 1, "members": members}}, {"force", true}}
		err := sess.Run(cmd, &result)
		if err != nil {
			return &msp.Error{
				Identifier:      msp.SlaveReplicaSetConfigError,
				Description:     fmt.Sprintf("Replica set %s could not be reconfigured with replicaset members on instance on port %d", m.ReplicaSetName, m.Port),
				LongDescription: fmt.Sprintf("Command %v failed with\n%s", cmd, err.Error()),
			}
		}

		return nil
	}

	return &msp.Error{
		Identifier:      msp.SlaveMongodProtocolError,
		Description:     "Protocol error",
		LongDescription: fmt.Sprintf("Invalid msp.Mongod.State value %s received", m.State),
	}
}
<commit_msg>SQUASHTHIS: add note on usage of 127.0.0.1 instead of localhost<commit_after>package slave

import (
	"fmt"
	"github.com/KIT-MAMID/mamid/msp"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	replSetStartup    = 0
	replSetPrimary    = 1
	replSetSecondary  = 2
	replSetRecovering = 3
	replSetUnknown    = 6
)

type replSetState int

type MongodConfigurator interface {
	MongodConfiguration(p msp.PortNumber) (msp.Mongod, *msp.Error)
	ApplyMongodConfiguration(m msp.Mongod) *msp.Error
}

type ConcreteMongodConfigurator struct {
	dial                      func(url string) (*mgo.Session, error)
	MongodSoftShutdownTimeout time.Duration
}

func (c *ConcreteMongodConfigurator) connect(port msp.PortNumber) (*mgo.Session, *msp.Error) {
	sess, err := c.dial(fmt.Sprintf("mongodb://127.0.0.1:%d/?connect=direct", port)) // TODO shouldn't we use localhost instead? otherwise, this will break the day IPv4 is dropped

	/*
		mgo.SetDebug(true)

		var aLogger *log.Logger
		aLogger = log.New(os.Stderr, "", log.LstdFlags)
		mgo.SetLogger(aLogger)
	*/

	if err != nil {
		return nil, &msp.Error{
			Identifier:      msp.SlaveConnectMongodError,
			Description:     fmt.Sprintf("Establishing a connection to mongod instance on port %d failed", port),
			LongDescription: fmt.Sprintf("ConcreteMongodConfigurator.dial() failed with: %s", err),
		}
	}
	sess.SetMode(mgo.Monotonic, true)

	return sess, nil
}

func (c *ConcreteMongodConfigurator) fetchConfiguration(sess *mgo.Session, port msp.PortNumber) (msp.Mongod, *msp.Error, replSetState) {
	running := bson.M{}
	if err := sess.Run("isMaster", &running); err != nil {
		return msp.Mongod{}, &msp.Error{
			Identifier:      msp.SlaveGetMongodStatusError,
			Description:     fmt.Sprintf("Getting master information from mongod instance on port %d failed", port),
			LongDescription: fmt.Sprintf("mgo/Session.Run(\"isMaster\") failed with\n%s", err.Error()),
		}, replSetUnknown
	}

	if _, exists := running["setName"]; !exists {
		return msp.Mongod{
			Port:                    port,
			ReplicaSetName:          "",
			ReplicaSetMembers:       nil,
			ShardingConfigServer:    false,
			StatusError:             nil,
			LastEstablishStateError: nil,
			State: msp.MongodStateNotRunning,
		}, nil, replSetStartup
	}

	status := bson.M{}
	if err := sess.Run("replSetGetStatus", &status); err != nil {
		return msp.Mongod{}, &msp.Error{
			Identifier:      msp.SlaveGetMongodStatusError,
			Description:     fmt.Sprintf("Getting replica set status information from mongod instance on port %d failed", port),
			LongDescription: fmt.Sprintf("mgo/Session.Run(\"replSetGetStatus\") failed with\n%s", err.Error()),
		}, replSetUnknown
	}

	members := make([]msp.HostPort, len(status["members"].([]interface{})))
	for k, member := range status["members"].([]interface{}) {
		pair := strings.Split(member.(bson.M)["name"].(string), ":")
		remotePort, _ := strconv.Atoi(pair[1])
		members[k] = msp.HostPort{pair[0], msp.PortNumber(remotePort)}
	}

	var state msp.MongodState
	if replSetState(status["myState"].(int)) == replSetRecovering {
		state = msp.MongodStateRecovering
	} else {
		state = msp.MongodStateRunning
	}

	return msp.Mongod{
		Port:                    port,
		ReplicaSetName:          status["set"].(string),
		ReplicaSetMembers:       members,
		ShardingConfigServer:    false,
		StatusError:             nil,
		LastEstablishStateError: nil,
		State: state,
	}, nil, replSetState(status["myState"].(int))
}

func (c *ConcreteMongodConfigurator) MongodConfiguration(port msp.PortNumber) (msp.Mongod, *msp.Error) {
	sess, err := c.connect(port)
	if err != nil {
		return msp.Mongod{}, err
	}
	defer sess.Close()

	mongod, err, _ := c.fetchConfiguration(sess, port)
	return mongod, err
}

type mongodMembers []msp.HostPort

func (m mongodMembers) Len() int {
	return len(m)
}
func (m mongodMembers) Less(i, j int) bool {
	diff := m[i].Port - m[j].Port
	if diff < 0 {
		return true
	}
	if diff > 0 {
		return false
	}
	return m[i].Hostname < m[j].Hostname
}
func (m mongodMembers) Swap(i, j int) {
	m[i], m[j] = m[j], m[i]
}

func (c *ConcreteMongodConfigurator) ApplyMongodConfiguration(m msp.Mongod) *msp.Error {
	sess, err := c.connect(m.Port)
	defer sess.Close()
	if err != nil {
		return err
	}

	current, err, state := c.fetchConfiguration(sess, m.Port)
	if err != nil {
		return err
	}

	sort.Sort(mongodMembers(current.ReplicaSetMembers))
	sort.Sort(mongodMembers(m.ReplicaSetMembers))

	if m.State == msp.MongodStateDestroyed {
		var result interface{}
		sess.Run(bson.D{{"shutdown", 1}, {"timeoutSecs", int64(c.MongodSoftShutdownTimeout.Seconds())}}, result)
		return nil // shutdown never errors ... We'll just try to force kill the process after another timeout
	}

	if m.State == msp.MongodStateRunning {
		if state == replSetStartup {
			var result interface{}
			err := sess.Run("replSetInitiate", &result)
			if err != nil {
				return &msp.Error{
					Identifier:      msp.SlaveReplicaSetInitError,
					Description:     fmt.Sprintf("Replica set %s could not be initiated on instance on port %d", m.ReplicaSetName, m.Port),
					LongDescription: fmt.Sprintf("Command replSetInitiate failed with\n%s", err.Error()),
				}
			}
		}

		members := make([]bson.M, len(m.ReplicaSetMembers))
		for k, member := range m.ReplicaSetMembers {
			members[k] = bson.M{"_id": k, "host": fmt.Sprintf("%s:%d", member.Hostname, member.Port)}
		}

		var result interface{}
		cmd := bson.D{{"replSetReconfig", bson.M{"_id": m.ReplicaSetName, "version": 1, "members": members}}, {"force", true}}
		err := sess.Run(cmd, &result)
		if err != nil {
			return &msp.Error{
				Identifier:      msp.SlaveReplicaSetConfigError,
				Description:     fmt.Sprintf("Replica set %s could not be reconfigured with replicaset members on instance on port %d", m.ReplicaSetName, m.Port),
				LongDescription: fmt.Sprintf("Command %v failed with\n%s", cmd, err.Error()),
			}
		}

		return nil
	}

	return &msp.Error{
		Identifier:      msp.SlaveMongodProtocolError,
		Description:     "Protocol error",
		LongDescription: fmt.Sprintf("Invalid msp.Mongod.State value %s received", m.State),
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 Istio 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 main

import (
	"encoding/json"
	"fmt"
	"regexp"
	"strings"
	"testing"

	"github.com/ghodss/yaml"

	"istio.io/istio/istioctl/pkg/kubernetes"
	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pkg/version"
)

var meshInfo = version.MeshInfo{
	{"Pilot", version.BuildInfo{"1.0.0", "gitSHA123", "user1", "host1", "go1.10", "hub.docker.com", "Clean", "Tag"}},
	{"Injector", version.BuildInfo{"1.0.1", "gitSHAabc", "user2", "host2", "go1.10.1", "hub.docker.com", "Modified", "OtherTag"}},
	{"Citadel", version.BuildInfo{"1.2", "gitSHA321", "user3", "host3", "go1.11.0", "hub.docker.com", "Clean", "Tag"}},
}

type outputKind int

const (
	rawOutputMock outputKind = iota
	shortOutputMock
	jsonOutputMock
	yamlOutputMock
)

func printMeshVersion(kind outputKind) string {
	switch kind {
	case yamlOutputMock:
		ver := &version.Version{MeshVersion: &meshInfo}
		res, _ := yaml.Marshal(ver)
		return string(res)
	case jsonOutputMock:
		res, _ := json.MarshalIndent(&meshInfo, "", "  ")
		return string(res)
	}

	res := ""
	for _, info := range meshInfo {
		switch kind {
		case rawOutputMock:
			res += fmt.Sprintf("%s version: %#v\n", info.Component, info.Info)
		case shortOutputMock:
			res += fmt.Sprintf("%s version: %s\n", info.Component, info.Info.Version)
		}
	}
	return res
}

func TestVersion(t *testing.T) {
	clientExecFactory = mockExecClientVersionTest

	cases := []testCase{
		{ // case 0 client-side only, normal output
			configs: []model.Config{},
			args:    strings.Split("version --remote=false", " "),
			expectedRegexp: regexp.MustCompile("version.BuildInfo{Version:\"unknown\", GitRevision:\"unknown\", " +
				"User:\"unknown\", Host:\"unknown\", GolangVersion:\"go1.([0-9+?(\\.)?]+)\", DockerHub:\"unknown\", BuildStatus:\"unknown\", GitTag:\"unknown\"}"),
		},
		{ // case 1 client-side only, short output
			configs:        []model.Config{},
			args:           strings.Split("version -s --remote=false", " "),
			expectedOutput: "unknown\n",
		},
		{ // case 2 client-side only, yaml output
			configs: []model.Config{},
			args:    strings.Split("version --remote=false -o yaml", " "),
			expectedRegexp: regexp.MustCompile("clientVersion:\n" +
				"  golang_version: go1.([0-9+?(\\.)?]+)\n" +
				"  host: unknown\n" +
				"  hub: unknown\n" +
				"  revision: unknown\n" +
				"  status: unknown\n" +
				"  tag: unknown\n" +
				"  user: unknown\n" +
				"  version: unknown\n\n"),
		},
		{ // case 3 client-side only, json output
			configs: []model.Config{},
			args:    strings.Split("version --remote=false -o json", " "),
			expectedRegexp: regexp.MustCompile("{\n" +
				"  \"clientVersion\": {\n" +
				"    \"version\": \"unknown\",\n" +
				"    \"revision\": \"unknown\",\n" +
				"    \"user\": \"unknown\",\n" +
				"    \"host\": \"unknown\",\n" +
				"    \"golang_version\": \"go1.([0-9+?(\\.)?]+)\",\n" +
				"    \"hub\": \"unknown\",\n" +
				"    \"status\": \"unknown\",\n" +
				"    \"tag\": \"unknown\"\n" +
				"  }\n" +
				"}\n"),
		},

		{ // case 4 remote, normal output
			configs: []model.Config{},
			args:    strings.Split("version --remote=true --short=false --output=", " "),
			expectedRegexp: regexp.MustCompile("client version: version.BuildInfo{Version:\"unknown\", GitRevision:\"unknown\", " +
				"User:\"unknown\", Host:\"unknown\", GolangVersion:\"go1.([0-9+?(\\.)?]+)\", DockerHub:\"unknown\", BuildStatus:\"unknown\", GitTag:\"unknown\"}\n" +
				printMeshVersion(rawOutputMock)),
		},
		{ // case 5 remote, short output
			configs:        []model.Config{},
			args:           strings.Split("version --short=true --remote=true --output=", " "),
			expectedOutput: "client version: unknown\n" + printMeshVersion(shortOutputMock),
		},
		{ // case 6 remote, yaml output
			configs: []model.Config{},
			args:    strings.Split("version --remote=true -o yaml", " "),
			expectedRegexp: regexp.MustCompile("clientVersion:\n" +
				"  golang_version: go1.([0-9+?(\\.)?]+)\n" +
				"  host: unknown\n" +
				"  hub: unknown\n" +
				"  revision: unknown\n" +
				"  status: unknown\n" +
				"  tag: unknown\n" +
				"  user: unknown\n" +
				"  version: unknown\n" + printMeshVersion(yamlOutputMock)),
		},
		{ // case 7 remote, json output
			configs: []model.Config{},
			args:    strings.Split("version --remote=true -o json", " "),
			expectedRegexp: regexp.MustCompile("{\n" +
				"  \"clientVersion\": {\n" +
				"    \"version\": \"unknown\",\n" +
				"    \"revision\": \"unknown\",\n" +
				"    \"user\": \"unknown\",\n" +
				"    \"host\": \"unknown\",\n" +
				"    \"golang_version\": \"go1.([0-9+?(\\.)?]+)\",\n" +
				"    \"hub\": \"unknown\",\n" +
				"    \"status\": \"unknown\",\n" +
				"    \"tag\": \"unknown\"\n" +
				"  },\n" +
				printMeshVersion(jsonOutputMock)),
		},

		{ // case 8 bogus arg
			configs:        []model.Config{},
			args:           strings.Split("version --typo", " "),
			expectedOutput: "Error: unknown flag: --typo\n",
			wantException:  true,
		},

		{ // case 9 bogus output arg
			configs:        []model.Config{},
			args:           strings.Split("version --output xyz", " "),
			expectedOutput: "Error: --output must be 'yaml' or 'json'\n",
			wantException:  true,
		},
	}

	for i, c := range cases {
		t.Run(fmt.Sprintf("case %d %s", i, strings.Join(c.args, " ")), func(t *testing.T) {
			verifyOutput(t, c)
		})
	}
}

type mockExecVersionConfig struct {
}

func (client mockExecVersionConfig) AllPilotsDiscoveryDo(pilotNamespace, method, path string, body []byte) (map[string][]byte, error) {
	return nil, nil
}

func (client mockExecVersionConfig) EnvoyDo(podName, podNamespace, method, path string, body []byte) ([]byte, error) {
	return nil, nil
}

func (client mockExecVersionConfig) PilotDiscoveryDo(pilotNamespace, method, path string, body []byte) ([]byte, error) {
	return nil, nil
}

// nolint: unparam
func (client mockExecVersionConfig) GetIstioVersions(namespace string) (*version.MeshInfo, error) {
	return &meshInfo, nil
}

func mockExecClientVersionTest(kubeconfig, configContext string) (kubernetes.ExecClient, error) {
	return &mockExecVersionConfig{}, nil
}
<commit_msg>Fix overly restrictive golang version match<commit_after>// Copyright 2018 Istio 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 main

import (
	"encoding/json"
	"fmt"
	"regexp"
	"strings"
	"testing"

	"github.com/ghodss/yaml"

	"istio.io/istio/istioctl/pkg/kubernetes"
	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pkg/version"
)

var meshInfo = version.MeshInfo{
	{"Pilot", version.BuildInfo{"1.0.0", "gitSHA123", "user1", "host1", "go1.10", "hub.docker.com", "Clean", "Tag"}},
	{"Injector", version.BuildInfo{"1.0.1", "gitSHAabc", "user2", "host2", "go1.10.1", "hub.docker.com", "Modified", "OtherTag"}},
	{"Citadel", version.BuildInfo{"1.2", "gitSHA321", "user3", "host3", "go1.11.0", "hub.docker.com", "Clean", "Tag"}},
}

type outputKind int

const (
	rawOutputMock outputKind = iota
	shortOutputMock
	jsonOutputMock
	yamlOutputMock
)

func printMeshVersion(kind outputKind) string {
	switch kind {
	case yamlOutputMock:
		ver := &version.Version{MeshVersion: &meshInfo}
		res, _ := yaml.Marshal(ver)
		return string(res)
	case jsonOutputMock:
		res, _ := json.MarshalIndent(&meshInfo, "", "  ")
		return string(res)
	}

	res := ""
	for _, info := range meshInfo {
		switch kind {
		case rawOutputMock:
			res += fmt.Sprintf("%s version: %#v\n", info.Component, info.Info)
		case shortOutputMock:
			res += fmt.Sprintf("%s version: %s\n", info.Component, info.Info.Version)
		}
	}
	return res
}

func TestVersion(t *testing.T) {
	clientExecFactory = mockExecClientVersionTest

	cases := []testCase{
		{ // case 0 client-side only, normal output
			configs: []model.Config{},
			args:    strings.Split("version --remote=false", " "),
			expectedRegexp: regexp.MustCompile("version.BuildInfo{Version:\"unknown\", GitRevision:\"unknown\", " +
				"User:\"unknown\", Host:\"unknown\", GolangVersion:\"go1.([0-9+?(\\.)?]+)(rc[0-9]?)?\", DockerHub:\"unknown\", BuildStatus:\"unknown\", GitTag:\"unknown\"}"),
		},
		{ // case 1 client-side only, short output
			configs:        []model.Config{},
			args:           strings.Split("version -s --remote=false", " "),
			expectedOutput: "unknown\n",
		},
		{ // case 2 client-side only, yaml output
			configs: []model.Config{},
			args:    strings.Split("version --remote=false -o yaml", " "),
			expectedRegexp: regexp.MustCompile("clientVersion:\n" +
				"  golang_version: go1.([0-9+?(\\.)?]+)(rc[0-9]?)?\n" +
				"  host: unknown\n" +
				"  hub: unknown\n" +
				"  revision: unknown\n" +
				"  status: unknown\n" +
				"  tag: unknown\n" +
				"  user: unknown\n" +
				"  version: unknown\n\n"),
		},
		{ // case 3 client-side only, json output
			configs: []model.Config{},
			args:    strings.Split("version --remote=false -o json", " "),
			expectedRegexp: regexp.MustCompile("{\n" +
				"  \"clientVersion\": {\n" +
				"    \"version\": \"unknown\",\n" +
				"    \"revision\": \"unknown\",\n" +
				"    \"user\": \"unknown\",\n" +
				"    \"host\": \"unknown\",\n" +
				"    \"golang_version\": \"go1.([0-9+?(\\.)?]+)(rc[0-9]?)?\",\n" +
				"    \"hub\": \"unknown\",\n" +
				"    \"status\": \"unknown\",\n" +
				"    \"tag\": \"unknown\"\n" +
				"  }\n" +
				"}\n"),
		},

		{ // case 4 remote, normal output
			configs: []model.Config{},
			args:    strings.Split("version --remote=true --short=false --output=", " "),
			expectedRegexp: regexp.MustCompile("client version: version.BuildInfo{Version:\"unknown\", GitRevision:\"unknown\", " +
				"User:\"unknown\", Host:\"unknown\", GolangVersion:\"go1.([0-9+?(\\.)?]+)(rc[0-9]?)?\", DockerHub:\"unknown\", BuildStatus:\"unknown\", GitTag:\"unknown\"}\n" +
				printMeshVersion(rawOutputMock)),
		},
		{ // case 5 remote, short output
			configs:        []model.Config{},
			args:           strings.Split("version --short=true --remote=true --output=", " "),
			expectedOutput: "client version: unknown\n" + printMeshVersion(shortOutputMock),
		},
		{ // case 6 remote, yaml output
			configs: []model.Config{},
			args:    strings.Split("version --remote=true -o yaml", " "),
			expectedRegexp: regexp.MustCompile("clientVersion:\n" +
				"  golang_version: go1.([0-9+?(\\.)?]+)(rc[0-9]?)?\n" +
				"  host: unknown\n" +
				"  hub: unknown\n" +
				"  revision: unknown\n" +
				"  status: unknown\n" +
				"  tag: unknown\n" +
				"  user: unknown\n" +
				"  version: unknown\n" + printMeshVersion(yamlOutputMock)),
		},
		{ // case 7 remote, json output
			configs: []model.Config{},
			args:    strings.Split("version --remote=true -o json", " "),
			expectedRegexp: regexp.MustCompile("{\n" +
				"  \"clientVersion\": {\n" +
				"    \"version\": \"unknown\",\n" +
				"    \"revision\": \"unknown\",\n" +
				"    \"user\": \"unknown\",\n" +
				"    \"host\": \"unknown\",\n" +
				"    \"golang_version\": \"go1.([0-9+?(\\.)?]+)(rc[0-9]?)?\",\n" +
				"    \"hub\": \"unknown\",\n" +
				"    \"status\": \"unknown\",\n" +
				"    \"tag\": \"unknown\"\n" +
				"  },\n" +
				printMeshVersion(jsonOutputMock)),
		},

		{ // case 8 bogus arg
			configs:        []model.Config{},
			args:           strings.Split("version --typo", " "),
			expectedOutput: "Error: unknown flag: --typo\n",
			wantException:  true,
		},

		{ // case 9 bogus output arg
			configs:        []model.Config{},
			args:           strings.Split("version --output xyz", " "),
			expectedOutput: "Error: --output must be 'yaml' or 'json'\n",
			wantException:  true,
		},
	}

	for i, c := range cases {
		t.Run(fmt.Sprintf("case %d %s", i, strings.Join(c.args, " ")), func(t *testing.T) {
			verifyOutput(t, c)
		})
	}
}

type mockExecVersionConfig struct {
}

func (client mockExecVersionConfig) AllPilotsDiscoveryDo(pilotNamespace, method, path string, body []byte) (map[string][]byte, error) {
	return nil, nil
}

func (client mockExecVersionConfig) EnvoyDo(podName, podNamespace, method, path string, body []byte) ([]byte, error) {
	return nil, nil
}

func (client mockExecVersionConfig) PilotDiscoveryDo(pilotNamespace, method, path string, body []byte) ([]byte, error) {
	return nil, nil
}

// nolint: unparam
func (client mockExecVersionConfig) GetIstioVersions(namespace string) (*version.MeshInfo, error) {
	return &meshInfo, nil
}

func mockExecClientVersionTest(kubeconfig, configContext string) (kubernetes.ExecClient, error) {
	return &mockExecVersionConfig{}, nil
}
<|endoftext|>


<commit_before>package mixpanel

import (
	"fmt"
	"strings"
	"testing"
)

func TestAddSignature(t *testing.T) {
	// TODO: write me
}

func TestMakeArgs(t *testing.T) {
	// TODO: write me
}

func TestTransformEventData(t *testing.T) {
	mix := New("product", "", "")
	input := strings.NewReader(`
{"event": "a0", "properties": {"a": null, "b": "b0", "c": true, "d": ["foo"]}}
{"event": "a1", "properties": {"a": null, "b": "b1", "c": true, "d": ["foo"]}}
{"event": "a2", "properties": {"a": null, "b": "b2", "c": true, "d": ["foo"]}}`)

	output := make(chan EventData)

	go mix.TransformEventData(input, output)

	for i := 0; i < 3; i++ {
		event := <-output

		expected := []struct {
			Name  string
			Value interface{}
		}{
			{"event", fmt.Sprintf("a%d", i)},
			{"a", nil},
			{"b", fmt.Sprintf("b%d", i)},
			{"c", true},
		}

		for _, e := range expected {
			if v, ok := event[e.Name]; !ok || v != e.Value {
				t.Errorf("bad value: expected %s=(%v) got %s=(%v)", e.Name, e.Value,
					e.Name, v)
			}
		}
	}
	close(output)
}

func TestExportDate(t *testing.T) {
	// TODO: write me
}

func BenchmarkTransformEventData(b *testing.B) {
	mix := New("product", "", "")
	input := strings.NewReader(
		strings.Repeat(`{"event": "a2", "properties": {"a": null, "b": "b2", "c": true, "d": ["foo"]}}`, b.N))
	output := make(chan EventData)

	b.ResetTimer()

	go mix.TransformEventData(input, output)
	for i := 0; i < b.N; i++ {
		<-output
	}
	close(output)
}
<commit_msg>Test failure conditions in TransformEventData<commit_after>package mixpanel

import (
	"fmt"
	"strings"
	"testing"
)

func TestAddSignature(t *testing.T) {
	// TODO: write me
}

func TestMakeArgs(t *testing.T) {
	// TODO: write me
}

func TestTransformEventData(t *testing.T) {
	mix := New("product", "", "")
	input := strings.NewReader(`
{"event": "a0", "properties": {"a": null, "b": "b0", "c": true, "d": ["foo"]}}
{"event": "a1", "properties": {"a": null, "b": "b1", "c": true, "d": ["foo"]}}
{"event": "a2", "properties": {"a": null, "b": "b2", "c": true, "d": ["foo"]}}`)

	output := make(chan EventData)

	go func() {
		if err := mix.TransformEventData(input, output); err != nil {
			t.Errorf("raised error: %v", err)
		}
	}()

	for i := 0; i < 3; i++ {
		event := <-output

		expected := []struct {
			Name  string
			Value interface{}
		}{
			{"event", fmt.Sprintf("a%d", i)},
			{"a", nil},
			{"b", fmt.Sprintf("b%d", i)},
			{"c", true},
		}

		for _, e := range expected {
			if v, ok := event[e.Name]; !ok || v != e.Value {
				t.Errorf("bad value: expected %s=(%v) got %s=(%v)", e.Name, e.Value,
					e.Name, v)
			}
		}
	}
	close(output)
}

func TestTransformEventDataApiError(t *testing.T) {
	mix := New("product", "", "")
	input := strings.NewReader(`{"error": "some api error"}`)

	output := make(chan EventData)

	go func() {
		if err := mix.TransformEventData(input, output); err == nil {
			t.Error("Expected error on bad json")
		} else if err.Error() != "product: API error: some api error" {
			t.Errorf("Bad error string: '%s'", err.Error())
		}
	}()

	close(output)
}

func TestTransformEventDataBadJson(t *testing.T) {
	mix := New("product", "", "")
	input := strings.NewReader(`{"event": "a", "properties": {"a": "1"}}
{"event": "bad_json"`)

	output := make(chan EventData)

	go func() {
		if err := mix.TransformEventData(input, output); err == nil {
			t.Error("Expected error on bad json")
		}
	}()

	event := <-output
	expected := []struct {
		Name  string
		Value interface{}
	}{
		{"event", "a"},
		{"a", "1"},
	}

	for _, e := range expected {
		if v, ok := event[e.Name]; !ok || v != e.Value {
			t.Errorf("bad value: expected %s=(%v) got %s=(%v)", e.Name, e.Value,
				e.Name, v)
		}
	}

	close(output)
}

func TestExportDate(t *testing.T) {
	// TODO: write me
}

func BenchmarkTransformEventData(b *testing.B) {
	mix := New("product", "", "")
	input := strings.NewReader(
		strings.Repeat(`{"event": "a2", "properties": {"a": null, "b": "b2", "c": true, "d": ["foo"]}}`, b.N))
	output := make(chan EventData)

	b.ResetTimer()

	go mix.TransformEventData(input, output)
	for i := 0; i < b.N; i++ {
		<-output
	}
	close(output)
}
<|endoftext|>


<commit_before>package ec2

import (
	"encoding/xml"
	"fmt"
	"time"
)

type KeyPair struct {
	KeyName        string `xml:"keyName"`
	KeyFingerprint string `xml:"keyFingerprint"`
}

type DescribeKeyPairsResponse struct {
	KeyPairs []*KeyPair `xml:"keySet>item"`
}

func (client *Client) DescribeKeyPairs() (pairs []*KeyPair, e error) {
	raw, e := client.DoSignedRequest("GET", client.Endpoint(), queryForAction("DescribeKeyPairs"), nil)
	if e != nil {
		return pairs, e
	}
	rsp := &DescribeKeyPairsResponse{}
	e = xml.Unmarshal(raw.Content, rsp)
	if e != nil {
		return pairs, e
	}
	return rsp.KeyPairs, nil

}

type DescribeImagesResponse struct {
	XMLName   xml.Name `xml:"DescribeImagesResponse"`
	RequestId string   `xml:"requestId"`
	Images    []*Image `xml:"imagesSet>item"`
}

type Image struct {
	ImageId             string                `xml:"imageId"`
	ImageLocation       string                `xml:"imageLocation"`
	ImageState          string                `xml:"imageState"`
	ImageOwnerId        string                `xml:"imageOwnerId"`
	IsPublic            bool                  `xml:"isPublic"`
	Architecture        string                `xml:"architecture"`
	ImageType           string                `xml:"imageType"`
	ImageOwnerAlias     string                `xml:"imageOwnerAlias"`
	Name                string                `xml:"name"`
	RootDeviceType      string                `xml:"rootDeviceType"`
	VirtualizationType  string                `xml:"virtualizationType"`
	Hypervisor          string                `xml:"hypervisor"`
	BlockDeviceMappings []*BlockDeviceMapping `xml:"blockDeviceMapping>item"`
	ProductCodes        []*ProductCode        `xml:"productCodes>item"`
	Tags                []*Tag                `xml:"tagSet>item"`
}

type ProductCode struct {
	ProductCode string `xml:"productCode"`
	Type        string `xml:"type"`
}

type Instance struct {
	InstanceId                string    `xml:"instanceId"`
	ImageId                   string    `xml:"imageId"`
	InstanceStateCode         int       `xml:"instanceState>code"`
	InstanceStateName         string    `xml:"instanceState>name"`
	PrivateDnsName            string    `xml:"privateDnsName"`
	DnsName                   string    `xml:"dnsName"`
	Reason                    string    `xml:"reason"`
	KeyName                   string    `xml:"keyName"`
	AmiLaunchIndex            int       `xml:"amiLaunchIndex"`
	InstanceType              string    `xml:"instanceType"`
	LaunchTime                time.Time `xml:"launchTime"`
	PlacementAvailabilityZone string    `xml:"placement>availabilityZone"`
	PlacementTenancy          string    `xml:"placement>tenancy"`
	KernelId                  string    `xml:"kernelId"`
	MonitoringState           string    `xml:"monitoring>state"`
	SubnetId                  string    `xml:"subnetId"`
	VpcId                     string    `xml:"vpcId"`
	PrivateIpAddress          string    `xml:"privateIpAddress"`
	IpAddress                 string    `xml:"ipAddress"`
	SourceDestCheck           string    `xml:"sourceDestCheck"`
	Architecture              string    `xml:"architecture"`
	RootDeviceType            string    `xml:"rootDeviceType"`
	RootDeviceName            string    `xml:"rootDeviceName"`
	VirtualizationType        string    `xml:"virtualizationType"`
	ClientToken               string    `xml:"clientToken"`
	Hypervisor                string    `xml:"hypervisor"`
	EbsOptimized              string    `xml:"ebsOptimized"`

	BlockDeviceMappings []*BlockDeviceMapping `xml:"blockDeviceMapping>item"`
	SecurityGroups      []*SecurityGroup      `xml:"groupSet>item"`
	Tags                []*Tag                `xml:"tagSet>item"`
	NetworkInterfaces   []*NetworkInterface   `xml:"networkInterfaceSet>item"`
}

func (instance *Instance) Name() string {
	for _, tag := range instance.Tags {
		if tag.Key == "Name" {
			return tag.Value
		}
	}
	return ""
}

type NetworkInterface struct {
	NetworkInterfaceId            string           `xml:"networkInterfaceId"`
	SubnetId                      string           `xml:"subnetId"`
	VpcId                         string           `xml:"vpcId"`
	Description                   string           `xml:"description"`
	OwnerId                       string           `xml:"ownerId"`
	Status                        string           `xml:"status"`
	MacAddress                    string           `xml:"macAddress"`
	PrivateIpAddress              string           `xml:"privateIpAddress"`
	PrivateDnsName                string           `xml:"privateDnsName"`
	SourceDestCheck               bool             `xml:"sourceDestCheck"`
	SecurityGroups                []*SecurityGroup `xml:"groupSet>item"`
	AttachmentAttachmentId        string           `xml:"attachment>attachmentId"`
	AttachmentDeviceIndex         int              `xml:"attachment>deviceIndex"`
	AttachmentStatus              string           `xml:"attachment>status"`
	AttachmentAttachTime          time.Time        `xml:"attachment>attachTime"`
	AttachmentDeleteOnTermination bool             `xml:"attachment>deleteOnTermination"`
	AssociationPublicIp           string           `xml:"association>publicIp"`
	AssociationPublicDnsName      string           `xml:"association>publicDnsName"`
	AssociationIpOwnerId          string           `xml:"association>ipOwnerId"`

	PrivateIpAddresses []*IpAddress `xml:"privateIpAddressesSet>item"`
}

type IpAddress struct {
	PrivateIpAddress string `xml:"privateIpAddress"`
	PrivateDnsName   string `xml:"privateDnsName"`
	Primary          bool   `xml:"primary"`
	PublicIp         string `xml:"publicIp"`
	PublicDnsName    string `xml:"publicDnsName"`
	IpOwnerId        string `xml:"ipOwnerId"`
}

type TagList []*Tag

func (list TagList) Len() int {
	return len(list)
}

func (list TagList) Swap(a, b int) {
	list[a], list[b] = list[b], list[a]
}

func (list TagList) Less(a, b int) bool {
	return list[a].String() < list[b].String()
}

func (tag *Tag) String() string {
	return fmt.Sprintf("%s %s %s %s", tag.ResourceType, tag.ResourceId, tag.Key, tag.Value)
}

type Tag struct {
	Key          string `xml:"key,omitempty"`
	Value        string `xml:"value,omitempty"`
	ResourceId   string `xml:"resourceId,omitempty"`
	ResourceType string `xml:"resourceType,omitempty"`
}

type BlockDeviceMapping struct {
	DeviceName string `xml:"deviceName,omitempty" json:",omitempty"`
	Ebs        *Ebs   `xml:"ebs,omitempty" json:",omitempty"`
}

const (
	VolumeTypeGp       = "gp2"
	VolumeTypeIo1      = "io1"
	VolumeTypeStandard = "standard"
)

type Ebs struct {
	SnapshotId          string `xml:"snapshotId,omitempty" json:",omitempty"`
	VolumeSize          int    `xml:"volumeSize,omitempty" json:",omitempty"`
	DeleteOnTermination bool   `xml:"deleteOnTermination,omitempty" json:",omitempty"`
	VolumeType          string `xml:"volumeType,omitempty json:",omitempty""` // see VolumeType... (e.g. gp, io1, standard)
	Iops                int    `xml:"iops,omitempty" json:",omitempty"`
	Encrypted           bool   `xml:"encrypted,omitempty" json:",omitempty"`
}

type Reservation struct {
	ReservationId string      `xml:"reservationId"`
	OwnerId       string      `xml:"ownerId"`
	Instances     []*Instance `xml:"instancesSet>item"`
}

type DescribeInstancesResponse struct {
	XMLName      xml.Name       `xml:"DescribeInstancesResponse"`
	RequestId    string         `xml:"requestId"`
	Reservations []*Reservation `xml:"reservationSet>item"`
}

func (rsp *DescribeInstancesResponse) Instances() []*Instance {
	instances := []*Instance{}
	for _, r := range rsp.Reservations {
		for _, i := range r.Instances {
			instances = append(instances, i)
		}
	}
	return instances
}
<commit_msg>add IamInstanceProfiles to ec2 instance mapping<commit_after>package ec2

import (
	"encoding/xml"
	"fmt"
	"time"
)

type KeyPair struct {
	KeyName        string `xml:"keyName"`
	KeyFingerprint string `xml:"keyFingerprint"`
}

type DescribeKeyPairsResponse struct {
	KeyPairs []*KeyPair `xml:"keySet>item"`
}

func (client *Client) DescribeKeyPairs() (pairs []*KeyPair, e error) {
	raw, e := client.DoSignedRequest("GET", client.Endpoint(), queryForAction("DescribeKeyPairs"), nil)
	if e != nil {
		return pairs, e
	}
	rsp := &DescribeKeyPairsResponse{}
	e = xml.Unmarshal(raw.Content, rsp)
	if e != nil {
		return pairs, e
	}
	return rsp.KeyPairs, nil

}

type DescribeImagesResponse struct {
	XMLName   xml.Name `xml:"DescribeImagesResponse"`
	RequestId string   `xml:"requestId"`
	Images    []*Image `xml:"imagesSet>item"`
}

type Image struct {
	ImageId             string                `xml:"imageId"`
	ImageLocation       string                `xml:"imageLocation"`
	ImageState          string                `xml:"imageState"`
	ImageOwnerId        string                `xml:"imageOwnerId"`
	IsPublic            bool                  `xml:"isPublic"`
	Architecture        string                `xml:"architecture"`
	ImageType           string                `xml:"imageType"`
	ImageOwnerAlias     string                `xml:"imageOwnerAlias"`
	Name                string                `xml:"name"`
	RootDeviceType      string                `xml:"rootDeviceType"`
	VirtualizationType  string                `xml:"virtualizationType"`
	Hypervisor          string                `xml:"hypervisor"`
	BlockDeviceMappings []*BlockDeviceMapping `xml:"blockDeviceMapping>item"`
	ProductCodes        []*ProductCode        `xml:"productCodes>item"`
	Tags                []*Tag                `xml:"tagSet>item"`
}

type ProductCode struct {
	ProductCode string `xml:"productCode"`
	Type        string `xml:"type"`
}

type Instance struct {
	InstanceId                string    `xml:"instanceId"`
	ImageId                   string    `xml:"imageId"`
	InstanceStateCode         int       `xml:"instanceState>code"`
	InstanceStateName         string    `xml:"instanceState>name"`
	PrivateDnsName            string    `xml:"privateDnsName"`
	DnsName                   string    `xml:"dnsName"`
	Reason                    string    `xml:"reason"`
	KeyName                   string    `xml:"keyName"`
	AmiLaunchIndex            int       `xml:"amiLaunchIndex"`
	InstanceType              string    `xml:"instanceType"`
	LaunchTime                time.Time `xml:"launchTime"`
	PlacementAvailabilityZone string    `xml:"placement>availabilityZone"`
	PlacementTenancy          string    `xml:"placement>tenancy"`
	KernelId                  string    `xml:"kernelId"`
	MonitoringState           string    `xml:"monitoring>state"`
	SubnetId                  string    `xml:"subnetId"`
	VpcId                     string    `xml:"vpcId"`
	PrivateIpAddress          string    `xml:"privateIpAddress"`
	IpAddress                 string    `xml:"ipAddress"`
	SourceDestCheck           string    `xml:"sourceDestCheck"`
	Architecture              string    `xml:"architecture"`
	RootDeviceType            string    `xml:"rootDeviceType"`
	RootDeviceName            string    `xml:"rootDeviceName"`
	VirtualizationType        string    `xml:"virtualizationType"`
	ClientToken               string    `xml:"clientToken"`
	Hypervisor                string    `xml:"hypervisor"`
	EbsOptimized              string    `xml:"ebsOptimized"`

	BlockDeviceMappings []*BlockDeviceMapping `xml:"blockDeviceMapping>item"`
	SecurityGroups      []*SecurityGroup      `xml:"groupSet>item"`
	Tags                []*Tag                `xml:"tagSet>item"`
	NetworkInterfaces   []*NetworkInterface   `xml:"networkInterfaceSet>item"`
	IamInstanceProfile  *IamInstanceProfile   `xml:"iamInstanceProfile"`
}

type IamInstanceProfile struct {
	Arn string `xml:"arn"`
	Id  string `xml:"id"`
}

func (instance *Instance) Name() string {
	for _, tag := range instance.Tags {
		if tag.Key == "Name" {
			return tag.Value
		}
	}
	return ""
}

type NetworkInterface struct {
	NetworkInterfaceId            string           `xml:"networkInterfaceId"`
	SubnetId                      string           `xml:"subnetId"`
	VpcId                         string           `xml:"vpcId"`
	Description                   string           `xml:"description"`
	OwnerId                       string           `xml:"ownerId"`
	Status                        string           `xml:"status"`
	MacAddress                    string           `xml:"macAddress"`
	PrivateIpAddress              string           `xml:"privateIpAddress"`
	PrivateDnsName                string           `xml:"privateDnsName"`
	SourceDestCheck               bool             `xml:"sourceDestCheck"`
	SecurityGroups                []*SecurityGroup `xml:"groupSet>item"`
	AttachmentAttachmentId        string           `xml:"attachment>attachmentId"`
	AttachmentDeviceIndex         int              `xml:"attachment>deviceIndex"`
	AttachmentStatus              string           `xml:"attachment>status"`
	AttachmentAttachTime          time.Time        `xml:"attachment>attachTime"`
	AttachmentDeleteOnTermination bool             `xml:"attachment>deleteOnTermination"`
	AssociationPublicIp           string           `xml:"association>publicIp"`
	AssociationPublicDnsName      string           `xml:"association>publicDnsName"`
	AssociationIpOwnerId          string           `xml:"association>ipOwnerId"`

	PrivateIpAddresses []*IpAddress `xml:"privateIpAddressesSet>item"`
}

type IpAddress struct {
	PrivateIpAddress string `xml:"privateIpAddress"`
	PrivateDnsName   string `xml:"privateDnsName"`
	Primary          bool   `xml:"primary"`
	PublicIp         string `xml:"publicIp"`
	PublicDnsName    string `xml:"publicDnsName"`
	IpOwnerId        string `xml:"ipOwnerId"`
}

type TagList []*Tag

func (list TagList) Len() int {
	return len(list)
}

func (list TagList) Swap(a, b int) {
	list[a], list[b] = list[b], list[a]
}

func (list TagList) Less(a, b int) bool {
	return list[a].String() < list[b].String()
}

func (tag *Tag) String() string {
	return fmt.Sprintf("%s %s %s %s", tag.ResourceType, tag.ResourceId, tag.Key, tag.Value)
}

type Tag struct {
	Key          string `xml:"key,omitempty"`
	Value        string `xml:"value,omitempty"`
	ResourceId   string `xml:"resourceId,omitempty"`
	ResourceType string `xml:"resourceType,omitempty"`
}

type BlockDeviceMapping struct {
	DeviceName string `xml:"deviceName,omitempty" json:",omitempty"`
	Ebs        *Ebs   `xml:"ebs,omitempty" json:",omitempty"`
}

const (
	VolumeTypeGp       = "gp2"
	VolumeTypeIo1      = "io1"
	VolumeTypeStandard = "standard"
)

type Ebs struct {
	SnapshotId          string `xml:"snapshotId,omitempty" json:",omitempty"`
	VolumeSize          int    `xml:"volumeSize,omitempty" json:",omitempty"`
	DeleteOnTermination bool   `xml:"deleteOnTermination,omitempty" json:",omitempty"`
	VolumeType          string `xml:"volumeType,omitempty json:",omitempty""` // see VolumeType... (e.g. gp, io1, standard)
	Iops                int    `xml:"iops,omitempty" json:",omitempty"`
	Encrypted           bool   `xml:"encrypted,omitempty" json:",omitempty"`
}

type Reservation struct {
	ReservationId string      `xml:"reservationId"`
	OwnerId       string      `xml:"ownerId"`
	Instances     []*Instance `xml:"instancesSet>item"`
}

type DescribeInstancesResponse struct {
	XMLName      xml.Name       `xml:"DescribeInstancesResponse"`
	RequestId    string         `xml:"requestId"`
	Reservations []*Reservation `xml:"reservationSet>item"`
}

func (rsp *DescribeInstancesResponse) Instances() []*Instance {
	instances := []*Instance{}
	for _, r := range rsp.Reservations {
		for _, i := range r.Instances {
			instances = append(instances, i)
		}
	}
	return instances
}
<|endoftext|>


<commit_before>package backupstore

import (
	"fmt"
	"net/url"
	"strconv"

	"github.com/yasker/backupstore/util"
)

type Volume struct {
	Name           string
	Driver         string
	Size           int64
	CreatedTime    string
	LastBackupName string
}

type Snapshot struct {
	Name        string
	CreatedTime string
}

type Backup struct {
	Name              string
	Driver            string
	VolumeName        string
	SnapshotName      string
	SnapshotCreatedAt string
	CreatedTime       string

	Blocks     []BlockMapping `json:",omitempty"`
	SingleFile BackupFile     `json:",omitempty"`
}

var (
	backupstoreBase = "backupstore"
)

func SetBackupstoreBase(base string) {
	backupstoreBase = base
}

func GetBackupstoreBase() string {
	return backupstoreBase
}

func addVolume(volume *Volume, driver BackupStoreDriver) error {
	if volumeExists(volume.Name, driver) {
		return nil
	}

	if err := saveVolume(volume, driver); err != nil {
		log.Error("Fail add volume ", volume.Name)
		return err
	}
	log.Debug("Added backupstore volume ", volume.Name)

	return nil
}

func removeVolume(volumeName string, driver BackupStoreDriver) error {
	if !volumeExists(volumeName, driver) {
		return fmt.Errorf("Volume %v doesn't exist in backupstore", volumeName)
	}

	volumeDir := getVolumePath(volumeName)
	if err := driver.Remove(volumeDir); err != nil {
		return err
	}
	log.Debug("Removed volume directory in backupstore: ", volumeDir)
	log.Debug("Removed backupstore volume ", volumeName)

	return nil
}

func encodeBackupURL(backupName, volumeName, destURL string) string {
	v := url.Values{}
	v.Add("volume", volumeName)
	v.Add("backup", backupName)
	return destURL + "?" + v.Encode()
}

func decodeBackupURL(backupURL string) (string, string, error) {
	u, err := url.Parse(backupURL)
	if err != nil {
		return "", "", err
	}
	v := u.Query()
	volumeName := v.Get("volume")
	backupName := v.Get("backup")
	if !util.ValidateName(volumeName) || !util.ValidateName(backupName) {
		return "", "", fmt.Errorf("Invalid name parsed, got %v and %v", backupName, volumeName)
	}
	return backupName, volumeName, nil
}

func addListVolume(resp map[string]map[string]string, volumeName string, driver BackupStoreDriver, storageDriverName string) error {
	if volumeName == "" {
		return fmt.Errorf("Invalid empty volume Name")
	}

	backupNames, err := getBackupNamesForVolume(volumeName, driver)
	if err != nil {
		return err
	}

	volume, err := loadVolume(volumeName, driver)
	if err != nil {
		return err
	}
	//Skip any volumes not owned by specified storage driver
	if volume.Driver != storageDriverName {
		return nil
	}

	for _, backupName := range backupNames {
		backup, err := loadBackup(backupName, volumeName, driver)
		if err != nil {
			return err
		}
		r := fillBackupInfo(backup, volume, driver.GetURL())
		resp[r["BackupURL"]] = r
	}
	return nil
}

func List(volumeName, destURL, storageDriverName string) (map[string]map[string]string, error) {
	driver, err := GetBackupStoreDriver(destURL)
	if err != nil {
		return nil, err
	}
	resp := make(map[string]map[string]string)
	if volumeName != "" {
		if err = addListVolume(resp, volumeName, driver, storageDriverName); err != nil {
			return nil, err
		}
	} else {
		volumeNames, err := getVolumeNames(driver)
		if err != nil {
			return nil, err
		}
		for _, volumeName := range volumeNames {
			if err := addListVolume(resp, volumeName, driver, storageDriverName); err != nil {
				return nil, err
			}
		}
	}
	return resp, nil
}

func fillBackupInfo(backup *Backup, volume *Volume, destURL string) map[string]string {
	return map[string]string{
		"BackupName":        backup.Name,
		"BackupURL":         encodeBackupURL(backup.Name, backup.VolumeName, destURL),
		"DriverName":        volume.Driver,
		"VolumeName":        backup.VolumeName,
		"VolumeSize":        strconv.FormatInt(volume.Size, 10),
		"VolumeCreatedAt":   volume.CreatedTime,
		"SnapshotName":      backup.SnapshotName,
		"SnapshotCreatedAt": backup.SnapshotCreatedAt,
		"CreatedTime":       backup.CreatedTime,
	}
}

func GetBackupInfo(backupURL string) (map[string]string, error) {
	driver, err := GetBackupStoreDriver(backupURL)
	if err != nil {
		return nil, err
	}
	backupName, volumeName, err := decodeBackupURL(backupURL)
	if err != nil {
		return nil, err
	}

	volume, err := loadVolume(volumeName, driver)
	if err != nil {
		return nil, err
	}

	backup, err := loadBackup(backupName, volumeName, driver)
	if err != nil {
		return nil, err
	}
	return fillBackupInfo(backup, volume, driver.GetURL()), nil
}

func LoadVolume(backupURL string) (*Volume, error) {
	_, volumeName, err := decodeBackupURL(backupURL)
	if err != nil {
		return nil, err
	}
	driver, err := GetBackupStoreDriver(backupURL)
	if err != nil {
		return nil, err
	}
	return loadVolume(volumeName, driver)
}
<commit_msg>Add volume name validations<commit_after>package backupstore

import (
	"fmt"
	"net/url"
	"strconv"

	"github.com/yasker/backupstore/util"
)

type Volume struct {
	Name           string
	Driver         string
	Size           int64
	CreatedTime    string
	LastBackupName string
}

type Snapshot struct {
	Name        string
	CreatedTime string
}

type Backup struct {
	Name              string
	Driver            string
	VolumeName        string
	SnapshotName      string
	SnapshotCreatedAt string
	CreatedTime       string

	Blocks     []BlockMapping `json:",omitempty"`
	SingleFile BackupFile     `json:",omitempty"`
}

var (
	backupstoreBase = "backupstore"
)

func SetBackupstoreBase(base string) {
	backupstoreBase = base
}

func GetBackupstoreBase() string {
	return backupstoreBase
}

func addVolume(volume *Volume, driver BackupStoreDriver) error {
	if volumeExists(volume.Name, driver) {
		return nil
	}

	if !util.ValidateName(volume.Name) {
		return fmt.Errorf("Invalid volume name %v", volume.Name)
	}

	if err := saveVolume(volume, driver); err != nil {
		log.Error("Fail add volume ", volume.Name)
		return err
	}
	log.Debug("Added backupstore volume ", volume.Name)

	return nil
}

func removeVolume(volumeName string, driver BackupStoreDriver) error {
	if !util.ValidateName(volumeName) {
		return fmt.Errorf("Invalid volume name %v", volumeName)
	}

	if !volumeExists(volumeName, driver) {
		return fmt.Errorf("Volume %v doesn't exist in backupstore", volumeName)
	}

	volumeDir := getVolumePath(volumeName)
	if err := driver.Remove(volumeDir); err != nil {
		return err
	}
	log.Debug("Removed volume directory in backupstore: ", volumeDir)
	log.Debug("Removed backupstore volume ", volumeName)

	return nil
}

func encodeBackupURL(backupName, volumeName, destURL string) string {
	v := url.Values{}
	v.Add("volume", volumeName)
	v.Add("backup", backupName)
	return destURL + "?" + v.Encode()
}

func decodeBackupURL(backupURL string) (string, string, error) {
	u, err := url.Parse(backupURL)
	if err != nil {
		return "", "", err
	}
	v := u.Query()
	volumeName := v.Get("volume")
	backupName := v.Get("backup")
	if !util.ValidateName(volumeName) || !util.ValidateName(backupName) {
		return "", "", fmt.Errorf("Invalid name parsed, got %v and %v", backupName, volumeName)
	}
	return backupName, volumeName, nil
}

func addListVolume(resp map[string]map[string]string, volumeName string, driver BackupStoreDriver, storageDriverName string) error {
	if volumeName == "" {
		return fmt.Errorf("Invalid empty volume Name")
	}

	if !util.ValidateName(volumeName) {
		return fmt.Errorf("Invalid volume name %v", volumeName)
	}

	backupNames, err := getBackupNamesForVolume(volumeName, driver)
	if err != nil {
		return err
	}

	volume, err := loadVolume(volumeName, driver)
	if err != nil {
		return err
	}
	//Skip any volumes not owned by specified storage driver
	if volume.Driver != storageDriverName {
		return nil
	}

	for _, backupName := range backupNames {
		backup, err := loadBackup(backupName, volumeName, driver)
		if err != nil {
			return err
		}
		r := fillBackupInfo(backup, volume, driver.GetURL())
		resp[r["BackupURL"]] = r
	}
	return nil
}

func List(volumeName, destURL, storageDriverName string) (map[string]map[string]string, error) {
	driver, err := GetBackupStoreDriver(destURL)
	if err != nil {
		return nil, err
	}
	resp := make(map[string]map[string]string)
	if volumeName != "" {
		if err = addListVolume(resp, volumeName, driver, storageDriverName); err != nil {
			return nil, err
		}
	} else {
		volumeNames, err := getVolumeNames(driver)
		if err != nil {
			return nil, err
		}
		for _, volumeName := range volumeNames {
			if err := addListVolume(resp, volumeName, driver, storageDriverName); err != nil {
				return nil, err
			}
		}
	}
	return resp, nil
}

func fillBackupInfo(backup *Backup, volume *Volume, destURL string) map[string]string {
	return map[string]string{
		"BackupName":        backup.Name,
		"BackupURL":         encodeBackupURL(backup.Name, backup.VolumeName, destURL),
		"DriverName":        volume.Driver,
		"VolumeName":        backup.VolumeName,
		"VolumeSize":        strconv.FormatInt(volume.Size, 10),
		"VolumeCreatedAt":   volume.CreatedTime,
		"SnapshotName":      backup.SnapshotName,
		"SnapshotCreatedAt": backup.SnapshotCreatedAt,
		"CreatedTime":       backup.CreatedTime,
	}
}

func GetBackupInfo(backupURL string) (map[string]string, error) {
	driver, err := GetBackupStoreDriver(backupURL)
	if err != nil {
		return nil, err
	}
	backupName, volumeName, err := decodeBackupURL(backupURL)
	if err != nil {
		return nil, err
	}

	volume, err := loadVolume(volumeName, driver)
	if err != nil {
		return nil, err
	}

	backup, err := loadBackup(backupName, volumeName, driver)
	if err != nil {
		return nil, err
	}
	return fillBackupInfo(backup, volume, driver.GetURL()), nil
}

func LoadVolume(backupURL string) (*Volume, error) {
	_, volumeName, err := decodeBackupURL(backupURL)
	if err != nil {
		return nil, err
	}
	driver, err := GetBackupStoreDriver(backupURL)
	if err != nil {
		return nil, err
	}
	return loadVolume(volumeName, driver)
}
<|endoftext|>


<commit_before>package farmer

import (
	"github.com/goarchit/archit/log"
	"github.com/valyala/gorpc"
	"io"
)

var RemoteAddr string

func newOnConnectFunc() gorpc.OnConnectFunc {
	return func(remoteAddr string, rwc io.ReadWriteCloser) (io.ReadWriteCloser, error) {
		log.Trace("Connection from", remoteAddr)
		RemoteAddr = remoteAddr
		return rwc, nil
	}
}
<commit_msg>Add some network debugging<commit_after>package farmer

import (
	"github.com/goarchit/archit/log"
	"github.com/valyala/gorpc"
	"io"
)

var RemoteAddr string

func newOnConnectFunc() gorpc.OnConnectFunc {
	return func(remoteAddr string, rwc io.ReadWriteCloser) (io.ReadWriteCloser, error) {
		log.Console("Connection from", remoteAddr)
		RemoteAddr = remoteAddr
		return rwc, nil
	}
}
<|endoftext|>


<commit_before>// Copyright ©2012 The bíogo.bam Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package bgzf

import (
	"bufio"
	"bytes"
	"compress/flate"
	"compress/gzip"
	"io"
)

// Reader implements BGZF blocked gzip decompression.
type Reader struct {
	gzip.Header
	r io.Reader

	// lastChunk is the virtual file offset
	// interval of the last successful read
	// or seek operation.
	lastChunk Chunk

	// nextBase is the file offset of the
	// block following the current block.
	nextBase int64

	active *decompressor

	// Cache is the Reader block cache. If Cache is not nil,
	// the cache is queried for blocks before an attempt to
	// read from the underlying io.Reader.
	Cache Cache

	err error
}

type decompressor struct {
	owner *Reader

	gz gzip.Reader

	// Underlying Reader.
	r flate.Reader

	// Current block size.
	blockSize int

	// Positions within underlying data stream
	srcOffset int64 // Offset of last read in underlying reader.
	offset    int64 // Current offset in stream - possibly virtual.
	mark      int64 // Offset at start of useUnderlying.

	// Buffered compressed data from read ahead.
	i   int // Current position in buffered data.
	n   int // Total size of buffered data.
	buf [MaxBlockSize]byte

	// Decompressed data.
	decompressed Block

	err error
}

func makeReader(r io.Reader) flate.Reader {
	switch r := r.(type) {
	case *decompressor:
		panic("bgzf: illegal use of internal type")
	case flate.Reader:
		return r
	default:
		return bufio.NewReader(r)
	}
}

func newDecompressor() *decompressor { return &decompressor{} }

// init initialises a decompressor to use the provided flate.Reader.
func (d *decompressor) init(r flate.Reader) (*decompressor, error) {
	d.r = r
	d.useUnderlying()
	err := d.gz.Reset(d)
	if err != nil {
		d.blockSize = -1
		return d, err
	}
	d.blockSize = expectedBlockSize(d.gz.Header)
	if d.blockSize < 0 {
		err = ErrNoBlockSize
	}
	return d, err
}

// lazyBlock conditionally creates a ready to use Block and returns whether
// the Block subsequently held by the decompressor needs to be reset before
// being filled.
func (d *decompressor) lazyBlock() bool {
	if d.decompressed == nil {
		if w, ok := d.owner.Cache.(Wrapper); ok {
			d.decompressed = w.Wrap(&block{owner: d.owner})
		} else {
			d.decompressed = &block{owner: d.owner}
		}
		return false
	}
	if !d.decompressed.ownedBy(d.owner) {
		d.decompressed.setOwner(d.owner)
	}
	return true
}

// header returns the current gzip header.
func (d *decompressor) header() gzip.Header {
	return d.gz.Header
}

// isBuffered returns whether the decompressor has buffered compressed data.
func (d *decompressor) isBuffered() bool { return d.n != 0 }

// Read provides the Read method for the decompressor's gzip.Reader.
func (d *decompressor) Read(p []byte) (int, error) {
	var (
		n   int
		err error
	)
	if d.isBuffered() {
		if d.i >= d.n {
			return 0, io.EOF
		}
		if n := d.n - d.i; len(p) > n {
			p = p[:n]
		}
		n = copy(p, d.buf[d.i:])
		d.i += n
	} else {
		n, err = d.r.Read(p)
		d.srcOffset += int64(n)
	}
	d.offset += int64(n)
	return n, err
}

// ReadByte provides the ReadByte method for the decompressor's gzip.Reader.
func (d *decompressor) ReadByte() (byte, error) {
	var (
		b   byte
		err error
	)
	if d.isBuffered() {
		if d.i == d.n {
			return 0, io.EOF
		}
		b = d.buf[d.i]
		d.i++
	} else {
		b, err = d.r.ReadByte()
		d.srcOffset++
	}
	if err == nil {
		d.offset++
	}
	return b, err
}

// reset makes the decompressor ready for reading decompressed data
// from its Block. It checks if there is a cached Block for the nextBase,
// otherwise it seeks to the correct location if decompressor is not
// correctly positioned, and then reads the compressed data and fills
// the decompressed Block.
func (d *decompressor) reset() {
	needReset := d.lazyBlock()

	if d.gotBlockFor(d.owner.nextBase) {
		return
	}

	if needReset && d.srcOffset != d.owner.nextBase {
		// It should not be possible for the expected next block base
		// to be out of register with the count reader unless Seek
		// has been called, so we know the base reader must be an
		// io.ReadSeeker.
		d.err = d.seek(d.owner.r.(io.ReadSeeker), d.owner.nextBase)
		if d.err != nil {
			return
		}
	}

	d.err = d.fill(needReset)
}

// seekRead is the seeking equivalent of reset. It checks if the seek
// is within the current Block and if not whether the seeked Block is
// cached, returning successfully if either is true. Otherwise it seeks
// to the offset and fills the decompressed Block.
func (d *decompressor) seekRead(r io.ReadSeeker, off int64) {
	d.lazyBlock()

	if off == d.decompressed.Base() && d.decompressed.hasData() {
		return
	}

	if d.gotBlockFor(off) {
		return
	}

	d.err = d.seek(r, off)
	if d.err != nil {
		return
	}

	d.err = d.fill(true)
}

// seek moves the decompressor to the specified offset using r as the
// underlying reader.
func (d *decompressor) seek(r io.ReadSeeker, off int64) error {
	_, err := r.Seek(off, 0)
	if err != nil {
		return err
	}

	type reseter interface {
		Reset(io.Reader)
	}
	switch cr := d.r.(type) {
	case reseter:
		cr.Reset(r)
	default:
		d.r = makeReader(r)
	}
	d.offset = off
	d.srcOffset = off

	return nil
}

// gotBlockFor returns true if the decompressor has access to a cache
// and that cache holds the block with given base and the correct
// owner, otherwise it returns false.
// gotBlockFor has side effects of recovering the block and putting
// the currently active block into the cache. If the cache returns
// a block owned by another reader, it is discarded.
func (d *decompressor) gotBlockFor(base int64) bool {
	if d.owner.Cache != nil {
		dec := d.decompressed
		if blk := d.owner.Cache.Get(base); blk != nil && blk.ownedBy(d.owner) {
			if dec != nil && dec.hasData() {
				// TODO(kortschak): Under some conditions, e.g. FIFO
				// cache we will be discarding a non-nil evicted Block.
				// Consider retaining these in a sync.Pool.
				d.owner.Cache.Put(dec)
			}
			if d.err = blk.seek(0); d.err == nil {
				d.decompressed = blk
				d.owner.nextBase = blk.nextBase()
				return true
			}
		}
		if dec != nil && dec.hasData() {
			dec, retained := d.owner.Cache.Put(dec)
			if retained {
				d.decompressed = dec
				d.lazyBlock()
			}
		}
	}

	return false
}

// useUnderlying set the decompressor to Read from the underlying flate.Reader.
// It marks the offset at from where the underlying reader has been used.
func (d *decompressor) useUnderlying() { d.n = 0; d.mark = d.offset }

// readAhead reads compressed data into the decompressor buffer. It reads until
// the underlying flate.Reader is positioned at the end of the gzip member in
// which the readAhead call was made. readAhead should not be called unless the
// decompressor has had init called successfully.
func (d *decompressor) readAhead() error {
	n := d.blockSize - d.deltaOffset()

	d.i, d.n = 0, n
	var err error
	lr := io.LimitedReader{R: d.r, N: int64(n)}
	for i, _n := 0, 0; i < n && err == nil; i, d.srcOffset = i+_n, d.srcOffset+int64(_n) {
		_n, err = lr.Read(d.buf[i:])
	}
	return err
}

// deltaOffset returns the number of bytes read since the last call to
// useUnderlying.
func (d *decompressor) deltaOffset() int { return int(d.offset - d.mark) }

// fill decompresses data into the decompressor's Block. If reset is true
// it first initialises the decompressor using its current flate.Reader
// and buffers the compressed data.
func (d *decompressor) fill(reset bool) error {
	dec := d.decompressed

	if reset {
		dec.setBase(d.offset)

		_, err := d.init(d.r)
		if err != nil {
			return err
		}
		err = d.readAhead()
		if err != nil {
			return err
		}
	}

	dec.setHeader(d.gz.Header)
	d.gz.Multistream(false)
	return dec.readFrom(&d.gz)
}

// expectedBlock size returns the size of the BGZF conformant gzip member.
// It returns -1 if no BGZF block size field is found.
func expectedBlockSize(h gzip.Header) int {
	i := bytes.Index(h.Extra, bgzfExtraPrefix)
	if i < 0 || i+5 >= len(h.Extra) {
		return -1
	}
	return (int(h.Extra[i+4]) | int(h.Extra[i+5])<<8) + 1
}

// NewReader returns a new BGZF reader.
//
// The number of concurrent read decompressors is specified by
// rd (currently ignored).
func NewReader(r io.Reader, rd int) (*Reader, error) {
	d, err := newDecompressor().init(makeReader(r))
	if err != nil {
		return nil, err
	}
	err = d.readAhead()
	if err != nil {
		return nil, err
	}
	bg := &Reader{
		Header: d.header(),
		r:      r,
		active: d,
	}
	d.owner = bg
	return bg, nil
}

// Offset is a BGZF virtual offset.
type Offset struct {
	File  int64
	Block uint16
}

// Chunk is a region of a BGZF file.
type Chunk struct {
	Begin Offset
	End   Offset
}

// Seek performs a seek operation to the given virtual offset.
func (bg *Reader) Seek(off Offset) error {
	rs, ok := bg.r.(io.ReadSeeker)
	if !ok {
		return ErrNotASeeker
	}

	bg.active.seekRead(rs, off.File)
	bg.err = bg.active.err
	if bg.err != nil {
		return bg.err
	}
	bg.Header = bg.active.header()
	bg.nextBase = bg.active.decompressed.nextBase()

	bg.err = bg.active.decompressed.seek(int64(off.Block))
	if bg.err == nil {
		bg.lastChunk = Chunk{Begin: off, End: off}
	}

	return bg.err
}

// LastChunk returns the region of the BGZF file read by the last read
// operation or the resulting virtual offset of the last successful
// seek operation.
func (bg *Reader) LastChunk() Chunk { return bg.lastChunk }

// Close closes the reader and releases resources.
func (bg *Reader) Close() error {
	bg.Cache = nil
	return bg.active.gz.Close()
}

// Read implements the io.Reader interface.
func (bg *Reader) Read(p []byte) (int, error) {
	if bg.err != nil {
		return 0, bg.err
	}

	dec := bg.active.decompressed

	if dec == nil || dec.len() == 0 {
		dec, bg.err = bg.resetDecompressor()
		if bg.err != nil {
			return 0, bg.err
		}
	}

	bg.lastChunk.Begin = dec.txOffset()

	var n int
	for n < len(p) && bg.err == nil {
		var _n int
		_n, bg.err = dec.Read(p[n:])
		if _n > 0 {
			bg.lastChunk.End = dec.txOffset()
		}
		n += _n
		if bg.err == io.EOF {
			if n == len(p) {
				bg.err = nil
				break
			}

			dec, bg.err = bg.resetDecompressor()
			if bg.err != nil {
				break
			}
		}
	}

	return n, bg.err
}

func (bg *Reader) resetDecompressor() (Block, error) {
	bg.active.reset()
	if bg.active.err != nil {
		return nil, bg.active.err
	}
	bg.Header = bg.active.header()
	bg.nextBase = bg.active.decompressed.nextBase()
	return bg.active.decompressed, nil
}
<commit_msg>Just keep underlying offset<commit_after>// Copyright ©2012 The bíogo.bam Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package bgzf

import (
	"bufio"
	"bytes"
	"compress/flate"
	"compress/gzip"
	"io"
)

// Reader implements BGZF blocked gzip decompression.
type Reader struct {
	gzip.Header
	r io.Reader

	// lastChunk is the virtual file offset
	// interval of the last successful read
	// or seek operation.
	lastChunk Chunk

	// nextBase is the file offset of the
	// block following the current block.
	nextBase int64

	active *decompressor

	// Cache is the Reader block cache. If Cache is not nil,
	// the cache is queried for blocks before an attempt to
	// read from the underlying io.Reader.
	Cache Cache

	err error
}

type decompressor struct {
	owner *Reader

	gz gzip.Reader

	// Underlying Reader.
	r flate.Reader

	// Current block size.
	blockSize int

	// Positions within underlying data stream
	offset int64 // Offset of last read in underlying reader.
	mark   int64 // Offset at start of useUnderlying.

	// Buffered compressed data from read ahead.
	i   int // Current position in buffered data.
	n   int // Total size of buffered data.
	buf [MaxBlockSize]byte

	// Decompressed data.
	decompressed Block

	err error
}

func makeReader(r io.Reader) flate.Reader {
	switch r := r.(type) {
	case *decompressor:
		panic("bgzf: illegal use of internal type")
	case flate.Reader:
		return r
	default:
		return bufio.NewReader(r)
	}
}

func newDecompressor() *decompressor { return &decompressor{} }

// init initialises a decompressor to use the provided flate.Reader.
func (d *decompressor) init(r flate.Reader) (*decompressor, error) {
	d.r = r
	d.useUnderlying()
	err := d.gz.Reset(d)
	if err != nil {
		d.blockSize = -1
		return d, err
	}
	d.blockSize = expectedBlockSize(d.gz.Header)
	if d.blockSize < 0 {
		err = ErrNoBlockSize
	}
	return d, err
}

// lazyBlock conditionally creates a ready to use Block and returns whether
// the Block subsequently held by the decompressor needs to be reset before
// being filled.
func (d *decompressor) lazyBlock() bool {
	if d.decompressed == nil {
		if w, ok := d.owner.Cache.(Wrapper); ok {
			d.decompressed = w.Wrap(&block{owner: d.owner})
		} else {
			d.decompressed = &block{owner: d.owner}
		}
		return false
	}
	if !d.decompressed.ownedBy(d.owner) {
		d.decompressed.setOwner(d.owner)
	}
	return true
}

// header returns the current gzip header.
func (d *decompressor) header() gzip.Header {
	return d.gz.Header
}

// isBuffered returns whether the decompressor has buffered compressed data.
func (d *decompressor) isBuffered() bool { return d.n != 0 }

// Read provides the Read method for the decompressor's gzip.Reader.
func (d *decompressor) Read(p []byte) (int, error) {
	var (
		n   int
		err error
	)
	if d.isBuffered() {
		if d.i >= d.n {
			return 0, io.EOF
		}
		if n := d.n - d.i; len(p) > n {
			p = p[:n]
		}
		n = copy(p, d.buf[d.i:])
		d.i += n
	} else {
		n, err = d.r.Read(p)
		d.offset += int64(n)
	}
	return n, err
}

// ReadByte provides the ReadByte method for the decompressor's gzip.Reader.
func (d *decompressor) ReadByte() (byte, error) {
	var (
		b   byte
		err error
	)
	if d.isBuffered() {
		if d.i == d.n {
			return 0, io.EOF
		}
		b = d.buf[d.i]
		d.i++
	} else {
		b, err = d.r.ReadByte()
		if err == nil {
			d.offset++
		}
	}
	return b, err
}

// reset makes the decompressor ready for reading decompressed data
// from its Block. It checks if there is a cached Block for the nextBase,
// otherwise it seeks to the correct location if decompressor is not
// correctly positioned, and then reads the compressed data and fills
// the decompressed Block.
func (d *decompressor) reset() {
	needReset := d.lazyBlock()

	if d.gotBlockFor(d.owner.nextBase) {
		return
	}

	if needReset && d.offset != d.owner.nextBase {
		// It should not be possible for the expected next block base
		// to be out of register with the count reader unless Seek
		// has been called, so we know the base reader must be an
		// io.ReadSeeker.
		d.err = d.seek(d.owner.r.(io.ReadSeeker), d.owner.nextBase)
		if d.err != nil {
			return
		}
	}

	d.err = d.fill(needReset)
}

// seekRead is the seeking equivalent of reset. It checks if the seek
// is within the current Block and if not whether the seeked Block is
// cached, returning successfully if either is true. Otherwise it seeks
// to the offset and fills the decompressed Block.
func (d *decompressor) seekRead(r io.ReadSeeker, off int64) {
	d.lazyBlock()

	if off == d.decompressed.Base() && d.decompressed.hasData() {
		return
	}

	if d.gotBlockFor(off) {
		return
	}

	d.err = d.seek(r, off)
	if d.err != nil {
		return
	}

	d.err = d.fill(true)
}

// seek moves the decompressor to the specified offset using r as the
// underlying reader.
func (d *decompressor) seek(r io.ReadSeeker, off int64) error {
	_, err := r.Seek(off, 0)
	if err != nil {
		return err
	}

	type reseter interface {
		Reset(io.Reader)
	}
	switch cr := d.r.(type) {
	case reseter:
		cr.Reset(r)
	default:
		d.r = makeReader(r)
	}
	d.offset = off

	return nil
}

// gotBlockFor returns true if the decompressor has access to a cache
// and that cache holds the block with given base and the correct
// owner, otherwise it returns false.
// gotBlockFor has side effects of recovering the block and putting
// the currently active block into the cache. If the cache returns
// a block owned by another reader, it is discarded.
func (d *decompressor) gotBlockFor(base int64) bool {
	if d.owner.Cache != nil {
		dec := d.decompressed
		if blk := d.owner.Cache.Get(base); blk != nil && blk.ownedBy(d.owner) {
			if dec != nil && dec.hasData() {
				// TODO(kortschak): Under some conditions, e.g. FIFO
				// cache we will be discarding a non-nil evicted Block.
				// Consider retaining these in a sync.Pool.
				d.owner.Cache.Put(dec)
			}
			if d.err = blk.seek(0); d.err == nil {
				d.decompressed = blk
				d.owner.nextBase = blk.nextBase()
				return true
			}
		}
		if dec != nil && dec.hasData() {
			dec, retained := d.owner.Cache.Put(dec)
			if retained {
				d.decompressed = dec
				d.lazyBlock()
			}
		}
	}

	return false
}

// useUnderlying set the decompressor to Read from the underlying flate.Reader.
// It marks the offset at from where the underlying reader has been used.
func (d *decompressor) useUnderlying() { d.n = 0; d.mark = d.offset }

// readAhead reads compressed data into the decompressor buffer. It reads until
// the underlying flate.Reader is positioned at the end of the gzip member in
// which the readAhead call was made. readAhead should not be called unless the
// decompressor has had init called successfully.
func (d *decompressor) readAhead() error {
	n := d.blockSize - d.deltaOffset()

	d.i, d.n = 0, n
	var err error
	lr := io.LimitedReader{R: d.r, N: int64(n)}
	for i, _n := 0, 0; i < n && err == nil; i, d.offset = i+_n, d.offset+int64(_n) {
		_n, err = lr.Read(d.buf[i:])
	}
	return err
}

// deltaOffset returns the number of bytes read since the last call to
// useUnderlying.
func (d *decompressor) deltaOffset() int { return int(d.offset - d.mark) }

// fill decompresses data into the decompressor's Block. If reset is true
// it first initialises the decompressor using its current flate.Reader
// and buffers the compressed data.
func (d *decompressor) fill(reset bool) error {
	dec := d.decompressed

	if reset {
		dec.setBase(d.offset)

		_, err := d.init(d.r)
		if err != nil {
			return err
		}
		err = d.readAhead()
		if err != nil {
			return err
		}
	}

	dec.setHeader(d.gz.Header)
	d.gz.Multistream(false)
	return dec.readFrom(&d.gz)
}

// expectedBlock size returns the size of the BGZF conformant gzip member.
// It returns -1 if no BGZF block size field is found.
func expectedBlockSize(h gzip.Header) int {
	i := bytes.Index(h.Extra, bgzfExtraPrefix)
	if i < 0 || i+5 >= len(h.Extra) {
		return -1
	}
	return (int(h.Extra[i+4]) | int(h.Extra[i+5])<<8) + 1
}

// NewReader returns a new BGZF reader.
//
// The number of concurrent read decompressors is specified by
// rd (currently ignored).
func NewReader(r io.Reader, rd int) (*Reader, error) {
	d, err := newDecompressor().init(makeReader(r))
	if err != nil {
		return nil, err
	}
	err = d.readAhead()
	if err != nil {
		return nil, err
	}
	bg := &Reader{
		Header: d.header(),
		r:      r,
		active: d,
	}
	d.owner = bg
	return bg, nil
}

// Offset is a BGZF virtual offset.
type Offset struct {
	File  int64
	Block uint16
}

// Chunk is a region of a BGZF file.
type Chunk struct {
	Begin Offset
	End   Offset
}

// Seek performs a seek operation to the given virtual offset.
func (bg *Reader) Seek(off Offset) error {
	rs, ok := bg.r.(io.ReadSeeker)
	if !ok {
		return ErrNotASeeker
	}

	bg.active.seekRead(rs, off.File)
	bg.err = bg.active.err
	if bg.err != nil {
		return bg.err
	}
	bg.Header = bg.active.header()
	bg.nextBase = bg.active.decompressed.nextBase()

	bg.err = bg.active.decompressed.seek(int64(off.Block))
	if bg.err == nil {
		bg.lastChunk = Chunk{Begin: off, End: off}
	}

	return bg.err
}

// LastChunk returns the region of the BGZF file read by the last read
// operation or the resulting virtual offset of the last successful
// seek operation.
func (bg *Reader) LastChunk() Chunk { return bg.lastChunk }

// Close closes the reader and releases resources.
func (bg *Reader) Close() error {
	bg.Cache = nil
	return bg.active.gz.Close()
}

// Read implements the io.Reader interface.
func (bg *Reader) Read(p []byte) (int, error) {
	if bg.err != nil {
		return 0, bg.err
	}

	dec := bg.active.decompressed

	if dec == nil || dec.len() == 0 {
		dec, bg.err = bg.resetDecompressor()
		if bg.err != nil {
			return 0, bg.err
		}
	}

	bg.lastChunk.Begin = dec.txOffset()

	var n int
	for n < len(p) && bg.err == nil {
		var _n int
		_n, bg.err = dec.Read(p[n:])
		if _n > 0 {
			bg.lastChunk.End = dec.txOffset()
		}
		n += _n
		if bg.err == io.EOF {
			if n == len(p) {
				bg.err = nil
				break
			}

			dec, bg.err = bg.resetDecompressor()
			if bg.err != nil {
				break
			}
		}
	}

	return n, bg.err
}

func (bg *Reader) resetDecompressor() (Block, error) {
	bg.active.reset()
	if bg.active.err != nil {
		return nil, bg.active.err
	}
	bg.Header = bg.active.header()
	bg.nextBase = bg.active.decompressed.nextBase()
	return bg.active.decompressed, nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package factom

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/FactomProject/btcutil/base58"
	ed "github.com/FactomProject/ed25519"
	"github.com/FactomProject/go-bip32"
	"github.com/FactomProject/go-bip39"
)

type addressStringType byte

const (
	InvalidAddress addressStringType = iota
	FactoidPub
	FactoidSec
	ECPub
	ECSec
)

var (
	ecPubPrefix = []byte{0x59, 0x2a}
	ecSecPrefix = []byte{0x5d, 0xb6}
	fcPubPrefix = []byte{0x5f, 0xb1}
	fcSecPrefix = []byte{0x64, 0x78}
)

func AddressStringType(s string) addressStringType {
	p := base58.Decode(s)

	if len(p) != 38 {
		return InvalidAddress
	}

	// verify the address checksum
	body := p[:len(p)-4]
	check := p[len(p)-4:]
	if !bytes.Equal(shad(body)[:4], check) {
		return InvalidAddress
	}

	prefix := p[:2]
	switch {
	case bytes.Equal(prefix, ecPubPrefix):
		return ECPub
	case bytes.Equal(prefix, ecSecPrefix):
		return ECSec
	case bytes.Equal(prefix, fcPubPrefix):
		return FactoidPub
	case bytes.Equal(prefix, fcSecPrefix):
		return FactoidSec
	default:
		return InvalidAddress
	}
}

func IsValidAddress(s string) bool {
	p := base58.Decode(s)

	if len(p) != 38 {
		return false
	}

	prefix := p[:2]
	switch {
	case bytes.Equal(prefix, ecPubPrefix):
		break
	case bytes.Equal(prefix, ecSecPrefix):
		break
	case bytes.Equal(prefix, fcPubPrefix):
		break
	case bytes.Equal(prefix, fcSecPrefix):
		break
	default:
		return false
	}

	// verify the address checksum
	body := p[:len(p)-4]
	check := p[len(p)-4:]
	if bytes.Equal(shad(body)[:4], check) {
		return true
	}

	return false
}

type ECAddress struct {
	Pub *[ed.PublicKeySize]byte
	Sec *[ed.PrivateKeySize]byte
}

func NewECAddress() *ECAddress {
	a := new(ECAddress)
	a.Pub = new([ed.PublicKeySize]byte)
	a.Sec = new([ed.PrivateKeySize]byte)
	return a
}

func (t *ECAddress) UnmarshalBinary(data []byte) error {
	_, err := t.UnmarshalBinaryData(data)
	return err
}

func (t *ECAddress) UnmarshalBinaryData(data []byte) ([]byte, error) {
	if len(data) < 32 {
		return nil, fmt.Errorf("secret key portion must be 32 bytes")
	}

	if t.Sec == nil {
		t.Sec = new([ed.PrivateKeySize]byte)
	}

	copy(t.Sec[:], data[:32])
	t.Pub = ed.GetPublicKey(t.Sec)

	return data[32:], nil
}

func (t *ECAddress) MarshalBinary() ([]byte, error) {
	return t.SecBytes(), nil
}

// GetECAddress takes a private address string (Es...) and returns an ECAddress.
func GetECAddress(s string) (*ECAddress, error) {
	if !IsValidAddress(s) {
		return nil, fmt.Errorf("Invalid Address")
	}

	p := base58.Decode(s)

	if !bytes.Equal(p[:2], ecSecPrefix) {
		return nil, fmt.Errorf("Invalid Entry Credit Private Address")
	}

	return MakeECAddress(p[2:34])
}

func MakeECAddress(sec []byte) (*ECAddress, error) {
	if len(sec) != 32 {
		return nil, fmt.Errorf("secret key portion must be 32 bytes")
	}

	a := NewECAddress()

	err := a.UnmarshalBinary(sec)
	if err != nil {
		return nil, err
	}

	return a, nil
}

func (a *ECAddress) PubBytes() []byte {
	return a.Pub[:]
}

func (a *ECAddress) PubFixed() *[32]byte {
	return a.Pub
}

func (a *ECAddress) PubString() string {
	buf := new(bytes.Buffer)

	// EC address prefix
	buf.Write(ecPubPrefix)

	// Public key
	buf.Write(a.PubBytes())

	// Checksum
	check := shad(buf.Bytes())[:4]
	buf.Write(check)

	return base58.Encode(buf.Bytes())
}

func (a *ECAddress) SecBytes() []byte {
	return a.Sec[:]
}

func (a *ECAddress) SecFixed() *[64]byte {
	return a.Sec
}

func (a *ECAddress) SecString() string {
	buf := new(bytes.Buffer)

	// EC address prefix
	buf.Write(ecSecPrefix)

	// Secret key
	buf.Write(a.SecBytes()[:32])

	// Checksum
	check := shad(buf.Bytes())[:4]
	buf.Write(check)

	return base58.Encode(buf.Bytes())
}

func (a *ECAddress) Sign(msg []byte) *[ed.SignatureSize]byte {
	return ed.Sign(a.SecFixed(), msg)
}

func (a *ECAddress) String() string {
	return a.PubString()
}

type FactoidAddress struct {
	RCD RCD
	Sec *[ed.PrivateKeySize]byte
}

func NewFactoidAddress() *FactoidAddress {
	a := new(FactoidAddress)
	r := NewRCD1()
	r.Pub = new([ed.PublicKeySize]byte)
	a.RCD = r
	a.Sec = new([ed.PrivateKeySize]byte)
	return a
}

func (t *FactoidAddress) UnmarshalBinary(data []byte) error {
	_, err := t.UnmarshalBinaryData(data)
	return err
}

func (t *FactoidAddress) UnmarshalBinaryData(data []byte) ([]byte, error) {
	if len(data) < 32 {
		return nil, fmt.Errorf("secret key portion must be 32 bytes")
	}

	if t.Sec == nil {
		t.Sec = new([ed.PrivateKeySize]byte)
	}

	copy(t.Sec[:], data[:32])
	r := NewRCD1()
	r.Pub = ed.GetPublicKey(t.Sec)
	t.RCD = r

	return data[32:], nil
}

func (t *FactoidAddress) MarshalBinary() ([]byte, error) {
	return t.SecBytes(), nil
}

// GetFactoidAddress takes a private address string (Fs...) and returns a
// FactoidAddress.
func GetFactoidAddress(s string) (*FactoidAddress, error) {
	if !IsValidAddress(s) {
		return nil, fmt.Errorf("Invalid Address")
	}

	p := base58.Decode(s)

	if !bytes.Equal(p[:2], fcSecPrefix) {
		return nil, fmt.Errorf("Invalid Factoid Private Address")
	}

	return MakeFactoidAddress(p[2:34])
}

func MakeFactoidAddress(sec []byte) (*FactoidAddress, error) {
	if len(sec) != 32 {
		return nil, fmt.Errorf("secret key portion must be 32 bytes")
	}

	a := NewFactoidAddress()
	err := a.UnmarshalBinary(sec)
	if err != nil {
		return nil, err
	}

	return a, nil
}

// MakeFactoidAddressFromMnemonic takes the 12 word string used in the Koinify
// sale and returns a Factoid Address.
func MakeFactoidAddressFromMnemonic(mnemonic string) (*FactoidAddress, error) {
	l := len(strings.Fields(mnemonic))
	if l < 12 {
		return nil, fmt.Errorf("Not enough words in mnemonic. Expecitng 12, found %d", l)
	}
	if l > 12 {
		return nil, fmt.Errorf("Too many words in mnemonic. Expecitng 12, found %d", l)
	}

	mnemonic = strings.ToLower(strings.TrimSpace(mnemonic))
	seed, err := bip39.NewSeedWithErrorChecking(mnemonic, "")
	if err != nil {
		return nil, err
	}

	masterKey, err := bip32.NewMasterKey(seed)
	if err != nil {
		return nil, err
	}

	child, err := masterKey.NewChildKey(bip32.FirstHardenedChild + 7)
	if err != nil {
		return nil, err
	}

	return MakeFactoidAddress(child.Key)
}

func (a *FactoidAddress) RCDHash() []byte {
	return a.RCD.Hash()
}

func (a *FactoidAddress) RCDType() uint8 {
	return a.RCD.Type()
}

func (a *FactoidAddress) PubBytes() []byte {
	return a.RCD.(*RCD1).PubBytes()
}

func (a *FactoidAddress) SecBytes() []byte {
	return a.Sec[:]
}

func (a *FactoidAddress) SecFixed() *[64]byte {
	return a.Sec
}

func (a *FactoidAddress) SecString() string {
	buf := new(bytes.Buffer)

	// Factoid address prefix
	buf.Write(fcSecPrefix)

	// Secret key
	buf.Write(a.SecBytes()[:32])

	// Checksum
	check := shad(buf.Bytes())[:4]
	buf.Write(check)

	return base58.Encode(buf.Bytes())
}

func (a *FactoidAddress) String() string {
	buf := new(bytes.Buffer)

	// FC address prefix
	buf.Write(fcPubPrefix)

	// RCD Hash
	buf.Write(a.RCDHash())

	// Checksum
	check := shad(buf.Bytes())[:4]
	buf.Write(check)

	return base58.Encode(buf.Bytes())
}
<commit_msg>comments for address methods<commit_after>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package factom

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/FactomProject/btcutil/base58"
	ed "github.com/FactomProject/ed25519"
	"github.com/FactomProject/go-bip32"
	"github.com/FactomProject/go-bip39"
)

type addressStringType byte

const (
	InvalidAddress addressStringType = iota
	FactoidPub
	FactoidSec
	ECPub
	ECSec
)

var (
	ecPubPrefix = []byte{0x59, 0x2a}
	ecSecPrefix = []byte{0x5d, 0xb6}
	fcPubPrefix = []byte{0x5f, 0xb1}
	fcSecPrefix = []byte{0x64, 0x78}
)

func AddressStringType(s string) addressStringType {
	p := base58.Decode(s)

	if len(p) != 38 {
		return InvalidAddress
	}

	// verify the address checksum
	body := p[:len(p)-4]
	check := p[len(p)-4:]
	if !bytes.Equal(shad(body)[:4], check) {
		return InvalidAddress
	}

	prefix := p[:2]
	switch {
	case bytes.Equal(prefix, ecPubPrefix):
		return ECPub
	case bytes.Equal(prefix, ecSecPrefix):
		return ECSec
	case bytes.Equal(prefix, fcPubPrefix):
		return FactoidPub
	case bytes.Equal(prefix, fcSecPrefix):
		return FactoidSec
	default:
		return InvalidAddress
	}
}

func IsValidAddress(s string) bool {
	p := base58.Decode(s)

	if len(p) != 38 {
		return false
	}

	prefix := p[:2]
	switch {
	case bytes.Equal(prefix, ecPubPrefix):
		break
	case bytes.Equal(prefix, ecSecPrefix):
		break
	case bytes.Equal(prefix, fcPubPrefix):
		break
	case bytes.Equal(prefix, fcSecPrefix):
		break
	default:
		return false
	}

	// verify the address checksum
	body := p[:len(p)-4]
	check := p[len(p)-4:]
	if bytes.Equal(shad(body)[:4], check) {
		return true
	}

	return false
}

type ECAddress struct {
	Pub *[ed.PublicKeySize]byte
	Sec *[ed.PrivateKeySize]byte
}

func NewECAddress() *ECAddress {
	a := new(ECAddress)
	a.Pub = new([ed.PublicKeySize]byte)
	a.Sec = new([ed.PrivateKeySize]byte)
	return a
}

func (a *ECAddress) UnmarshalBinary(data []byte) error {
	_, err := a.UnmarshalBinaryData(data)
	return err
}

func (a *ECAddress) UnmarshalBinaryData(data []byte) ([]byte, error) {
	if len(data) < 32 {
		return nil, fmt.Errorf("secret key portion must be 32 bytes")
	}

	if a.Sec == nil {
		a.Sec = new([ed.PrivateKeySize]byte)
	}

	copy(t.Sec[:], data[:32])
	a.Pub = ed.GetPublicKey(a.Sec)

	return data[32:], nil
}

func (a *ECAddress) MarshalBinary() ([]byte, error) {
	return a.SecBytes(), nil
}

// GetECAddress takes a private address string (Es...) and returns an ECAddress.
func GetECAddress(s string) (*ECAddress, error) {
	if !IsValidAddress(s) {
		return nil, fmt.Errorf("Invalid Address")
	}

	p := base58.Decode(s)

	if !bytes.Equal(p[:2], ecSecPrefix) {
		return nil, fmt.Errorf("Invalid Entry Credit Private Address")
	}

	return MakeECAddress(p[2:34])
}

func MakeECAddress(sec []byte) (*ECAddress, error) {
	if len(sec) != 32 {
		return nil, fmt.Errorf("secret key portion must be 32 bytes")
	}

	a := NewECAddress()

	err := a.UnmarshalBinary(sec)
	if err != nil {
		return nil, err
	}

	return a, nil
}

// PubBytes returns the []byte representation of the public key
func (a *ECAddress) PubBytes() []byte {
	return a.Pub[:]
}

// PubFixed returns the fixed size public key
func (a *ECAddress) PubFixed() *[ed.PublicKeySize]byte {
	return a.Pub
}

// PubString returns the string encoding of the public key
func (a *ECAddress) PubString() string {
	buf := new(bytes.Buffer)

	// EC address prefix
	buf.Write(ecPubPrefix)

	// Public key
	buf.Write(a.PubBytes())

	// Checksum
	check := shad(buf.Bytes())[:4]
	buf.Write(check)

	return base58.Encode(buf.Bytes())
}

// SecBytes returns the []byte representation of the secret key
func (a *ECAddress) SecBytes() []byte {
	return a.Sec[:]
}

// SecFixed returns the fixed size secret key
func (a *ECAddress) SecFixed() *[64]byte {
	return a.Sec
}

// SecString returns the string encoding of the secret key
func (a *ECAddress) SecString() string {
	buf := new(bytes.Buffer)

	// EC address prefix
	buf.Write(ecSecPrefix)

	// Secret key
	buf.Write(a.SecBytes()[:32])

	// Checksum
	check := shad(buf.Bytes())[:4]
	buf.Write(check)

	return base58.Encode(buf.Bytes())
}

// Sign the message with the ECAddress private key
func (a *ECAddress) Sign(msg []byte) *[ed.SignatureSize]byte {
	return ed.Sign(a.SecFixed(), msg)
}

func (a *ECAddress) String() string {
	return a.PubString()
}

type FactoidAddress struct {
	RCD RCD
	Sec *[ed.PrivateKeySize]byte
}

func NewFactoidAddress() *FactoidAddress {
	a := new(FactoidAddress)
	r := NewRCD1()
	r.Pub = new([ed.PublicKeySize]byte)
	a.RCD = r
	a.Sec = new([ed.PrivateKeySize]byte)
	return a
}

func (t *FactoidAddress) UnmarshalBinary(data []byte) error {
	_, err := t.UnmarshalBinaryData(data)
	return err
}

func (t *FactoidAddress) UnmarshalBinaryData(data []byte) ([]byte, error) {
	if len(data) < 32 {
		return nil, fmt.Errorf("secret key portion must be 32 bytes")
	}

	if t.Sec == nil {
		t.Sec = new([ed.PrivateKeySize]byte)
	}

	copy(t.Sec[:], data[:32])
	r := NewRCD1()
	r.Pub = ed.GetPublicKey(t.Sec)
	t.RCD = r

	return data[32:], nil
}

func (t *FactoidAddress) MarshalBinary() ([]byte, error) {
	return t.SecBytes(), nil
}

// GetFactoidAddress takes a private address string (Fs...) and returns a
// FactoidAddress.
func GetFactoidAddress(s string) (*FactoidAddress, error) {
	if !IsValidAddress(s) {
		return nil, fmt.Errorf("Invalid Address")
	}

	p := base58.Decode(s)

	if !bytes.Equal(p[:2], fcSecPrefix) {
		return nil, fmt.Errorf("Invalid Factoid Private Address")
	}

	return MakeFactoidAddress(p[2:34])
}

func MakeFactoidAddress(sec []byte) (*FactoidAddress, error) {
	if len(sec) != 32 {
		return nil, fmt.Errorf("secret key portion must be 32 bytes")
	}

	a := NewFactoidAddress()
	err := a.UnmarshalBinary(sec)
	if err != nil {
		return nil, err
	}

	return a, nil
}

// MakeFactoidAddressFromMnemonic takes the 12 word string used in the Koinify
// sale and returns a Factoid Address.
func MakeFactoidAddressFromMnemonic(mnemonic string) (*FactoidAddress, error) {
	l := len(strings.Fields(mnemonic))
	if l < 12 {
		return nil, fmt.Errorf("Not enough words in mnemonic. Expecitng 12, found %d", l)
	}
	if l > 12 {
		return nil, fmt.Errorf("Too many words in mnemonic. Expecitng 12, found %d", l)
	}

	mnemonic = strings.ToLower(strings.TrimSpace(mnemonic))
	seed, err := bip39.NewSeedWithErrorChecking(mnemonic, "")
	if err != nil {
		return nil, err
	}

	masterKey, err := bip32.NewMasterKey(seed)
	if err != nil {
		return nil, err
	}

	child, err := masterKey.NewChildKey(bip32.FirstHardenedChild + 7)
	if err != nil {
		return nil, err
	}

	return MakeFactoidAddress(child.Key)
}

func (a *FactoidAddress) RCDHash() []byte {
	return a.RCD.Hash()
}

func (a *FactoidAddress) RCDType() uint8 {
	return a.RCD.Type()
}

func (a *FactoidAddress) PubBytes() []byte {
	return a.RCD.(*RCD1).PubBytes()
}

func (a *FactoidAddress) SecBytes() []byte {
	return a.Sec[:]
}

func (a *FactoidAddress) SecFixed() *[64]byte {
	return a.Sec
}

func (a *FactoidAddress) SecString() string {
	buf := new(bytes.Buffer)

	// Factoid address prefix
	buf.Write(fcSecPrefix)

	// Secret key
	buf.Write(a.SecBytes()[:32])

	// Checksum
	check := shad(buf.Bytes())[:4]
	buf.Write(check)

	return base58.Encode(buf.Bytes())
}

func (a *FactoidAddress) String() string {
	buf := new(bytes.Buffer)

	// FC address prefix
	buf.Write(fcPubPrefix)

	// RCD Hash
	buf.Write(a.RCDHash())

	// Checksum
	check := shad(buf.Bytes())[:4]
	buf.Write(check)

	return base58.Encode(buf.Bytes())
}
<|endoftext|>