<commit_before>package gokeepasslib

import (
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"encoding/xml"
	"io"
)

// Header to be put before xml content in kdbx file
var xmlHeader = []byte(`<?xml version="1.0" encoding="utf-8" standalone="yes"?>` + "\n")

// Encoder is used to automaticaly encrypt and write a database to a file, network, etc
type Encoder struct {
	w io.Writer
}

// NewEncoder creates a new encoder with writer w, identical to gokeepasslib.Encoder{w}
func NewEncoder(w io.Writer) *Encoder {
	return &Encoder{w: w}
}

// Encode writes db to e's internal writer
func (e *Encoder) Encode(db *Database) error {
	// Unlock protected entries ensuring that we have them prepared in the order that is matching
	// the xml marshalling order
	err := db.UnlockProtectedEntries()
	if err != nil {
		return err
	}
	// Re-Lock the protected values mapping to ensure that they are locked in memory and
	// follow the order in which they would be written again
	err = db.LockProtectedEntries()
	if err != nil {
		return err
	}

	// ensure timestamps will be formatted correctly
	db.ensureKdbxFormatVersion()

	// Calculate transformed key to make HMAC and encrypt
	transformedKey, err := db.getTransformedKey()
	if err != nil {
		return err
	}

	// Write header then hashes before decode content (necessary to update HeaderHash)
	// db.Header writeTo will change its hash
	if err = db.Header.writeTo(e.w); err != nil {
		return err
	}

	// Update header hash into db.Hashes then write the data
	hash := db.Header.GetSha256()
	if db.Header.IsKdbx4() {
		db.Hashes.Sha256 = hash

		hmacKey := buildHmacKey(db, transformedKey)
		hmacHash := db.Header.GetHmacSha256(hmacKey)
		db.Hashes.Hmac = hmacHash

		if err = db.Hashes.writeTo(e.w); err != nil {
			return err
		}
	} else {
		db.Content.Meta.HeaderHash = base64.StdEncoding.EncodeToString(hash[:])
	}

	// Encode xml and append header to the top
	rawContent, err := xml.MarshalIndent(db.Content, "", "\t")
	if err != nil {
		return err
	}
	rawContent = append(xmlHeader, rawContent...)

	// Write InnerHeader (Kdbx v4)
	if db.Header.IsKdbx4() {
		var ih bytes.Buffer
		if err = db.Content.InnerHeader.writeTo(&ih); err != nil {
			return err
		}

		rawContent = append(ih.Bytes(), rawContent...)
	}

	// Encode raw content
	encodedContent, err := encodeRawContent(db, rawContent, transformedKey)
	if err != nil {
		return err
	}

	// Writes the encrypted database content
	_, err = e.w.Write(encodedContent)
	return err
}

func encodeRawContent(db *Database, content []byte, transformedKey []byte) (encoded []byte, err error) {
	// Compress if the header compression flag is 1 (gzip)
	if db.Header.FileHeaders.CompressionFlags == GzipCompressionFlag {
		b := new(bytes.Buffer)
		w := gzip.NewWriter(b)

		if _, err = w.Write(content); err != nil {
			return encoded, err
		}

		// Close() needs to be explicitly called to write Gzip stream footer,
		// Flush() is not enough. some gzip decoders treat missing footer as error
		// while some don't). internally Close() also does flush.
		if err = w.Close(); err != nil {
			return encoded, err
		}

		content = b.Bytes()
	}

	// Compose blocks (Kdbx v3.1)
	if !db.Header.IsKdbx4() {
		var blocks bytes.Buffer
		composeContentBlocks31(&blocks, content)

		// Append blocks to StreamStartBytes
		content = append(db.Header.FileHeaders.StreamStartBytes, blocks.Bytes()...)
	}

	// Adds padding to data as required to encrypt properly
	if len(content)%16 != 0 {
		padding := make([]byte, 16-(len(content)%16))
		for i := 0; i < len(padding); i++ {
			padding[i] = byte(len(padding))
		}
		content = append(content, padding...)
	}

	// Encrypt content
	// Decrypt content
	encrypter, err := db.GetEncrypterManager(transformedKey)
	if err != nil {
		return encoded, err
	}
	encrypted := encrypter.Encrypt(content)

	// Compose blocks (Kdbx v4)
	if db.Header.IsKdbx4() {
		var blocks bytes.Buffer
		composeContentBlocks4(&blocks, encrypted, db.Header.FileHeaders.MasterSeed, transformedKey)

		encrypted = blocks.Bytes()
	}
	return encrypted, nil
}
<commit_msg>Clarify comment<commit_after>package gokeepasslib

import (
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"encoding/xml"
	"io"
)

// Header to be put before xml content in kdbx file
var xmlHeader = []byte(`<?xml version="1.0" encoding="utf-8" standalone="yes"?>` + "\n")

// Encoder is used to automaticaly encrypt and write a database to a file, network, etc
type Encoder struct {
	w io.Writer
}

// NewEncoder creates a new encoder with writer w, identical to gokeepasslib.Encoder{w}
func NewEncoder(w io.Writer) *Encoder {
	return &Encoder{w: w}
}

// Encode writes db to e's internal writer
func (e *Encoder) Encode(db *Database) error {
	// Unlock protected entries ensuring that we have them prepared in the order that is matching
	// the xml unmarshalling order
	err := db.UnlockProtectedEntries()
	if err != nil {
		return err
	}
	// Re-Lock the protected values mapping to ensure that they are locked in memory and
	// follow the order in which they would be written again
	err = db.LockProtectedEntries()
	if err != nil {
		return err
	}

	// ensure timestamps will be formatted correctly
	db.ensureKdbxFormatVersion()

	// Calculate transformed key to make HMAC and encrypt
	transformedKey, err := db.getTransformedKey()
	if err != nil {
		return err
	}

	// Write header then hashes before decode content (necessary to update HeaderHash)
	// db.Header writeTo will change its hash
	if err = db.Header.writeTo(e.w); err != nil {
		return err
	}

	// Update header hash into db.Hashes then write the data
	hash := db.Header.GetSha256()
	if db.Header.IsKdbx4() {
		db.Hashes.Sha256 = hash

		hmacKey := buildHmacKey(db, transformedKey)
		hmacHash := db.Header.GetHmacSha256(hmacKey)
		db.Hashes.Hmac = hmacHash

		if err = db.Hashes.writeTo(e.w); err != nil {
			return err
		}
	} else {
		db.Content.Meta.HeaderHash = base64.StdEncoding.EncodeToString(hash[:])
	}

	// Encode xml and append header to the top
	rawContent, err := xml.MarshalIndent(db.Content, "", "\t")
	if err != nil {
		return err
	}
	rawContent = append(xmlHeader, rawContent...)

	// Write InnerHeader (Kdbx v4)
	if db.Header.IsKdbx4() {
		var ih bytes.Buffer
		if err = db.Content.InnerHeader.writeTo(&ih); err != nil {
			return err
		}

		rawContent = append(ih.Bytes(), rawContent...)
	}

	// Encode raw content
	encodedContent, err := encodeRawContent(db, rawContent, transformedKey)
	if err != nil {
		return err
	}

	// Writes the encrypted database content
	_, err = e.w.Write(encodedContent)
	return err
}

func encodeRawContent(db *Database, content []byte, transformedKey []byte) (encoded []byte, err error) {
	// Compress if the header compression flag is 1 (gzip)
	if db.Header.FileHeaders.CompressionFlags == GzipCompressionFlag {
		b := new(bytes.Buffer)
		w := gzip.NewWriter(b)

		if _, err = w.Write(content); err != nil {
			return encoded, err
		}

		// Close() needs to be explicitly called to write Gzip stream footer,
		// Flush() is not enough. some gzip decoders treat missing footer as error
		// while some don't). internally Close() also does flush.
		if err = w.Close(); err != nil {
			return encoded, err
		}

		content = b.Bytes()
	}

	// Compose blocks (Kdbx v3.1)
	if !db.Header.IsKdbx4() {
		var blocks bytes.Buffer
		composeContentBlocks31(&blocks, content)

		// Append blocks to StreamStartBytes
		content = append(db.Header.FileHeaders.StreamStartBytes, blocks.Bytes()...)
	}

	// Adds padding to data as required to encrypt properly
	if len(content)%16 != 0 {
		padding := make([]byte, 16-(len(content)%16))
		for i := 0; i < len(padding); i++ {
			padding[i] = byte(len(padding))
		}
		content = append(content, padding...)
	}

	// Encrypt content
	// Decrypt content
	encrypter, err := db.GetEncrypterManager(transformedKey)
	if err != nil {
		return encoded, err
	}
	encrypted := encrypter.Encrypt(content)

	// Compose blocks (Kdbx v4)
	if db.Header.IsKdbx4() {
		var blocks bytes.Buffer
		composeContentBlocks4(&blocks, encrypted, db.Header.FileHeaders.MasterSeed, transformedKey)

		encrypted = blocks.Bytes()
	}
	return encrypted, nil
}
<|endoftext|>


<commit_before>package sacloud

// NFS NFS
type NFS struct {
	*Appliance // アプライアンス共通属性

	Remark   *NFSRemark   `json:",omitempty"` // リマーク
	Settings *NFSSettings `json:",omitempty"` // NFS設定
}

// NFSRemark リマーク
type NFSRemark struct {
	*ApplianceRemarkBase
	propPlanID
	// TODO Zone
	//Zone *Resource
	//SourceAppliance *Resource // クローン元DB
}

// NFSSettings NFS設定リスト
type NFSSettings struct {
}

// NFSPlan NFSプラン
type NFSPlan int

var (
	// NFSPlan100G 100Gプラン
	NFSPlan100G = NFSPlan(100)
	// NFSPlan500G 500Gプラン
	NFSPlan500G = NFSPlan(500)
	// NFSPlan1T 1T(1024GB)プラン
	NFSPlan1T = NFSPlan(1024 * 1)
	// NFSPlan2T 2T(2048GB)プラン
	NFSPlan2T = NFSPlan(1024 * 2)
	// NFSPlan4T 4T(4096GB)プラン
	NFSPlan4T = NFSPlan(1024 * 4)
)

// AllowNFSPlans 指定可能なNFSプラン
func AllowNFSPlans() []int {
	return []int{
		int(NFSPlan100G),
		int(NFSPlan500G),
		int(NFSPlan1T),
		int(NFSPlan2T),
		int(NFSPlan4T),
	}
}

// CreateNFSValue NFS作成用パラメーター
type CreateNFSValue struct {
	SwitchID        string    // 接続先スイッチID
	Plan            NFSPlan   // プラン
	IPAddress       string    // IPアドレス
	MaskLen         int       // ネットワークマスク長
	DefaultRoute    string    // デフォルトルート
	Name            string    // 名称
	Description     string    // 説明
	Tags            []string  // タグ
	Icon            *Resource // アイコン
	SourceAppliance *Resource // クローン元NFS
}

// NewCreateNFSValue NFS作成用パラメーター
func NewCreateNFSValue() *CreateNFSValue {
	return &CreateNFSValue{
		Plan: NFSPlan100G,
	}
}

// NewNFS NFS作成(冗長化なし)
func NewNFS(values *CreateNFSValue) *NFS {

	if int(values.Plan) == 0 {
		values.Plan = NFSPlan100G
	}

	return &NFS{
		Appliance: &Appliance{
			Class:           "nfs",
			propName:        propName{Name: values.Name},
			propDescription: propDescription{Description: values.Description},
			propTags:        propTags{Tags: values.Tags},
			propPlanID:      propPlanID{Plan: &Resource{ID: int64(values.Plan)}},
			propIcon: propIcon{
				&Icon{
					Resource: values.Icon,
				},
			},
		},
		Remark: &NFSRemark{
			ApplianceRemarkBase: &ApplianceRemarkBase{
				Switch: &ApplianceRemarkSwitch{
					ID: values.SwitchID,
				},
				Network: &ApplianceRemarkNetwork{
					NetworkMaskLen: values.MaskLen,
					DefaultRoute:   values.DefaultRoute,
				},
				Servers: []interface{}{
					map[string]interface{}{"IPAddress": values.IPAddress},
				},
			},
			propPlanID: propPlanID{Plan: &Resource{ID: int64(values.Plan)}},
			//SourceAppliance: values.SourceAppliance,
		},
	}

}

// IPAddress IPアドレスを取得
func (n *NFS) IPAddress() string {
	if len(n.Remark.Servers) < 1 {
		return ""
	}

	v, ok := n.Remark.Servers[0].(map[string]interface{})
	if !ok {
		return ""
	}

	if ip, ok := v["IPAddress"]; ok {
		return ip.(string)
	}
	return ""
}

// NetworkMaskLen ネットワークマスク長を取得
func (n *NFS) NetworkMaskLen() int {
	if n.Remark.Network == nil {
		return -1
	}
	return n.Remark.Network.NetworkMaskLen
}

// DefaultRoute デフォルトゲートウェイを取得
func (n *NFS) DefaultRoute() string {
	if n.Remark.Network == nil {
		return ""
	}
	return n.Remark.Network.DefaultRoute
}
<commit_msg>Add 8TB/12TB plan to NFS<commit_after>package sacloud

// NFS NFS
type NFS struct {
	*Appliance // アプライアンス共通属性

	Remark   *NFSRemark   `json:",omitempty"` // リマーク
	Settings *NFSSettings `json:",omitempty"` // NFS設定
}

// NFSRemark リマーク
type NFSRemark struct {
	*ApplianceRemarkBase
	propPlanID
	// TODO Zone
	//Zone *Resource
	//SourceAppliance *Resource // クローン元DB
}

// NFSSettings NFS設定リスト
type NFSSettings struct {
}

// NFSPlan NFSプラン
type NFSPlan int

var (
	// NFSPlan100G 100Gプラン
	NFSPlan100G = NFSPlan(100)
	// NFSPlan500G 500Gプラン
	NFSPlan500G = NFSPlan(500)
	// NFSPlan1T 1T(1024GB)プラン
	NFSPlan1T = NFSPlan(1024 * 1)
	// NFSPlan2T 2T(2048GB)プラン
	NFSPlan2T = NFSPlan(1024 * 2)
	// NFSPlan4T 4T(4096GB)プラン
	NFSPlan4T = NFSPlan(1024 * 4)
	// NFSPlan8T 8TBプラン
	NFSPlan8T = NFSPlan(1024 * 8)
	// NFSPlan12T 12TBプラン
	NFSPlan12T = NFSPlan(1024 * 12)
)

