<commit_before>package credentials

import (
	"bytes"
	"encoding/binary"
	"errors"
	"io/ioutil"
	"strings"
	"time"
	"unsafe"

	"github.com/jcmturner/gofork/encoding/asn1"
	"gopkg.in/jcmturner/gokrb5.v6/types"
)

const (
	headerFieldTagKDCOffset = 1
)

// The first byte of the file always has the value 5.
// The value of the second byte contains the version number (1 through 4)
// Versions 1 and 2 of the file format use native byte order for integer representations.
// Versions 3 and 4 always use big-endian byte order
// After the two-byte version indicator, the file has three parts:
//   1) the header (in version 4 only)
//   2) the default principal name
//   3) a sequence of credentials

// CCache is the file credentials cache as define here: https://web.mit.edu/kerberos/krb5-latest/doc/formats/ccache_file_format.html
type CCache struct {
	Version          uint8
	Header           header
	DefaultPrincipal principal
	Credentials      []credential
	Path             string
}

type header struct {
	length uint16
	fields []headerField
}

type headerField struct {
	tag    uint16
	length uint16
	value  []byte
}

// Credential cache entry principal struct.
type principal struct {
	Realm         string
	PrincipalName types.PrincipalName
}

type credential struct {
	Client       principal
	Server       principal
	Key          types.EncryptionKey
	AuthTime     time.Time
	StartTime    time.Time
	EndTime      time.Time
	RenewTill    time.Time
	IsSKey       bool
	TicketFlags  asn1.BitString
	Addresses    []types.HostAddress
	AuthData     []types.AuthorizationDataEntry
	Ticket       []byte
	SecondTicket []byte
}

// LoadCCache loads a credential cache file into a CCache type.
func LoadCCache(cpath string) (CCache, error) {
	k, err := ioutil.ReadFile(cpath)
	if err != nil {
		return CCache{}, err
	}
	c, err := ParseCCache(k)
	c.Path = cpath
	return c, err
}

// ParseCCache byte slice of credential cache data into CCache type.
func ParseCCache(b []byte) (c CCache, err error) {
	p := 0
	//The first byte of the file always has the value 5
	if int8(b[p]) != 5 {
		err = errors.New("Invalid credential cache data. First byte does not equal 5")
		return
	}
	p++
	//Get credential cache version
	//The second byte contains the version number (1 to 4)
	c.Version = uint8(b[p])
	if c.Version < 1 || c.Version > 4 {
		err = errors.New("Invalid credential cache data. Keytab version is not within 1 to 4")
		if err != nil {
			return
		}
	}
	p++
	//Version 1 or 2 of the file format uses native byte order for integer representations. Versions 3 & 4 always uses big-endian byte order
	var endian binary.ByteOrder
	endian = binary.BigEndian
	if (c.Version == 1 || c.Version == 2) && isNativeEndianLittle() {
		endian = binary.LittleEndian
	}
	if c.Version == 4 {
		err = parseHeader(b, &p, &c, &endian)
		if err != nil {
			return
		}
	}
	c.DefaultPrincipal = parsePrincipal(b, &p, &c, &endian)
	for p < len(b) {
		cred, e := parseCredential(b, &p, &c, &endian)
		if e != nil {
			err = e
			return
		}
		c.Credentials = append(c.Credentials, cred)
	}
	return
}

func parseHeader(b []byte, p *int, c *CCache, e *binary.ByteOrder) error {
	if c.Version != 4 {
		return errors.New("Credentials cache version is not 4 so there is no header to parse.")
	}
	h := header{}
	h.length = uint16(readInt16(b, p, e))
	for *p <= int(h.length) {
		f := headerField{}
		f.tag = uint16(readInt16(b, p, e))
		f.length = uint16(readInt16(b, p, e))
		f.value = b[*p : *p+int(f.length)]
		*p += int(f.length)
		if !f.valid() {
			return errors.New("Invalid credential cache header found")
		}
		h.fields = append(h.fields, f)
	}
	c.Header = h
	return nil
}

// Parse the Keytab bytes of a principal into a Keytab entry's principal.
func parsePrincipal(b []byte, p *int, c *CCache, e *binary.ByteOrder) (princ principal) {
	if c.Version != 1 {
		//Name Type is omitted in version 1
		princ.PrincipalName.NameType = int32(readInt32(b, p, e))
	}
	nc := int(readInt32(b, p, e))
	if c.Version == 1 {
		//In version 1 the number of components includes the realm. Minus 1 to make consistent with version 2
		nc--
	}
	lenRealm := readInt32(b, p, e)
	princ.Realm = string(readBytes(b, p, int(lenRealm), e))
	for i := 0; i < int(nc); i++ {
		l := readInt32(b, p, e)
		princ.PrincipalName.NameString = append(princ.PrincipalName.NameString, string(readBytes(b, p, int(l), e)))
	}
	return princ
}

func parseCredential(b []byte, p *int, c *CCache, e *binary.ByteOrder) (cred credential, err error) {
	cred.Client = parsePrincipal(b, p, c, e)
	cred.Server = parsePrincipal(b, p, c, e)
	key := types.EncryptionKey{}
	key.KeyType = int32(readInt16(b, p, e))
	if c.Version == 3 {
		//repeated twice in version 3
		key.KeyType = int32(readInt16(b, p, e))
	}
	key.KeyValue = readData(b, p, e)
	cred.Key = key
	cred.AuthTime = readTimestamp(b, p, e)
	cred.StartTime = readTimestamp(b, p, e)
	cred.EndTime = readTimestamp(b, p, e)
	cred.RenewTill = readTimestamp(b, p, e)
	if ik := readInt8(b, p, e); ik == 0 {
		cred.IsSKey = false
	} else {
		cred.IsSKey = true
	}
	cred.TicketFlags = types.NewKrbFlags()
	cred.TicketFlags.Bytes = readBytes(b, p, 4, e)
	l := int(readInt32(b, p, e))
	cred.Addresses = make([]types.HostAddress, l, l)
	for i := range cred.Addresses {
		cred.Addresses[i] = readAddress(b, p, e)
	}
	l = int(readInt32(b, p, e))
	cred.AuthData = make([]types.AuthorizationDataEntry, l, l)
	for i := range cred.AuthData {
		cred.AuthData[i] = readAuthDataEntry(b, p, e)
	}
	cred.Ticket = readData(b, p, e)
	cred.SecondTicket = readData(b, p, e)
	return
}

// GetClientPrincipalName returns a PrincipalName type for the client the credentials cache is for.
func (c *CCache) GetClientPrincipalName() types.PrincipalName {
	return c.DefaultPrincipal.PrincipalName
}

// GetClientRealm returns the reals of the client the credentials cache is for.
func (c *CCache) GetClientRealm() string {
	return c.DefaultPrincipal.Realm
}

// GetClientCredentials returns a Credentials object representing the client of the credentials cache.
func (c *CCache) GetClientCredentials() *Credentials {
	return &Credentials{
		Username: c.DefaultPrincipal.PrincipalName.GetPrincipalNameString(),
		Realm:    c.GetClientRealm(),
		CName:    c.DefaultPrincipal.PrincipalName,
	}
}

// Contains tests if the cache contains a credential for the provided server PrincipalName
func (c *CCache) Contains(p types.PrincipalName) bool {
	for _, cred := range c.Credentials {
		if cred.Server.PrincipalName.Equal(p) {
			return true
		}
	}
	return false
}

// GetEntry returns a specific credential for the PrincipalName provided.
func (c *CCache) GetEntry(p types.PrincipalName) (credential, bool) {
	var cred credential
	var found bool
	for i := range c.Credentials {
		if c.Credentials[i].Server.PrincipalName.Equal(p) {
			cred = c.Credentials[i]
			found = true
			break
		}
	}
	if !found {
		return cred, false
	}
	return cred, true
}

// GetEntries filters out configuration entries an returns a slice of credentials.
func (c *CCache) GetEntries() []credential {
	var creds []credential
	for _, cred := range c.Credentials {
		// Filter out configuration entries
		if strings.HasPrefix(cred.Server.Realm, "X-CACHECONF") {
			continue
		}
		creds = append(creds, cred)
	}
	return creds
}

func (h *headerField) valid() bool {
	// At this time there is only one defined header field.
	// Its tag value is 1, its length is always 8.
	// Its contents are two 32-bit integers giving the seconds and microseconds
	// of the time offset of the KDC relative to the client.
	// Adding this offset to the current time on the client should give the current time on the KDC, if that offset has not changed since the initial authentication.

	// Done as a switch in case other tag values are added in the future.
	switch h.tag {
	case headerFieldTagKDCOffset:
		if h.length != 8 || len(h.value) != 8 {
			return false
		}
		return true
	}
	return false
}

func readData(b []byte, p *int, e *binary.ByteOrder) []byte {
	l := readInt32(b, p, e)
	return readBytes(b, p, int(l), e)
}

func readAddress(b []byte, p *int, e *binary.ByteOrder) types.HostAddress {
	a := types.HostAddress{}
	a.AddrType = int32(readInt16(b, p, e))
	a.Address = readData(b, p, e)
	return a
}

func readAuthDataEntry(b []byte, p *int, e *binary.ByteOrder) types.AuthorizationDataEntry {
	a := types.AuthorizationDataEntry{}
	a.ADType = int32(readInt16(b, p, e))
	a.ADData = readData(b, p, e)
	return a
}

// Read bytes representing a timestamp.
func readTimestamp(b []byte, p *int, e *binary.ByteOrder) time.Time {
	return time.Unix(int64(readInt32(b, p, e)), 0)
}

// Read bytes representing an eight bit integer.
func readInt8(b []byte, p *int, e *binary.ByteOrder) (i int8) {
	buf := bytes.NewBuffer(b[*p : *p+1])
	binary.Read(buf, *e, &i)
	*p++
	return
}

// Read bytes representing a sixteen bit integer.
func readInt16(b []byte, p *int, e *binary.ByteOrder) (i int16) {
	buf := bytes.NewBuffer(b[*p : *p+2])
	binary.Read(buf, *e, &i)
	*p += 2
	return
}

// Read bytes representing a thirty two bit integer.
func readInt32(b []byte, p *int, e *binary.ByteOrder) (i int32) {
	buf := bytes.NewBuffer(b[*p : *p+4])
	binary.Read(buf, *e, &i)
	*p += 4
	return
}

func readBytes(b []byte, p *int, s int, e *binary.ByteOrder) []byte {
	buf := bytes.NewBuffer(b[*p : *p+s])
	r := make([]byte, s)
	binary.Read(buf, *e, &r)
	*p += s
	return r
}

func isNativeEndianLittle() bool {
	var x = 0x012345678
	var p = unsafe.Pointer(&x)
	var bp = (*[4]byte)(p)

	var endian bool
	if 0x01 == bp[0] {
		endian = false
	} else if (0x78 & 0xff) == (bp[0] & 0xff) {
		endian = true
	} else {
		// Default to big endian
		endian = false
	}
	return endian
}
<commit_msg>credentials/ccache.go: Fix unconvert issues<commit_after>package credentials

import (
	"bytes"
	"encoding/binary"
	"errors"
	"io/ioutil"
	"strings"
	"time"
	"unsafe"

	"github.com/jcmturner/gofork/encoding/asn1"
	"gopkg.in/jcmturner/gokrb5.v6/types"
)

const (
	headerFieldTagKDCOffset = 1
)

// The first byte of the file always has the value 5.
// The value of the second byte contains the version number (1 through 4)
// Versions 1 and 2 of the file format use native byte order for integer representations.
// Versions 3 and 4 always use big-endian byte order
// After the two-byte version indicator, the file has three parts:
//   1) the header (in version 4 only)
//   2) the default principal name
//   3) a sequence of credentials

// CCache is the file credentials cache as define here: https://web.mit.edu/kerberos/krb5-latest/doc/formats/ccache_file_format.html
type CCache struct {
	Version          uint8
	Header           header
	DefaultPrincipal principal
	Credentials      []credential
	Path             string
}

type header struct {
	length uint16
	fields []headerField
}

type headerField struct {
	tag    uint16
	length uint16
	value  []byte
}

// Credential cache entry principal struct.
type principal struct {
	Realm         string
	PrincipalName types.PrincipalName
}