// AllowNFSPlans 指定可能なNFSプラン
func AllowNFSPlans() []int {
	return []int{
		int(NFSPlan100G),
		int(NFSPlan500G),
		int(NFSPlan1T),
		int(NFSPlan2T),
		int(NFSPlan4T),
		int(NFSPlan8T),
		int(NFSPlan12T),
	}
}

// CreateNFSValue NFS作成用パラメーター
type CreateNFSValue struct {
	SwitchID        string    // 接続先スイッチID
	Plan            NFSPlan   // プラン
	IPAddress       string    // IPアドレス
	MaskLen         int       // ネットワークマスク長
	DefaultRoute    string    // デフォルトルート
	Name            string    // 名称
	Description     string    // 説明
	Tags            []string  // タグ
	Icon            *Resource // アイコン
	SourceAppliance *Resource // クローン元NFS
}

// NewCreateNFSValue NFS作成用パラメーター
func NewCreateNFSValue() *CreateNFSValue {
	return &CreateNFSValue{
		Plan: NFSPlan100G,
	}
}

// NewNFS NFS作成(冗長化なし)
func NewNFS(values *CreateNFSValue) *NFS {

	if int(values.Plan) == 0 {
		values.Plan = NFSPlan100G
	}

	return &NFS{
		Appliance: &Appliance{
			Class:           "nfs",
			propName:        propName{Name: values.Name},
			propDescription: propDescription{Description: values.Description},
			propTags:        propTags{Tags: values.Tags},
			propPlanID:      propPlanID{Plan: &Resource{ID: int64(values.Plan)}},
			propIcon: propIcon{
				&Icon{
					Resource: values.Icon,
				},
			},
		},
		Remark: &NFSRemark{
			ApplianceRemarkBase: &ApplianceRemarkBase{
				Switch: &ApplianceRemarkSwitch{
					ID: values.SwitchID,
				},
				Network: &ApplianceRemarkNetwork{
					NetworkMaskLen: values.MaskLen,
					DefaultRoute:   values.DefaultRoute,
				},
				Servers: []interface{}{
					map[string]interface{}{"IPAddress": values.IPAddress},
				},
			},
			propPlanID: propPlanID{Plan: &Resource{ID: int64(values.Plan)}},
			//SourceAppliance: values.SourceAppliance,
		},
	}

}

// IPAddress IPアドレスを取得
func (n *NFS) IPAddress() string {
	if len(n.Remark.Servers) < 1 {
		return ""
	}

	v, ok := n.Remark.Servers[0].(map[string]interface{})
	if !ok {
		return ""
	}

	if ip, ok := v["IPAddress"]; ok {
		return ip.(string)
	}
	return ""
}

// NetworkMaskLen ネットワークマスク長を取得
func (n *NFS) NetworkMaskLen() int {
	if n.Remark.Network == nil {
		return -1
	}
	return n.Remark.Network.NetworkMaskLen
}

// DefaultRoute デフォルトゲートウェイを取得
func (n *NFS) DefaultRoute() string {
	if n.Remark.Network == nil {
		return ""
	}
	return n.Remark.Network.DefaultRoute
}
<|endoftext|>


<commit_before>package env

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/pkg/errors"
	"gopkg.in/src-d/go-git.v4"
	"gopkg.in/src-d/go-git.v4/plumbing"
	"gopkg.in/src-d/go-git.v4/plumbing/object"
)

type Git struct {
	Branch      string `json:"branch" structs:"branch"`
	CommitSHA   string `json:"commit_sha" structs:"commit_sha"`
	CommittedAt int    `json:"committed_at" structs:"committed_at"`
}

func (g Git) String() string {
	out := &bytes.Buffer{}
	out.WriteString("GIT_BRANCH=")
	out.WriteString(g.Branch)
	out.WriteString("\nGIT_COMMIT_SHA=")
	out.WriteString(g.CommitSHA)
	out.WriteString("\nGIT_COMMITTED_AT=")
	out.WriteString(fmt.Sprint(g.CommittedAt))
	return out.String()
}

func GetHead() (*object.Commit, error) {
	r, err := git.PlainOpen(".")
	if err != nil {
		return nil, errors.WithStack(err)
	}

	ref, err := r.Head()
	if err != nil {
		return nil, errors.WithStack(err)
	}

	commit, err := r.CommitObject(ref.Hash())
	if err != nil {
		return nil, errors.WithStack(err)
	}

	return commit, nil
}

func findGitInfo() (Git, error) {
	g, err := loadGitFromENV()
	if err == nil {
		return g, nil
	}

	logrus.Debug("couldn't load git info from ENV, trying git...")
	g = Git{}
	_, err = exec.LookPath("git")
	if err != nil {
		return g, errors.New("can't find git or load git info from ENV")
	}

	g, err = loadFromGit()
	if err != nil {
		return g, errors.WithStack(err)
	}

	return g, nil
}

func GitSHA(path string) (string, error) {
	args := []string{"log", "-1", "--follow", "--pretty=format:%H"}
	if path != "" {
		if pwd, err := os.Getwd(); err == nil {
			path = strings.TrimPrefix(path, pwd)
			path = filepath.Join(".", path)
		}
		args = append(args, path)
	}
	cmd := exec.Command("git", args...)
	cmd.Stderr = os.Stderr
	out, err := cmd.Output()
	if err != nil {
		return "", errors.WithStack(err)
	}
	return strings.TrimSpace(string(out)), nil
}

var GitBlob = func(path string, commit *object.Commit) (string, error) {
	if commit != nil {
		file, err := commit.File(path)
		if err == nil {
			logrus.Debugf("getting git blob_id for source file %s", path)

			blob := strings.TrimSpace(file.Hash.String())
			return blob, nil
		}
	}

	blob, err := fallbackBlob(path)

	if err != nil {
		return "", errors.WithStack(err)
	}

	return blob, nil
}

func fallbackBlob(path string) (string, error) {
	logrus.Debugf("getting fallback blob_id for source file %s", path)
	file, err := ioutil.ReadFile(path)

	if err != nil {
		logrus.Errorf("failed to read file %s\n%s", path, err)
		return "", errors.WithStack(err)
	}

	hash := plumbing.ComputeHash(plumbing.BlobObject, []byte(file))
	res := hash.String()

	return res, nil
}

func loadGitFromENV() (Git, error) {
	g := Git{}
	var err error

	g.Branch = findVar(gitBranchVars)
	if g.Branch == "" {
		return g, errors.New("git branch ENV not found")
	}

	g.CommitSHA = findVar(gitCommitShaVars)
	if g.CommitSHA == "" {
		return g, errors.New("git commit SHA ENV not found")
	}

	committedAt := findVar(gitCommittedAtVars)

	if committedAt == "" {
		return g, errors.New("git committed_at ENV not found")
	}

	g.CommittedAt, err = strconv.Atoi(committedAt)
	if err != nil {
		return g, errors.WithStack(err)
	}

	return g, nil
}

func loadFromGit() (Git, error) {
	g := Git{}

	cmd := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD")
	cmd.Stderr = os.Stderr
	out, err := cmd.Output()
	if err != nil {
		return g, errors.WithStack(err)
	}
	g.Branch = strings.TrimSpace(string(out))

	cmd = exec.Command("git", "log", "-1", "--pretty=format:%H")
	cmd.Stderr = os.Stderr
	out, err = cmd.Output()
	if err != nil {
		return g, errors.WithStack(err)
	}
	g.CommitSHA = strings.TrimSpace(string(out))

	cmd = exec.Command("git", "log", "-1", "--pretty=format:%ct")
	cmd.Stderr = os.Stderr
	out, err = cmd.Output()
	if err != nil {
		return g, errors.WithStack(err)
	}
	g.CommittedAt, err = strconv.Atoi(strings.TrimSpace(string(out)))
	if err != nil {
		return g, errors.WithStack(err)
	}

	return g, nil
}

var gitBranchVars = []string{"GIT_BRANCH", "APPVEYOR_REPO_BRANCH", "BRANCH_NAME", "BUILDKITE_BRANCH", "CIRCLE_BRANCH", "CI_BRANCH", "CI_BUILD_REF_NAME", "TRAVIS_BRANCH", "WERCKER_GIT_BRANCH"}

var gitCommitShaVars = []string{"GIT_COMMIT_SHA", "APPVEYOR_REPO_COMMIT", "BUILDKITE_COMMIT", "CIRCLE_SHA1", "CI_BUILD_REF", "CI_BUILD_SHA", "CI_COMMIT", "CI_COMMIT_ID", "GIT_COMMIT", "WERCKER_GIT_COMMIT"}

var gitCommittedAtVars = []string{"GIT_COMMITTED_AT", "GIT_COMMITED_AT", "CI_COMMITTED_AT", "CI_COMMITED_AT"}

var blobRegex = regexp.MustCompile(`^\d.+\s+blob\s(\w+)`)
<commit_msg>Add missing travis ENV vars (#186)<commit_after>package env

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/pkg/errors"
	"gopkg.in/src-d/go-git.v4"
	"gopkg.in/src-d/go-git.v4/plumbing"
	"gopkg.in/src-d/go-git.v4/plumbing/object"
)

type Git struct {
	Branch      string `json:"branch" structs:"branch"`
	CommitSHA   string `json:"commit_sha" structs:"commit_sha"`
	CommittedAt int    `json:"committed_at" structs:"committed_at"`
}

func (g Git) String() string {
	out := &bytes.Buffer{}
	out.WriteString("GIT_BRANCH=")
	out.WriteString(g.Branch)
	out.WriteString("\nGIT_COMMIT_SHA=")
	out.WriteString(g.CommitSHA)
	out.WriteString("\nGIT_COMMITTED_AT=")
	out.WriteString(fmt.Sprint(g.CommittedAt))
	return out.String()
}

func GetHead() (*object.Commit, error) {
	r, err := git.PlainOpen(".")
	if err != nil {
		return nil, errors.WithStack(err)
	}

	ref, err := r.Head()
	if err != nil {
		return nil, errors.WithStack(err)
	}

	commit, err := r.CommitObject(ref.Hash())
	if err != nil {
		return nil, errors.WithStack(err)
	}

	return commit, nil
}

func findGitInfo() (Git, error) {
	g, err := loadGitFromENV()
	if err == nil {
		return g, nil
	}

	logrus.Debug("couldn't load git info from ENV, trying git...")
	g = Git{}
	_, err = exec.LookPath("git")
	if err != nil {
		return g, errors.New("can't find git or load git info from ENV")
	}

	g, err = loadFromGit()
	if err != nil {
		return g, errors.WithStack(err)
	}

	return g, nil
}

func GitSHA(path string) (string, error) {
	args := []string{"log", "-1", "--follow", "--pretty=format:%H"}
	if path != "" {
		if pwd, err := os.Getwd(); err == nil {
			path = strings.TrimPrefix(path, pwd)
			path = filepath.Join(".", path)
		}
		args = append(args, path)
	}
	cmd := exec.Command("git", args...)
	cmd.Stderr = os.Stderr
	out, err := cmd.Output()
	if err != nil {
		return "", errors.WithStack(err)
	}
	return strings.TrimSpace(string(out)), nil
}

var GitBlob = func(path string, commit *object.Commit) (string, error) {
	if commit != nil {
		file, err := commit.File(path)
		if err == nil {
			logrus.Debugf("getting git blob_id for source file %s", path)

			blob := strings.TrimSpace(file.Hash.String())
			return blob, nil
		}
	}

	blob, err := fallbackBlob(path)

	if err != nil {
		return "", errors.WithStack(err)
	}

	return blob, nil
}

func fallbackBlob(path string) (string, error) {
	logrus.Debugf("getting fallback blob_id for source file %s", path)
	file, err := ioutil.ReadFile(path)

	if err != nil {
		logrus.Errorf("failed to read file %s\n%s", path, err)
		return "", errors.WithStack(err)
	}

	hash := plumbing.ComputeHash(plumbing.BlobObject, []byte(file))
	res := hash.String()

	return res, nil
}

func loadGitFromENV() (Git, error) {
	g := Git{}
	var err error

	g.Branch = findVar(gitBranchVars)
	if g.Branch == "" {
		return g, errors.New("git branch ENV not found")
	}

	g.CommitSHA = findVar(gitCommitShaVars)
	if g.CommitSHA == "" {
		return g, errors.New("git commit SHA ENV not found")
	}

	committedAt := findVar(gitCommittedAtVars)

	if committedAt == "" {
		return g, errors.New("git committed_at ENV not found")
	}

	g.CommittedAt, err = strconv.Atoi(committedAt)
	if err != nil {
		return g, errors.WithStack(err)
	}

	return g, nil
}

func loadFromGit() (Git, error) {
	g := Git{}

	cmd := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD")
	cmd.Stderr = os.Stderr
	out, err := cmd.Output()
	if err != nil {
		return g, errors.WithStack(err)
	}
	g.Branch = strings.TrimSpace(string(out))

	cmd = exec.Command("git", "log", "-1", "--pretty=format:%H")
	cmd.Stderr = os.Stderr
	out, err = cmd.Output()
	if err != nil {
		return g, errors.WithStack(err)
	}
	g.CommitSHA = strings.TrimSpace(string(out))

	cmd = exec.Command("git", "log", "-1", "--pretty=format:%ct")
	cmd.Stderr = os.Stderr
	out, err = cmd.Output()
	if err != nil {
		return g, errors.WithStack(err)
	}
	g.CommittedAt, err = strconv.Atoi(strings.TrimSpace(string(out)))
	if err != nil {
		return g, errors.WithStack(err)
	}

	return g, nil
}

var gitBranchVars = []string{"GIT_BRANCH", "APPVEYOR_REPO_BRANCH", "BRANCH_NAME", "BUILDKITE_BRANCH", "CIRCLE_BRANCH", "CI_BRANCH", "CI_BUILD_REF_NAME", "TRAVIS_PULL_REQUEST_BRANCH", "TRAVIS_BRANCH", "WERCKER_GIT_BRANCH"}

var gitCommitShaVars = []string{"GIT_COMMIT_SHA", "APPVEYOR_REPO_COMMIT", "BUILDKITE_COMMIT", "CIRCLE_SHA1", "CI_BUILD_REF", "CI_BUILD_SHA", "CI_COMMIT", "CI_COMMIT_ID", "GIT_COMMIT", "WERCKER_GIT_COMMIT", "TRAVIS_PULL_REQUEST_SHA", "TRAVIS_COMMIT"}

var gitCommittedAtVars = []string{"GIT_COMMITTED_AT", "GIT_COMMITED_AT", "CI_COMMITTED_AT", "CI_COMMITED_AT"}

var blobRegex = regexp.MustCompile(`^\d.+\s+blob\s(\w+)`)
<|endoftext|>


<commit_before>package main

import "github.com/nsf/termbox-go"

type DataScreen struct {
	tabs       []*DataTab
	active_tab int
	show_tabs  bool
}

func (screen *DataScreen) initializeWithFiles(files []FileInfo) {
	var tabs []*DataTab
	for _, file := range files {
		tab := NewDataTab(file)
		tabs = append(tabs, &tab)
	}

	screen.tabs = tabs
	screen.show_tabs = len(tabs) > 1
}

func (screen *DataScreen) receiveEvents(input <-chan termbox.Event, output chan<- int, quit <-chan bool) {
	for _, t := range screen.tabs {
		go func() {
			t.receiveEvents(output)
		}()
	}

	for {
		do_quit := false
		select {
		case event := <-input:
			output <- screen.handleKeyEvent(event, output)
		case <-quit:
			do_quit = true
		}
		if do_quit {
			for _, t := range screen.tabs {
				t.quit_channel <- true
			}
			break
		}
	}
}

func (screen *DataScreen) handleKeyEvent(event termbox.Event, output chan<- int) int {
	active_tab := screen.tabs[screen.active_tab]
	if active_tab.field_editor != nil {
		return active_tab.handleKeyEvent(event)
	} else if event.Key == termbox.KeyCtrlP { // color palette
		return PALETTE_SCREEN_INDEX
	} else if event.Ch == '?' { // about
		return ABOUT_SCREEN_INDEX
	} else if event.Ch == 'T' {
		screen.show_tabs = !screen.show_tabs
		return DATA_SCREEN_INDEX
	} else if event.Key == termbox.KeyCtrlT {
		var new_tabs []*DataTab
		for index, old_tab := range screen.tabs {
			new_tabs = append(new_tabs, old_tab)
			if old_tab == active_tab {
				tab_copy := NewDataTab(FileInfo{filename: old_tab.filename, bytes: old_tab.bytes})
				tab_copy.cursor = old_tab.cursor
				tab_copy.view_port = old_tab.view_port
				tab_copy.cursor.pos = tab_copy.view_port.first_row * tab_copy.view_port.bytes_per_row
				tab_copy.cursor.mode = StringMode
				new_tabs = append(new_tabs, &tab_copy)
				screen.active_tab = index + 1
				go func() {
					(&tab_copy).receiveEvents(output)
				}()
			}
		}
		screen.tabs = new_tabs
		screen.show_tabs = true
		return DATA_SCREEN_INDEX
	} else if event.Key == termbox.KeyCtrlW {
		if len(screen.tabs) > 1 {
			var new_tabs []*DataTab
			for _, old_tab := range screen.tabs {
				if old_tab != active_tab {
					new_tabs = append(new_tabs, old_tab)
				}
			}
			active_tab.quit_channel <- true
			screen.tabs = new_tabs
			if screen.active_tab >= len(new_tabs) {
				screen.active_tab = len(new_tabs) - 1
			}
			return DATA_SCREEN_INDEX
		}
	} else if event.Key == termbox.KeyTab && screen.show_tabs {
		screen.active_tab = (screen.active_tab + 1) % len(screen.tabs)
		return DATA_SCREEN_INDEX
	}
	return active_tab.handleKeyEvent(event)
}

func (screen *DataScreen) performLayout() {
	width, height := termbox.Size()

	for _, tab := range screen.tabs {
		if screen.show_tabs {
			tab.performLayout(width, height-3)
		} else {
			tab.performLayout(width, height)
		}
	}
}

func (screen *DataScreen) drawScreen(style Style) {
	width, _ := termbox.Size()
	active_tab := screen.tabs[screen.active_tab]
	if screen.show_tabs {
		fg := style.default_fg
		bg := style.default_bg
		x_pos := 0
		for i := 0; i < 4; i++ {
			drawStringAtPoint("━", x_pos, 2, fg, bg)
			x_pos++
		}
		for _, tab := range screen.tabs {
			name_fg := fg
			if tab != active_tab {
				name_fg = style.rune_fg
			}
			drawStringAtPoint("╭", x_pos, 0, fg, bg)
			drawStringAtPoint("│", x_pos, 1, fg, bg)
			if tab == active_tab {
				drawStringAtPoint("┙", x_pos, 2, fg, bg)
			} else {
				drawStringAtPoint("┷", x_pos, 2, fg, bg)
			}
			x_pos++

			nameLength := drawStringAtPoint(tab.filename, x_pos+2, 1, name_fg, bg)
			for i := 0; i < 2+nameLength+2; i++ {
				drawStringAtPoint("─", x_pos, 0, fg, bg)
				if tab != active_tab {
					drawStringAtPoint("━", x_pos, 2, fg, bg)
				}
				x_pos++
			}
			drawStringAtPoint("╮", x_pos, 0, fg, bg)
			drawStringAtPoint("│", x_pos, 1, fg, bg)
			if tab == active_tab {
				drawStringAtPoint("┕", x_pos, 2, fg, bg)
			} else {
				drawStringAtPoint("┷", x_pos, 2, fg, bg)
			}
			x_pos++
		}
		for x_pos < width {
			drawStringAtPoint("━", x_pos, 2, fg, bg)
			x_pos++
		}
		active_tab.drawTab(style, 3)
	} else {
		active_tab.drawTab(style, 0)
	}
}
<commit_msg>Fix tab search bug<commit_after>package main

import "github.com/nsf/termbox-go"

type DataScreen struct {
	tabs       []*DataTab
	active_tab int
	show_tabs  bool
}

func (screen *DataScreen) initializeWithFiles(files []FileInfo) {
	var tabs []*DataTab
	for _, file := range files {
		tab := NewDataTab(file)
		tabs = append(tabs, &tab)
	}

	screen.tabs = tabs
	screen.show_tabs = len(tabs) > 1
}

func (screen *DataScreen) receiveEvents(input <-chan termbox.Event, output chan<- int, quit <-chan bool) {
	for _, t := range screen.tabs {
		go func(tab *DataTab) {
			tab.receiveEvents(output)
		}(t)
	}

	for {
		do_quit := false
		select {
		case event := <-input:
			output <- screen.handleKeyEvent(event, output)
		case <-quit:
			do_quit = true
		}
		if do_quit {
			for _, t := range screen.tabs {
				t.quit_channel <- true
			}
			break
		}
	}
}

func (screen *DataScreen) handleKeyEvent(event termbox.Event, output chan<- int) int {
	active_tab := screen.tabs[screen.active_tab]
	if active_tab.field_editor != nil {
		return active_tab.handleKeyEvent(event)
	} else if event.Key == termbox.KeyCtrlP { // color palette
		return PALETTE_SCREEN_INDEX
	} else if event.Ch == '?' { // about
		return ABOUT_SCREEN_INDEX
	} else if event.Ch == 'T' {
		screen.show_tabs = !screen.show_tabs
		return DATA_SCREEN_INDEX
	} else if event.Key == termbox.KeyCtrlT {
		var new_tabs []*DataTab
		for index, old_tab := range screen.tabs {
			new_tabs = append(new_tabs, old_tab)
			if old_tab == active_tab {
				tab_copy := NewDataTab(FileInfo{filename: old_tab.filename, bytes: old_tab.bytes})
				tab_copy.cursor = old_tab.cursor
				tab_copy.view_port = old_tab.view_port
				tab_copy.cursor.pos = tab_copy.view_port.first_row * tab_copy.view_port.bytes_per_row
				tab_copy.cursor.mode = StringMode
				new_tabs = append(new_tabs, &tab_copy)
				screen.active_tab = index + 1
				go func() {
					(&tab_copy).receiveEvents(output)
				}()
			}
		}
		screen.tabs = new_tabs
		screen.show_tabs = true
		return DATA_SCREEN_INDEX
	} else if event.Key == termbox.KeyCtrlW {
		if len(screen.tabs) > 1 {
			var new_tabs []*DataTab
			for _, old_tab := range screen.tabs {
				if old_tab != active_tab {
					new_tabs = append(new_tabs, old_tab)
				}
			}
			active_tab.quit_channel <- true
			screen.tabs = new_tabs
			if screen.active_tab >= len(new_tabs) {
				screen.active_tab = len(new_tabs) - 1
			}
			return DATA_SCREEN_INDEX
		}
	} else if event.Key == termbox.KeyTab && screen.show_tabs {
		screen.active_tab = (screen.active_tab + 1) % len(screen.tabs)
		return DATA_SCREEN_INDEX
	}
	return active_tab.handleKeyEvent(event)
}

func (screen *DataScreen) performLayout() {
	width, height := termbox.Size()

	for _, tab := range screen.tabs {
		if screen.show_tabs {
			tab.performLayout(width, height-3)
		} else {
			tab.performLayout(width, height)
		}
	}
}

func (screen *DataScreen) drawScreen(style Style) {
	width, _ := termbox.Size()
	active_tab := screen.tabs[screen.active_tab]
	if screen.show_tabs {
		fg := style.default_fg
		bg := style.default_bg
		x_pos := 0
		for i := 0; i < 4; i++ {
			drawStringAtPoint("━", x_pos, 2, fg, bg)
			x_pos++
		}
		for _, tab := range screen.tabs {
			name_fg := fg
			if tab != active_tab {
				name_fg = style.rune_fg
			}
			drawStringAtPoint("╭", x_pos, 0, fg, bg)
			drawStringAtPoint("│", x_pos, 1, fg, bg)
			if tab == active_tab {
				drawStringAtPoint("┙", x_pos, 2, fg, bg)
			} else {
				drawStringAtPoint("┷", x_pos, 2, fg, bg)
			}
			x_pos++

			nameLength := drawStringAtPoint(tab.filename, x_pos+2, 1, name_fg, bg)
			for i := 0; i < 2+nameLength+2; i++ {
				drawStringAtPoint("─", x_pos, 0, fg, bg)
				if tab != active_tab {
					drawStringAtPoint("━", x_pos, 2, fg, bg)
				}
				x_pos++
			}
			drawStringAtPoint("╮", x_pos, 0, fg, bg)
			drawStringAtPoint("│", x_pos, 1, fg, bg)
			if tab == active_tab {
				drawStringAtPoint("┕", x_pos, 2, fg, bg)
			} else {
				drawStringAtPoint("┷", x_pos, 2, fg, bg)
			}
			x_pos++
		}
		for x_pos < width {
			drawStringAtPoint("━", x_pos, 2, fg, bg)
			x_pos++
		}
		active_tab.drawTab(style, 3)
	} else {
		active_tab.drawTab(style, 0)
	}
}
<|endoftext|>


<commit_before>package schedule

import (
	"math/rand"
	"testing"
)

func XTestTextSchedule(t *testing.T) {
	var tests = []struct {
		text        string
		numCommits  int
		isPrintable bool
	}{
		{"hello", 123, true}, // TODO replace with actual number of commits
		{"", 0, false},
		{"this is to long to print", 0, false},
	}
	for _, test := range tests {
		git := &MockGit{}
		git.Init()
		filegen := MockFileGenerator{}
		err := TextSchedule(test.text, git, filegen)

		if (err == nil) != test.isPrintable {
			fmt := "The message %s should have been isPrintable==%b"
			t.Errorf(fmt, test.text, test.isPrintable)
		}

		if git.numCommitCalls != test.numCommits {
			fmt := "The message %s should have had %d commits, but got %d."
			t.Errorf(fmt, test.text, test.numCommits, git.numCommitCalls)
		}
	}
}

func XTestGetTextCommitSchedule(t *testing.T) {
	var tests = []struct {
		text        string
		numCommits  int
		isPrintable bool
	}{
		{"hello", 123, true}, // TODO replace with actual number of commits
		{"", 0, false},
		{"this is to long to print", 0, false},
	}
	for _, test := range tests {
		days := GetDaysSinceNowMinusOneYear()
		messageBase := GetCommitMessageBase()
		commits, err := getTextCommitSchedule(test.text, days, messageBase)

		if (err == nil) != test.isPrintable {
			fmt := "The message %s should have been isPrintable==%b, but was %b"
			t.Errorf(fmt, test.text, test.isPrintable, (err == nil))
		}

		if len(commits) != test.numCommits {
			fmt := "The message %s should have had %d commits, but got %d."
			t.Errorf(fmt, test.text, test.numCommits, len(commits))
		}
	}
}

func TestTextFits(t *testing.T) {
	var tests = []struct {
		text        string
		isPrintable bool
	}{
		{"hello", true},
		{"", false},
		{"this is to long to print", false},
	}
	for _, test := range tests {
		actual := textFits(test.text)
		if actual != test.isPrintable {
			t.Errorf("Expected check to be %v, but was %v", test.isPrintable, actual)
		}
	}
}

func TestGetTextWidth(t *testing.T) {
	var tests = []struct {
		text      string
		textWidth int
	}{
		{"hello", 24},
		{"", -1},
		{"this is to long to print", 101},
	}
	for _, test := range tests {
		actual := getTextWidth(test.text)
		if actual != test.textWidth {
			t.Errorf("Expected width to be %d, but was %d", test.textWidth, actual)
		}
	}
}

func XTestConvertScheduleToCommits(t *testing.T) {
	var tests = []struct {
		numCommits int
	}{
		{0}, {1}, {365},
	}
	for _, test := range tests {
		days := GetDaysSinceNowMinusOneYear()
		schedule := BuildCommitSchedule(days)
		addCommitsToSchedule(&schedule, test.numCommits)
		commits := convertScheduleToCommits(schedule)
		actual := len(commits)
		if actual != test.numCommits {
			t.Errorf("Expected %d commits, but got %d", test.numCommits, actual)
		}
	}
}