type credential struct {
	Client       principal
	Server       principal
	Key          types.EncryptionKey
	AuthTime     time.Time
	StartTime    time.Time
	EndTime      time.Time
	RenewTill    time.Time
	IsSKey       bool
	TicketFlags  asn1.BitString
	Addresses    []types.HostAddress
	AuthData     []types.AuthorizationDataEntry
	Ticket       []byte
	SecondTicket []byte
}

// LoadCCache loads a credential cache file into a CCache type.
func LoadCCache(cpath string) (CCache, error) {
	k, err := ioutil.ReadFile(cpath)
	if err != nil {
		return CCache{}, err
	}
	c, err := ParseCCache(k)
	c.Path = cpath
	return c, err
}

// ParseCCache byte slice of credential cache data into CCache type.
func ParseCCache(b []byte) (c CCache, err error) {
	p := 0
	//The first byte of the file always has the value 5
	if int8(b[p]) != 5 {
		err = errors.New("Invalid credential cache data. First byte does not equal 5")
		return
	}
	p++
	//Get credential cache version
	//The second byte contains the version number (1 to 4)
	c.Version = b[p]
	if c.Version < 1 || c.Version > 4 {
		err = errors.New("Invalid credential cache data. Keytab version is not within 1 to 4")
		if err != nil {
			return
		}
	}
	p++
	//Version 1 or 2 of the file format uses native byte order for integer representations. Versions 3 & 4 always uses big-endian byte order
	var endian binary.ByteOrder
	endian = binary.BigEndian
	if (c.Version == 1 || c.Version == 2) && isNativeEndianLittle() {
		endian = binary.LittleEndian
	}
	if c.Version == 4 {
		err = parseHeader(b, &p, &c, &endian)
		if err != nil {
			return
		}
	}
	c.DefaultPrincipal = parsePrincipal(b, &p, &c, &endian)
	for p < len(b) {
		cred, e := parseCredential(b, &p, &c, &endian)
		if e != nil {
			err = e
			return
		}
		c.Credentials = append(c.Credentials, cred)
	}
	return
}

func parseHeader(b []byte, p *int, c *CCache, e *binary.ByteOrder) error {
	if c.Version != 4 {
		return errors.New("Credentials cache version is not 4 so there is no header to parse.")
	}
	h := header{}
	h.length = uint16(readInt16(b, p, e))
	for *p <= int(h.length) {
		f := headerField{}
		f.tag = uint16(readInt16(b, p, e))
		f.length = uint16(readInt16(b, p, e))
		f.value = b[*p : *p+int(f.length)]
		*p += int(f.length)
		if !f.valid() {
			return errors.New("Invalid credential cache header found")
		}
		h.fields = append(h.fields, f)
	}
	c.Header = h
	return nil
}

// Parse the Keytab bytes of a principal into a Keytab entry's principal.
func parsePrincipal(b []byte, p *int, c *CCache, e *binary.ByteOrder) (princ principal) {
	if c.Version != 1 {
		//Name Type is omitted in version 1
		princ.PrincipalName.NameType = readInt32(b, p, e)
	}
	nc := int(readInt32(b, p, e))
	if c.Version == 1 {
		//In version 1 the number of components includes the realm. Minus 1 to make consistent with version 2
		nc--
	}
	lenRealm := readInt32(b, p, e)
	princ.Realm = string(readBytes(b, p, int(lenRealm), e))
	for i := 0; i < nc; i++ {
		l := readInt32(b, p, e)
		princ.PrincipalName.NameString = append(princ.PrincipalName.NameString, string(readBytes(b, p, int(l), e)))
	}
	return princ
}

func parseCredential(b []byte, p *int, c *CCache, e *binary.ByteOrder) (cred credential, err error) {
	cred.Client = parsePrincipal(b, p, c, e)
	cred.Server = parsePrincipal(b, p, c, e)
	key := types.EncryptionKey{}
	key.KeyType = int32(readInt16(b, p, e))
	if c.Version == 3 {
		//repeated twice in version 3
		key.KeyType = int32(readInt16(b, p, e))
	}
	key.KeyValue = readData(b, p, e)
	cred.Key = key
	cred.AuthTime = readTimestamp(b, p, e)
	cred.StartTime = readTimestamp(b, p, e)
	cred.EndTime = readTimestamp(b, p, e)
	cred.RenewTill = readTimestamp(b, p, e)
	if ik := readInt8(b, p, e); ik == 0 {
		cred.IsSKey = false
	} else {
		cred.IsSKey = true
	}
	cred.TicketFlags = types.NewKrbFlags()
	cred.TicketFlags.Bytes = readBytes(b, p, 4, e)
	l := int(readInt32(b, p, e))
	cred.Addresses = make([]types.HostAddress, l, l)
	for i := range cred.Addresses {
		cred.Addresses[i] = readAddress(b, p, e)
	}
	l = int(readInt32(b, p, e))
	cred.AuthData = make([]types.AuthorizationDataEntry, l, l)
	for i := range cred.AuthData {
		cred.AuthData[i] = readAuthDataEntry(b, p, e)
	}
	cred.Ticket = readData(b, p, e)
	cred.SecondTicket = readData(b, p, e)
	return
}

// GetClientPrincipalName returns a PrincipalName type for the client the credentials cache is for.
func (c *CCache) GetClientPrincipalName() types.PrincipalName {
	return c.DefaultPrincipal.PrincipalName
}

// GetClientRealm returns the reals of the client the credentials cache is for.
func (c *CCache) GetClientRealm() string {
	return c.DefaultPrincipal.Realm
}

// GetClientCredentials returns a Credentials object representing the client of the credentials cache.
func (c *CCache) GetClientCredentials() *Credentials {
	return &Credentials{
		Username: c.DefaultPrincipal.PrincipalName.GetPrincipalNameString(),
		Realm:    c.GetClientRealm(),
		CName:    c.DefaultPrincipal.PrincipalName,
	}
}

// Contains tests if the cache contains a credential for the provided server PrincipalName
func (c *CCache) Contains(p types.PrincipalName) bool {
	for _, cred := range c.Credentials {
		if cred.Server.PrincipalName.Equal(p) {
			return true
		}
	}
	return false
}

// GetEntry returns a specific credential for the PrincipalName provided.
func (c *CCache) GetEntry(p types.PrincipalName) (credential, bool) {
	var cred credential
	var found bool
	for i := range c.Credentials {
		if c.Credentials[i].Server.PrincipalName.Equal(p) {
			cred = c.Credentials[i]
			found = true
			break
		}
	}
	if !found {
		return cred, false
	}
	return cred, true
}

// GetEntries filters out configuration entries an returns a slice of credentials.
func (c *CCache) GetEntries() []credential {
	var creds []credential
	for _, cred := range c.Credentials {
		// Filter out configuration entries
		if strings.HasPrefix(cred.Server.Realm, "X-CACHECONF") {
			continue
		}
		creds = append(creds, cred)
	}
	return creds
}

func (h *headerField) valid() bool {
	// At this time there is only one defined header field.
	// Its tag value is 1, its length is always 8.
	// Its contents are two 32-bit integers giving the seconds and microseconds
	// of the time offset of the KDC relative to the client.
	// Adding this offset to the current time on the client should give the current time on the KDC, if that offset has not changed since the initial authentication.

	// Done as a switch in case other tag values are added in the future.
	switch h.tag {
	case headerFieldTagKDCOffset:
		if h.length != 8 || len(h.value) != 8 {
			return false
		}
		return true
	}
	return false
}

func readData(b []byte, p *int, e *binary.ByteOrder) []byte {
	l := readInt32(b, p, e)
	return readBytes(b, p, int(l), e)
}

func readAddress(b []byte, p *int, e *binary.ByteOrder) types.HostAddress {
	a := types.HostAddress{}
	a.AddrType = int32(readInt16(b, p, e))
	a.Address = readData(b, p, e)
	return a
}

func readAuthDataEntry(b []byte, p *int, e *binary.ByteOrder) types.AuthorizationDataEntry {
	a := types.AuthorizationDataEntry{}
	a.ADType = int32(readInt16(b, p, e))
	a.ADData = readData(b, p, e)
	return a
}

// Read bytes representing a timestamp.
func readTimestamp(b []byte, p *int, e *binary.ByteOrder) time.Time {
	return time.Unix(int64(readInt32(b, p, e)), 0)
}

// Read bytes representing an eight bit integer.
func readInt8(b []byte, p *int, e *binary.ByteOrder) (i int8) {
	buf := bytes.NewBuffer(b[*p : *p+1])
	binary.Read(buf, *e, &i)
	*p++
	return
}

// Read bytes representing a sixteen bit integer.
func readInt16(b []byte, p *int, e *binary.ByteOrder) (i int16) {
	buf := bytes.NewBuffer(b[*p : *p+2])
	binary.Read(buf, *e, &i)
	*p += 2
	return
}

// Read bytes representing a thirty two bit integer.
func readInt32(b []byte, p *int, e *binary.ByteOrder) (i int32) {
	buf := bytes.NewBuffer(b[*p : *p+4])
	binary.Read(buf, *e, &i)
	*p += 4
	return
}

func readBytes(b []byte, p *int, s int, e *binary.ByteOrder) []byte {
	buf := bytes.NewBuffer(b[*p : *p+s])
	r := make([]byte, s)
	binary.Read(buf, *e, &r)
	*p += s
	return r
}

func isNativeEndianLittle() bool {
	var x = 0x012345678
	var p = unsafe.Pointer(&x)
	var bp = (*[4]byte)(p)

	var endian bool
	if 0x01 == bp[0] {
		endian = false
	} else if (0x78 & 0xff) == (bp[0] & 0xff) {
		endian = true
	} else {
		// Default to big endian
		endian = false
	}
	return endian
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"strings"
	"testing"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	"github.com/mitchellh/goamz/route53"
)

func TestAccRoute53Record(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckRoute53RecordDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccRoute53RecordConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckRoute53RecordExists("aws_route53_record.default"),
				),
			},
		},
	})
}

func testAccCheckRoute53RecordDestroy(s *terraform.State) error {
	conn := testAccProvider.route53
	for _, rs := range s.Resources {
		if rs.Type != "aws_route53_record" {
			continue
		}

		parts := strings.Split(rs.ID, "_")
		zone := parts[0]
		name := parts[1]
		rType := parts[2]

		lopts := &route53.ListOpts{Name: name, Type: rType}
		resp, err := conn.ListResourceRecordSets(zone, lopts)
		if err != nil {
			return err
		}
		if len(resp.Records) == 0 {
			return nil
		}
		rec := resp.Records[0]
		if route53.FQDN(rec.Name) == route53.FQDN(name) && rec.Type == rType {
			return fmt.Errorf("Record still exists: %#v", rec)
		}
	}
	return nil
}

func testAccCheckRoute53RecordExists(n string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		conn := testAccProvider.route53
		rs, ok := s.Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.ID == "" {
			return fmt.Errorf("No hosted zone ID is set")
		}

		parts := strings.Split(rs.ID, "_")
		zone := parts[0]
		name := parts[1]
		rType := parts[2]

		lopts := &route53.ListOpts{Name: name, Type: rType}
		resp, err := conn.ListResourceRecordSets(zone, lopts)
		if err != nil {
			return err
		}
		if len(resp.Records) == 0 {
			return fmt.Errorf("Record does not exist")
		}
		rec := resp.Records[0]
		if route53.FQDN(rec.Name) == route53.FQDN(name) && rec.Type == rType {
			return nil
		}
		return fmt.Errorf("Record does not exist: %#v", rec)
	}
}

const testAccRoute53RecordConfig = `
resource "aws_route53_zone" "main" {
	name = "example.com"
}

resource "aws_route53_record" "default" {
	zone_id = "${aws_route53_zone.main.zone_id}"
	name = "www.example.com"
	type = "A"
	ttl = "30"
	records = ["127.0.0.1"]
}
`
<commit_msg>providers/aws: can create records with multiple values [GH-221]<commit_after>package aws

import (
	"fmt"
	"strings"
	"testing"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	"github.com/mitchellh/goamz/route53"
)

func TestAccRoute53Record(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckRoute53RecordDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccRoute53RecordConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckRoute53RecordExists("aws_route53_record.default"),
				),
			},
		},
	})
}