func addCommitsToSchedule(schedule *CommitSchedule, numCommits int) {
	for i := 0; i < numCommits; i++ {
		randRow := rand.Intn(7)
		randCol := rand.Intn(53)
		schedule[randCol][randRow].NumCommits += 1
	}
}

func XTestBuildTextCommitSchedule(t *testing.T) {
	var tests = []struct {
		text       string
		numCommits int
	}{
		{"hello", 62}, {"i", 6},
	}
	for _, test := range tests {
		days := GetDaysSinceNowMinusOneYear()
		commits := buildTextCommitSchedule(days, test.text)
		actual := len(commits)
		if actual != test.numCommits {
			t.Errorf("Expected width to be %d, but was %d", test.numCommits, actual)
		}
	}
}

func TestMapTextOntoCommitSchedule(t *testing.T) {
	var tests = []struct {
		text      string
		numPixels int
	}{
		{"a", 14}, {"i", 6}, {" ", 0},
	}
	for _, test := range tests {
		days := GetDaysSinceNowMinusOneYear()
		schedule := BuildCommitSchedule(days)
		mapTextOntoCommitSchedule(test.text, &schedule)
		actual := getSumCommits(schedule)
		if actual != test.numPixels {
			t.Errorf("Expected width to be %d, but was %d", test.numPixels, actual)
		}
	}
}

func getSumCommits(schedule CommitSchedule) int {
	sum := 0
	for _, row := range schedule {
		for _, entry := range row {
			if entry.NumCommits > 0 {
				sum += entry.NumCommits
			}
		}
	}
	return sum
}

func TestBuildTextFields(t *testing.T) {
	var tests = []struct {
		text   string
		length int
	}{
		{"hello world", 21}, {"t", 1}, {"", 0},
	}
	for _, test := range tests {
		letters := buildTextFields(test.text)
		actual := len(letters)
		if actual != test.length {
			t.Errorf("Expected length to be %d, but was %d", test.length, actual)
		}
	}
}
<commit_msg>Fix test logic for random commits and include new tests.<commit_after>package schedule

import (
	"math/rand"
	"testing"
)

func XTestTextSchedule(t *testing.T) {
	var tests = []struct {
		text        string
		numCommits  int
		isPrintable bool
	}{
		{"hello", 123, true}, // TODO replace with actual number of commits
		{"", 0, false},
		{"this is to long to print", 0, false},
	}
	for _, test := range tests {
		git := &MockGit{}
		git.Init()
		filegen := MockFileGenerator{}
		err := TextSchedule(test.text, git, filegen)

		if (err == nil) != test.isPrintable {
			fmt := "The message %s should have been isPrintable==%b"
			t.Errorf(fmt, test.text, test.isPrintable)
		}

		if git.numCommitCalls != test.numCommits {
			fmt := "The message %s should have had %d commits, but got %d."
			t.Errorf(fmt, test.text, test.numCommits, git.numCommitCalls)
		}
	}
}

func XTestGetTextCommitSchedule(t *testing.T) {
	var tests = []struct {
		text        string
		numCommits  int
		isPrintable bool
	}{
		{"hello", 123, true}, // TODO replace with actual number of commits
		{"", 0, false},
		{"this is to long to print", 0, false},
	}
	for _, test := range tests {
		days := GetDaysSinceNowMinusOneYear()
		messageBase := GetCommitMessageBase()
		commits, err := getTextCommitSchedule(test.text, days, messageBase)

		if (err == nil) != test.isPrintable {
			fmt := "The message %s should have been isPrintable==%b, but was %b"
			t.Errorf(fmt, test.text, test.isPrintable, (err == nil))
		}

		if len(commits) != test.numCommits {
			fmt := "The message %s should have had %d commits, but got %d."
			t.Errorf(fmt, test.text, test.numCommits, len(commits))
		}
	}
}

func TestTextFits(t *testing.T) {
	var tests = []struct {
		text        string
		isPrintable bool
	}{
		{"hello", true},
		{"", false},
		{"this is to long to print", false},
	}
	for _, test := range tests {
		actual := textFits(test.text)
		if actual != test.isPrintable {
			t.Errorf("Expected check to be %v, but was %v", test.isPrintable, actual)
		}
	}
}

func TestGetTextWidth(t *testing.T) {
	var tests = []struct {
		text      string
		textWidth int
	}{
		{"hello", 24},
		{"", -1},
		{"this is to long to print", 101},
	}
	for _, test := range tests {
		actual := getTextWidth(test.text)
		if actual != test.textWidth {
			t.Errorf("Expected width to be %d, but was %d", test.textWidth, actual)
		}
	}
}

func TestConvertScheduleToCommits(t *testing.T) {
	var tests = []struct {
		numCommits int
	}{
		{0}, {1}, {365},
	}
	for _, test := range tests {
		days := GetDaysSinceNowMinusOneYear()
		schedule := BuildCommitSchedule(days)
		addCommitsToSchedule(&schedule, test.numCommits)
		commits := convertScheduleToCommits(schedule)
		actual := len(commits)
		if actual != test.numCommits {
			t.Errorf("Expected %d commits, but got %d", test.numCommits, actual)
		}
	}
}

func addCommitsToSchedule(schedule *CommitSchedule, numCommits int) {
	for i := 0; i < numCommits; i++ {
		randRow := rand.Intn(7)
		randCol := rand.Intn(51) // avoid getting a NOT_A_FIELD field in the margins
		schedule[randRow][randCol+1].NumCommits += 1
	}
}

func TestBuildTextCommitSchedule(t *testing.T) {
	var tests = []struct {
		text       string
		numCommits int
	}{
		{"hello", 62}, {"i", 6},
	}
	for _, test := range tests {
		days := GetDaysSinceNowMinusOneYear()
		schedule := buildTextCommitSchedule(days, test.text)
		actual := getSumCommits(schedule)
		if actual != test.numCommits {
			t.Errorf("Expected width to be %d, but was %d", test.numCommits, actual)
		}
	}
}

func TestMapTextOntoCommitSchedule(t *testing.T) {
	var tests = []struct {
		text      string
		numPixels int
	}{
		{"a", 14}, {"i", 6}, {" ", 0},
	}
	for _, test := range tests {
		days := GetDaysSinceNowMinusOneYear()
		schedule := BuildCommitSchedule(days)
		mapTextOntoCommitSchedule(test.text, &schedule)
		actual := getSumCommits(schedule)
		if actual != test.numPixels {
			t.Errorf("Expected width to be %d, but was %d", test.numPixels, actual)
		}
	}
}

func getSumCommits(schedule CommitSchedule) int {
	sum := 0
	for _, row := range schedule {
		for _, entry := range row {
			if entry.NumCommits > 0 {
				sum += entry.NumCommits
			}
		}
	}
	return sum
}

func TestBuildTextFields(t *testing.T) {
	var tests = []struct {
		text   string
		length int
	}{
		{"hello world", 21}, {"t", 1}, {"", 0},
	}
	for _, test := range tests {
		letters := buildTextFields(test.text)
		actual := len(letters)
		if actual != test.length {
			t.Errorf("Expected length to be %d, but was %d", test.length, actual)
		}
	}
}
<|endoftext|>


<commit_before>package tests

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/fgrosse/goldi"
	"github.com/fgrosse/goldi/tests/testAPI"
)

var _ = Describe("TypeRegistry", func() {

	var registry goldi.TypeRegistry

	BeforeEach(func() {
		registry = goldi.NewTypeRegistry()
	})

	Describe("RegisterType", func() {
		It("should store the type generator", func() {
			typeID := "goldi.test_type"
			generator := &testAPI.MockTypeFactory{}
			Expect(registry.RegisterType(typeID, generator.NewMockType)).To(Succeed())

			generatorWrapper, typeIsRegistered := registry[typeID]
			Expect(typeIsRegistered).To(BeTrue())
			Expect(generatorWrapper).NotTo(BeNil())

			config := map[string]interface{}{}
			generatorWrapper.Generate(config)
			Expect(generator.HasBeenUsed).To(BeTrue())
		})

		It("should recover panics from NewType", func() {
			Expect(func() { registry.RegisterType("goldi.test_type", testAPI.NewMockTypeWithArgs) }).NotTo(Panic())
			Expect(registry.RegisterType("goldi.test_type", testAPI.NewMockTypeWithArgs)).NotTo(Succeed())
		})

		It("should return an error if the type has been defined previously", func() {
			typeID := "goldi.test_type"
			generator := &testAPI.MockTypeFactory{}
			Expect(registry.RegisterType(typeID, generator.NewMockType)).To(Succeed())
			Expect(registry.RegisterType(typeID, generator.NewMockType)).NotTo(Succeed())
		})
	})
})
<commit_msg>Add more tests<commit_after>package tests

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/fgrosse/goldi"
	"github.com/fgrosse/goldi/tests/testAPI"
)

var _ = Describe("TypeRegistry", func() {
	var (
		registry goldi.TypeRegistry
		config   = map[string]interface{}{} // for test convenience
	)

	BeforeEach(func() {
		registry = goldi.NewTypeRegistry()
	})

	Describe("RegisterType", func() {
		It("should store the type generator", func() {
			typeID := "goldi.test_type"
			generator := &testAPI.MockTypeFactory{}
			Expect(registry.RegisterType(typeID, generator.NewMockType)).To(Succeed())

			generatorWrapper, typeIsRegistered := registry[typeID]
			Expect(typeIsRegistered).To(BeTrue())
			Expect(generatorWrapper).NotTo(BeNil())

			generatorWrapper.Generate(config)
			Expect(generator.HasBeenUsed).To(BeTrue())
		})

		It("should recover panics from NewType", func() {
			Expect(func() { registry.RegisterType("goldi.test_type", testAPI.NewMockTypeWithArgs) }).NotTo(Panic())
			Expect(registry.RegisterType("goldi.test_type", testAPI.NewMockTypeWithArgs)).NotTo(Succeed())
		})

		It("should return an error if the type has been defined previously", func() {
			typeID := "goldi.test_type"
			generator := &testAPI.MockTypeFactory{}
			Expect(registry.RegisterType(typeID, generator.NewMockType)).To(Succeed())
			Expect(registry.RegisterType(typeID, generator.NewMockType)).NotTo(Succeed())
		})

		It("should pass parameters to the new type", func() {
			typeID := "goldi.test_type"
			Expect(registry.RegisterType(typeID, testAPI.NewMockTypeWithArgs, "foo", true)).To(Succeed())
			Expect(registry).To(HaveKey(typeID))
			Expect(registry["goldi.test_type"].Generate(config).(*testAPI.MockType).StringParameter).To(Equal("foo"))
			Expect(registry["goldi.test_type"].Generate(config).(*testAPI.MockType).BoolParameter).To(Equal(true))
		})
	})
})
<|endoftext|>


<commit_before>package slackapi

import (
	"encoding/json"
	"testing"
)

func CheckResponse(t *testing.T, x interface{}, y string) {
	out, err := json.Marshal(x)
	if err != nil {
		t.Fatal("json fromat;", err)
	}
	if string(out) != y {
		t.Fatalf("invalid json response;\n- %s\n+ %s\n", y, out)
	}
}

func TestAPITest(t *testing.T) {
	s := New()
	x := s.APITest()
	y := `{"ok":true}`
	CheckResponse(t, x, y)
}

func TestAppsList(t *testing.T) {
	s := New()
	x := s.AppsList()
	y := `{"ok":false,"error":"not_authed","apps":null,"cache_ts":""}`
	CheckResponse(t, x, y)
}

func TestAuthRevoke(t *testing.T) {
	s := New()
	x := s.AuthRevoke()
	y := `{"ok":false,"error":"not_authed","revoked":false}`
	CheckResponse(t, x, y)
}

func TestAuthTest(t *testing.T) {
	s := New()
	x, err := s.AuthTest()
	if err != nil {
		t.Fatal(err)
	}
	y := `{"ok":false,"error":"not_authed","team":"","team_id":"","url":"","user":"","user_id":""}`
	CheckResponse(t, x, y)
}

func TestBotsInfo(t *testing.T) {
	s := New()
	x := s.BotsInfo("user")
	y := `{"ok":false,"error":"not_authed","bot":{"id":"","deleted":false,"name":"","icons":null}}`
	CheckResponse(t, x, y)
}

func TestChannelsID(t *testing.T) {
	s := New()
	x := s.ChannelsID("channel")
	y := `"channel"`
	CheckResponse(t, x, y)
}

func TestChannelsMyHistory(t *testing.T) {
	s := New()
	x := s.ChannelsMyHistory("channel", "1234567890")
	y := `{"Filtered":0,"Latest":"","Messages":null,"Oldest":"","Total":0,"Username":""}`
	CheckResponse(t, x, y)
}

func TestChannelsPurgeHistory(t *testing.T) {
	s := New()
	x := s.ChannelsPurgeHistory("channel", "1234567890", true)
	y := `{"Deleted":0,"NotDeleted":0,"Messages":null}`
	CheckResponse(t, x, y)
}

func TestChannelsSetRetention(t *testing.T) {
	s := New()
	x := s.ChannelsSetRetention("channel", 1)
	y := `{"ok":false,"error":"not_authed"}`
	CheckResponse(t, x, y)
}

func TestChannelsSuggestions(t *testing.T) {
	s := New()
	x := s.ChannelsSuggestions()
	y := `{"ok":false,"error":"not_authed","status":{"ok":false},"suggestion_types_tried":null}`
	CheckResponse(t, x, y)
}
<commit_msg>Remove channels.suggestions unit test due to API uncertanties<commit_after>package slackapi

import (
	"encoding/json"
	"testing"
)

func CheckResponse(t *testing.T, x interface{}, y string) {
	out, err := json.Marshal(x)
	if err != nil {
		t.Fatal("json fromat;", err)
	}
	if string(out) != y {
		t.Fatalf("invalid json response;\n- %s\n+ %s\n", y, out)
	}
}

func TestAPITest(t *testing.T) {
	s := New()
	x := s.APITest()
	y := `{"ok":true}`
	CheckResponse(t, x, y)
}

func TestAppsList(t *testing.T) {
	s := New()
	x := s.AppsList()
	y := `{"ok":false,"error":"not_authed","apps":null,"cache_ts":""}`
	CheckResponse(t, x, y)
}

func TestAuthRevoke(t *testing.T) {
	s := New()
	x := s.AuthRevoke()
	y := `{"ok":false,"error":"not_authed","revoked":false}`
	CheckResponse(t, x, y)
}

func TestAuthTest(t *testing.T) {
	s := New()
	x, err := s.AuthTest()
	if err != nil {
		t.Fatal(err)
	}
	y := `{"ok":false,"error":"not_authed","team":"","team_id":"","url":"","user":"","user_id":""}`
	CheckResponse(t, x, y)
}

func TestBotsInfo(t *testing.T) {
	s := New()
	x := s.BotsInfo("user")
	y := `{"ok":false,"error":"not_authed","bot":{"id":"","deleted":false,"name":"","icons":null}}`
	CheckResponse(t, x, y)
}

func TestChannelsID(t *testing.T) {
	s := New()
	x := s.ChannelsID("channel")
	y := `"channel"`
	CheckResponse(t, x, y)
}

func TestChannelsMyHistory(t *testing.T) {
	s := New()
	x := s.ChannelsMyHistory("channel", "1234567890")
	y := `{"Filtered":0,"Latest":"","Messages":null,"Oldest":"","Total":0,"Username":""}`
	CheckResponse(t, x, y)
}

func TestChannelsPurgeHistory(t *testing.T) {
	s := New()
	x := s.ChannelsPurgeHistory("channel", "1234567890", true)
	y := `{"Deleted":0,"NotDeleted":0,"Messages":null}`
	CheckResponse(t, x, y)
}

func TestChannelsSetRetention(t *testing.T) {
	s := New()
	x := s.ChannelsSetRetention("channel", 1)
	y := `{"ok":false,"error":"not_authed"}`
	CheckResponse(t, x, y)
}
<|endoftext|>


<commit_before>// Package bugsnagrevel adds Bugsnag to revel.
// It lets you pass *revel.Controller into bugsnag.Notify(),
// and provides a Filter to catch errors.
package bugsnagrevel

import (
	"github.com/bugsnag/bugsnag-go"
	"github.com/revel/revel"
	"net/http"
	"strconv"
	"strings"
	"sync"
)

var once sync.Once

const FrameworkName string = "Revel"

var errorHandlingState = bugsnag.HandledState{
	bugsnag.SeverityReasonUnhandledMiddlewareError,
	bugsnag.SeverityError,
	true,
	FrameworkName,
}

// Filter should be added to the filter chain just after the PanicFilter.
// It sends errors to Bugsnag automatically. Configuration is read out of
// conf/app.conf, you should set bugsnag.apikey, and can also set
// bugsnag.endpoint, bugsnag.releasestage, bugsnag.apptype, bugsnag.appversion,
// bugsnag.projectroot, bugsnag.projectpackages if needed.
func Filter(c *revel.Controller, fc []revel.Filter) {
	defer bugsnag.AutoNotify(c, errorHandlingState)
	fc[0](c, fc[1:])
}

// Add support to bugsnag for reading data out of *revel.Controllers
func middleware(event *bugsnag.Event, config *bugsnag.Configuration) error {
	for _, datum := range event.RawData {
		if controller, ok := datum.(*revel.Controller); ok {
			// make the request visible to the builtin HttpMiddleware
			if version("0.18.0") {
				event.RawData = append(event.RawData, controller.Request)
			} else {
				req := struct{ *http.Request }{}
				event.RawData = append(event.RawData, req.Request)
			}
			event.RawData = append(event.RawData, controller.Request)
			event.Context = controller.Action
			event.MetaData.AddStruct("Session", controller.Session)
		}
	}

	return nil
}

func init() {
	revel.OnAppStart(func() {
		bugsnag.OnBeforeNotify(middleware)

		var projectPackages []string
		if packages, ok := revel.Config.String("bugsnag.projectpackages"); ok {
			projectPackages = strings.Split(packages, ",")
		} else {
			projectPackages = []string{revel.ImportPath + "/app/*", revel.ImportPath + "/app"}
		}

		bugsnag.Configure(bugsnag.Configuration{
			APIKey:          revel.Config.StringDefault("bugsnag.apikey", ""),
			Endpoint:        revel.Config.StringDefault("bugsnag.endpoint", ""),
			AppType:         revel.Config.StringDefault("bugsnag.apptype", ""),
			AppVersion:      revel.Config.StringDefault("bugsnag.appversion", ""),
			ReleaseStage:    revel.Config.StringDefault("bugsnag.releasestage", revel.RunMode),
			ProjectPackages: projectPackages,
			Logger:          revel.ERROR,
		})
	})
}

// Very basic semantic versioning.
// Returns true if given version matches or is above revel.Version
func version(reqVersion string) bool {
	req := strings.Split(reqVersion, ".")
	cur := strings.Split(revel.Version, ".")
	for i := 0; i < 2; i++ {
		rV, _ := strconv.Atoi(req[i])
		cV, _ := strconv.Atoi(cur[i])
		if (rV < cV && i == 0) || (rV < cV && i == 1) {
			return true
		}
	}
	return false
}
<commit_msg>fix: Remove extraneous request metadata<commit_after>// Package bugsnagrevel adds Bugsnag to revel.
// It lets you pass *revel.Controller into bugsnag.Notify(),
// and provides a Filter to catch errors.
package bugsnagrevel

import (
	"github.com/bugsnag/bugsnag-go"
	"github.com/revel/revel"
	"net/http"
	"strconv"
	"strings"
	"sync"
)

var once sync.Once

const FrameworkName string = "Revel"

var errorHandlingState = bugsnag.HandledState{
	bugsnag.SeverityReasonUnhandledMiddlewareError,
	bugsnag.SeverityError,
	true,
	FrameworkName,
}

// Filter should be added to the filter chain just after the PanicFilter.
// It sends errors to Bugsnag automatically. Configuration is read out of
// conf/app.conf, you should set bugsnag.apikey, and can also set
// bugsnag.endpoint, bugsnag.releasestage, bugsnag.apptype, bugsnag.appversion,
// bugsnag.projectroot, bugsnag.projectpackages if needed.
func Filter(c *revel.Controller, fc []revel.Filter) {
	defer bugsnag.AutoNotify(c, errorHandlingState)
	fc[0](c, fc[1:])
}

// Add support to bugsnag for reading data out of *revel.Controllers
func middleware(event *bugsnag.Event, config *bugsnag.Configuration) error {
	for _, datum := range event.RawData {
		if controller, ok := datum.(*revel.Controller); ok {
			// make the request visible to the builtin HttpMiddleware
			if version("0.18.0") {
				event.RawData = append(event.RawData, controller.Request)
			} else {
				req := struct{ *http.Request }{}
				event.RawData = append(event.RawData, req.Request)
			}
			event.Context = controller.Action
			event.MetaData.AddStruct("Session", controller.Session)
		}
	}

	return nil
}

func init() {
	revel.OnAppStart(func() {
		bugsnag.OnBeforeNotify(middleware)

		var projectPackages []string
		if packages, ok := revel.Config.String("bugsnag.projectpackages"); ok {
			projectPackages = strings.Split(packages, ",")
		} else {
			projectPackages = []string{revel.ImportPath + "/app/*", revel.ImportPath + "/app"}
		}

		bugsnag.Configure(bugsnag.Configuration{
			APIKey:          revel.Config.StringDefault("bugsnag.apikey", ""),
			Endpoint:        revel.Config.StringDefault("bugsnag.endpoint", ""),
			AppType:         revel.Config.StringDefault("bugsnag.apptype", ""),
			AppVersion:      revel.Config.StringDefault("bugsnag.appversion", ""),
			ReleaseStage:    revel.Config.StringDefault("bugsnag.releasestage", revel.RunMode),
			ProjectPackages: projectPackages,
			Logger:          revel.ERROR,
		})
	})
}