func testAccCheckRoute53RecordDestroy(s *terraform.State) error {
	conn := testAccProvider.route53
	for _, rs := range s.Resources {
		if rs.Type != "aws_route53_record" {
			continue
		}

		parts := strings.Split(rs.ID, "_")
		zone := parts[0]
		name := parts[1]
		rType := parts[2]

		lopts := &route53.ListOpts{Name: name, Type: rType}
		resp, err := conn.ListResourceRecordSets(zone, lopts)
		if err != nil {
			return err
		}
		if len(resp.Records) == 0 {
			return nil
		}
		rec := resp.Records[0]
		if route53.FQDN(rec.Name) == route53.FQDN(name) && rec.Type == rType {
			return fmt.Errorf("Record still exists: %#v", rec)
		}
	}
	return nil
}

func testAccCheckRoute53RecordExists(n string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		conn := testAccProvider.route53
		rs, ok := s.Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.ID == "" {
			return fmt.Errorf("No hosted zone ID is set")
		}

		parts := strings.Split(rs.ID, "_")
		zone := parts[0]
		name := parts[1]
		rType := parts[2]

		lopts := &route53.ListOpts{Name: name, Type: rType}
		resp, err := conn.ListResourceRecordSets(zone, lopts)
		if err != nil {
			return err
		}
		if len(resp.Records) == 0 {
			return fmt.Errorf("Record does not exist")
		}
		rec := resp.Records[0]
		if route53.FQDN(rec.Name) == route53.FQDN(name) && rec.Type == rType {
			return nil
		}
		return fmt.Errorf("Record does not exist: %#v", rec)
	}
}

const testAccRoute53RecordConfig = `
resource "aws_route53_zone" "main" {
	name = "notexample.com"
}

resource "aws_route53_record" "default" {
	zone_id = "${aws_route53_zone.main.zone_id}"
	name = "www.notexample.com"
	type = "A"
	ttl = "30"
	records = ["127.0.0.1", "127.0.0.27"]
}
`
<|endoftext|>


<commit_before>package cmder

var cmders = map[string]Cmder{}

func Register(name string, cmder Cmder) {
	cmders[name] = cmder
}

func Get(name string) Cmder {
	cmder, _ := cmders[name]
	return cmder
}

func Unregister(name string) {
	if _, ok := cmders[name]; ok {
		delete(cmders, name)
	}
}
<commit_msg>update<commit_after>package cmder

var cmders = map[string]Cmder{}

func Register(name string, cmder Cmder) {
	cmders[name] = cmder
}

func Get(name string) Cmder {
	cmder := cmders[name]
	return cmder
}

func Has(name string) bool {
	_, ok := cmders[name]
	return ok
}

func Unregister(name string) {
	delete(cmders, name)
}
<|endoftext|>


<commit_before>package middleware

import (
	check "gopkg.in/check.v1"
	"testing"
)

func Test(t *testing.T) { check.TestingT(t) }

type MiddlewareSuite struct{}

var _ = check.Suite(&MiddlewareSuite{})

func (s *MiddlewareSuite) TestNoConfig(c *check.C) {
	options := make(map[string]interface{})
	_, err := newRedirectStorageMiddleware(nil, options)
	c.Assert(err, check.ErrorMatches, "no baseurl provided")
}

func (s *MiddlewareSuite) TestMissingScheme(c *check.C) {
	options := make(map[string]interface{})
	options["baseurl"] = "example.com"
	_, err := newRedirectStorageMiddleware(nil, options)
	c.Assert(err, check.ErrorMatches, "no scheme specified for redirect baseurl")
}

func (s *MiddlewareSuite) TestHTTPS(c *check.C) {
	options := make(map[string]interface{})
	options["baseurl"] = "https://example.com"
	middleware, err := newRedirectStorageMiddleware(nil, options)
	c.Assert(err, check.Equals, nil)

	m, ok := middleware.(*redirectStorageMiddleware)
	c.Assert(ok, check.Equals, true)
	c.Assert(m.scheme, check.Equals, "https")
	c.Assert(m.host, check.Equals, "example.com")

	url, err := middleware.URLFor(nil, "/rick/data", nil)
	c.Assert(err, check.Equals, nil)
	c.Assert(url, check.Equals, "https://example.com/rick/data")
}

func (s *MiddlewareSuite) TestHTTP(c *check.C) {
	options := make(map[string]interface{})
	options["baseurl"] = "http://example.com"
	middleware, err := newRedirectStorageMiddleware(nil, options)
	c.Assert(err, check.Equals, nil)

	m, ok := middleware.(*redirectStorageMiddleware)
	c.Assert(ok, check.Equals, true)
	c.Assert(m.scheme, check.Equals, "http")
	c.Assert(m.host, check.Equals, "example.com")

	url, err := middleware.URLFor(nil, "morty/data", nil)
	c.Assert(err, check.Equals, nil)
	c.Assert(url, check.Equals, "http://example.com/morty/data")
}
<commit_msg>modify redirect test to include port<commit_after>package middleware

import (
	check "gopkg.in/check.v1"
	"testing"
)

func Test(t *testing.T) { check.TestingT(t) }

type MiddlewareSuite struct{}

var _ = check.Suite(&MiddlewareSuite{})

func (s *MiddlewareSuite) TestNoConfig(c *check.C) {
	options := make(map[string]interface{})
	_, err := newRedirectStorageMiddleware(nil, options)
	c.Assert(err, check.ErrorMatches, "no baseurl provided")
}

func (s *MiddlewareSuite) TestMissingScheme(c *check.C) {
	options := make(map[string]interface{})
	options["baseurl"] = "example.com"
	_, err := newRedirectStorageMiddleware(nil, options)
	c.Assert(err, check.ErrorMatches, "no scheme specified for redirect baseurl")
}

func (s *MiddlewareSuite) TestHttpsPort(c *check.C) {
	options := make(map[string]interface{})
	options["baseurl"] = "https://example.com:5443"
	middleware, err := newRedirectStorageMiddleware(nil, options)
	c.Assert(err, check.Equals, nil)

	m, ok := middleware.(*redirectStorageMiddleware)
	c.Assert(ok, check.Equals, true)
	c.Assert(m.scheme, check.Equals, "https")
	c.Assert(m.host, check.Equals, "example.com:5443")

	url, err := middleware.URLFor(nil, "/rick/data", nil)
	c.Assert(err, check.Equals, nil)
	c.Assert(url, check.Equals, "https://example.com:5443/rick/data")
}

func (s *MiddlewareSuite) TestHTTP(c *check.C) {
	options := make(map[string]interface{})
	options["baseurl"] = "http://example.com"
	middleware, err := newRedirectStorageMiddleware(nil, options)
	c.Assert(err, check.Equals, nil)

	m, ok := middleware.(*redirectStorageMiddleware)
	c.Assert(ok, check.Equals, true)
	c.Assert(m.scheme, check.Equals, "http")
	c.Assert(m.host, check.Equals, "example.com")

	url, err := middleware.URLFor(nil, "morty/data", nil)
	c.Assert(err, check.Equals, nil)
	c.Assert(url, check.Equals, "http://example.com/morty/data")
}
<|endoftext|>


<commit_before>package registry

import (
	"github.com/dotcloud/docker/engine"
)

// Service exposes registry capabilities in the standard Engine
// interface. Once installed, it extends the engine with the
// following calls:
//
//  'auth': Authenticate against the public registry
//  'search': Search for images on the public registry (TODO)
//  'pull': Download images from any registry (TODO)
//  'push': Upload images to any registry (TODO)
type Service struct {
}

// NewService returns a new instance of Service ready to be
// installed no an engine.
func NewService() *Service {
	return &Service{}
}

// Install installs registry capabilities to eng.
func (s *Service) Install(eng *engine.Engine) error {
	eng.Register("auth", s.Auth)
	return nil
}

// Auth contacts the public registry with the provided credentials,
// and returns OK if authentication was sucessful.
// It can be used to verify the validity of a client's credentials.
func (s *Service) Auth(job *engine.Job) engine.Status {
	var (
		err        error
		authConfig = &AuthConfig{}
	)

	job.GetenvJson("authConfig", authConfig)
	// TODO: this is only done here because auth and registry need to be merged into one pkg
	if addr := authConfig.ServerAddress; addr != "" && addr != IndexServerAddress() {
		addr, err = ExpandAndVerifyRegistryUrl(addr)
		if err != nil {
			return job.Error(err)
		}
		authConfig.ServerAddress = addr
	}
	status, err := Login(authConfig, HTTPRequestFactory(nil))
	if err != nil {
		return job.Error(err)
	}
	job.Printf("%s\n", status)
	return engine.StatusOK
}
<commit_msg>Move 'search' to the registry subsystem<commit_after>package registry

import (
	"github.com/dotcloud/docker/engine"
)

// Service exposes registry capabilities in the standard Engine
// interface. Once installed, it extends the engine with the
// following calls:
//
//  'auth': Authenticate against the public registry
//  'search': Search for images on the public registry
//  'pull': Download images from any registry (TODO)
//  'push': Upload images to any registry (TODO)
type Service struct {
}

// NewService returns a new instance of Service ready to be
// installed no an engine.
func NewService() *Service {
	return &Service{}
}

// Install installs registry capabilities to eng.
func (s *Service) Install(eng *engine.Engine) error {
	eng.Register("auth", s.Auth)
	eng.Register("search", s.Search)
	return nil
}

// Auth contacts the public registry with the provided credentials,
// and returns OK if authentication was sucessful.
// It can be used to verify the validity of a client's credentials.
func (s *Service) Auth(job *engine.Job) engine.Status {
	var (
		err        error
		authConfig = &AuthConfig{}
	)

	job.GetenvJson("authConfig", authConfig)
	// TODO: this is only done here because auth and registry need to be merged into one pkg
	if addr := authConfig.ServerAddress; addr != "" && addr != IndexServerAddress() {
		addr, err = ExpandAndVerifyRegistryUrl(addr)
		if err != nil {
			return job.Error(err)
		}
		authConfig.ServerAddress = addr
	}
	status, err := Login(authConfig, HTTPRequestFactory(nil))
	if err != nil {
		return job.Error(err)
	}
	job.Printf("%s\n", status)
	return engine.StatusOK
}

// Search queries the public registry for images matching the specified
// search terms, and returns the results.
//
// Argument syntax: search TERM
//
// Option environment:
//	'authConfig': json-encoded credentials to authenticate against the registry.
//		The search extends to images only accessible via the credentials.
//
//	'metaHeaders': extra HTTP headers to include in the request to the registry.
//		The headers should be passed as a json-encoded dictionary.
//
// Output:
//	Results are sent as a collection of structured messages (using engine.Table).
//	Each result is sent as a separate message.
//	Results are ordered by number of stars on the public registry.
func (s *Service) Search(job *engine.Job) engine.Status {
	if n := len(job.Args); n != 1 {
		return job.Errorf("Usage: %s TERM", job.Name)
	}
	var (
		term        = job.Args[0]
		metaHeaders = map[string][]string{}
		authConfig  = &AuthConfig{}
	)
	job.GetenvJson("authConfig", authConfig)
	job.GetenvJson("metaHeaders", metaHeaders)

	r, err := NewRegistry(authConfig, HTTPRequestFactory(metaHeaders), IndexServerAddress())
	if err != nil {
		return job.Error(err)
	}
	results, err := r.SearchRepositories(term)
	if err != nil {
		return job.Error(err)
	}
	outs := engine.NewTable("star_count", 0)
	for _, result := range results.Results {
		out := &engine.Env{}
		out.Import(result)
		outs.Add(out)
	}
	outs.ReverseSort()
	if _, err := outs.WriteListTo(job.Stdout); err != nil {
		return job.Error(err)
	}
	return engine.StatusOK
}
<|endoftext|>


<commit_before>package sqldb_test

import (
	"encoding/json"

	"github.com/cloudfoundry-incubator/bbs/db/sqldb"
	"github.com/cloudfoundry-incubator/bbs/models"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Version", func() {
	Describe("SetVersion", func() {
		Context("when the version is not set", func() {
			It("sets the version into the database", func() {
				expectedVersion := &models.Version{CurrentVersion: 99, TargetVersion: 100}
				err := sqlDB.SetVersion(logger, expectedVersion)
				Expect(err).NotTo(HaveOccurred())

				rows, err := db.Query("SELECT value FROM configurations WHERE id = ?", sqldb.VersionID)
				Expect(err).NotTo(HaveOccurred())

				Expect(rows.Next()).To(BeTrue())

				var versionData string
				err = rows.Scan(&versionData)
				Expect(err).NotTo(HaveOccurred())

				var actualVersion models.Version
				err = json.Unmarshal([]byte(versionData), &actualVersion)
				Expect(err).NotTo(HaveOccurred())

				Expect(actualVersion).To(Equal(*expectedVersion))
			})
		})

		Context("when a version is already set", func() {
			var existingVersion *models.Version
			BeforeEach(func() {
				existingVersion = &models.Version{CurrentVersion: 99, TargetVersion: 100}
				versionJSON, err := json.Marshal(existingVersion)
				Expect(err).NotTo(HaveOccurred())

				result, err := db.Exec("INSERT INTO configurations (id, value) VALUES (?, ?)", sqldb.VersionID, versionJSON)
				Expect(err).NotTo(HaveOccurred())
				Expect(result.RowsAffected()).To(BeEquivalentTo(1))
			})

			It("updates the version in the db", func() {
				version := &models.Version{CurrentVersion: 20, TargetVersion: 1001}

				err := sqlDB.SetVersion(logger, version)
				Expect(err).NotTo(HaveOccurred())

				rows, err := db.Query("SELECT value FROM configurations WHERE id = ?", sqldb.VersionID)
				Expect(err).NotTo(HaveOccurred())

				Expect(rows.Next()).To(BeTrue())

				var versionData string
				err = rows.Scan(&versionData)
				Expect(err).NotTo(HaveOccurred())

				var actualVersion models.Version
				err = json.Unmarshal([]byte(versionData), &actualVersion)
				Expect(err).NotTo(HaveOccurred())

				Expect(actualVersion).To(Equal(*version))
			})
		})
	})

	Describe("Version", func() {
		Context("when the version exists", func() {
			It("retrieves the version from the database", func() {
				expectedVersion := &models.Version{CurrentVersion: 199, TargetVersion: 200}
				value, err := json.Marshal(expectedVersion)
				Expect(err).NotTo(HaveOccurred())

				_, err = db.Exec("INSERT INTO configurations (id, value) VALUES (?, ?)", sqldb.VersionID, value)
				Expect(err).NotTo(HaveOccurred())

				version, err := sqlDB.Version(logger)
				Expect(err).NotTo(HaveOccurred())

				Expect(*version).To(Equal(*expectedVersion))
			})
		})

		Context("when the version key does not exist", func() {
			It("returns a ErrResourceNotFound", func() {
				version, err := sqlDB.Version(logger)
				Expect(err).To(MatchError(models.ErrResourceNotFound))
				Expect(version).To(BeNil())
			})
		})

		Context("when the version key is not valid json", func() {
			It("returns a ErrDeserialize", func() {
				_, err := db.Exec("INSERT INTO configurations (id, value) VALUES (?, ?)", sqldb.VersionID, "{{")
				Expect(err).NotTo(HaveOccurred())

				_, err = sqlDB.Version(logger)
				Expect(err).To(MatchError(models.ErrDeserializenk))
			})
		})
	})
})
<commit_msg>Fix typo in sql tests<commit_after>package sqldb_test

import (
	"encoding/json"

	"github.com/cloudfoundry-incubator/bbs/db/sqldb"
	"github.com/cloudfoundry-incubator/bbs/models"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Version", func() {
	Describe("SetVersion", func() {
		Context("when the version is not set", func() {
			It("sets the version into the database", func() {
				expectedVersion := &models.Version{CurrentVersion: 99, TargetVersion: 100}
				err := sqlDB.SetVersion(logger, expectedVersion)
				Expect(err).NotTo(HaveOccurred())

				rows, err := db.Query("SELECT value FROM configurations WHERE id = ?", sqldb.VersionID)
				Expect(err).NotTo(HaveOccurred())

				Expect(rows.Next()).To(BeTrue())

				var versionData string
				err = rows.Scan(&versionData)
				Expect(err).NotTo(HaveOccurred())

				var actualVersion models.Version
				err = json.Unmarshal([]byte(versionData), &actualVersion)
				Expect(err).NotTo(HaveOccurred())

				Expect(actualVersion).To(Equal(*expectedVersion))
			})
		})

		Context("when a version is already set", func() {
			var existingVersion *models.Version
			BeforeEach(func() {
				existingVersion = &models.Version{CurrentVersion: 99, TargetVersion: 100}
				versionJSON, err := json.Marshal(existingVersion)
				Expect(err).NotTo(HaveOccurred())

				result, err := db.Exec("INSERT INTO configurations (id, value) VALUES (?, ?)", sqldb.VersionID, versionJSON)
				Expect(err).NotTo(HaveOccurred())
				Expect(result.RowsAffected()).To(BeEquivalentTo(1))
			})

			It("updates the version in the db", func() {
				version := &models.Version{CurrentVersion: 20, TargetVersion: 1001}

				err := sqlDB.SetVersion(logger, version)
				Expect(err).NotTo(HaveOccurred())

				rows, err := db.Query("SELECT value FROM configurations WHERE id = ?", sqldb.VersionID)
				Expect(err).NotTo(HaveOccurred())

				Expect(rows.Next()).To(BeTrue())

				var versionData string
				err = rows.Scan(&versionData)
				Expect(err).NotTo(HaveOccurred())

				var actualVersion models.Version
				err = json.Unmarshal([]byte(versionData), &actualVersion)
				Expect(err).NotTo(HaveOccurred())

				Expect(actualVersion).To(Equal(*version))
			})
		})
	})

	Describe("Version", func() {
		Context("when the version exists", func() {
			It("retrieves the version from the database", func() {
				expectedVersion := &models.Version{CurrentVersion: 199, TargetVersion: 200}
				value, err := json.Marshal(expectedVersion)
				Expect(err).NotTo(HaveOccurred())

				_, err = db.Exec("INSERT INTO configurations (id, value) VALUES (?, ?)", sqldb.VersionID, value)
				Expect(err).NotTo(HaveOccurred())

				version, err := sqlDB.Version(logger)
				Expect(err).NotTo(HaveOccurred())

				Expect(*version).To(Equal(*expectedVersion))
			})
		})

		Context("when the version key does not exist", func() {
			It("returns a ErrResourceNotFound", func() {
				version, err := sqlDB.Version(logger)
				Expect(err).To(MatchError(models.ErrResourceNotFound))
				Expect(version).To(BeNil())
			})
		})

		Context("when the version key is not valid json", func() {
			It("returns a ErrDeserialize", func() {
				_, err := db.Exec("INSERT INTO configurations (id, value) VALUES (?, ?)", sqldb.VersionID, "{{")
				Expect(err).NotTo(HaveOccurred())

				_, err = sqlDB.Version(logger)
				Expect(err).To(MatchError(models.ErrDeserialize))
			})
		})
	})
})
<|endoftext|>


<commit_before>// Copyright 2016 The Serviced 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 rpcutils

import (
	"bytes"
	"encoding/binary"
	"errors"
	"io"
	"net/rpc"
	"net/rpc/jsonrpc"
	"sync"

	"github.com/control-center/serviced/auth"
	"github.com/control-center/serviced/logging"
)

var (
	// RPC Calls that do not require authentication or who handle authentication separately:
	NonAuthenticatingCalls = []string{"Master.AuthenticateHost", "Agent.BuildHost"}
	// RPC calls that do not require admin access:
	NonAdminRequiredCalls = map[string]struct{}{
		"Master.GetHost":                         struct{}{},
		"Master.GetHosts":                        struct{}{},
		"Master.GetEvaluatedService":             struct{}{},
		"Master.GetSystemUser":                   struct{}{},
		"Master.ReportHealthStatus":              struct{}{},
		"Master.ReportInstanceDead":              struct{}{},
		"ControlCenter.GetServices":              struct{}{},
		"ControlCenterAgent.GetEvaluatedService": struct{}{},
		"ControlCenterAgent.GetHostID":           struct{}{},
		"ControlCenterAgent.GetZkInfo":           struct{}{},
		"ControlCenterAgent.Ping":                struct{}{},
		"ControlCenterAgent.GetISvcEndpoints":    struct{}{},
		"ControlCenterAgent.ReportHealthStatus":  struct{}{},
		"ControlCenterAgent.ReportInstanceDead":  struct{}{},
		"ControlCenterAgent.SendLogMessage":      struct{}{},
	}
	endian = binary.BigEndian

	ErrNoAdmin = errors.New("Delegate does not have admin access")

	log = logging.PackageLogger()
)

// Checks the RPC method name to see if authentication is required.
//  If it is, calls on the client side will include a signed header, which will be
//  Verified on the server side
func requiresAuthentication(callName string) bool {
	for _, name := range NonAuthenticatingCalls {
		if name == callName {
			return false
		}
	}
	return true
}

// Checks the RPC method name to see if admin-level permissions are required.
//  If they are, it will also check the "admin" attribute on the identity after validating it.
func requiresAdmin(callName string) bool {
	_, ok := NonAdminRequiredCalls[callName]
	return !ok
}

// We nead a ReadWriteCloser that we can pass to the underlying codec and use
//  To buffer requests and responses from the actual connection
type ByteBufferReadWriteCloser struct {
	ReadBuff  bytes.Buffer // Reads will happen from this buffer
	WriteBuff bytes.Buffer // Writes will happen to this buffer
}

func (b *ByteBufferReadWriteCloser) Read(p []byte) (int, error) {
	return b.ReadBuff.Read(p)
}

func (b *ByteBufferReadWriteCloser) Write(p []byte) (int, error) {
	return b.WriteBuff.Write(p)
}

func (b *ByteBufferReadWriteCloser) Close() error {
	return nil
}

type ServerCodecCreator func(io.ReadWriteCloser) rpc.ServerCodec

// Server Codec
type AuthServerCodec struct {
	conn         io.ReadWriteCloser
	buff         *ByteBufferReadWriteCloser
	wrappedcodec rpc.ServerCodec
	parser       auth.RPCHeaderParser
	wBuffMutex   sync.Mutex // Make sure we buffer one response at a time
	lastError    error
}

func NewDefaultAuthServerCodec(conn io.ReadWriteCloser) rpc.ServerCodec {
	return NewAuthServerCodec(conn, jsonrpc.NewServerCodec, &auth.RPCHeaderHandler{})
}

func NewAuthServerCodec(conn io.ReadWriteCloser, createCodec ServerCodecCreator, parser auth.RPCHeaderParser) rpc.ServerCodec {
	buff := &ByteBufferReadWriteCloser{}
	return &AuthServerCodec{
		conn:         conn,
		buff:         buff,
		wrappedcodec: createCodec(buff),
		parser:       parser,
	}
}

// Reads the request header and populates the rpc.Request object.
//  This implementation reads the auth header off the stream first, then
//  lets the underlying codec read the rest.
//  Finally, it validates the identity if necessary.
func (a *AuthServerCodec) ReadRequestHeader(r *rpc.Request) error {

	// There is no need for synchronization here, since go's RPC server
	//  ensures that requests are read one-at-a-time

	// Reset state
	a.lastError = nil
	a.buff.ReadBuff.Reset()

	ident, body, err := a.parser.ReadHeader(a.conn)
	if err != nil {
		log.WithError(err).WithField("ServiceMethod", r.ServiceMethod).Debug("Could not authenticate RPC request")
		if e, ok := err.(*auth.AuthHeaderError); ok {
			body = e.Payload
			a.lastError = e.Err
		} else {
			return err
		}
	}

	// Now write the actual request to the buffer
	if _, err = a.buff.ReadBuff.Write(body); err != nil {
		return err
	}

	// Let the underlying codec read the request from the buffer and parse it
	if err := a.wrappedcodec.ReadRequestHeader(r); err != nil {
		return err
	}

	log.WithField("ServiceMethod", r.ServiceMethod).Debug("Received RPC request")

	// Now we can get the method name from r and authenticate if required
	//  If this fails, save the error to return later
	//  If we return an error now, the server will simply close the connection
	//  This is safe because go's rpc server always calls ReadRequestHeader and ReadRequestBody back-to-back
	//   (unless ReadRequestHeader returns an error)
	if requiresAuthentication(r.ServiceMethod) {
		if a.lastError == nil {
			if requiresAdmin(r.ServiceMethod) && (ident == nil || !ident.HasAdminAccess()) {
				log.WithField("ServiceMethod", r.ServiceMethod).Debug("Received unauthorized RPC request")
				a.lastError = ErrNoAdmin
			}
		}
		//TODO: save the identity so we can inject it into the request body later
	}
	return nil
}