// Very basic semantic versioning.
// Returns true if given version matches or is above revel.Version
func version(reqVersion string) bool {
	req := strings.Split(reqVersion, ".")
	cur := strings.Split(revel.Version, ".")
	for i := 0; i < 2; i++ {
		rV, _ := strconv.Atoi(req[i])
		cV, _ := strconv.Atoi(cur[i])
		if (rV < cV && i == 0) || (rV < cV && i == 1) {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package route

import (
	"errors"
	"fmt"
	"log"
	"net"
	"net/http"
	"strings"
)

const (
	ipAllowTag = "allow:ip"
	ipDenyTag  = "deny:ip"
)

// AccessDeniedHTTP checks rules on the target for HTTP proxy routes.
func (t *Target) AccessDeniedHTTP(r *http.Request) bool {
	host, _, err := net.SplitHostPort(r.RemoteAddr)

	if err != nil {
		log.Printf("[ERROR] Failed to get host from RemoteAddr %s: %s",
			r.RemoteAddr, err.Error())
		return false
	}

	// prefer xff header if set
	if xff := r.Header.Get("X-Forwarded-For"); xff != "" && xff != host {
		host = xff
	}

	// currently only one function - more may be added in the future
	return t.denyByIP(net.ParseIP(host))
}

// AccessDeniedTCP checks rules on the target for TCP proxy routes.
func (t *Target) AccessDeniedTCP(c net.Conn) bool {
	// currently only one function - more may be added in the future
	return t.denyByIP(net.ParseIP(c.RemoteAddr().String()))
}

func (t *Target) denyByIP(ip net.IP) bool {
	if ip == nil || t.accessRules == nil {
		return false
	}

	// check allow (whitelist) first if it exists
	if _, ok := t.accessRules[ipAllowTag]; ok {
		var block *net.IPNet
		for _, x := range t.accessRules[ipAllowTag] {
			if block, ok = x.(*net.IPNet); !ok {
				log.Print("[ERROR] failed to assert ip block while checking allow rule for ", t.Service)
				continue
			}
			if block.Contains(ip) {
				// specific allow matched - allow this request
				return false
			}
		}
		// we checked all the blocks - deny this request
		return true
	}

	// still going - check deny (blacklist) if it exists
	if _, ok := t.accessRules[ipDenyTag]; ok {
		var block *net.IPNet
		for _, x := range t.accessRules[ipDenyTag] {
			if block, ok = x.(*net.IPNet); !ok {
				log.Print("[INFO] failed to assert ip block while checking deny rule for ", t.Service)
				continue
			}
			if block.Contains(ip) {
				// specific deny matched - deny this request
				return true
			}
		}
	}

	// default - do not deny
	return false
}

func (t *Target) parseAccessRule(allowDeny string) error {
	var accessTag string
	var temps []string

	// init rules if needed
	if t.accessRules == nil {
		t.accessRules = make(map[string][]interface{})
	}

	// loop over rule elements
	for _, c := range strings.Split(t.Opts[allowDeny], ",") {
		if temps = strings.SplitN(c, ":", 2); len(temps) != 2 {
			return fmt.Errorf("invalid access item, expected <type>:<data>, got %s", temps)
		}
		accessTag = allowDeny + ":" + strings.ToLower(temps[0])
		switch accessTag {
		case ipAllowTag, ipDenyTag:
			_, net, err := net.ParseCIDR(strings.TrimSpace(temps[1]))
			if err != nil {
				return fmt.Errorf("failed to parse CIDR %s with error: %s",
					c, err.Error())
			}
			// add element to rule map
			t.accessRules[accessTag] = append(t.accessRules[accessTag], net)
		default:
			return fmt.Errorf("unknown access item type: %s", temps[0])
		}
	}
	return nil
}

func (t *Target) processAccessRules() error {
	if t.Opts["allow"] != "" && t.Opts["deny"] != "" {
		return errors.New("specifying allow and deny on the same route is not supported")
	}

	for _, allowDeny := range []string{"allow", "deny"} {
		if t.Opts[allowDeny] != "" {
			if err := t.parseAccessRule(allowDeny); err != nil {
				return err
			}
		}
	}
	return nil
}
<commit_msg>prevent access control bypass via xff header<commit_after>package route

import (
	"errors"
	"fmt"
	"log"
	"net"
	"net/http"
	"strings"
)

const (
	ipAllowTag = "allow:ip"
	ipDenyTag  = "deny:ip"
)

// AccessDeniedHTTP checks rules on the target for HTTP proxy routes.
func (t *Target) AccessDeniedHTTP(r *http.Request) bool {
	var ip net.IP
	host, _, err := net.SplitHostPort(r.RemoteAddr)

	if err != nil {
		log.Printf("[ERROR] failed to get host from remote header %s: %s",
			r.RemoteAddr, err.Error())
		return false
	}

	if ip = net.ParseIP(host); ip == nil {
		log.Printf("[WARN] failed to parse remote address %s", host)
	}

	// check remote source and return if denied
	if ip != nil && t.denyByIP(ip) {
		return true
	}

	// check xff source if present
	if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
		// only use left-most element (client)
		xff = strings.TrimSpace(strings.SplitN(xff, ",", 2)[0])
		// only continue if xff differs from host
		if xff != host {
			if ip = net.ParseIP(xff); ip == nil {
				log.Printf("[WARN] failed to parse xff address %s", xff)
			}
			if ip != nil && t.denyByIP(ip) {
				return true
			}
		}
	}

	// default allow
	return false
}

// AccessDeniedTCP checks rules on the target for TCP proxy routes.
func (t *Target) AccessDeniedTCP(c net.Conn) bool {
	// currently only one function - more may be added in the future
	return t.denyByIP(net.ParseIP(c.RemoteAddr().String()))
}

func (t *Target) denyByIP(ip net.IP) bool {
	if ip == nil || t.accessRules == nil {
		return false
	}

	// check allow (whitelist) first if it exists
	if _, ok := t.accessRules[ipAllowTag]; ok {
		var block *net.IPNet
		for _, x := range t.accessRules[ipAllowTag] {
			if block, ok = x.(*net.IPNet); !ok {
				log.Print("[ERROR] failed to assert ip block while checking allow rule for ", t.Service)
				continue
			}
			if block.Contains(ip) {
				// specific allow matched - allow this request
				return false
			}
		}
		// we checked all the blocks - deny this request
		return true
	}

	// still going - check deny (blacklist) if it exists
	if _, ok := t.accessRules[ipDenyTag]; ok {
		var block *net.IPNet
		for _, x := range t.accessRules[ipDenyTag] {
			if block, ok = x.(*net.IPNet); !ok {
				log.Print("[INFO] failed to assert ip block while checking deny rule for ", t.Service)
				continue
			}
			if block.Contains(ip) {
				// specific deny matched - deny this request
				return true
			}
		}
	}

	// default - do not deny
	return false
}

func (t *Target) parseAccessRule(allowDeny string) error {
	var accessTag string
	var temps []string

	// init rules if needed
	if t.accessRules == nil {
		t.accessRules = make(map[string][]interface{})
	}

	// loop over rule elements
	for _, c := range strings.Split(t.Opts[allowDeny], ",") {
		if temps = strings.SplitN(c, ":", 2); len(temps) != 2 {
			return fmt.Errorf("invalid access item, expected <type>:<data>, got %s", temps)
		}
		accessTag = allowDeny + ":" + strings.ToLower(temps[0])
		switch accessTag {
		case ipAllowTag, ipDenyTag:
			_, net, err := net.ParseCIDR(strings.TrimSpace(temps[1]))
			if err != nil {
				return fmt.Errorf("failed to parse CIDR %s with error: %s",
					c, err.Error())
			}
			// add element to rule map
			t.accessRules[accessTag] = append(t.accessRules[accessTag], net)
		default:
			return fmt.Errorf("unknown access item type: %s", temps[0])
		}
	}
	return nil
}

func (t *Target) processAccessRules() error {
	if t.Opts["allow"] != "" && t.Opts["deny"] != "" {
		return errors.New("specifying allow and deny on the same route is not supported")
	}

	for _, allowDeny := range []string{"allow", "deny"} {
		if t.Opts[allowDeny] != "" {
			if err := t.parseAccessRule(allowDeny); err != nil {
				return err
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2013 tsuru 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 router

import (
	"launchpad.net/gocheck"
)

func (s *S) TestRegisterAndGet(c *gocheck.C) {
	var r Router
	Register("router", r)
	got, err := Get("router")
	c.Assert(err, gocheck.IsNil)
	c.Assert(r, gocheck.DeepEquals, got)
	_, err = Get("unknown-router")
	c.Assert(err, gocheck.Not(gocheck.IsNil))
	expectedMessage := `Unknown router: "unknown-router".`
	c.Assert(expectedMessage, gocheck.Equals, err.Error())
}

func (s *S) TestStore(c *gocheck.C) {
	err := Store("appname", "routername")
	c.Assert(err, gocheck.IsNil)
	name, err := Retrieve("appname")
	c.Assert(err, gocheck.IsNil)
	c.Assert(name, gocheck.Equals, "routername")
	err = Remove("appname")
	c.Assert(err, gocheck.IsNil)
}
<commit_msg>router: simplified the import.<commit_after>// Copyright 2013 tsuru 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 router

import "launchpad.net/gocheck"

func (s *S) TestRegisterAndGet(c *gocheck.C) {
	var r Router
	Register("router", r)
	got, err := Get("router")
	c.Assert(err, gocheck.IsNil)
	c.Assert(r, gocheck.DeepEquals, got)
	_, err = Get("unknown-router")
	c.Assert(err, gocheck.Not(gocheck.IsNil))
	expectedMessage := `Unknown router: "unknown-router".`
	c.Assert(expectedMessage, gocheck.Equals, err.Error())
}

func (s *S) TestStore(c *gocheck.C) {
	err := Store("appname", "routername")
	c.Assert(err, gocheck.IsNil)
	name, err := Retrieve("appname")
	c.Assert(err, gocheck.IsNil)
	c.Assert(name, gocheck.Equals, "routername")
	err = Remove("appname")
	c.Assert(err, gocheck.IsNil)
}
<|endoftext|>


<commit_before>package hostdb

import (
	"testing"

	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)

// TestFindHostAnnouncements probes the findHostAnnouncements function
func TestFindHostAnnouncements(t *testing.T) {
	// Create a block with a host announcement.
	announcement := append(modules.PrefixHostAnnouncement[:], encoding.Marshal(modules.HostAnnouncement{})...)
	b := types.Block{
		Transactions: []types.Transaction{
			types.Transaction{
				ArbitraryData: [][]byte{announcement},
			},
		},
	}
	announcements := findHostAnnouncements(b)
	if len(announcements) != 1 {
		t.Error("host announcement not found in block")
	}

	// Try with an altered prefix
	b.Transactions[0].ArbitraryData[0][0]++
	announcements = findHostAnnouncements(b)
	if len(announcements) != 0 {
		t.Error("host announcement found when there was an invalid prefix")
	}
	b.Transactions[0].ArbitraryData[0][0]--

	// Try with an invalid host encoding.
	b.Transactions[0].ArbitraryData[0][17]++
	announcements = findHostAnnouncements(b)
	if len(announcements) != 0 {
		t.Error("host announcement found when there was an invalid encoding of a host announcement")
	}
}

// TestReceiveConsensusSetUpdate probes the ReveiveConsensusSetUpdate method of
// the hostdb type.
func TestReceiveConsensusSetUpdate(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	ht, err := newHostDBTester("TestFindHostAnnouncements")
	if err != nil {
		t.Fatal(err)
	}

	// Put a host announcement into the blockchain.
	announcement := encoding.Marshal(modules.HostAnnouncement{
		IPAddress: ht.gateway.Address(),
	})
	txnBuilder := ht.wallet.StartTransaction()
	txnBuilder.AddArbitraryData(append(modules.PrefixHostAnnouncement[:], announcement...))
	txnSet, err := txnBuilder.Sign(true)
	if err != nil {
		t.Fatal(err)
	}
	err = ht.tpool.AcceptTransactionSet(txnSet)
	if err != nil {
		t.Fatal(err)
	}

	// Check that, prior to mining, the hostdb has no hosts.
	if len(ht.hostdb.AllHosts()) != 0 {
		t.Fatal("Hostdb should not yet have any hosts")
	}

	// Mine a block to get the transaction into the consensus set.
	b, _ := ht.miner.FindBlock()
	err = ht.cs.AcceptBlock(b)
	if err != nil {
		t.Fatal(err)
	}

	// Check that there is now a host in the hostdb.
	if len(ht.hostdb.AllHosts()) != 1 {
		t.Fatal("hostdb should have a host after getting a host announcement transcation")
	}
}
<commit_msg>add IsOffline test<commit_after>package hostdb

import (
	"testing"
	"time"

	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)

// TestFindHostAnnouncements probes the findHostAnnouncements function
func TestFindHostAnnouncements(t *testing.T) {
	// Create a block with a host announcement.
	announcement := append(modules.PrefixHostAnnouncement[:], encoding.Marshal(modules.HostAnnouncement{})...)
	b := types.Block{
		Transactions: []types.Transaction{
			types.Transaction{
				ArbitraryData: [][]byte{announcement},
			},
		},
	}
	announcements := findHostAnnouncements(b)
	if len(announcements) != 1 {
		t.Error("host announcement not found in block")
	}

	// Try with an altered prefix
	b.Transactions[0].ArbitraryData[0][0]++
	announcements = findHostAnnouncements(b)
	if len(announcements) != 0 {
		t.Error("host announcement found when there was an invalid prefix")
	}
	b.Transactions[0].ArbitraryData[0][0]--

	// Try with an invalid host encoding.
	b.Transactions[0].ArbitraryData[0][17]++
	announcements = findHostAnnouncements(b)
	if len(announcements) != 0 {
		t.Error("host announcement found when there was an invalid encoding of a host announcement")
	}
}

// TestReceiveConsensusSetUpdate probes the ReveiveConsensusSetUpdate method of
// the hostdb type.
func TestReceiveConsensusSetUpdate(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	ht, err := newHostDBTester("TestFindHostAnnouncements")
	if err != nil {
		t.Fatal(err)
	}

	// Put a host announcement into the blockchain.
	announcement := encoding.Marshal(modules.HostAnnouncement{
		IPAddress: ht.gateway.Address(),
	})
	txnBuilder := ht.wallet.StartTransaction()
	txnBuilder.AddArbitraryData(append(modules.PrefixHostAnnouncement[:], announcement...))
	txnSet, err := txnBuilder.Sign(true)
	if err != nil {
		t.Fatal(err)
	}
	err = ht.tpool.AcceptTransactionSet(txnSet)
	if err != nil {
		t.Fatal(err)
	}

	// Check that, prior to mining, the hostdb has no hosts.
	if len(ht.hostdb.AllHosts()) != 0 {
		t.Fatal("Hostdb should not yet have any hosts")
	}

	// Mine a block to get the transaction into the consensus set.
	b, _ := ht.miner.FindBlock()
	err = ht.cs.AcceptBlock(b)
	if err != nil {
		t.Fatal(err)
	}

	// Check that there is now a host in the hostdb.
	if len(ht.hostdb.AllHosts()) != 1 {
		t.Fatal("hostdb should have a host after getting a host announcement transcation")
	}
}

// TestIsOffline tests the IsOffline method.
func TestIsOffline(t *testing.T) {
	hdb := &HostDB{
		allHosts: map[modules.NetAddress]*hostEntry{
			"foo:1234": &hostEntry{online: true},
			"bar:1234": &hostEntry{online: false},
			"baz:1234": &hostEntry{online: true},
		},
		activeHosts: map[modules.NetAddress]*hostNode{
			"foo:1234": nil,
		},
		scanPool: make(chan *hostEntry),
	}

	tests := []struct {
		addr    modules.NetAddress
		offline bool
	}{
		{"foo:1234", false},
		{"bar:1234", true},
		{"baz:1234", false},
		{"quux:1234", false},
	}
	for _, test := range tests {
		if offline := hdb.IsOffline(test.addr); offline != test.offline {
			t.Errorf("IsOffline(%v) = %v, expected %v", test.addr, offline, test.offline)
		}
	}

	// quux should have sent host down scanPool
	select {
	case h := <-hdb.scanPool:
		if h.NetAddress != "quux:1234" {
			t.Error("wrong host in scan pool:", h.NetAddress)
		}
	case <-time.After(time.Second):
		t.Error("unknown host was not added to scan pool")
	}
}
<|endoftext|>


<commit_before>package router

import (
	"context"
	"strings"

	"v2ray.com/core/app/dispatcher"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/session"
	"v2ray.com/core/common/strmatcher"
)

type Condition interface {
	Apply(ctx context.Context) bool
}

type ConditionChan []Condition

func NewConditionChan() *ConditionChan {
	var condChan ConditionChan = make([]Condition, 0, 8)
	return &condChan
}

func (v *ConditionChan) Add(cond Condition) *ConditionChan {
	*v = append(*v, cond)
	return v
}

func (v *ConditionChan) Apply(ctx context.Context) bool {
	for _, cond := range *v {
		if !cond.Apply(ctx) {
			return false
		}
	}
	return true
}

func (v *ConditionChan) Len() int {
	return len(*v)
}

var matcherTypeMap = map[Domain_Type]strmatcher.Type{
	Domain_Plain:  strmatcher.Substr,
	Domain_Regex:  strmatcher.Regex,
	Domain_Domain: strmatcher.Domain,
	Domain_Full:   strmatcher.Full,
}

func domainToMatcher(domain *Domain) (strmatcher.Matcher, error) {
	matcherType, f := matcherTypeMap[domain.Type]
	if !f {
		return nil, newError("unsupported domain type", domain.Type)
	}

	matcher, err := matcherType.New(domain.Value)
	if err != nil {
		return nil, newError("failed to create domain matcher").Base(err)
	}

	return matcher, nil
}

type DomainMatcher struct {
	matchers strmatcher.IndexMatcher
}

func NewDomainMatcher(domains []*Domain) (*DomainMatcher, error) {
	g := new(strmatcher.MatcherGroup)
	for _, d := range domains {
		m, err := domainToMatcher(d)
		if err != nil {
			return nil, err
		}
		g.Add(m)
	}

	return &DomainMatcher{
		matchers: g,
	}, nil
}

func (m *DomainMatcher) ApplyDomain(domain string) bool {
	return m.matchers.Match(domain) > 0
}

func (m *DomainMatcher) Apply(ctx context.Context) bool {
	outbound := session.OutboundFromContext(ctx)
	if outbound == nil || !outbound.Target.IsValid() {
		return false
	}
	dest := outbound.Target
	if !dest.Address.Family().IsDomain() {
		return false
	}
	return m.ApplyDomain(dest.Address.Domain())
}

func sourceFromContext(ctx context.Context) net.Destination {
	inbound := session.InboundFromContext(ctx)
	if inbound == nil {
		return net.Destination{}
	}
	return inbound.Source
}

func targetFromContent(ctx context.Context) net.Destination {
	outbound := session.OutboundFromContext(ctx)
	if outbound == nil {
		return net.Destination{}
	}
	return outbound.Target
}

type MultiGeoIPMatcher struct {
	matchers []*GeoIPMatcher
	destFunc func(context.Context) net.Destination
}

func NewMultiGeoIPMatcher(geoips []*GeoIP, onSource bool) (*MultiGeoIPMatcher, error) {
	var matchers []*GeoIPMatcher
	for _, geoip := range geoips {
		matcher, err := globalGeoIPContainer.Add(geoip)
		if err != nil {
			return nil, err
		}
		matchers = append(matchers, matcher)
	}

	var destFunc func(context.Context) net.Destination
	if onSource {
		destFunc = sourceFromContext
	} else {
		destFunc = targetFromContent
	}

	return &MultiGeoIPMatcher{
		matchers: matchers,
		destFunc: destFunc,
	}, nil
}

func (m *MultiGeoIPMatcher) Apply(ctx context.Context) bool {
	ips := make([]net.IP, 0, 4)

	dest := m.destFunc(ctx)

	if dest.IsValid() && dest.Address.Family().IsIP() {
		ips = append(ips, dest.Address.IP())
	} else if resolver, ok := ResolvedIPsFromContext(ctx); ok {
		resolvedIPs := resolver.Resolve()
		for _, rip := range resolvedIPs {
			ips = append(ips, rip.IP())
		}
	}

	for _, ip := range ips {
		for _, matcher := range m.matchers {
			if matcher.Match(ip) {
				return true
			}
		}
	}
	return false
}

type PortMatcher struct {
	port net.PortRange
}

func NewPortMatcher(portRange net.PortRange) *PortMatcher {
	return &PortMatcher{
		port: portRange,
	}
}

func (v *PortMatcher) Apply(ctx context.Context) bool {
	outbound := session.OutboundFromContext(ctx)
	if outbound == nil || !outbound.Target.IsValid() {
		return false
	}
	return v.port.Contains(outbound.Target.Port)
}

type NetworkMatcher struct {
	list [8]bool
}

func NewNetworkMatcher(network *net.NetworkList) NetworkMatcher {
	var matcher NetworkMatcher
	for _, n := range network.Network {
		matcher.list[int(n)] = true
	}
	return matcher
}

func (v NetworkMatcher) Apply(ctx context.Context) bool {
	outbound := session.OutboundFromContext(ctx)
	if outbound == nil || !outbound.Target.IsValid() {
		return false
	}
	return v.list[int(outbound.Target.Network)]
}

type UserMatcher struct {
	user []string
}

func NewUserMatcher(users []string) *UserMatcher {
	usersCopy := make([]string, 0, len(users))
	for _, user := range users {
		if len(user) > 0 {
			usersCopy = append(usersCopy, user)
		}
	}
	return &UserMatcher{
		user: usersCopy,
	}
}

func (v *UserMatcher) Apply(ctx context.Context) bool {
	inbound := session.InboundFromContext(ctx)
	if inbound == nil {
		return false
	}

	user := inbound.User
	if user == nil {
		return false
	}
	for _, u := range v.user {
		if u == user.Email {
			return true
		}
	}
	return false
}

type InboundTagMatcher struct {
	tags []string
}

func NewInboundTagMatcher(tags []string) *InboundTagMatcher {
	tagsCopy := make([]string, 0, len(tags))
	for _, tag := range tags {
		if len(tag) > 0 {
			tagsCopy = append(tagsCopy, tag)
		}
	}
	return &InboundTagMatcher{
		tags: tagsCopy,
	}
}

func (v *InboundTagMatcher) Apply(ctx context.Context) bool {
	inbound := session.InboundFromContext(ctx)
	if inbound == nil || len(inbound.Tag) == 0 {
		return false
	}
	tag := inbound.Tag
	for _, t := range v.tags {
		if t == tag {
			return true
		}
	}
	return false
}

type ProtocolMatcher struct {
	protocols []string
}

func NewProtocolMatcher(protocols []string) *ProtocolMatcher {
	pCopy := make([]string, 0, len(protocols))

	for _, p := range protocols {
		if len(p) > 0 {
			pCopy = append(pCopy, p)
		}
	}

	return &ProtocolMatcher{
		protocols: pCopy,
	}
}

func (m *ProtocolMatcher) Apply(ctx context.Context) bool {
	result := dispatcher.SniffingResultFromContext(ctx)

	if result == nil {
		return false
	}

	protocol := result.Protocol()
	for _, p := range m.protocols {
		if strings.HasPrefix(protocol, p) {
			return true
		}
	}

	return false
}
<commit_msg>remove usage of NetworkList in NetworkMatcher<commit_after>package router

import (
	"context"
	"strings"

	"v2ray.com/core/app/dispatcher"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/session"
	"v2ray.com/core/common/strmatcher"
)

type Condition interface {
	Apply(ctx context.Context) bool
}

type ConditionChan []Condition

func NewConditionChan() *ConditionChan {
	var condChan ConditionChan = make([]Condition, 0, 8)
	return &condChan
}

func (v *ConditionChan) Add(cond Condition) *ConditionChan {
	*v = append(*v, cond)
	return v
}

func (v *ConditionChan) Apply(ctx context.Context) bool {
	for _, cond := range *v {
		if !cond.Apply(ctx) {
			return false
		}
	}
	return true
}

func (v *ConditionChan) Len() int {
	return len(*v)
}

var matcherTypeMap = map[Domain_Type]strmatcher.Type{
	Domain_Plain:  strmatcher.Substr,
	Domain_Regex:  strmatcher.Regex,
	Domain_Domain: strmatcher.Domain,
	Domain_Full:   strmatcher.Full,
}

func domainToMatcher(domain *Domain) (strmatcher.Matcher, error) {
	matcherType, f := matcherTypeMap[domain.Type]
	if !f {
		return nil, newError("unsupported domain type", domain.Type)
	}

	matcher, err := matcherType.New(domain.Value)
	if err != nil {
		return nil, newError("failed to create domain matcher").Base(err)
	}

	return matcher, nil
}

type DomainMatcher struct {
	matchers strmatcher.IndexMatcher
}

func NewDomainMatcher(domains []*Domain) (*DomainMatcher, error) {
	g := new(strmatcher.MatcherGroup)
	for _, d := range domains {
		m, err := domainToMatcher(d)
		if err != nil {
			return nil, err
		}
		g.Add(m)
	}

	return &DomainMatcher{
		matchers: g,
	}, nil
}

func (m *DomainMatcher) ApplyDomain(domain string) bool {
	return m.matchers.Match(domain) > 0
}

func (m *DomainMatcher) Apply(ctx context.Context) bool {
	outbound := session.OutboundFromContext(ctx)
	if outbound == nil || !outbound.Target.IsValid() {
		return false
	}
	dest := outbound.Target
	if !dest.Address.Family().IsDomain() {
		return false
	}
	return m.ApplyDomain(dest.Address.Domain())
}

func sourceFromContext(ctx context.Context) net.Destination {
	inbound := session.InboundFromContext(ctx)
	if inbound == nil {
		return net.Destination{}
	}
	return inbound.Source
}

func targetFromContent(ctx context.Context) net.Destination {
	outbound := session.OutboundFromContext(ctx)
	if outbound == nil {
		return net.Destination{}
	}
	return outbound.Target
}

type MultiGeoIPMatcher struct {
	matchers []*GeoIPMatcher
	destFunc func(context.Context) net.Destination
}

func NewMultiGeoIPMatcher(geoips []*GeoIP, onSource bool) (*MultiGeoIPMatcher, error) {
	var matchers []*GeoIPMatcher
	for _, geoip := range geoips {
		matcher, err := globalGeoIPContainer.Add(geoip)
		if err != nil {
			return nil, err
		}
		matchers = append(matchers, matcher)
	}

	var destFunc func(context.Context) net.Destination
	if onSource {
		destFunc = sourceFromContext
	} else {
		destFunc = targetFromContent
	}

	return &MultiGeoIPMatcher{
		matchers: matchers,
		destFunc: destFunc,
	}, nil
}

func (m *MultiGeoIPMatcher) Apply(ctx context.Context) bool {
	ips := make([]net.IP, 0, 4)

	dest := m.destFunc(ctx)

	if dest.IsValid() && dest.Address.Family().IsIP() {
		ips = append(ips, dest.Address.IP())
	} else if resolver, ok := ResolvedIPsFromContext(ctx); ok {
		resolvedIPs := resolver.Resolve()
		for _, rip := range resolvedIPs {
			ips = append(ips, rip.IP())
		}
	}

	for _, ip := range ips {
		for _, matcher := range m.matchers {
			if matcher.Match(ip) {
				return true
			}
		}
	}
	return false
}

type PortMatcher struct {
	port net.PortRange
}

func NewPortMatcher(portRange net.PortRange) *PortMatcher {
	return &PortMatcher{
		port: portRange,
	}
}

func (v *PortMatcher) Apply(ctx context.Context) bool {
	outbound := session.OutboundFromContext(ctx)
	if outbound == nil || !outbound.Target.IsValid() {
		return false
	}
	return v.port.Contains(outbound.Target.Port)
}

type NetworkMatcher struct {
	list [8]bool
}

func NewNetworkMatcher(network []net.Network) NetworkMatcher {
	var matcher NetworkMatcher
	for _, n := range network {
		matcher.list[int(n)] = true
	}
	return matcher
}

func (v NetworkMatcher) Apply(ctx context.Context) bool {
	outbound := session.OutboundFromContext(ctx)
	if outbound == nil || !outbound.Target.IsValid() {
		return false
	}
	return v.list[int(outbound.Target.Network)]
}

type UserMatcher struct {
	user []string
}

func NewUserMatcher(users []string) *UserMatcher {
	usersCopy := make([]string, 0, len(users))
	for _, user := range users {
		if len(user) > 0 {
			usersCopy = append(usersCopy, user)
		}
	}
	return &UserMatcher{
		user: usersCopy,
	}
}

func (v *UserMatcher) Apply(ctx context.Context) bool {
	inbound := session.InboundFromContext(ctx)
	if inbound == nil {
		return false
	}

	user := inbound.User
	if user == nil {
		return false
	}
	for _, u := range v.user {
		if u == user.Email {
			return true
		}
	}
	return false
}

type InboundTagMatcher struct {
	tags []string
}

func NewInboundTagMatcher(tags []string) *InboundTagMatcher {
	tagsCopy := make([]string, 0, len(tags))
	for _, tag := range tags {
		if len(tag) > 0 {
			tagsCopy = append(tagsCopy, tag)
		}
	}
	return &InboundTagMatcher{
		tags: tagsCopy,
	}
}

func (v *InboundTagMatcher) Apply(ctx context.Context) bool {
	inbound := session.InboundFromContext(ctx)
	if inbound == nil || len(inbound.Tag) == 0 {
		return false
	}
	tag := inbound.Tag
	for _, t := range v.tags {
		if t == tag {
			return true
		}
	}
	return false
}

type ProtocolMatcher struct {
	protocols []string
}

func NewProtocolMatcher(protocols []string) *ProtocolMatcher {
	pCopy := make([]string, 0, len(protocols))

	for _, p := range protocols {
		if len(p) > 0 {
			pCopy = append(pCopy, p)
		}
	}

	return &ProtocolMatcher{
		protocols: pCopy,
	}
}

func (m *ProtocolMatcher) Apply(ctx context.Context) bool {
	result := dispatcher.SniffingResultFromContext(ctx)

	if result == nil {
		return false
	}

	protocol := result.Protocol()
	for _, p := range m.protocols {
		if strings.HasPrefix(protocol, p) {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsIamPolicyAttachment() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsIamPolicyAttachmentCreate,
		Read:   resourceAwsIamPolicyAttachmentRead,
		Update: resourceAwsIamPolicyAttachmentUpdate,
		Delete: resourceAwsIamPolicyAttachmentDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"users": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},
			"roles": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},
			"groups": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},
			"policy_arn": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
		},
	}
}

func resourceAwsIamPolicyAttachmentCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).iamconn

	name := d.Get("name").(string)
	arn := d.Get("policy_arn").(string)
	users := expandStringList(d.Get("users").(*schema.Set).List())
	roles := expandStringList(d.Get("roles").(*schema.Set).List())
	groups := expandStringList(d.Get("groups").(*schema.Set).List())

	if users == "" && roles == "" && groups == "" {
		return fmt.Errorf("[WARN] No Users, Roles, or Groups specified for %s", name)
	} else {
		var userErr, roleErr, groupErr error
		if users != nil {
			userErr = attachPolicyToUsers(conn, users, arn)
		}
		if roles != nil {
			roleErr = attachPolicyToRoles(conn, roles, arn)
		}
		if groups != nil {
			groupErr = attachPolicyToGroups(conn, groups, arn)
		}
		if userErr != nil || roleErr != nil || groupErr != nil {
			return fmt.Errorf("[WARN] Error attaching policy with IAM Policy Attach (%s), error:\n users - %v\n roles - %v\n groups - %v", name, userErr, roleErr, groupErr)
		}
	}
	d.SetId(d.Get("name").(string))
	return resourceAwsIamPolicyAttachmentRead(d, meta)
}

func resourceAwsIamPolicyAttachmentRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).iamconn
	arn := d.Get("policy_arn").(string)
	name := d.Get("name").(string)

	_, err := conn.GetPolicy(&iam.GetPolicyInput{
		PolicyARN: aws.String(arn),
	})

	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok {
			if awsErr.Code() == "NoSuchIdentity" {
				d.SetId("")
				return nil
			}
		}
		return err
	}

	policyEntities, err := conn.ListEntitiesForPolicy(&iam.ListEntitiesForPolicyInput{
		PolicyARN: aws.String(arn),
	})

	if err != nil {
		return err
	}

	ul := make([]string, 0, len(policyEntities.PolicyUsers))
	rl := make([]string, 0, len(policyEntities.PolicyRoles))
	gl := make([]string, 0, len(policyEntities.PolicyGroups))

	for _, u := range policyEntities.PolicyUsers {
		ul = append(ul, *u.UserName)
	}

	for _, r := range policyEntities.PolicyRoles {
		rl = append(rl, *r.RoleName)
	}

	for _, g := range policyEntities.PolicyGroups {
		gl = append(gl, *g.GroupName)
	}

	userErr := d.Set("users", ul)
	roleErr := d.Set("roles", rl)
	groupErr := d.Set("groups", gl)

	if userErr != nil || roleErr != nil || groupErr != nil {
		return fmt.Errorf("[WARN} Error setting user, role, or group list from IAM Policy Attach (%s):\n user error - %s\n role error - %s\n group error - %s", name, userErr, roleErr, groupErr)
	}

	return nil
}
func resourceAwsIamPolicyAttachmentUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).iamconn
	name := d.Get("name").(string)
	var userErr, roleErr, groupErr error

	if d.HasChange("users") {
		userErr = updateUsers(conn, d, meta)
	}
	if d.HasChange("roles") {
		roleErr = updateRoles(conn, d, meta)
	}
	if d.HasChange("groups") {
		groupErr = updateGroups(conn, d, meta)
	}
	if userErr != nil || roleErr != nil || groupErr != nil {
		return fmt.Errorf("[WARN] Error updating user, role, or group list from IAM Policy Attach (%s):\n user error - %s\n role error - %s\n group error - %s", name, userErr, roleErr, groupErr)
	}
	return resourceAwsIamPolicyAttachmentRead(d, meta)
}

func resourceAwsIamPolicyAttachmentDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).iamconn
	name := d.Get("name").(string)
	arn := d.Get("policy_arn").(string)
	users := expandStringList(d.Get("users").(*schema.Set).List())
	roles := expandStringList(d.Get("roles").(*schema.Set).List())
	groups := expandStringList(d.Get("groups").(*schema.Set).List())

	var userErr, roleErr, groupErr error
	if users != "" {
		userErr = detachPolicyFromUsers(conn, users, arn)
	}
	if roles != "" {
		roleErr = detachPolicyFromRoles(conn, roles, arn)
	}
	if groups != "" {
		groupErr = detachPolicyFromGroups(conn, groups, arn)
	}
	if userErr != nil || roleErr != nil || groupErr != nil {
		return fmt.Errorf("[WARN] Error removing user, role, or group list from IAM Policy Detach (%s), error:\n users - %v\n roles - %v\n groups - %v", name, userErr, roleErr, groupErr)
	}
	return nil
}
func attachPolicyToUsers(conn *iam.IAM, users []*string, arn string) error {
	for _, u := range users {
		_, err := conn.AttachUserPolicy(&iam.AttachUserPolicyInput{
			UserName:  u,
			PolicyARN: aws.String(arn),
		})
		if err != nil {
			return err
		}
	}
	return nil
}
func attachPolicyToRoles(conn *iam.IAM, roles []*string, arn string) error {
	for _, r := range roles {
		_, err := conn.AttachRolePolicy(&iam.AttachRolePolicyInput{
			RoleName:  r,
			PolicyARN: aws.String(arn),
		})
		if err != nil {
			return err
		}
	}
	return nil
}
func attachPolicyToGroups(conn *iam.IAM, groups []*string, arn string) error {
	for _, g := range groups {
		_, err := conn.AttachGroupPolicy(&iam.AttachGroupPolicyInput{
			GroupName: g,
			PolicyARN: aws.String(arn),
		})
		if err != nil {
			return err
		}
	}
	return nil
}
func updateUsers(conn *iam.IAM, d *schema.ResourceData, meta interface{}) error {
	arn := d.Get("policy_arn").(string)
	o, n := d.GetChange("users")
	if o == nil {
		o = new(schema.Set)
	}
	if n == nil {
		n = new(schema.Set)
	}
	os := o.(*schema.Set)
	ns := n.(*schema.Set)
	remove := expandStringList(os.Difference(ns).List())
	add := expandStringList(ns.Difference(os).List())

	if rErr := detachPolicyFromUsers(conn, remove, arn); rErr != nil {
		return rErr
	}
	if aErr := attachPolicyToUsers(conn, add, arn); aErr != nil {
		return aErr
	}
	return nil
}
func updateRoles(conn *iam.IAM, d *schema.ResourceData, meta interface{}) error {
	arn := d.Get("policy_arn").(string)
	o, n := d.GetChange("roles")
	if o == nil {
		o = new(schema.Set)
	}
	if n == nil {
		n = new(schema.Set)
	}
	os := o.(*schema.Set)
	ns := n.(*schema.Set)
	remove := expandStringList(os.Difference(ns).List())
	add := expandStringList(ns.Difference(os).List())

	if rErr := detachPolicyFromRoles(conn, remove, arn); rErr != nil {
		return rErr
	}
	if aErr := attachPolicyToRoles(conn, add, arn); aErr != nil {
		return aErr
	}
	return nil
}
func updateGroups(conn *iam.IAM, d *schema.ResourceData, meta interface{}) error {
	arn := d.Get("policy_arn").(string)
	o, n := d.GetChange("groups")
	if o == nil {
		o = new(schema.Set)
	}
	if n == nil {
		n = new(schema.Set)
	}
	os := o.(*schema.Set)
	ns := n.(*schema.Set)
	remove := expandStringList(os.Difference(ns).List())
	add := expandStringList(ns.Difference(os).List())

	if rErr := detachPolicyFromGroups(conn, remove, arn); rErr != nil {
		return rErr
	}
	if aErr := attachPolicyToGroups(conn, add, arn); aErr != nil {
		return aErr
	}
	return nil

}
func detachPolicyFromUsers(conn *iam.IAM, users []*string, arn string) error {
	for _, u := range users {
		_, err := conn.DetachUserPolicy(&iam.DetachUserPolicyInput{
			UserName:  u,
			PolicyARN: aws.String(arn),
		})
		if err != nil {
			return err
		}
	}
	return nil
}
func detachPolicyFromRoles(conn *iam.IAM, roles []*string, arn string) error {
	for _, r := range roles {
		_, err := conn.DetachRolePolicy(&iam.DetachRolePolicyInput{
			RoleName:  r,
			PolicyARN: aws.String(arn),
		})
		if err != nil {
			return err
		}
	}
	return nil
}
func detachPolicyFromGroups(conn *iam.IAM, groups []*string, arn string) error {
	for _, g := range groups {
		_, err := conn.DetachGroupPolicy(&iam.DetachGroupPolicyInput{
			GroupName: g,
			PolicyARN: aws.String(arn),
		})
		if err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>check length of slices instead of using incorrect type<commit_after>package aws

import (
	"fmt"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsIamPolicyAttachment() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsIamPolicyAttachmentCreate,
		Read:   resourceAwsIamPolicyAttachmentRead,
		Update: resourceAwsIamPolicyAttachmentUpdate,
		Delete: resourceAwsIamPolicyAttachmentDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"users": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},
			"roles": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},
			"groups": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},
			"policy_arn": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
		},
	}
}

func resourceAwsIamPolicyAttachmentCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).iamconn

	name := d.Get("name").(string)
	arn := d.Get("policy_arn").(string)
	users := expandStringList(d.Get("users").(*schema.Set).List())
	roles := expandStringList(d.Get("roles").(*schema.Set).List())
	groups := expandStringList(d.Get("groups").(*schema.Set).List())

	if len(users) > 0 && len(roles) > 0 && len(groups) > 0 {
		return fmt.Errorf("[WARN] No Users, Roles, or Groups specified for %s", name)
	} else {
		var userErr, roleErr, groupErr error
		if users != nil {
			userErr = attachPolicyToUsers(conn, users, arn)
		}
		if roles != nil {
			roleErr = attachPolicyToRoles(conn, roles, arn)
		}
		if groups != nil {
			groupErr = attachPolicyToGroups(conn, groups, arn)
		}
		if userErr != nil || roleErr != nil || groupErr != nil {
			return fmt.Errorf("[WARN] Error attaching policy with IAM Policy Attach (%s), error:\n users - %v\n roles - %v\n groups - %v", name, userErr, roleErr, groupErr)
		}
	}
	d.SetId(d.Get("name").(string))
	return resourceAwsIamPolicyAttachmentRead(d, meta)
}

func resourceAwsIamPolicyAttachmentRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).iamconn
	arn := d.Get("policy_arn").(string)
	name := d.Get("name").(string)

	_, err := conn.GetPolicy(&iam.GetPolicyInput{
		PolicyARN: aws.String(arn),
	})

	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok {
			if awsErr.Code() == "NoSuchIdentity" {
				d.SetId("")
				return nil
			}
		}
		return err
	}

	policyEntities, err := conn.ListEntitiesForPolicy(&iam.ListEntitiesForPolicyInput{
		PolicyARN: aws.String(arn),
	})

	if err != nil {
		return err
	}

	ul := make([]string, 0, len(policyEntities.PolicyUsers))
	rl := make([]string, 0, len(policyEntities.PolicyRoles))
	gl := make([]string, 0, len(policyEntities.PolicyGroups))

	for _, u := range policyEntities.PolicyUsers {
		ul = append(ul, *u.UserName)
	}

	for _, r := range policyEntities.PolicyRoles {
		rl = append(rl, *r.RoleName)
	}

	for _, g := range policyEntities.PolicyGroups {
		gl = append(gl, *g.GroupName)
	}

	userErr := d.Set("users", ul)
	roleErr := d.Set("roles", rl)
	groupErr := d.Set("groups", gl)

	if userErr != nil || roleErr != nil || groupErr != nil {
		return fmt.Errorf("[WARN} Error setting user, role, or group list from IAM Policy Attach (%s):\n user error - %s\n role error - %s\n group error - %s", name, userErr, roleErr, groupErr)
	}

	return nil
}
func resourceAwsIamPolicyAttachmentUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).iamconn
	name := d.Get("name").(string)
	var userErr, roleErr, groupErr error

	if d.HasChange("users") {
		userErr = updateUsers(conn, d, meta)
	}
	if d.HasChange("roles") {
		roleErr = updateRoles(conn, d, meta)
	}
	if d.HasChange("groups") {
		groupErr = updateGroups(conn, d, meta)
	}
	if userErr != nil || roleErr != nil || groupErr != nil {
		return fmt.Errorf("[WARN] Error updating user, role, or group list from IAM Policy Attach (%s):\n user error - %s\n role error - %s\n group error - %s", name, userErr, roleErr, groupErr)
	}
	return resourceAwsIamPolicyAttachmentRead(d, meta)
}

func resourceAwsIamPolicyAttachmentDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).iamconn
	name := d.Get("name").(string)
	arn := d.Get("policy_arn").(string)
	users := expandStringList(d.Get("users").(*schema.Set).List())
	roles := expandStringList(d.Get("roles").(*schema.Set).List())
	groups := expandStringList(d.Get("groups").(*schema.Set).List())

	var userErr, roleErr, groupErr error
	if len(users) != 0 {
		userErr = detachPolicyFromUsers(conn, users, arn)
	}
	if len(roles) != 0 {
		roleErr = detachPolicyFromRoles(conn, roles, arn)
	}
	if len(groups) != 0 {
		groupErr = detachPolicyFromGroups(conn, groups, arn)
	}
	if userErr != nil || roleErr != nil || groupErr != nil {
		return fmt.Errorf("[WARN] Error removing user, role, or group list from IAM Policy Detach (%s), error:\n users - %v\n roles - %v\n groups - %v", name, userErr, roleErr, groupErr)
	}
	return nil
}

//func composeErrors(desc string, uErr error, rErr error, gErr error) error {
//	errMsg := fmt.Sprintf(desc)
//	errs := []error{uErr, rErr, gErr}
//	return nil
//}

func attachPolicyToUsers(conn *iam.IAM, users []*string, arn string) error {
	for _, u := range users {
		_, err := conn.AttachUserPolicy(&iam.AttachUserPolicyInput{
			UserName:  u,
			PolicyARN: aws.String(arn),
		})
		if err != nil {
			return err
		}
	}
	return nil
}
func attachPolicyToRoles(conn *iam.IAM, roles []*string, arn string) error {
	for _, r := range roles {
		_, err := conn.AttachRolePolicy(&iam.AttachRolePolicyInput{
			RoleName:  r,
			PolicyARN: aws.String(arn),
		})
		if err != nil {
			return err
		}
	}
	return nil
}
func attachPolicyToGroups(conn *iam.IAM, groups []*string, arn string) error {
	for _, g := range groups {
		_, err := conn.AttachGroupPolicy(&iam.AttachGroupPolicyInput{
			GroupName: g,
			PolicyARN: aws.String(arn),
		})
		if err != nil {
			return err
		}
	}
	return nil
}
func updateUsers(conn *iam.IAM, d *schema.ResourceData, meta interface{}) error {
	arn := d.Get("policy_arn").(string)
	o, n := d.GetChange("users")
	if o == nil {
		o = new(schema.Set)
	}
	if n == nil {
		n = new(schema.Set)
	}
	os := o.(*schema.Set)
	ns := n.(*schema.Set)
	remove := expandStringList(os.Difference(ns).List())
	add := expandStringList(ns.Difference(os).List())

	if rErr := detachPolicyFromUsers(conn, remove, arn); rErr != nil {
		return rErr
	}
	if aErr := attachPolicyToUsers(conn, add, arn); aErr != nil {
		return aErr
	}
	return nil
}
func updateRoles(conn *iam.IAM, d *schema.ResourceData, meta interface{}) error {
	arn := d.Get("policy_arn").(string)
	o, n := d.GetChange("roles")
	if o == nil {
		o = new(schema.Set)
	}
	if n == nil {
		n = new(schema.Set)
	}
	os := o.(*schema.Set)
	ns := n.(*schema.Set)
	remove := expandStringList(os.Difference(ns).List())
	add := expandStringList(ns.Difference(os).List())

	if rErr := detachPolicyFromRoles(conn, remove, arn); rErr != nil {
		return rErr
	}
	if aErr := attachPolicyToRoles(conn, add, arn); aErr != nil {
		return aErr
	}
	return nil
}
func updateGroups(conn *iam.IAM, d *schema.ResourceData, meta interface{}) error {
	arn := d.Get("policy_arn").(string)
	o, n := d.GetChange("groups")
	if o == nil {
		o = new(schema.Set)
	}
	if n == nil {
		n = new(schema.Set)
	}
	os := o.(*schema.Set)
	ns := n.(*schema.Set)
	remove := expandStringList(os.Difference(ns).List())
	add := expandStringList(ns.Difference(os).List())

	if rErr := detachPolicyFromGroups(conn, remove, arn); rErr != nil {
		return rErr
	}
	if aErr := attachPolicyToGroups(conn, add, arn); aErr != nil {
		return aErr
	}
	return nil

}
func detachPolicyFromUsers(conn *iam.IAM, users []*string, arn string) error {
	for _, u := range users {
		_, err := conn.DetachUserPolicy(&iam.DetachUserPolicyInput{
			UserName:  u,
			PolicyARN: aws.String(arn),
		})
		if err != nil {
			return err
		}
	}
	return nil
}
func detachPolicyFromRoles(conn *iam.IAM, roles []*string, arn string) error {
	for _, r := range roles {
		_, err := conn.DetachRolePolicy(&iam.DetachRolePolicyInput{
			RoleName:  r,
			PolicyARN: aws.String(arn),
		})
		if err != nil {
			return err
		}
	}
	return nil
}
func detachPolicyFromGroups(conn *iam.IAM, groups []*string, arn string) error {
	for _, g := range groups {
		_, err := conn.DetachGroupPolicy(&iam.DetachGroupPolicyInput{
			GroupName: g,
			PolicyARN: aws.String(arn),
		})
		if err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>