// Decodes the request and populates the body object with the body of the request
//  We don't change anything here, just let the underlying codec handle it.
//  This always gets called after ReadRequestHeader
func (a *AuthServerCodec) ReadRequestBody(body interface{}) error {
	if a.lastError != nil {
		return a.lastError
	}
	// TODO: Use reflection and add the identity to the body if necessary
	return a.wrappedcodec.ReadRequestBody(body)
}

//  Encodes the response before sending it back down to the client.
//  We don't change anything here, just let the underlying codec handle it.
func (a *AuthServerCodec) WriteResponse(r *rpc.Response, body interface{}) error {
	// We do need a lock here, because the ServerCodec interface specifies
	//  that WriteResponse must be safe for concurrent use by multiple goroutines
	a.wBuffMutex.Lock()
	defer a.wBuffMutex.Unlock()

	a.buff.WriteBuff.Reset()

	// Let the underlying codec write the response to the buffer
	if err := a.wrappedcodec.WriteResponse(r, body); err != nil {
		return err
	}

	// Get the response from the buffer and write it to the actual connection
	response := a.buff.WriteBuff.Bytes()
	if err := auth.WriteLengthAndBytes(response, a.conn); err != nil {
		return err
	}

	return nil
}

// Closes the connection on the server side
//  We don't change anything here, just let the underlying codec handle it.
func (a *AuthServerCodec) Close() error {
	var err error
	if err = a.wrappedcodec.Close(); err != nil {
		log.WithError(err).Debug("Error closing wrapped RPC client codec")
	}
	if ourErr := a.conn.Close(); ourErr != nil {
		log.WithError(ourErr).Debug("Error closing RPC client connection")
		// This error is probably more important
		err = ourErr
	}
	return err
}

// Client Codec
type ClientCodecCreator func(io.ReadWriteCloser) rpc.ClientCodec
type AuthClientCodec struct {
	conn          io.ReadWriteCloser
	buff          *ByteBufferReadWriteCloser
	wrappedcodec  rpc.ClientCodec
	headerBuilder auth.RPCHeaderBuilder
	wBuffMutex    sync.Mutex // Make sure we buffer a whole request before starting the next one
}

func NewDefaultAuthClientCodec(conn io.ReadWriteCloser) rpc.ClientCodec {
	return NewAuthClientCodec(conn, jsonrpc.NewClientCodec, &auth.RPCHeaderHandler{})
}

func NewAuthClientCodec(conn io.ReadWriteCloser, createCodec ClientCodecCreator, headerBuilder auth.RPCHeaderBuilder) rpc.ClientCodec {
	buff := &ByteBufferReadWriteCloser{}
	return &AuthClientCodec{
		conn:          conn,
		buff:          buff,
		wrappedcodec:  createCodec(buff),
		headerBuilder: headerBuilder,
	}
}

// Encodes the request and sends it to the server.
// This implementation gets an auth header when appropriate, and writes it to the stream
//  before letting the underlying codec send the rest of the request.
func (a *AuthClientCodec) WriteRequest(r *rpc.Request, body interface{}) error {
	// Lock to ensure we write the header and the rest of the request back-to-back
	//  This method may be called by multiple goroutines concurrently
	a.wBuffMutex.Lock()
	defer a.wBuffMutex.Unlock()
	a.buff.WriteBuff.Reset()

	// Let the underlying codec write the request to the buffer
	if err := a.wrappedcodec.WriteRequest(r, body); err != nil {
		return err
	}

	// Get the request off the buffer
	request := a.buff.WriteBuff.Bytes()

	needsAuth := requiresAuthentication(r.ServiceMethod)
	if err := a.headerBuilder.WriteHeader(a.conn, request, needsAuth); err != nil {
		return err
	}

	log.WithField("ServiceMethod", r.ServiceMethod).Debug("Successfully sent RPC request")

	return nil
}

// Decodes the response and reads the header, building the rpc.Response object
//  We don't change anything here, just let the underlying codec handle it.
func (a *AuthClientCodec) ReadResponseHeader(r *rpc.Response) error {

	// No need for synchronization here, Go's RPC Client makes sure only
	//  One response is read at a time.

	a.buff.ReadBuff.Reset()

	// Read the response from the connection
	response, err := auth.ReadLengthAndBytes(a.conn)
	if err != nil {
		// It is common to get harmless errors here whenever the client is closed
		return err
	}

	// Write the response to the buffer
	if _, err = a.buff.ReadBuff.Write(response); err != nil {
		return err
	}

	// Let the underlying codec read and parse the response from the buffer
	if err = a.wrappedcodec.ReadResponseHeader(r); err != nil {
		return err
	}

	log.WithField("ServiceMethod", r.ServiceMethod).Debug("Successfully read RPC response")

	return nil
}

// Decodes the body of the response and builds the body object.
// We don't change anything here, just let the underlying codec handle it.
func (a *AuthClientCodec) ReadResponseBody(body interface{}) error {
	return a.wrappedcodec.ReadResponseBody(body)
}

// Closes the connection on the client side
//  We don't change anything here, just let the underlying codec handle it.
func (a *AuthClientCodec) Close() error {
	var err error
	if err = a.wrappedcodec.Close(); err != nil {
		log.WithError(err).Debug("Error closing wrapped RPC client codec")
	}
	if ourErr := a.conn.Close(); ourErr != nil {
		log.WithError(ourErr).Debug("Error closing RPC client connection")
		// This error is more important
		err = ourErr
	}
	return err
}

// NewDefaultAuthClient returns a new rpc.Client that uses our default client codec
func NewDefaultAuthClient(conn io.ReadWriteCloser) *rpc.Client {
	return rpc.NewClientWithCodec(NewDefaultAuthClientCodec(conn))
}
<commit_msg>Add Master.UpdateHost to the non-admin RPC list<commit_after>// Copyright 2016 The Serviced 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 rpcutils

import (
	"bytes"
	"encoding/binary"
	"errors"
	"io"
	"net/rpc"
	"net/rpc/jsonrpc"
	"sync"

	"github.com/control-center/serviced/auth"
	"github.com/control-center/serviced/logging"
)

var (
	// RPC Calls that do not require authentication or who handle authentication separately:
	NonAuthenticatingCalls = []string{"Master.AuthenticateHost", "Agent.BuildHost"}
	// RPC calls that do not require admin access:
	NonAdminRequiredCalls = map[string]struct{}{
		"Master.GetHost":                         struct{}{},
		"Master.GetHosts":                        struct{}{},
		"Master.GetEvaluatedService":             struct{}{},
		"Master.GetSystemUser":                   struct{}{},
		"Master.ReportHealthStatus":              struct{}{},
		"Master.ReportInstanceDead":              struct{}{},
		"Master.UpdateHost":                      struct{}{},
		"ControlCenter.GetServices":              struct{}{},
		"ControlCenterAgent.GetEvaluatedService": struct{}{},
		"ControlCenterAgent.GetHostID":           struct{}{},
		"ControlCenterAgent.GetZkInfo":           struct{}{},
		"ControlCenterAgent.Ping":                struct{}{},
		"ControlCenterAgent.GetISvcEndpoints":    struct{}{},
		"ControlCenterAgent.ReportHealthStatus":  struct{}{},
		"ControlCenterAgent.ReportInstanceDead":  struct{}{},
		"ControlCenterAgent.SendLogMessage":      struct{}{},
	}
	endian = binary.BigEndian

	ErrNoAdmin = errors.New("Delegate does not have admin access")

	log = logging.PackageLogger()
)

// Checks the RPC method name to see if authentication is required.
//  If it is, calls on the client side will include a signed header, which will be
//  Verified on the server side
func requiresAuthentication(callName string) bool {
	for _, name := range NonAuthenticatingCalls {
		if name == callName {
			return false
		}
	}
	return true
}

// Checks the RPC method name to see if admin-level permissions are required.
//  If they are, it will also check the "admin" attribute on the identity after validating it.
func requiresAdmin(callName string) bool {
	_, ok := NonAdminRequiredCalls[callName]
	return !ok
}

// We nead a ReadWriteCloser that we can pass to the underlying codec and use
//  To buffer requests and responses from the actual connection
type ByteBufferReadWriteCloser struct {
	ReadBuff  bytes.Buffer // Reads will happen from this buffer
	WriteBuff bytes.Buffer // Writes will happen to this buffer
}

func (b *ByteBufferReadWriteCloser) Read(p []byte) (int, error) {
	return b.ReadBuff.Read(p)
}

func (b *ByteBufferReadWriteCloser) Write(p []byte) (int, error) {
	return b.WriteBuff.Write(p)
}

func (b *ByteBufferReadWriteCloser) Close() error {
	return nil
}

type ServerCodecCreator func(io.ReadWriteCloser) rpc.ServerCodec

// Server Codec
type AuthServerCodec struct {
	conn         io.ReadWriteCloser
	buff         *ByteBufferReadWriteCloser
	wrappedcodec rpc.ServerCodec
	parser       auth.RPCHeaderParser
	wBuffMutex   sync.Mutex // Make sure we buffer one response at a time
	lastError    error
}

func NewDefaultAuthServerCodec(conn io.ReadWriteCloser) rpc.ServerCodec {
	return NewAuthServerCodec(conn, jsonrpc.NewServerCodec, &auth.RPCHeaderHandler{})
}

func NewAuthServerCodec(conn io.ReadWriteCloser, createCodec ServerCodecCreator, parser auth.RPCHeaderParser) rpc.ServerCodec {
	buff := &ByteBufferReadWriteCloser{}
	return &AuthServerCodec{
		conn:         conn,
		buff:         buff,
		wrappedcodec: createCodec(buff),
		parser:       parser,
	}
}

// Reads the request header and populates the rpc.Request object.
//  This implementation reads the auth header off the stream first, then
//  lets the underlying codec read the rest.
//  Finally, it validates the identity if necessary.
func (a *AuthServerCodec) ReadRequestHeader(r *rpc.Request) error {

	// There is no need for synchronization here, since go's RPC server
	//  ensures that requests are read one-at-a-time

	// Reset state
	a.lastError = nil
	a.buff.ReadBuff.Reset()

	ident, body, err := a.parser.ReadHeader(a.conn)
	if err != nil {
		log.WithError(err).WithField("ServiceMethod", r.ServiceMethod).Debug("Could not authenticate RPC request")
		if e, ok := err.(*auth.AuthHeaderError); ok {
			body = e.Payload
			a.lastError = e.Err
		} else {
			return err
		}
	}

	// Now write the actual request to the buffer
	if _, err = a.buff.ReadBuff.Write(body); err != nil {
		return err
	}

	// Let the underlying codec read the request from the buffer and parse it
	if err := a.wrappedcodec.ReadRequestHeader(r); err != nil {
		return err
	}

	log.WithField("ServiceMethod", r.ServiceMethod).Debug("Received RPC request")

	// Now we can get the method name from r and authenticate if required
	//  If this fails, save the error to return later
	//  If we return an error now, the server will simply close the connection
	//  This is safe because go's rpc server always calls ReadRequestHeader and ReadRequestBody back-to-back
	//   (unless ReadRequestHeader returns an error)
	if requiresAuthentication(r.ServiceMethod) {
		if a.lastError == nil {
			if requiresAdmin(r.ServiceMethod) && (ident == nil || !ident.HasAdminAccess()) {
				log.WithField("ServiceMethod", r.ServiceMethod).Debug("Received unauthorized RPC request")
				a.lastError = ErrNoAdmin
			}
		}
		//TODO: save the identity so we can inject it into the request body later
	}
	return nil
}

// Decodes the request and populates the body object with the body of the request
//  We don't change anything here, just let the underlying codec handle it.
//  This always gets called after ReadRequestHeader
func (a *AuthServerCodec) ReadRequestBody(body interface{}) error {
	if a.lastError != nil {
		return a.lastError
	}
	// TODO: Use reflection and add the identity to the body if necessary
	return a.wrappedcodec.ReadRequestBody(body)
}

//  Encodes the response before sending it back down to the client.
//  We don't change anything here, just let the underlying codec handle it.
func (a *AuthServerCodec) WriteResponse(r *rpc.Response, body interface{}) error {
	// We do need a lock here, because the ServerCodec interface specifies
	//  that WriteResponse must be safe for concurrent use by multiple goroutines
	a.wBuffMutex.Lock()
	defer a.wBuffMutex.Unlock()

	a.buff.WriteBuff.Reset()

	// Let the underlying codec write the response to the buffer
	if err := a.wrappedcodec.WriteResponse(r, body); err != nil {
		return err
	}

	// Get the response from the buffer and write it to the actual connection
	response := a.buff.WriteBuff.Bytes()
	if err := auth.WriteLengthAndBytes(response, a.conn); err != nil {
		return err
	}

	return nil
}

// Closes the connection on the server side
//  We don't change anything here, just let the underlying codec handle it.
func (a *AuthServerCodec) Close() error {
	var err error
	if err = a.wrappedcodec.Close(); err != nil {
		log.WithError(err).Debug("Error closing wrapped RPC client codec")
	}
	if ourErr := a.conn.Close(); ourErr != nil {
		log.WithError(ourErr).Debug("Error closing RPC client connection")
		// This error is probably more important
		err = ourErr
	}
	return err
}

// Client Codec
type ClientCodecCreator func(io.ReadWriteCloser) rpc.ClientCodec
type AuthClientCodec struct {
	conn          io.ReadWriteCloser
	buff          *ByteBufferReadWriteCloser
	wrappedcodec  rpc.ClientCodec
	headerBuilder auth.RPCHeaderBuilder
	wBuffMutex    sync.Mutex // Make sure we buffer a whole request before starting the next one
}

func NewDefaultAuthClientCodec(conn io.ReadWriteCloser) rpc.ClientCodec {
	return NewAuthClientCodec(conn, jsonrpc.NewClientCodec, &auth.RPCHeaderHandler{})
}

func NewAuthClientCodec(conn io.ReadWriteCloser, createCodec ClientCodecCreator, headerBuilder auth.RPCHeaderBuilder) rpc.ClientCodec {
	buff := &ByteBufferReadWriteCloser{}
	return &AuthClientCodec{
		conn:          conn,
		buff:          buff,
		wrappedcodec:  createCodec(buff),
		headerBuilder: headerBuilder,
	}
}

// Encodes the request and sends it to the server.
// This implementation gets an auth header when appropriate, and writes it to the stream
//  before letting the underlying codec send the rest of the request.
func (a *AuthClientCodec) WriteRequest(r *rpc.Request, body interface{}) error {
	// Lock to ensure we write the header and the rest of the request back-to-back
	//  This method may be called by multiple goroutines concurrently
	a.wBuffMutex.Lock()
	defer a.wBuffMutex.Unlock()
	a.buff.WriteBuff.Reset()

	// Let the underlying codec write the request to the buffer
	if err := a.wrappedcodec.WriteRequest(r, body); err != nil {
		return err
	}

	// Get the request off the buffer
	request := a.buff.WriteBuff.Bytes()

	needsAuth := requiresAuthentication(r.ServiceMethod)
	if err := a.headerBuilder.WriteHeader(a.conn, request, needsAuth); err != nil {
		return err
	}

	log.WithField("ServiceMethod", r.ServiceMethod).Debug("Successfully sent RPC request")

	return nil
}

// Decodes the response and reads the header, building the rpc.Response object
//  We don't change anything here, just let the underlying codec handle it.
func (a *AuthClientCodec) ReadResponseHeader(r *rpc.Response) error {

	// No need for synchronization here, Go's RPC Client makes sure only
	//  One response is read at a time.

	a.buff.ReadBuff.Reset()

	// Read the response from the connection
	response, err := auth.ReadLengthAndBytes(a.conn)
	if err != nil {
		// It is common to get harmless errors here whenever the client is closed
		return err
	}

	// Write the response to the buffer
	if _, err = a.buff.ReadBuff.Write(response); err != nil {
		return err
	}

	// Let the underlying codec read and parse the response from the buffer
	if err = a.wrappedcodec.ReadResponseHeader(r); err != nil {
		return err
	}

	log.WithField("ServiceMethod", r.ServiceMethod).Debug("Successfully read RPC response")

	return nil
}

// Decodes the body of the response and builds the body object.
// We don't change anything here, just let the underlying codec handle it.
func (a *AuthClientCodec) ReadResponseBody(body interface{}) error {
	return a.wrappedcodec.ReadResponseBody(body)
}

// Closes the connection on the client side
//  We don't change anything here, just let the underlying codec handle it.
func (a *AuthClientCodec) Close() error {
	var err error
	if err = a.wrappedcodec.Close(); err != nil {
		log.WithError(err).Debug("Error closing wrapped RPC client codec")
	}
	if ourErr := a.conn.Close(); ourErr != nil {
		log.WithError(ourErr).Debug("Error closing RPC client connection")
		// This error is more important
		err = ourErr
	}
	return err
}

// NewDefaultAuthClient returns a new rpc.Client that uses our default client codec
func NewDefaultAuthClient(conn io.ReadWriteCloser) *rpc.Client {
	return rpc.NewClientWithCodec(NewDefaultAuthClientCodec(conn))
}
<|endoftext|>


<commit_before>package angularjs

import "github.com/gopherjs/gopherjs/js"

type Module struct{ *js.Object }

func (m *Module) NewController(name string, constructor func(scope *Scope)) {
	m.Call("controller", name, func(dollar_scope *js.Object) {
		constructor(&Scope{dollar_scope})
	})
}

type Scope struct{ *js.Object }

func (s *Scope) Apply(f func()) {
	s.Call("$apply", f)
}

func (s *Scope) EvalAsync(f func()) {
	s.Call("$evalAsync", f)
}

type JQueryElement struct{ *js.Object }

func (e *JQueryElement) Prop(name string) *js.Object {
	return e.Call("prop", name)
}

func (e *JQueryElement) SetProp(name, value interface{}) {
	e.Call("prop", name, value)
}

func (e *JQueryElement) On(events string, handler func(*Event)) {
	e.Call("on", events, func(e *js.Object) {
		handler(&Event{Object: e})
	})
}

func (e *JQueryElement) Val() *js.Object {
	return e.Call("val")
}

func (e *JQueryElement) SetVal(value interface{}) {
	e.Call("val", value)
}

type Event struct {
	*js.Object
	KeyCode int `js:"keyCode"`
}

func (e *Event) PreventDefault() {
	e.Call("preventDefault")
}

func NewModule(name string, requires []string, configFn func()) *Module {
	return &Module{js.Global.Get("angular").Call("module", name, requires, configFn)}
}

func ElementById(id string) *JQueryElement {
	return &JQueryElement{js.Global.Get("angular").Call("element", js.Global.Get("document").Call("getElementById", id))}
}

func Service(name string) *js.Object {
	return js.Global.Get("angular").Call("element", js.Global.Get("document")).Call("injector").Call("get", name)
}

type HttpService struct{}

var HTTP = new(HttpService)

func (s *HttpService) Get(url string, callback func(data string, status int)) {
	future := Service("$http").Call("get", url)
	future.Call("success", func(data string, status int, headers *js.Object, config *js.Object) {
		callback(data, status)
	})
	future.Call("error", func(data string, status int, headers *js.Object, config *js.Object) {
		callback(data, status)
	})
}
<commit_msg>explicit names for AngularJS injector<commit_after>package angularjs

import "github.com/gopherjs/gopherjs/js"

type Module struct{ *js.Object }

func (m *Module) NewController(name string, constructor func(scope *Scope)) {
	m.Call("controller", name, js.S{"$scope", func(scope *js.Object) {
		constructor(&Scope{scope})
	}})
}

type Scope struct{ *js.Object }

func (s *Scope) Apply(f func()) {
	s.Call("$apply", f)
}

func (s *Scope) EvalAsync(f func()) {
	s.Call("$evalAsync", f)
}

type JQueryElement struct{ *js.Object }

func (e *JQueryElement) Prop(name string) *js.Object {
	return e.Call("prop", name)
}

func (e *JQueryElement) SetProp(name, value interface{}) {
	e.Call("prop", name, value)
}

func (e *JQueryElement) On(events string, handler func(*Event)) {
	e.Call("on", events, func(e *js.Object) {
		handler(&Event{Object: e})
	})
}

func (e *JQueryElement) Val() *js.Object {
	return e.Call("val")
}

func (e *JQueryElement) SetVal(value interface{}) {
	e.Call("val", value)
}

type Event struct {
	*js.Object
	KeyCode int `js:"keyCode"`
}

func (e *Event) PreventDefault() {
	e.Call("preventDefault")
}

func NewModule(name string, requires []string, configFn func()) *Module {
	return &Module{js.Global.Get("angular").Call("module", name, requires, configFn)}
}

func ElementById(id string) *JQueryElement {
	return &JQueryElement{js.Global.Get("angular").Call("element", js.Global.Get("document").Call("getElementById", id))}
}

func Service(name string) *js.Object {
	return js.Global.Get("angular").Call("element", js.Global.Get("document")).Call("injector").Call("get", name)
}

type HttpService struct{}

var HTTP = new(HttpService)

func (s *HttpService) Get(url string, callback func(data string, status int)) {
	future := Service("$http").Call("get", url)
	future.Call("success", func(data string, status int, headers *js.Object, config *js.Object) {
		callback(data, status)
	})
	future.Call("error", func(data string, status int, headers *js.Object, config *js.Object) {
		callback(data, status)
	})
}
<|endoftext|>


<commit_before>// Copyright 2012, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tabletserver

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

	log "github.com/golang/glog"
	"github.com/youtube/vitess/go/mysql"
	"github.com/youtube/vitess/go/tb"
	"github.com/youtube/vitess/go/vt/logutil"
)

const (
	// ErrFail is returned when a query fails
	ErrFail = iota

	// ErrRetry is returned when a query can be retried
	ErrRetry

	// ErrFatal is returned when a query cannot be retried
	ErrFatal

	// ErrTxPoolFull is returned when we can't get a connection
	ErrTxPoolFull

	// ErrNotInTx is returned when we're not in a transaction but should be
	ErrNotInTx
)

var logTxPoolFull = logutil.NewThrottledLogger("TxPoolFull", 1*time.Minute)

// TabletError is the erro type we use in this library
type TabletError struct {
	ErrorType int
	Message   string
	SqlError  int
}

// This is how go-mysql exports its error number
type hasNumber interface {
	Number() int
}

// NewTabletError returns a TabletError of the given type
func NewTabletError(errorType int, format string, args ...interface{}) *TabletError {
	return &TabletError{
		ErrorType: errorType,
		Message:   fmt.Sprintf(format, args...),
	}
}

// NewTabletErrorSql returns a TabletError based on the error
func NewTabletErrorSql(errorType int, err error) *TabletError {
	var errnum int
	errstr := err.Error()
	if sqlErr, ok := err.(hasNumber); ok {
		errnum = sqlErr.Number()
		// Override error type if MySQL is in read-only mode. It's probably because
		// there was a remaster and there are old clients still connected.
		if errnum == mysql.ErrOptionPreventsStatement && strings.Contains(errstr, "read-only") {
			errorType = ErrRetry
		}
	}
	return &TabletError{
		ErrorType: errorType,
		Message:   errstr,
		SqlError:  errnum,
	}
}

var errExtract = regexp.MustCompile(`.*\(errno ([0-9]*)\).*`)

// IsConnErr returns true if the error is a connection error. If
// the error is of type TabletError or hasNumber, it checks the error
// code. Otherwise, it parses the string looking for (errno xxxx)
// and uses the extracted value to determine if it's a conn error.
func IsConnErr(err error) bool {
	var sqlError int
	switch err := err.(type) {
	case *TabletError:
		sqlError = err.SqlError
	case hasNumber:
		sqlError = err.Number()
	default:
		match := errExtract.FindStringSubmatch(err.Error())
		if len(match) < 2 {
			return false
		}
		var convErr error
		sqlError, convErr = strconv.Atoi(match[1])
		if convErr != nil {
			return false
		}
	}
	// 2013 means that someone sniped the query.
	if sqlError == 2013 {
		return false
	}
	return sqlError >= 2000 && sqlError <= 2018
}

func (te *TabletError) Error() string {
	format := "error: %s"
	switch te.ErrorType {
	case ErrRetry:
		format = "retry: %s"
	case ErrFatal:
		format = "fatal: %s"
	case ErrTxPoolFull:
		format = "tx_pool_full: %s"
	case ErrNotInTx:
		format = "not_in_tx: %s"
	}
	return fmt.Sprintf(format, te.Message)
}

// RecordStats will record the error in the proper stat bucket
func (te *TabletError) RecordStats() {
	switch te.ErrorType {
	case ErrRetry:
		infoErrors.Add("Retry", 1)
	case ErrFatal:
		infoErrors.Add("Fatal", 1)
	case ErrTxPoolFull:
		errorStats.Add("TxPoolFull", 1)
	case ErrNotInTx:
		errorStats.Add("NotInTx", 1)
	default:
		switch te.SqlError {
		case mysql.ErrDupEntry:
			infoErrors.Add("DupKey", 1)
		case mysql.ErrLockWaitTimeout, mysql.ErrLockDeadlock:
			errorStats.Add("Deadlock", 1)
		default:
			errorStats.Add("Fail", 1)
		}
	}
}

func handleError(err *error, logStats *SQLQueryStats) {
	if x := recover(); x != nil {
		terr, ok := x.(*TabletError)
		if !ok {
			log.Errorf("Uncaught panic:\n%v\n%s", x, tb.Stack(4))
			*err = NewTabletError(ErrFail, "%v: uncaught panic", x)
			internalErrors.Add("Panic", 1)
			return
		}
		*err = terr
		terr.RecordStats()
		if terr.ErrorType == ErrRetry { // Retry errors are too spammy
			return
		}
		if terr.ErrorType == ErrTxPoolFull {
			logTxPoolFull.Errorf("%v", terr)
		} else {
			log.Errorf("%v", terr)
		}
	}
	if logStats != nil {
		logStats.Error = *err
		logStats.Send()
	}
}

func logError() {
	if x := recover(); x != nil {
		terr, ok := x.(*TabletError)
		if !ok {
			log.Errorf("Uncaught panic:\n%v\n%s", x, tb.Stack(4))
			internalErrors.Add("Panic", 1)
			return
		}
		if terr.ErrorType == ErrTxPoolFull {
			logTxPoolFull.Errorf("%v", terr)
		} else {
			log.Errorf("%v", terr)
		}
	}
}
<commit_msg>tabletserver: readable error messages<commit_after>// Copyright 2012, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tabletserver

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

	log "github.com/golang/glog"
	"github.com/youtube/vitess/go/mysql"
	"github.com/youtube/vitess/go/tb"
	"github.com/youtube/vitess/go/vt/logutil"
)

const (
	// ErrFail is returned when a query fails
	ErrFail = iota

	// ErrRetry is returned when a query can be retried
	ErrRetry

	// ErrFatal is returned when a query cannot be retried
	ErrFatal

	// ErrTxPoolFull is returned when we can't get a connection
	ErrTxPoolFull

	// ErrNotInTx is returned when we're not in a transaction but should be
	ErrNotInTx
)

const (
	maxErrLen = 5000
)

var logTxPoolFull = logutil.NewThrottledLogger("TxPoolFull", 1*time.Minute)

// TabletError is the erro type we use in this library
type TabletError struct {
	ErrorType int
	Message   string
	SqlError  int
}

// This is how go-mysql exports its error number
type hasNumber interface {
	Number() int
}

// NewTabletError returns a TabletError of the given type
func NewTabletError(errorType int, format string, args ...interface{}) *TabletError {
	return &TabletError{
		ErrorType: errorType,
		Message:   printable(fmt.Sprintf(format, args...)),
	}
}

// NewTabletErrorSql returns a TabletError based on the error
func NewTabletErrorSql(errorType int, err error) *TabletError {
	var errnum int
	errstr := err.Error()
	if sqlErr, ok := err.(hasNumber); ok {
		errnum = sqlErr.Number()
		// Override error type if MySQL is in read-only mode. It's probably because
		// there was a remaster and there are old clients still connected.
		if errnum == mysql.ErrOptionPreventsStatement && strings.Contains(errstr, "read-only") {
			errorType = ErrRetry
		}
	}
	return &TabletError{
		ErrorType: errorType,
		Message:   printable(errstr),
		SqlError:  errnum,
	}
}

func printable(in string) string {
	if len(in) > maxErrLen {
		in = in[:maxErrLen]
	}
	in = fmt.Sprintf("%q", in)
	return in[1 : len(in)-1]
}

var errExtract = regexp.MustCompile(`.*\(errno ([0-9]*)\).*`)

// IsConnErr returns true if the error is a connection error. If
// the error is of type TabletError or hasNumber, it checks the error
// code. Otherwise, it parses the string looking for (errno xxxx)
// and uses the extracted value to determine if it's a conn error.
func IsConnErr(err error) bool {
	var sqlError int
	switch err := err.(type) {
	case *TabletError:
		sqlError = err.SqlError
	case hasNumber:
		sqlError = err.Number()
	default:
		match := errExtract.FindStringSubmatch(err.Error())
		if len(match) < 2 {
			return false
		}
		var convErr error
		sqlError, convErr = strconv.Atoi(match[1])
		if convErr != nil {
			return false
		}
	}
	// 2013 means that someone sniped the query.
	if sqlError == 2013 {
		return false
	}
	return sqlError >= 2000 && sqlError <= 2018
}

func (te *TabletError) Error() string {
	prefix := "error: "
	switch te.ErrorType {
	case ErrRetry:
		prefix = "retry: "
	case ErrFatal:
		prefix = "fatal: "
	case ErrTxPoolFull:
		prefix = "tx_pool_full: "
	case ErrNotInTx:
		prefix = "not_in_tx: "
	}
	return prefix + te.Message
}

// RecordStats will record the error in the proper stat bucket
func (te *TabletError) RecordStats() {
	switch te.ErrorType {
	case ErrRetry:
		infoErrors.Add("Retry", 1)
	case ErrFatal:
		infoErrors.Add("Fatal", 1)
	case ErrTxPoolFull:
		errorStats.Add("TxPoolFull", 1)
	case ErrNotInTx:
		errorStats.Add("NotInTx", 1)
	default:
		switch te.SqlError {
		case mysql.ErrDupEntry:
			infoErrors.Add("DupKey", 1)
		case mysql.ErrLockWaitTimeout, mysql.ErrLockDeadlock:
			errorStats.Add("Deadlock", 1)
		default:
			errorStats.Add("Fail", 1)
		}
	}
}

func handleError(err *error, logStats *SQLQueryStats) {
	if x := recover(); x != nil {
		terr, ok := x.(*TabletError)
		if !ok {
			log.Errorf("Uncaught panic:\n%v\n%s", x, tb.Stack(4))
			*err = NewTabletError(ErrFail, "%v: uncaught panic", x)
			internalErrors.Add("Panic", 1)
			return
		}
		*err = terr
		terr.RecordStats()
		if terr.ErrorType == ErrRetry { // Retry errors are too spammy
			return
		}
		if terr.ErrorType == ErrTxPoolFull {
			logTxPoolFull.Errorf("%v", terr)
		} else {
			log.Errorf("%v", terr)
		}
	}
	if logStats != nil {
		logStats.Error = *err
		logStats.Send()
	}
}

func logError() {
	if x := recover(); x != nil {
		terr, ok := x.(*TabletError)
		if !ok {
			log.Errorf("Uncaught panic:\n%v\n%s", x, tb.Stack(4))
			internalErrors.Add("Panic", 1)
			return
		}
		if terr.ErrorType == ErrTxPoolFull {
			logTxPoolFull.Errorf("%v", terr)
		} else {
			log.Errorf("%v", terr)
		}
	}
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package state

import (
	"bytes"
	"errors"
	"sort"

	"github.com/ava-labs/gecko/ids"
	"github.com/ava-labs/gecko/snow/consensus/snowstorm"
	"github.com/ava-labs/gecko/snow/engine/avalanche/vertex"
	"github.com/ava-labs/gecko/utils"
	"github.com/ava-labs/gecko/utils/hashing"
	"github.com/ava-labs/gecko/utils/wrappers"
)

// maxSize is the maximum allowed vertex size. It is necessary to deter DoS.
const maxSize = 1 << 20

var (
	errBadCodec       = errors.New("invalid codec")
	errExtraSpace     = errors.New("trailing buffer space")
	errInvalidParents = errors.New("vertex contains non-sorted or duplicated parentIDs")
	errInvalidTxs     = errors.New("vertex contains non-sorted or duplicated transactions")
	errNoTxs          = errors.New("vertex contains no transactions")
)

type innerVertex struct {
	id ids.ID

	chainID ids.ID
	height  uint64

	parentIDs []ids.ID
	txs       []snowstorm.Tx

	bytes []byte
}

func (vtx *innerVertex) ID() ids.ID    { return vtx.id }
func (vtx *innerVertex) Bytes() []byte { return vtx.bytes }

func (vtx *innerVertex) Verify() error {
	switch {
	case !ids.IsSortedAndUniqueIDs(vtx.parentIDs):
		return errInvalidParents
	case len(vtx.txs) == 0:
		return errNoTxs
	case !isSortedAndUniqueTxs(vtx.txs):
		return errInvalidTxs
	default:
		return nil
	}
}

/*
 * Vertex:
 * Codec        | 04 Bytes
 * Chain        | 32 Bytes
 * Height       | 08 Bytes
 * NumParents   | 04 Bytes
 * Repeated (NumParents):
 *     ParentID | 32 bytes
 * NumTxs       | 04 Bytes
 * Repeated (NumTxs):
 *     TxSize   | 04 bytes
 *     Tx       | ?? bytes
 */

// Marshal creates the byte representation of the vertex
func (vtx *innerVertex) Marshal() ([]byte, error) {
	p := wrappers.Packer{MaxSize: maxSize}

	p.PackInt(uint32(CustomID))
	p.PackFixedBytes(vtx.chainID.Bytes())
	p.PackLong(vtx.height)

	p.PackInt(uint32(len(vtx.parentIDs)))
	for _, parentID := range vtx.parentIDs {
		p.PackFixedBytes(parentID.Bytes())
	}

	p.PackInt(uint32(len(vtx.txs)))
	for _, tx := range vtx.txs {
		p.PackBytes(tx.Bytes())
	}
	return p.Bytes, p.Err
}

// Unmarshal attempts to set the contents of this vertex to the value encoded in
// the stream of bytes.
func (vtx *innerVertex) Unmarshal(b []byte, vm vertex.DAGVM) error {
	p := wrappers.Packer{Bytes: b}

	if codecID := ID(p.UnpackInt()); codecID != CustomID {
		p.Add(errBadCodec)
	}

	chainID, _ := ids.ToID(p.UnpackFixedBytes(hashing.HashLen))
	height := p.UnpackLong()

	parentIDs := []ids.ID(nil)
	for i := p.UnpackInt(); i > 0 && !p.Errored(); i-- {
		parentID, _ := ids.ToID(p.UnpackFixedBytes(hashing.HashLen))
		parentIDs = append(parentIDs, parentID)
	}

	txs := []snowstorm.Tx(nil)
	for i := p.UnpackInt(); i > 0 && !p.Errored(); i-- {
		tx, err := vm.ParseTx(p.UnpackBytes())
		p.Add(err)
		txs = append(txs, tx)
	}

	if p.Offset != len(b) {
		p.Add(errExtraSpace)
	}

	if p.Errored() {
		return p.Err
	}

	*vtx = innerVertex{
		id:        ids.NewID(hashing.ComputeHash256Array(b)),
		parentIDs: parentIDs,
		chainID:   chainID,
		height:    height,
		txs:       txs,
		bytes:     b,
	}
	return nil
}

type sortTxsData []snowstorm.Tx

func (txs sortTxsData) Less(i, j int) bool {
	return bytes.Compare(txs[i].ID().Bytes(), txs[j].ID().Bytes()) == -1
}
func (txs sortTxsData) Len() int      { return len(txs) }
func (txs sortTxsData) Swap(i, j int) { txs[j], txs[i] = txs[i], txs[j] }

func sortTxs(txs []snowstorm.Tx) { sort.Sort(sortTxsData(txs)) }
func isSortedAndUniqueTxs(txs []snowstorm.Tx) bool {
	return utils.IsSortedAndUnique(sortTxsData(txs))
}
<commit_msg>Added epochs to the vertex format<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package state

import (
	"bytes"
	"errors"
	"sort"

	"github.com/ava-labs/gecko/ids"
	"github.com/ava-labs/gecko/snow/consensus/snowstorm"
	"github.com/ava-labs/gecko/snow/engine/avalanche/vertex"
	"github.com/ava-labs/gecko/utils"
	"github.com/ava-labs/gecko/utils/hashing"
	"github.com/ava-labs/gecko/utils/wrappers"
)

// maxSize is the maximum allowed vertex size. It is necessary to deter DoS.
const maxSize = 1 << 20

var (
	errBadCodec       = errors.New("invalid codec")
	errBadEpoch       = errors.New("invalid epoch")
	errExtraSpace     = errors.New("trailing buffer space")
	errInvalidParents = errors.New("vertex contains non-sorted or duplicated parentIDs")
	errInvalidTxs     = errors.New("vertex contains non-sorted or duplicated transactions")
	errNoTxs          = errors.New("vertex contains no transactions")
)

type innerVertex struct {
	id ids.ID

	chainID ids.ID
	height  uint64

	parentIDs []ids.ID
	txs       []snowstorm.Tx

	bytes []byte
}

func (vtx *innerVertex) ID() ids.ID    { return vtx.id }
func (vtx *innerVertex) Bytes() []byte { return vtx.bytes }

func (vtx *innerVertex) Verify() error {
	switch {
	case !ids.IsSortedAndUniqueIDs(vtx.parentIDs):
		return errInvalidParents
	case len(vtx.txs) == 0:
		return errNoTxs
	case !isSortedAndUniqueTxs(vtx.txs):
		return errInvalidTxs
	default:
		return nil
	}
}

/*
 * Vertex:
 * Codec        | 04 Bytes
 * Chain        | 32 Bytes
 * Height       | 08 Bytes
 * Epoch        | 04 Bytes
 * NumParents   | 04 Bytes
 * Repeated (NumParents):
 *     ParentID | 32 bytes
 * NumTxs       | 04 Bytes
 * Repeated (NumTxs):
 *     TxSize   | 04 bytes
 *     Tx       | ?? bytes
 */

// Marshal creates the byte representation of the vertex
func (vtx *innerVertex) Marshal() ([]byte, error) {
	p := wrappers.Packer{MaxSize: maxSize}

	p.PackInt(uint32(CustomID))
	p.PackFixedBytes(vtx.chainID.Bytes())
	p.PackLong(vtx.height)
	p.PackInt(0)

	p.PackInt(uint32(len(vtx.parentIDs)))
	for _, parentID := range vtx.parentIDs {
		p.PackFixedBytes(parentID.Bytes())
	}

	p.PackInt(uint32(len(vtx.txs)))
	for _, tx := range vtx.txs {
		p.PackBytes(tx.Bytes())
	}
	return p.Bytes, p.Err
}

// Unmarshal attempts to set the contents of this vertex to the value encoded in
// the stream of bytes.
func (vtx *innerVertex) Unmarshal(b []byte, vm vertex.DAGVM) error {
	p := wrappers.Packer{Bytes: b}

	if codecID := ID(p.UnpackInt()); codecID != CustomID {
		p.Add(errBadCodec)
	}

	chainID, _ := ids.ToID(p.UnpackFixedBytes(hashing.HashLen))
	height := p.UnpackLong()
	if epoch := p.UnpackInt(); epoch != 0 {
		p.Add(errBadEpoch)
	}

	parentIDs := []ids.ID(nil)
	for i := p.UnpackInt(); i > 0 && !p.Errored(); i-- {
		parentID, _ := ids.ToID(p.UnpackFixedBytes(hashing.HashLen))
		parentIDs = append(parentIDs, parentID)
	}

	txs := []snowstorm.Tx(nil)
	for i := p.UnpackInt(); i > 0 && !p.Errored(); i-- {
		tx, err := vm.ParseTx(p.UnpackBytes())
		p.Add(err)
		txs = append(txs, tx)
	}

	if p.Offset != len(b) {
		p.Add(errExtraSpace)
	}

	if p.Errored() {
		return p.Err
	}

	*vtx = innerVertex{
		id:        ids.NewID(hashing.ComputeHash256Array(b)),
		parentIDs: parentIDs,
		chainID:   chainID,
		height:    height,
		txs:       txs,
		bytes:     b,
	}
	return nil
}

type sortTxsData []snowstorm.Tx

func (txs sortTxsData) Less(i, j int) bool {
	return bytes.Compare(txs[i].ID().Bytes(), txs[j].ID().Bytes()) == -1
}
func (txs sortTxsData) Len() int      { return len(txs) }
func (txs sortTxsData) Swap(i, j int) { txs[j], txs[i] = txs[i], txs[j] }

func sortTxs(txs []snowstorm.Tx) { sort.Sort(sortTxsData(txs)) }
func isSortedAndUniqueTxs(txs []snowstorm.Tx) bool {
	return utils.IsSortedAndUnique(sortTxsData(txs))
}
<|endoftext|>


<commit_before>package app

import (
	"fmt"
	"os"
	"testing"
	"time"

	"github.com/goph/fxt/dev"
	"github.com/goph/fxt/log"
	"github.com/goph/fxt/test/nettest"
	"github.com/goph/nest"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func init() {
	dev.LoadEnvFromFile("../.env.test")
	dev.LoadEnvFromFile("../.env.dist")
}

func newConfig() (Config, error) {
	debugPort, _ := nettest.GetFreePort()

	config := Config{
		Environment: "test",
		LogFormat:   "logfmt",
		DebugAddr:   fmt.Sprintf("127.0.0.1:%d", debugPort),
	}

	configurator := nest.NewConfigurator()
	configurator.SetName(FriendlyServiceName)
	configurator.SetArgs([]string{})

	err := configurator.Load(&config)

	return config, err
}

func TestConfig(t *testing.T) {
	os.Clearenv()
	defer func() {
		os.Clearenv()
		dev.LoadEnvFromFile("../.env.test")
		dev.LoadEnvFromFile("../.env.dist")
	}()

	env := map[string]string{
		"ENVIRONMENT": "test",
		"DEBUG":       "false",
		"LOG_FORMAT":  "json",
	}

	for key, value := range env {
		os.Setenv(key, value)
	}

	expected := Config{
		Environment:     "test",
		Debug:           false,
		LogFormat:       log.JsonFormat.String(),
		DebugAddr:       ":10000",
		ShutdownTimeout: 15 * time.Second,
	}
	actual := Config{}

	configurator := nest.NewConfigurator()
	configurator.SetName(FriendlyServiceName)
	configurator.SetArgs([]string{})

	err := configurator.Load(&actual)
	require.NoError(t, err)
	assert.Equal(t, expected, actual)
}
<commit_msg>Improve config tests<commit_after>package app

import (
	"fmt"
	"os"
	"testing"
	"time"

	"github.com/goph/fxt/dev"
	"github.com/goph/fxt/log"
	"github.com/goph/fxt/test/nettest"
	"github.com/goph/nest"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func init() {
	dev.LoadEnvFromFile("../.env.test")
	dev.LoadEnvFromFile("../.env.dist")
}

func newConfig() (Config, error) {
	debugPort, _ := nettest.GetFreePort()

	config := Config{
		Environment: "test",
		LogFormat:   "logfmt",
		DebugAddr:   fmt.Sprintf("127.0.0.1:%d", debugPort),
	}

	configurator := nest.NewConfigurator()
	configurator.SetName(FriendlyServiceName)
	configurator.SetArgs([]string{})

	err := configurator.Load(&config)

	return config, err
}

func TestConfig(t *testing.T) {
	defer func() {
		os.Clearenv()
		dev.LoadEnvFromFile("../.env.test")
		dev.LoadEnvFromFile("../.env.dist")
	}()

	tests := map[string]struct {
		env      map[string]string
		args     []string
		actual   Config
		expected Config
	}{
		"full config": {
			map[string]string{
				"ENVIRONMENT": "test",
				"DEBUG":       "false",
				"LOG_FORMAT":  "logfmt",
			},
			[]string{"service", "--debug-addr", ":10001", "--shutdown-timeout", "10s"},
			Config{},
			Config{
				Environment:     "test",
				Debug:           false,
				LogFormat:       log.LogfmtFormat.String(),
				DebugAddr:       ":10001",
				ShutdownTimeout: 10 * time.Second,
			},
		},
		"defaults": {
			map[string]string{},
			[]string{},
			Config{},
			Config{
				Environment:     "production",
				Debug:           false,
				LogFormat:       log.JsonFormat.String(),
				DebugAddr:       ":10000",
				ShutdownTimeout: 15 * time.Second,
			},
		},
	}

	for name, test := range tests {
		t.Run(name, func(t *testing.T) {
			os.Clearenv()

			for key, value := range test.env {
				os.Setenv(key, value)
			}

			configurator := nest.NewConfigurator()
			configurator.SetName(FriendlyServiceName)
			configurator.SetArgs(test.args)

			err := configurator.Load(&test.actual)
			require.NoError(t, err)
			assert.Equal(t, test.expected, test.actual)
		})
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	mm "github.com/mattermost/platform/model"
)

/*
main
Usage: go run main.go -u <username> -p <password> <server-url> [team-name]
Authenticates your login information, then gives you your AuthToken.
If the team name is unentered or invalid main shows valid team names.
*/
func main() {
	username := flag.String("u", "", "Username")
	password := flag.String("p", "", "Password")
	flag.Parse()
	url := flag.Arg(0)
	teamName := flag.Arg(1)
	client := mm.NewClient(url)
	_, err := client.Login(*username, *password)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println("Auth successful! Token: ", client.AuthToken)

	//Gathers all availible teams in a map,
	teamListResult, teamListAppError := client.GetAllTeamListings()
	teamMap := teamListResult.Data.(map[string]*mm.Team)
	if teamListAppError != nil {
		fmt.Println(teamListAppError)
		return
	}
	//Validates input team name
	teamObjMap, teamError := client.GetTeamByName(teamName)
	if teamError != nil {
		fmt.Println(teamError)
		return
	}
	//Prints availible teams
	fmt.Println("teams:")
	for _, value := range teamMap {
		fmt.Println("\t", value.Name)
	}
	//Creates team map that can be accessed without string key, then assigns team ID
	localTeamSlice := make([]*mm.Team, len(teamMap))
	i := 0
	for _, value := range teamMap {
		localTeamSlice[i] = value
		i++
	}
	client.SetTeamId(localTeamSlice[0].Id)
	//Gather map of channels availible
	channelResult, channelErr := client.GetChannels(teamObjMap.Etag)
	if channelErr != nil {
		fmt.Println("Channel Error")
		fmt.Println(channeslErr)
		return
	}
	//List availible channels (direct messages appear as address string, still in progress)
	channelSlice := channelResult.Data.(*mm.ChannelList)
	fmt.Print("\nChannels:\n")
	index := 0
	for _, channel := range *channelSlice {
		fmt.Print("\t", index, ": ")
		fmt.Println(channel.Name)
		index++
	}

}
<commit_msg>Made some changes including displaying index with chennel list. Fixed a bug<commit_after>package main

import (
	"flag"
	"fmt"
	mm "github.com/mattermost/platform/model"
)

/*
main
Usage: go run main.go -u <username> -p <password> <server-url> [team-name]
Authenticates your login information, then gives you your AuthToken.
If the team name is unentered or invalid main shows valid team names.
*/
func main() {
	username := flag.String("u", "", "Username")
	password := flag.String("p", "", "Password")
	flag.Parse()
	url := flag.Arg(0)
	teamName := flag.Arg(1)
	client := mm.NewClient(url)
	_, err := client.Login(*username, *password)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println("Auth successful! Token: ", client.AuthToken)

	//Gathers all availible teams in a map,
	teamListResult, teamListAppError := client.GetAllTeamListings()
	teamMap := teamListResult.Data.(map[string]*mm.Team)
	if teamListAppError != nil {
		fmt.Println(teamListAppError)
		return
	}
	//Validates input team name
	teamObjMap, teamError := client.GetTeamByName(teamName)
	if teamError != nil {
		fmt.Println(teamError)
		return
	}
	//Prints availible teams
	fmt.Println("teams:")
	for _, value := range teamMap {
		fmt.Println("\t", value.Name)
	}
	//Creates team map that can be accessed without string key, then assigns team ID
	localTeamSlice := make([]*mm.Team, len(teamMap))
	i := 0
	for _, value := range teamMap {
		localTeamSlice[i] = value
		i++
	}
	client.SetTeamId(localTeamSlice[0].Id)
	//Gather map of channels availible
	channelResult, channelErr := client.GetChannels(teamObjMap.Etag)
	if channelErr != nil {
		fmt.Println("Channel Error")
		fmt.Println(channelErr)
		return
	}
	//List availible channels (direct messages appear as address string, still in progress)
	channelSlice := channelResult.Data.(*mm.ChannelList)
	fmt.Print("\nChannels:\n")
	index := 0
	for _, channel := range *channelSlice {
		fmt.Print("\t", index, ": ")
		fmt.Println(channel.DisplayName)
		index++
	}

}
<|endoftext|>