<commit_before>package protocol

import (
	"github.com/ethereum/go-ethereum/common"
	"math/big"
	"testing"
	"time"
)

func newTestSmartPool() *SmartPool {
	return NewSmartPool(
		&testPoolMonitor{},
		&testShareReceiver{}, &testNetworkClient{},
		&testClaimRepo{}, &testContract{},
		common.HexToAddress("0x001aDBc838eDe392B5B054A47f8B8c28f2fA9F3F"),
		common.HexToAddress("0x001aDBc838eDe392B5B054A47f8B8c28f2fA9F3F"),
		"extradata", time.Minute,
		100, true,
	)
}

func TestSmartPoolRegisterMinerAfterRegister(t *testing.T) {
	sp := newTestSmartPool()
	testContract := sp.Contract.(*testContract)
	testContract.Registered = true
	if !sp.Register(common.Address{}) {
		t.Fail()
	}
}

func TestSmartPoolRegisterMinerWhenUnableToRegister(t *testing.T) {
	sp := newTestSmartPool()
	testContract := sp.Contract.(*testContract)
	testContract.Registerable = false
	if sp.Register(common.Address{}) {
		t.Fail()
	}
}

func TestSmartPoolRegisterMinerWhenAbleToRegister(t *testing.T) {
	sp := newTestSmartPool()
	testContract := sp.Contract.(*testContract)
	testContract.Registerable = true
	sp.Contract = testContract
	if !sp.Register(common.Address{}) {
		t.Fail()
	}
}

func TestSmartPoolReturnAWorkToMiner(t *testing.T) {
	sp := newTestSmartPool()
	sp.GetWork()
}

func TestSmartPoolAcceptSolution(t *testing.T) {
	sp := newTestSmartPool()
	if !sp.AcceptSolution(&testSolution{Counter: big.NewInt(10)}) {
		t.Fail()
	}
}

func TestSmartPoolNotAcceptSolution(t *testing.T) {
	sp := newTestSmartPool()
	sp.LatestCounter = big.NewInt(10)
	if sp.AcceptSolution(&testSolution{Counter: big.NewInt(9)}) {
		t.Fail()
	}
}

func TestSmartPoolPackageAllCurrentShares(t *testing.T) {
	sp := newTestSmartPool()
	sp.LatestCounter = big.NewInt(5)
	claim := sp.GetCurrentClaim(1)
	if claim != nil {
		t.Fail()
	}
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(9)})
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(8)})
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(10)})
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(5)})
	claim = sp.GetCurrentClaim(1)
	if claim.NumShares().Cmp(big.NewInt(3)) != 0 {
		t.Fail()
	}
}

func TestSmartPoolSubmitCorrectClaim(t *testing.T) {
	sp := newTestSmartPool()
	sp.ShareThreshold = 1
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(9)})
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(8)})
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(10)})
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(5)})
	sp.Submit()

	testContract := sp.Contract.(*testContract)
	claim := testContract.GetLastSubmittedClaim()
	if claim.NumShares().Cmp(big.NewInt(4)) != 0 {
		t.Fail()
	}
}

func TestSmartPoolReturnFalseIfNoClaim(t *testing.T) {
	sp := newTestSmartPool()
	if ok, _ := sp.Submit(); ok {
		t.Fail()
	}
}

func TestSmartPoolSuccessfullySubmitAndVerifyClaim(t *testing.T) {
	sp := newTestSmartPool()
	sp.ShareThreshold = 1
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(9)})
	if ok, _ := sp.Submit(); !ok {
		t.Fail()
	}
}

func TestSmartPoolGetCorrectShareIndex(t *testing.T) {
	sp := newTestSmartPool()
	sp.ShareThreshold = 1
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(9)})
	sp.Submit()
	c := sp.Contract.(*testContract)
	if c.IndexRequestedTime == nil {
		t.Fail()
	}
}

func TestSmartPoolGetCorrectShareIndexAfterSubmitClaim(t *testing.T) {
	sp := newTestSmartPool()
	sp.ShareThreshold = 1
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(9)})
	sp.Submit()
	c := sp.Contract.(*testContract)
	if (*c.SubmitTime).After(*c.IndexRequestedTime) {
		t.Fail()
	}
}

func TestSmartPoolSubmitReturnFalseWhenUnableToSubmit(t *testing.T) {
	sp := newTestSmartPool()
	c := sp.Contract.(*testContract)
	c.SubmitFailed = true
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(9)})
	if ok, _ := sp.Submit(); ok {
		t.Fail()
	}
}

func TestSmartPoolSubmitReturnFalseWhenUnableToVerify(t *testing.T) {
	sp := newTestSmartPool()
	c := sp.Contract.(*testContract)
	c.VerifyFailed = true
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(9)})
	if ok, _ := sp.Submit(); ok {
		t.Fail()
	}
}

func TestSmartPoolDoesntRunWhenMinerRegistered(t *testing.T) {
	sp := newTestSmartPool()
	if sp.Run() {
		t.Fail()
	}
}

func TestSmartPoolOnlySubmitPeriodly(t *testing.T) {
	sp := newTestSmartPool()
	ct := sp.Contract.(*testContract)
	ct.Registered = true
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(9)})
	c := sp.Contract.(*testContract)
	sp.SubmitInterval = 40 * time.Millisecond
	sp.ShareThreshold = 1
	sp.Run()
	if c.GetLastSubmittedClaim() != nil {
		t.Fail()
	}
	time.Sleep(60 * time.Millisecond)
	if c.GetLastSubmittedClaim() == nil {
		t.Fail()
	}
}

func TestSmartPoolOnlySubmitWhenMeetShareThreshold(t *testing.T) {
	sp := newTestSmartPool()
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(9)})
	c := sp.Contract.(*testContract)
	sp.SubmitInterval = 40 * time.Millisecond
	sp.ShareThreshold = 3
	sp.Run()
	time.Sleep(60 * time.Millisecond)
	if c.GetLastSubmittedClaim() != nil {
		t.Fail()
	}
}

func TestSmartPoolOnlyRunAfterNetworkReady(t *testing.T) {
	sp := newTestSmartPool()
	testContract := sp.Contract.(*testContract)
	testContract.Registered = true
	nw := sp.NetworkClient.(*testNetworkClient)
	nw.NotReadyToMine = true
	ran := make(chan bool, 1)
	timeout := make(chan bool, 1)
	go func() {
		ran <- sp.Run()
	}()
	go func() {
		time.Sleep(100 * time.Millisecond)
		timeout <- true
	}()
	select {
	case <-ran:
		t.Fail()
	case <-timeout:
		break
	}
}

func TestSmartPoolStopIfClientVersionChangedInHotStopMode(t *testing.T) {
	sp := newTestSmartPool()
	testContract := sp.Contract.(*testContract)
	testContract.Registered = true
	timeout := make(chan bool, 1)
	sp.PoolMonitor.(*testPoolMonitor).ClientUpdate = true
	sp.Run()
	go func() {
		time.Sleep(100 * time.Millisecond)
		timeout <- true
	}()
	select {
	case <-sp.SubmitterStopped:
		break
	case <-timeout:
		t.Fail()
	}
}

func TestSmartPoolDoesntStopIfHotStopModeIsDisabled(t *testing.T) {
	sp := newTestSmartPool()
	sp.HotStop = false
	testContract := sp.Contract.(*testContract)
	testContract.Registered = true
	timeout := make(chan bool, 1)
	sp.PoolMonitor.(*testPoolMonitor).ContractUpdate = true
	sp.Run()
	go func() {
		time.Sleep(100 * time.Millisecond)
		timeout <- true
	}()
	select {
	case <-sp.SubmitterStopped:
		t.Fail()
	case <-timeout:
		break
	}
}

func TestSmartPoolStopIfContractAddressChangedInHotStopMode(t *testing.T) {
	sp := newTestSmartPool()
	testContract := sp.Contract.(*testContract)
	testContract.Registered = true
	timeout := make(chan bool, 1)
	sp.PoolMonitor.(*testPoolMonitor).ContractUpdate = true
	sp.Run()
	go func() {
		time.Sleep(100 * time.Millisecond)
		timeout <- true
	}()
	select {
	case <-sp.SubmitterStopped:
		break
	case <-timeout:
		t.Fail()
	}
}

func TestSmartPoolRememberLatestCounterAfterFormAClaim(t *testing.T) {
	sp := newTestSmartPool()
	sp.ShareThreshold = 1
	sp.LatestCounter = big.NewInt(5)
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(100)})
	ct := sp.Contract.(*testContract)
	ct.DelayedVerification = true
	go sp.Submit()
	t.Logf("latest counter: %s\n", sp.LatestCounter)
	if sp.LatestCounter.Int64() != 100 {
		t.Fail()
	}
}

//
// func TestSmartPoolConstructsAShare(t *testing.T) {
// 	sp := newTestSmartPool()
// }
//
<commit_msg>remove redundant test<commit_after>package protocol

import (
	"github.com/ethereum/go-ethereum/common"
	"math/big"
	"testing"
	"time"
)

func newTestSmartPool() *SmartPool {
	return NewSmartPool(
		&testPoolMonitor{},
		&testShareReceiver{}, &testNetworkClient{},
		&testClaimRepo{}, &testContract{},
		common.HexToAddress("0x001aDBc838eDe392B5B054A47f8B8c28f2fA9F3F"),
		common.HexToAddress("0x001aDBc838eDe392B5B054A47f8B8c28f2fA9F3F"),
		"extradata", time.Minute,
		100, true,
	)
}

func TestSmartPoolRegisterMinerAfterRegister(t *testing.T) {
	sp := newTestSmartPool()
	testContract := sp.Contract.(*testContract)
	testContract.Registered = true
	if !sp.Register(common.Address{}) {
		t.Fail()
	}
}

func TestSmartPoolRegisterMinerWhenUnableToRegister(t *testing.T) {
	sp := newTestSmartPool()
	testContract := sp.Contract.(*testContract)
	testContract.Registerable = false
	if sp.Register(common.Address{}) {
		t.Fail()
	}
}

func TestSmartPoolRegisterMinerWhenAbleToRegister(t *testing.T) {
	sp := newTestSmartPool()
	testContract := sp.Contract.(*testContract)
	testContract.Registerable = true
	sp.Contract = testContract
	if !sp.Register(common.Address{}) {
		t.Fail()
	}
}

func TestSmartPoolReturnAWorkToMiner(t *testing.T) {
	sp := newTestSmartPool()
	sp.GetWork()
}

func TestSmartPoolAcceptSolution(t *testing.T) {
	sp := newTestSmartPool()
	if !sp.AcceptSolution(&testSolution{Counter: big.NewInt(10)}) {
		t.Fail()
	}
}

func TestSmartPoolNotAcceptSolution(t *testing.T) {
	sp := newTestSmartPool()
	sp.LatestCounter = big.NewInt(10)
	if sp.AcceptSolution(&testSolution{Counter: big.NewInt(9)}) {
		t.Fail()
	}
}

func TestSmartPoolPackageAllCurrentShares(t *testing.T) {
	sp := newTestSmartPool()
	sp.LatestCounter = big.NewInt(5)
	claim := sp.GetCurrentClaim(1)
	if claim != nil {
		t.Fail()
	}
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(9)})
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(8)})
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(10)})
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(5)})
	claim = sp.GetCurrentClaim(1)
	if claim.NumShares().Cmp(big.NewInt(3)) != 0 {
		t.Fail()
	}
}

func TestSmartPoolSubmitCorrectClaim(t *testing.T) {
	sp := newTestSmartPool()
	sp.ShareThreshold = 1
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(9)})
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(8)})
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(10)})
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(5)})
	sp.Submit()

	testContract := sp.Contract.(*testContract)
	claim := testContract.GetLastSubmittedClaim()
	if claim.NumShares().Cmp(big.NewInt(4)) != 0 {
		t.Fail()
	}
}

func TestSmartPoolReturnFalseIfNoClaim(t *testing.T) {
	sp := newTestSmartPool()
	if ok, _ := sp.Submit(); ok {
		t.Fail()
	}
}

func TestSmartPoolSuccessfullySubmitAndVerifyClaim(t *testing.T) {
	sp := newTestSmartPool()
	sp.ShareThreshold = 1
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(9)})
	if ok, _ := sp.Submit(); !ok {
		t.Fail()
	}
}

func TestSmartPoolGetCorrectShareIndex(t *testing.T) {
	sp := newTestSmartPool()
	sp.ShareThreshold = 1
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(9)})
	sp.Submit()
	c := sp.Contract.(*testContract)
	if c.IndexRequestedTime == nil {
		t.Fail()
	}
}

func TestSmartPoolGetCorrectShareIndexAfterSubmitClaim(t *testing.T) {
	sp := newTestSmartPool()
	sp.ShareThreshold = 1
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(9)})
	sp.Submit()
	c := sp.Contract.(*testContract)
	if (*c.SubmitTime).After(*c.IndexRequestedTime) {
		t.Fail()
	}
}

func TestSmartPoolSubmitReturnFalseWhenUnableToSubmit(t *testing.T) {
	sp := newTestSmartPool()
	c := sp.Contract.(*testContract)
	c.SubmitFailed = true
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(9)})
	if ok, _ := sp.Submit(); ok {
		t.Fail()
	}
}

func TestSmartPoolSubmitReturnFalseWhenUnableToVerify(t *testing.T) {
	sp := newTestSmartPool()
	c := sp.Contract.(*testContract)
	c.VerifyFailed = true
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(9)})
	if ok, _ := sp.Submit(); ok {
		t.Fail()
	}
}

func TestSmartPoolDoesntRunWhenMinerRegistered(t *testing.T) {
	sp := newTestSmartPool()
	if sp.Run() {
		t.Fail()
	}
}

func TestSmartPoolOnlySubmitPeriodly(t *testing.T) {
	sp := newTestSmartPool()
	ct := sp.Contract.(*testContract)
	ct.Registered = true
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(9)})
	c := sp.Contract.(*testContract)
	sp.SubmitInterval = 40 * time.Millisecond
	sp.ShareThreshold = 1
	sp.Run()
	if c.GetLastSubmittedClaim() != nil {
		t.Fail()
	}
	time.Sleep(60 * time.Millisecond)
	if c.GetLastSubmittedClaim() == nil {
		t.Fail()
	}
}

func TestSmartPoolOnlySubmitWhenMeetShareThreshold(t *testing.T) {
	sp := newTestSmartPool()
	sp.AcceptSolution(&testSolution{Counter: big.NewInt(9)})
	c := sp.Contract.(*testContract)
	sp.SubmitInterval = 40 * time.Millisecond
	sp.ShareThreshold = 3
	sp.Run()
	time.Sleep(60 * time.Millisecond)
	if c.GetLastSubmittedClaim() != nil {
		t.Fail()
	}
}

func TestSmartPoolOnlyRunAfterNetworkReady(t *testing.T) {
	sp := newTestSmartPool()
	testContract := sp.Contract.(*testContract)
	testContract.Registered = true
	nw := sp.NetworkClient.(*testNetworkClient)
	nw.NotReadyToMine = true
	ran := make(chan bool, 1)
	timeout := make(chan bool, 1)
	go func() {
		ran <- sp.Run()
	}()
	go func() {
		time.Sleep(100 * time.Millisecond)
		timeout <- true
	}()
	select {
	case <-ran:
		t.Fail()
	case <-timeout:
		break
	}
}

func TestSmartPoolStopIfClientVersionChangedInHotStopMode(t *testing.T) {
	sp := newTestSmartPool()
	testContract := sp.Contract.(*testContract)
	testContract.Registered = true
	timeout := make(chan bool, 1)
	sp.PoolMonitor.(*testPoolMonitor).ClientUpdate = true
	sp.Run()
	go func() {
		time.Sleep(100 * time.Millisecond)
		timeout <- true
	}()
	select {
	case <-sp.SubmitterStopped:
		break
	case <-timeout:
		t.Fail()
	}
}

func TestSmartPoolDoesntStopIfHotStopModeIsDisabled(t *testing.T) {
	sp := newTestSmartPool()
	sp.HotStop = false
	testContract := sp.Contract.(*testContract)
	testContract.Registered = true
	timeout := make(chan bool, 1)
	sp.PoolMonitor.(*testPoolMonitor).ContractUpdate = true
	sp.Run()
	go func() {
		time.Sleep(100 * time.Millisecond)
		timeout <- true
	}()
	select {
	case <-sp.SubmitterStopped:
		t.Fail()
	case <-timeout:
		break
	}
}

func TestSmartPoolStopIfContractAddressChangedInHotStopMode(t *testing.T) {
	sp := newTestSmartPool()
	testContract := sp.Contract.(*testContract)
	testContract.Registered = true
	timeout := make(chan bool, 1)
	sp.PoolMonitor.(*testPoolMonitor).ContractUpdate = true
	sp.Run()
	go func() {
		time.Sleep(100 * time.Millisecond)
		timeout <- true
	}()
	select {
	case <-sp.SubmitterStopped:
		break
	case <-timeout:
		t.Fail()
	}
}
<|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 juju

import (
	"github.com/flaviamissi/go-elb/aws"
	"github.com/flaviamissi/go-elb/elb"
	"github.com/flaviamissi/go-elb/elb/elbtest"
	"github.com/globocom/config"
	"github.com/globocom/tsuru/db"
	"github.com/globocom/tsuru/provision"
	"github.com/globocom/tsuru/testing"
	"labix.org/v2/mgo/bson"
	. "launchpad.net/gocheck"
)

type ELBSuite struct {
	server *elbtest.Server
	client *elb.ELB
	cName  string
}

var _ = Suite(&ELBSuite{})

func (s *ELBSuite) SetUpSuite(c *C) {
	var err error
	db.Session, err = db.Open("127.0.0.1:27017", "juju_tests")
	c.Assert(err, IsNil)
	s.server, err = elbtest.NewServer()
	c.Assert(err, IsNil)
	config.Set("juju:elb-endpoint", s.server.URL())
	config.Set("juju:use-elb", true)
	region := aws.SAEast
	region.ELBEndpoint = s.server.URL()
	s.client = elb.New(aws.Auth{AccessKey: "some", SecretKey: "thing"}, region)
	c.Assert(err, IsNil)
	s.cName = "juju_test_elbs"
	config.Set("juju:elb-collection", s.cName)
	config.Set("juju:elb-avail-zones", []interface{}{"my-zone-1a", "my-zone-1b"})
	config.Set("aws:access-key-id", "access")
	config.Set("aws:secret-access-key", "s3cr3t")
}

func (s *ELBSuite) TearDownSuite(c *C) {
	config.Unset("juju:use-elb")
	db.Session.Close()
	s.server.Quit()
}

func (s *ELBSuite) TestGetCollection(c *C) {
	manager := ELBManager{}
	coll := manager.collection()
	other := db.Session.Collection(s.cName)
	c.Assert(coll, DeepEquals, other)
}

func (s *ELBSuite) TestGetELBClient(c *C) {
	manager := ELBManager{}
	elb := manager.elb()
	c.Assert(elb.ELBEndpoint, Equals, s.server.URL())
}

func (s *ELBSuite) TestCreateELB(c *C) {
	app := testing.NewFakeApp("together", "gotthard", 1)
	manager := ELBManager{}
	manager.e = s.client
	err := manager.Create(app)
	c.Assert(err, IsNil)
	defer s.client.DeleteLoadBalancer(app.GetName())
	defer manager.collection().Remove(bson.M{"name": app.GetName()})
	resp, err := s.client.DescribeLoadBalancers("together")
	c.Assert(err, IsNil)
	c.Assert(resp.LoadBalancerDescriptions, HasLen, 1)
	c.Assert(resp.LoadBalancerDescriptions[0].ListenerDescriptions, HasLen, 1)
	listener := resp.LoadBalancerDescriptions[0].ListenerDescriptions[0].Listener
	c.Assert(listener.InstancePort, Equals, 80)
	c.Assert(listener.LoadBalancerPort, Equals, 80)
	c.Assert(listener.InstanceProtocol, Equals, "HTTP")
	c.Assert(listener.Protocol, Equals, "HTTP")
	c.Assert(listener.SSLCertificateId, Equals, "")
	dnsName := resp.LoadBalancerDescriptions[0].DNSName
	var lb loadBalancer
	err = db.Session.Collection(s.cName).Find(bson.M{"name": app.GetName()}).One(&lb)
	c.Assert(err, IsNil)
	c.Assert(lb.DNSName, Equals, dnsName)
}

func (s *ELBSuite) TestCreateELBUsingVPC(c *C) {
	old, _ := config.Get("juju:elb-avail-zones")
	config.Unset("juju:elb-avail-zones")
	config.Set("juju:elb-use-vpc", true)
	config.Set("juju:elb-vpc-subnets", []string{"subnet-a4a3a2a1", "subnet-002200"})
	config.Set("juju:elb-vpc-secgroups", []string{"sg-0900"})
	defer func() {
		config.Set("juju:elb-avail-zones", old)
		config.Unset("juju:elb-use-vpc")
		config.Unset("juju:elb-vpc-subnets")
		config.Unset("juju:elb-vpc-secgroups")
	}()
	app := testing.NewFakeApp("relax", "who", 1)
	manager := ELBManager{}
	err := manager.Create(app)
	c.Assert(err, IsNil)
	defer s.client.DeleteLoadBalancer(app.GetName())
	defer manager.collection().Remove(bson.M{"name": app.GetName()})
	resp, err := s.client.DescribeLoadBalancers(app.GetName())
	c.Assert(err, IsNil)
	c.Assert(resp.LoadBalancerDescriptions, HasLen, 1)
	lbd := resp.LoadBalancerDescriptions[0]
	c.Assert(lbd.Subnets, DeepEquals, []string{"subnet-a4a3a2a1", "subnet-002200"})
	c.Assert(lbd.SecurityGroups, DeepEquals, []string{"sg-0900"})
	c.Assert(lbd.Scheme, Equals, "internal")
	c.Assert(lbd.AvailZones, HasLen, 0)
}

func (s *ELBSuite) TestDestroyELB(c *C) {
	app := testing.NewFakeApp("blue", "who", 1)
	manager := ELBManager{}
	manager.e = s.client
	err := manager.Create(app)
	c.Assert(err, IsNil)
	defer s.client.DeleteLoadBalancer(app.GetName())                 // sanity
	defer manager.collection().Remove(bson.M{"name": app.GetName()}) // sanity
	err = manager.Destroy(app)
	c.Assert(err, IsNil)
	_, err = s.client.DescribeLoadBalancers(app.GetName())
	c.Assert(err, NotNil)
	c.Assert(err, ErrorMatches, `^.*\(LoadBalancerNotFound\)$`)
	n, err := manager.collection().Find(bson.M{"name": app.GetName()}).Count()
	c.Assert(err, IsNil)
	c.Assert(n, Equals, 0)
}

func (s *ELBSuite) TestRegisterUnit(c *C) {
	id1 := s.server.NewInstance()
	defer s.server.RemoveInstance(id1)
	id2 := s.server.NewInstance()
	defer s.server.RemoveInstance(id2)
	app := testing.NewFakeApp("fooled", "who", 1)
	manager := ELBManager{}
	manager.e = s.client
	err := manager.Create(app)
	c.Assert(err, IsNil)
	defer manager.Destroy(app)
	err = manager.Register(app, provision.Unit{InstanceId: id1}, provision.Unit{InstanceId: id2})
	c.Assert(err, IsNil)
	resp, err := s.client.DescribeLoadBalancers(app.GetName())
	c.Assert(err, IsNil)
	c.Assert(resp.LoadBalancerDescriptions, HasLen, 1)
	c.Assert(resp.LoadBalancerDescriptions[0].Instances, HasLen, 2)
	instances := resp.LoadBalancerDescriptions[0].Instances
	c.Assert(instances[0].InstanceId, Equals, id1)
	c.Assert(instances[1].InstanceId, Equals, id2)
}

func (s *ELBSuite) TestDeregisterUnit(c *C) {
	id1 := s.server.NewInstance()
	defer s.server.RemoveInstance(id1)
	id2 := s.server.NewInstance()
	defer s.server.RemoveInstance(id2)
	unit1 := provision.Unit{InstanceId: id1}
	unit2 := provision.Unit{InstanceId: id2}
	app := testing.NewFakeApp("dirty", "who", 1)
	manager := ELBManager{}
	manager.e = s.client
	err := manager.Create(app)
	c.Assert(err, IsNil)
	defer manager.Destroy(app)
	err = manager.Register(app, unit1, unit2)
	c.Assert(err, IsNil)
	err = manager.Deregister(app, unit1, unit2)
	c.Assert(err, IsNil)
	resp, err := s.client.DescribeLoadBalancers(app.GetName())
	c.Assert(err, IsNil)
	c.Assert(resp.LoadBalancerDescriptions, HasLen, 1)
	c.Assert(resp.LoadBalancerDescriptions[0].Instances, HasLen, 0)
}

func (s *ELBSuite) TestAddr(c *C) {
	app := testing.NewFakeApp("enough", "who", 1)
	manager := ELBManager{}
	manager.e = s.client
	err := manager.Create(app)
	c.Assert(err, IsNil)
	defer manager.Destroy(app)
	var lb loadBalancer
	err = manager.collection().Find(bson.M{"name": app.GetName()}).One(&lb)
	c.Assert(err, IsNil)
	addr, err := manager.Addr(app)
	c.Assert(err, IsNil)
	c.Assert(addr, Equals, lb.DNSName)
}

func (s *ELBSuite) TestAddrUnknownLoadBalancer(c *C) {
	app := testing.NewFakeApp("five", "who", 1)
	manager := ELBManager{}
	addr, err := manager.Addr(app)
	c.Assert(addr, Equals, "")
	c.Assert(err, NotNil)
	c.Assert(err.Error(), Equals, "not found")
}
<commit_msg>provision/juju: change assertion, so it's more reliable<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 juju

import (
	"github.com/flaviamissi/go-elb/aws"
	"github.com/flaviamissi/go-elb/elb"
	"github.com/flaviamissi/go-elb/elb/elbtest"
	"github.com/globocom/config"
	"github.com/globocom/tsuru/db"
	"github.com/globocom/tsuru/provision"
	"github.com/globocom/tsuru/testing"
	"labix.org/v2/mgo/bson"
	. "launchpad.net/gocheck"
	"sort"
)

type ELBSuite struct {
	server *elbtest.Server
	client *elb.ELB
	cName  string
}

var _ = Suite(&ELBSuite{})

func (s *ELBSuite) SetUpSuite(c *C) {
	var err error
	db.Session, err = db.Open("127.0.0.1:27017", "juju_tests")
	c.Assert(err, IsNil)
	s.server, err = elbtest.NewServer()
	c.Assert(err, IsNil)
	config.Set("juju:elb-endpoint", s.server.URL())
	config.Set("juju:use-elb", true)
	region := aws.SAEast
	region.ELBEndpoint = s.server.URL()
	s.client = elb.New(aws.Auth{AccessKey: "some", SecretKey: "thing"}, region)
	c.Assert(err, IsNil)
	s.cName = "juju_test_elbs"
	config.Set("juju:elb-collection", s.cName)
	config.Set("juju:elb-avail-zones", []interface{}{"my-zone-1a", "my-zone-1b"})
	config.Set("aws:access-key-id", "access")
	config.Set("aws:secret-access-key", "s3cr3t")
}

func (s *ELBSuite) TearDownSuite(c *C) {
	config.Unset("juju:use-elb")
	db.Session.Close()
	s.server.Quit()
}

func (s *ELBSuite) TestGetCollection(c *C) {
	manager := ELBManager{}
	coll := manager.collection()
	other := db.Session.Collection(s.cName)
	c.Assert(coll, DeepEquals, other)
}

func (s *ELBSuite) TestGetELBClient(c *C) {
	manager := ELBManager{}
	elb := manager.elb()
	c.Assert(elb.ELBEndpoint, Equals, s.server.URL())
}

func (s *ELBSuite) TestCreateELB(c *C) {
	app := testing.NewFakeApp("together", "gotthard", 1)
	manager := ELBManager{}
	manager.e = s.client
	err := manager.Create(app)
	c.Assert(err, IsNil)
	defer s.client.DeleteLoadBalancer(app.GetName())
	defer manager.collection().Remove(bson.M{"name": app.GetName()})
	resp, err := s.client.DescribeLoadBalancers("together")
	c.Assert(err, IsNil)
	c.Assert(resp.LoadBalancerDescriptions, HasLen, 1)
	c.Assert(resp.LoadBalancerDescriptions[0].ListenerDescriptions, HasLen, 1)
	listener := resp.LoadBalancerDescriptions[0].ListenerDescriptions[0].Listener
	c.Assert(listener.InstancePort, Equals, 80)
	c.Assert(listener.LoadBalancerPort, Equals, 80)
	c.Assert(listener.InstanceProtocol, Equals, "HTTP")
	c.Assert(listener.Protocol, Equals, "HTTP")
	c.Assert(listener.SSLCertificateId, Equals, "")
	dnsName := resp.LoadBalancerDescriptions[0].DNSName
	var lb loadBalancer
	err = db.Session.Collection(s.cName).Find(bson.M{"name": app.GetName()}).One(&lb)
	c.Assert(err, IsNil)
	c.Assert(lb.DNSName, Equals, dnsName)
}

func (s *ELBSuite) TestCreateELBUsingVPC(c *C) {
	old, _ := config.Get("juju:elb-avail-zones")
	config.Unset("juju:elb-avail-zones")
	config.Set("juju:elb-use-vpc", true)
	config.Set("juju:elb-vpc-subnets", []string{"subnet-a4a3a2a1", "subnet-002200"})
	config.Set("juju:elb-vpc-secgroups", []string{"sg-0900"})
	defer func() {
		config.Set("juju:elb-avail-zones", old)
		config.Unset("juju:elb-use-vpc")
		config.Unset("juju:elb-vpc-subnets")
		config.Unset("juju:elb-vpc-secgroups")
	}()
	app := testing.NewFakeApp("relax", "who", 1)
	manager := ELBManager{}
	err := manager.Create(app)
	c.Assert(err, IsNil)
	defer s.client.DeleteLoadBalancer(app.GetName())
	defer manager.collection().Remove(bson.M{"name": app.GetName()})
	resp, err := s.client.DescribeLoadBalancers(app.GetName())
	c.Assert(err, IsNil)
	c.Assert(resp.LoadBalancerDescriptions, HasLen, 1)
	lbd := resp.LoadBalancerDescriptions[0]
	c.Assert(lbd.Subnets, DeepEquals, []string{"subnet-a4a3a2a1", "subnet-002200"})
	c.Assert(lbd.SecurityGroups, DeepEquals, []string{"sg-0900"})
	c.Assert(lbd.Scheme, Equals, "internal")
	c.Assert(lbd.AvailZones, HasLen, 0)
}

func (s *ELBSuite) TestDestroyELB(c *C) {
	app := testing.NewFakeApp("blue", "who", 1)
	manager := ELBManager{}
	manager.e = s.client
	err := manager.Create(app)
	c.Assert(err, IsNil)
	defer s.client.DeleteLoadBalancer(app.GetName())                 // sanity
	defer manager.collection().Remove(bson.M{"name": app.GetName()}) // sanity
	err = manager.Destroy(app)
	c.Assert(err, IsNil)
	_, err = s.client.DescribeLoadBalancers(app.GetName())
	c.Assert(err, NotNil)
	c.Assert(err, ErrorMatches, `^.*\(LoadBalancerNotFound\)$`)
	n, err := manager.collection().Find(bson.M{"name": app.GetName()}).Count()
	c.Assert(err, IsNil)
	c.Assert(n, Equals, 0)
}

func (s *ELBSuite) TestRegisterUnit(c *C) {
	id1 := s.server.NewInstance()
	defer s.server.RemoveInstance(id1)
	id2 := s.server.NewInstance()
	defer s.server.RemoveInstance(id2)
	app := testing.NewFakeApp("fooled", "who", 1)
	manager := ELBManager{}
	manager.e = s.client
	err := manager.Create(app)
	c.Assert(err, IsNil)
	defer manager.Destroy(app)
	err = manager.Register(app, provision.Unit{InstanceId: id1}, provision.Unit{InstanceId: id2})
	c.Assert(err, IsNil)
	resp, err := s.client.DescribeLoadBalancers(app.GetName())
	c.Assert(err, IsNil)
	c.Assert(resp.LoadBalancerDescriptions, HasLen, 1)
	c.Assert(resp.LoadBalancerDescriptions[0].Instances, HasLen, 2)
	instances := resp.LoadBalancerDescriptions[0].Instances
	ids := []string{instances[0].InstanceId, instances[1].InstanceId}
	sort.Strings(ids)
	c.Assert(ids, DeepEquals, []string{id1, id2})
}

func (s *ELBSuite) TestDeregisterUnit(c *C) {
	id1 := s.server.NewInstance()
	defer s.server.RemoveInstance(id1)
	id2 := s.server.NewInstance()
	defer s.server.RemoveInstance(id2)
	unit1 := provision.Unit{InstanceId: id1}
	unit2 := provision.Unit{InstanceId: id2}
	app := testing.NewFakeApp("dirty", "who", 1)
	manager := ELBManager{}
	manager.e = s.client
	err := manager.Create(app)
	c.Assert(err, IsNil)
	defer manager.Destroy(app)
	err = manager.Register(app, unit1, unit2)
	c.Assert(err, IsNil)
	err = manager.Deregister(app, unit1, unit2)
	c.Assert(err, IsNil)
	resp, err := s.client.DescribeLoadBalancers(app.GetName())
	c.Assert(err, IsNil)
	c.Assert(resp.LoadBalancerDescriptions, HasLen, 1)
	c.Assert(resp.LoadBalancerDescriptions[0].Instances, HasLen, 0)
}

func (s *ELBSuite) TestAddr(c *C) {
	app := testing.NewFakeApp("enough", "who", 1)
	manager := ELBManager{}
	manager.e = s.client
	err := manager.Create(app)
	c.Assert(err, IsNil)
	defer manager.Destroy(app)
	var lb loadBalancer
	err = manager.collection().Find(bson.M{"name": app.GetName()}).One(&lb)
	c.Assert(err, IsNil)
	addr, err := manager.Addr(app)
	c.Assert(err, IsNil)
	c.Assert(addr, Equals, lb.DNSName)
}

func (s *ELBSuite) TestAddrUnknownLoadBalancer(c *C) {
	app := testing.NewFakeApp("five", "who", 1)
	manager := ELBManager{}
	addr, err := manager.Addr(app)
	c.Assert(addr, Equals, "")
	c.Assert(err, NotNil)
	c.Assert(err.Error(), Equals, "not found")
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

// Package statemanager implements simple constructs for saving and restoring
// state from disk.
// It provides the interface for a StateManager which can read/write arbitrary
// json data from/to disk.
package statemanager

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"sync"
	"time"

	"github.com/aws/amazon-ecs-agent/agent/config"
	"github.com/aws/amazon-ecs-agent/agent/logger"
)

// The current version of saved data. Any backwards or forwards incompatible
// changes to the data-format should increment this number and retain the
// ability to read old data versions.
const EcsDataVersion = 1

// Filename in the ECS_DATADIR
const ecsDataFile = "ecs_agent_data.json"

// How frequently to flush to disk
const minSaveInterval = 10 * time.Second

var log = logger.ForModule("statemanager")

// Saveable types should be able to be json serializable and deserializable
// Properly, this should have json.Marshaler/json.Unmarshaler here, but string
// and so on can be marshaled/unmarshaled sanely but don't fit those interfaces.
type Saveable interface{}

// Saver is a type that can be saved
type Saver interface {
	Save() error
	ForceSave() error
}

// Option functions are functions that may be used as part of constructing a new
// StateManager
type Option func(StateManager)

type saveableState map[string]*Saveable
type intermediateSaveableState map[string]json.RawMessage

// State is a struct of all data that should be saveable/loadable to disk. Each
// element should be json-serializable.
//
// Note, changing this to work with BinaryMarshaler or another more compact
// format would be fine, but everything already needs a json representation
// since that's our wire format and the extra space taken / IO-time is expected
// to be fairly negligable.
type state struct {
	Data saveableState

	Version int
}

type intermediateState struct {
	Data intermediateSaveableState
}

type versionOnlyState struct {
	Version int
}

// A StateManager can load and save state from disk.
// Load is not expected to return an error if there is no state to load.
type StateManager interface {
	Saver
	Load() error
}

type basicStateManager struct {
	statePath string // The path to a file in which state can be serialized

	state *state // pointers to the data we should save / load into

	sync.Mutex                // guards save times
	lastSave        time.Time //the last time a save completed
	nextPlannedSave time.Time //the next time a save is planned
}

// NewStateManager constructs a new StateManager which saves data at the
// location specified in cfg and operates under the given options.
// The returned StateManager will not save more often than every 10 seconds and
// will not reliably return errors with Save, but will log them appropriately.
func NewStateManager(cfg *config.Config, options ...Option) (StateManager, error) {
	fi, err := os.Stat(cfg.DataDir)
	if err != nil {
		return nil, err
	}
	if !fi.IsDir() {
		return nil, errors.New("State manager DataDir must exist")
	}

	state := &state{
		Data:    make(saveableState),
		Version: EcsDataVersion,
	}
	manager := &basicStateManager{
		statePath: cfg.DataDir,
		state:     state,
	}

	for _, option := range options {
		option(manager)
	}

	return manager, nil
}

// AddSaveable is an option that adds a given saveable as one that should be saved
// under the given name. The name must be the same across uses of the
// statemanager (e.g. program invocations) for it to be serialized and
// deserialized correctly.
func AddSaveable(name string, saveable Saveable) Option {
	return (Option)(func(m StateManager) {
		manager, ok := m.(*basicStateManager)
		if !ok {
			log.Crit("Unable to add to state manager; unknown instantiation")
			return
		}
		manager.state.Data[name] = &saveable
	})
}

// Save triggers a save to file, though respects a minimum save interval to wait
// between saves.
func (manager *basicStateManager) Save() error {
	manager.Lock()
	defer manager.Unlock()
	if time.Since(manager.lastSave) >= minSaveInterval {
		// we can just save
		err := manager.ForceSave()
		manager.lastSave = time.Now()
		manager.nextPlannedSave = time.Time{} // re-zero it; assume all pending desires to save are fulfilled
		return err
	} else if manager.nextPlannedSave.IsZero() {
		// No save planned yet, we should plan one.
		next := manager.lastSave.Add(minSaveInterval)
		manager.nextPlannedSave = next
		go func() {
			time.Sleep(next.Sub(time.Now()))
			manager.Save()
		}()
	}
	// else nextPlannedSave wasn't Zero so there's a save planned elsewhere that'll
	// fulfill this
	return nil
}

// ForceSave saves the given State to a file. It is an atomic operation on POSIX
// systems (by Renaming over the target file).
// This function logs errors at will and does not necessarily expect the caller
// to handle the error because there's little a caller can do in general other
// than just keep going.
// In addition, the StateManager internally buffers save requests in order to
// only save at most every STATE_SAVE_INTERVAL.
func (manager *basicStateManager) ForceSave() error {
	log.Info("Saving state!")
	s := manager.state
	s.Version = EcsDataVersion

	data, err := json.Marshal(s)
	if err != nil {
		log.Error("Error saving state; could not marshal data; this is odd", "err", err)
		return err
	}
	// Make our temp-file on the same volume as our data-file to ensure we can
	// actually move it atomically; cross-device renaming will error out.
	tmpfile, err := ioutil.TempFile(manager.statePath, "tmp_ecs_agent_data")
	if err != nil {
		log.Error("Error saving state; could not create temp file to save state", "err", err)
		return err
	}
	_, err = tmpfile.Write(data)
	if err != nil {
		log.Error("Error saving state; could not write to temp file to save state", "err", err)
		return err
	}
	err = os.Rename(tmpfile.Name(), filepath.Join(manager.statePath, ecsDataFile))
	if err != nil {
		log.Error("Error saving state; could not move to data file", "err", err)
	}
	return err
}

// Load reads state off the disk from the well-known filepath and loads it into
// the passed State object.
func (manager *basicStateManager) Load() error {
	// Note that even if Save overwrites the file we're looking at here, we
	// still hold the old inode and should read the old data so no locking is
	// needed (given Linux and the ext* family of fs at least).
	s := manager.state
	log.Info("Loading state!")
	file, err := os.Open(filepath.Join(manager.statePath, ecsDataFile))
	if err != nil {
		if os.IsNotExist(err) {
			// Happens every first run; not a real error
			return nil
		}
		return err
	}
	data, err := ioutil.ReadAll(file)
	if err != nil {
		log.Error("Error reading existing state file", "err", err)
		return err
	}
	// Dry-run to make sure this is a version we can understand
	tmps := versionOnlyState{}
	err = json.Unmarshal(data, &tmps)
	if err != nil {
		log.Crit("Could not unmarshal existing state; corrupted data?", "err", err, "data", data)
		return err
	}
	if tmps.Version > EcsDataVersion {
		strversion := strconv.Itoa(tmps.Version)
		return errors.New("Unsupported data format: Version " + strversion + " not " + strconv.Itoa(EcsDataVersion))
	}
	// Now load it into the actual state. The reason we do this with the
	// intermediate state is that we *must* unmarshal directly into the
	// "saveable" pointers we were given in AddSaveable; if we unmarshal
	// directly into a map with values of pointers, those pointers are lost.
	// We *must* unmarshal this way because the existing pointers could have
	// semi-initialized data (and are actually expected to)

	var intermediate intermediateState
	err = json.Unmarshal(data, &intermediate)
	if err != nil {
		log.Debug("Could not unmarshal into intermediate")
		return err
	}

	for key, rawJSON := range intermediate.Data {
		actualPointer, ok := manager.state.Data[key]
		if !ok {
			log.Error("Loading state: potentially malformed json key of " + key)
			continue
		}
		err = json.Unmarshal(rawJSON, actualPointer)
		if err != nil {
			log.Debug("Could not unmarshal into actual")
			return err
		}
	}

	log.Debug("Loaded state!", "state", s)
	return nil
}
<commit_msg>State: Bump data format version<commit_after>// Copyright 2014-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

// Package statemanager implements simple constructs for saving and restoring
// state from disk.
// It provides the interface for a StateManager which can read/write arbitrary
// json data from/to disk.
package statemanager

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"sync"
	"time"

	"github.com/aws/amazon-ecs-agent/agent/config"
	"github.com/aws/amazon-ecs-agent/agent/logger"
)

// The current version of saved data. Any backwards or forwards incompatible
// changes to the data-format should increment this number and retain the
// ability to read old data versions.
const EcsDataVersion = 2

// Filename in the ECS_DATADIR
const ecsDataFile = "ecs_agent_data.json"

// How frequently to flush to disk
const minSaveInterval = 10 * time.Second

var log = logger.ForModule("statemanager")

// Saveable types should be able to be json serializable and deserializable
// Properly, this should have json.Marshaler/json.Unmarshaler here, but string
// and so on can be marshaled/unmarshaled sanely but don't fit those interfaces.
type Saveable interface{}

// Saver is a type that can be saved
type Saver interface {
	Save() error
	ForceSave() error
}

// Option functions are functions that may be used as part of constructing a new
// StateManager
type Option func(StateManager)

type saveableState map[string]*Saveable
type intermediateSaveableState map[string]json.RawMessage

// State is a struct of all data that should be saveable/loadable to disk. Each
// element should be json-serializable.
//
// Note, changing this to work with BinaryMarshaler or another more compact
// format would be fine, but everything already needs a json representation
// since that's our wire format and the extra space taken / IO-time is expected
// to be fairly negligable.
type state struct {
	Data saveableState

	Version int
}

type intermediateState struct {
	Data intermediateSaveableState
}

type versionOnlyState struct {
	Version int
}

// A StateManager can load and save state from disk.
// Load is not expected to return an error if there is no state to load.
type StateManager interface {
	Saver
	Load() error
}

type basicStateManager struct {
	statePath string // The path to a file in which state can be serialized

	state *state // pointers to the data we should save / load into

	sync.Mutex                // guards save times
	lastSave        time.Time //the last time a save completed
	nextPlannedSave time.Time //the next time a save is planned
}

// NewStateManager constructs a new StateManager which saves data at the
// location specified in cfg and operates under the given options.
// The returned StateManager will not save more often than every 10 seconds and
// will not reliably return errors with Save, but will log them appropriately.
func NewStateManager(cfg *config.Config, options ...Option) (StateManager, error) {
	fi, err := os.Stat(cfg.DataDir)
	if err != nil {
		return nil, err
	}
	if !fi.IsDir() {
		return nil, errors.New("State manager DataDir must exist")
	}

	state := &state{
		Data:    make(saveableState),
		Version: EcsDataVersion,
	}
	manager := &basicStateManager{
		statePath: cfg.DataDir,
		state:     state,
	}

	for _, option := range options {
		option(manager)
	}

	return manager, nil
}

// AddSaveable is an option that adds a given saveable as one that should be saved
// under the given name. The name must be the same across uses of the
// statemanager (e.g. program invocations) for it to be serialized and
// deserialized correctly.
func AddSaveable(name string, saveable Saveable) Option {
	return (Option)(func(m StateManager) {
		manager, ok := m.(*basicStateManager)
		if !ok {
			log.Crit("Unable to add to state manager; unknown instantiation")
			return
		}
		manager.state.Data[name] = &saveable
	})
}

// Save triggers a save to file, though respects a minimum save interval to wait
// between saves.
func (manager *basicStateManager) Save() error {
	manager.Lock()
	defer manager.Unlock()
	if time.Since(manager.lastSave) >= minSaveInterval {
		// we can just save
		err := manager.ForceSave()
		manager.lastSave = time.Now()
		manager.nextPlannedSave = time.Time{} // re-zero it; assume all pending desires to save are fulfilled
		return err
	} else if manager.nextPlannedSave.IsZero() {
		// No save planned yet, we should plan one.
		next := manager.lastSave.Add(minSaveInterval)
		manager.nextPlannedSave = next
		go func() {
			time.Sleep(next.Sub(time.Now()))
			manager.Save()
		}()
	}
	// else nextPlannedSave wasn't Zero so there's a save planned elsewhere that'll
	// fulfill this
	return nil
}

// ForceSave saves the given State to a file. It is an atomic operation on POSIX
// systems (by Renaming over the target file).
// This function logs errors at will and does not necessarily expect the caller
// to handle the error because there's little a caller can do in general other
// than just keep going.
// In addition, the StateManager internally buffers save requests in order to
// only save at most every STATE_SAVE_INTERVAL.
func (manager *basicStateManager) ForceSave() error {
	log.Info("Saving state!")
	s := manager.state
	s.Version = EcsDataVersion

	data, err := json.Marshal(s)
	if err != nil {
		log.Error("Error saving state; could not marshal data; this is odd", "err", err)
		return err
	}
	// Make our temp-file on the same volume as our data-file to ensure we can
	// actually move it atomically; cross-device renaming will error out.
	tmpfile, err := ioutil.TempFile(manager.statePath, "tmp_ecs_agent_data")
	if err != nil {
		log.Error("Error saving state; could not create temp file to save state", "err", err)
		return err
	}
	_, err = tmpfile.Write(data)
	if err != nil {
		log.Error("Error saving state; could not write to temp file to save state", "err", err)
		return err
	}
	err = os.Rename(tmpfile.Name(), filepath.Join(manager.statePath, ecsDataFile))
	if err != nil {
		log.Error("Error saving state; could not move to data file", "err", err)
	}
	return err
}

// Load reads state off the disk from the well-known filepath and loads it into
// the passed State object.
func (manager *basicStateManager) Load() error {
	// Note that even if Save overwrites the file we're looking at here, we
	// still hold the old inode and should read the old data so no locking is
	// needed (given Linux and the ext* family of fs at least).
	s := manager.state
	log.Info("Loading state!")
	file, err := os.Open(filepath.Join(manager.statePath, ecsDataFile))
	if err != nil {
		if os.IsNotExist(err) {
			// Happens every first run; not a real error
			return nil
		}
		return err
	}
	data, err := ioutil.ReadAll(file)
	if err != nil {
		log.Error("Error reading existing state file", "err", err)
		return err
	}
	// Dry-run to make sure this is a version we can understand
	tmps := versionOnlyState{}
	err = json.Unmarshal(data, &tmps)
	if err != nil {
		log.Crit("Could not unmarshal existing state; corrupted data?", "err", err, "data", data)
		return err
	}
	if tmps.Version > EcsDataVersion {
		strversion := strconv.Itoa(tmps.Version)
		return errors.New("Unsupported data format: Version " + strversion + " not " + strconv.Itoa(EcsDataVersion))
	}
	// Now load it into the actual state. The reason we do this with the
	// intermediate state is that we *must* unmarshal directly into the
	// "saveable" pointers we were given in AddSaveable; if we unmarshal
	// directly into a map with values of pointers, those pointers are lost.
	// We *must* unmarshal this way because the existing pointers could have
	// semi-initialized data (and are actually expected to)

	var intermediate intermediateState
	err = json.Unmarshal(data, &intermediate)
	if err != nil {
		log.Debug("Could not unmarshal into intermediate")
		return err
	}

	for key, rawJSON := range intermediate.Data {
		actualPointer, ok := manager.state.Data[key]
		if !ok {
			log.Error("Loading state: potentially malformed json key of " + key)
			continue
		}
		err = json.Unmarshal(rawJSON, actualPointer)
		if err != nil {
			log.Debug("Could not unmarshal into actual")
			return err
		}
	}

	log.Debug("Loaded state!", "state", s)
	return nil
}
<|endoftext|>


<commit_before>//  Copyright (c) 2014 Couchbase, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 		http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package snowball

import (
	"fmt"

	"github.com/blevesearch/bleve/analysis"
	"github.com/blevesearch/bleve/registry"

	"github.com/kljensen/snowball"
)

const Name = "stemmer_snowball"

type SnowballStemmer struct {
	language string
}

func NewSnowballStemmer(language string) *SnowballStemmer {
	return &SnowballStemmer{
		language: language,
	}
}

func (s *SnowballStemmer) Filter(input analysis.TokenStream) analysis.TokenStream {
	for _, token := range input {
		// if it is not a protected keyword, stem it
		if !token.KeyWord {
			stemmed, _ := snowball.Stem(string(token.Term), s.langauge, true)
			token.Term = []byte(stemmed)
		}
	}
	return input
}

func SnowballStemmerConstructor(config map[string]interface{}, cache *registry.Cache) (analysis.TokenFilter, error) {
	language, ok := config["language"].(string)
	if !ok {
		return nil, fmt.Errorf("must specify language")
	}
	return NewSnowballStemmer(language), nil
}

func init() {
	registry.RegisterTokenFilter(Name, SnowballStemmerConstructor)
}
<commit_msg>Fix another spelling for CI<commit_after>//  Copyright (c) 2014 Couchbase, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 		http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package snowball

import (
	"fmt"

	"github.com/blevesearch/bleve/analysis"
	"github.com/blevesearch/bleve/registry"

	"github.com/kljensen/snowball"
)

const Name = "stemmer_snowball"

type SnowballStemmer struct {
	language string
}

func NewSnowballStemmer(language string) *SnowballStemmer {
	return &SnowballStemmer{
		language: language,
	}
}

func (s *SnowballStemmer) Filter(input analysis.TokenStream) analysis.TokenStream {
	for _, token := range input {
		// if it is not a protected keyword, stem it
		if !token.KeyWord {
			stemmed, _ := snowball.Stem(string(token.Term), s.language, true)
			token.Term = []byte(stemmed)
		}
	}
	return input
}

func SnowballStemmerConstructor(config map[string]interface{}, cache *registry.Cache) (analysis.TokenFilter, error) {
	language, ok := config["language"].(string)
	if !ok {
		return nil, fmt.Errorf("must specify language")
	}
	return NewSnowballStemmer(language), nil
}

func init() {
	registry.RegisterTokenFilter(Name, SnowballStemmerConstructor)
}
<|endoftext|>


<commit_before>package gitManip

import (
	"fmt"

	"github.com/fatih/color"

	"bytes"

	"github.com/k0pernicus/goyave/traces"
	git "gopkg.in/libgit2/git2go.v26"
)

/*Map to match the RepositoryState enum type with a string
 */
var repositoryStateToString = map[git.RepositoryState]string{
	git.RepositoryStateNone:                 "None",
	git.RepositoryStateMerge:                "Merge",
	git.RepositoryStateRevert:               "Revert",
	git.RepositoryStateCherrypick:           "Cherrypick",
	git.RepositoryStateBisect:               "Bisect",
	git.RepositoryStateRebase:               "Rebase",
	git.RepositoryStateRebaseInteractive:    "Rebase Interactive",
	git.RepositoryStateRebaseMerge:          "Rebase Merge",
	git.RepositoryStateApplyMailbox:         "Apply Mailbox",
	git.RepositoryStateApplyMailboxOrRebase: "Apply Mailbox or Rebase",
}

/*Global variable to set the StatusOption parameter, in order to list each file status
 */
var statusOption = git.StatusOptions{
	Show:     git.StatusShowIndexAndWorkdir,
	Flags:    git.StatusOptIncludeUntracked,
	Pathspec: []string{},
}

/*GitObject contains informations about the current git repository
 *
 *The structure is:
 *  accessible:
 *		Is the repository still exists in the hard drive?
 *	path:
 *		The path file.
 *	repository:
 *		The object repository.
 */
type GitObject struct {
	accessible error
	path       string
	repository git.Repository
}

/*New is a constructor for GitObject
 *
 * It neeeds:
 *	path:
 *		The path of the current repository.
 */
func New(path string) *GitObject {
	r, err := git.OpenRepository(path)
	return &GitObject{accessible: err, path: path, repository: *r}
}

/*Clone is cloning a given repository, from a public URL
 *
 * It needs:
 * path:
 *		The local path to clone the repository.
 *	URL:
 *		The remote URL to fetch the repository.
 */
func Clone(path, URL string) error {
	_, err := git.Clone(URL, path, &git.CloneOptions{})
	return err
}

/*GetRemoteURL returns the associated remote URL of a given local path repository
 *
 * It needs:
 *	path
 *		The local path of a git repository
 */
func GetRemoteURL(path string) string {
	r, err := git.OpenRepository(path)
	if err != nil {
		fmt.Println("The repository can't be opened")
		return ""
	}
	remoteCollection := r.Remotes
	originRemote, err := remoteCollection.Lookup("origin")
	if err != nil {
		traces.WarningTracer.Printf("can't lookup origin remote URL for %s", path)
		return ""
	}
	return originRemote.Url()
}

/*isAccesible returns the information that is the current git repository is existing or not.
 *This method returns a boolean value: true if the git repository is still accesible (still exists), or false if not.
 */
func (g *GitObject) isAccessible() bool {
	return g.accessible == nil
}

/*Status prints the current status of the repository, accessible via the structure path field.
 *This method works only if the repository is accessible.
 */
func (g *GitObject) Status() {
	if g.isAccessible() {
		if err := g.printChanges(); err != nil {
			color.RedString("Impossible to get stats from %s, due to error %s", g.path, err)
		}
	} else {
		color.RedString("Repository %s not found!", g.path)
	}
}

/*getDiffWithWT returns the difference between the working tree and the index, for the current git repository.
 *If there is an error processing the request, it returns an error.
 */
func (g *GitObject) getDiffWithWT() (*git.Diff, error) {
	// Get the index of the repository
	currentIndex, err := g.repository.Index()
	if err != nil {
		return nil, err
	}
	// Get the default diff options, and add it custom flags
	defaultDiffOptions, err := git.DefaultDiffOptions()
	if err != nil {
		return nil, err
	}
	defaultDiffOptions.Flags = defaultDiffOptions.Flags | git.DiffIncludeUntracked | git.DiffIncludeTypeChange
	// Check the difference between the working directory and the index
	diff, err := g.repository.DiffIndexToWorkdir(currentIndex, &defaultDiffOptions)
	if err != nil {
		return nil, err
	}
	return diff, nil
}

func (g *GitObject) getCommitsAheadBehind() (int, int, error) {
	repositoryHead, err := g.repository.Head()
	// Check upstream branch head
	cBranch := repositoryHead.Branch()
	cReference, err := cBranch.Upstream()
	if err != nil {
		return -1, -1, err
	}
	cReferenceTarget := cReference.Target()
	cRepositoryTarget := repositoryHead.Target()
	commitsAhead, commitsBehind, err := g.repository.AheadBehind(cRepositoryTarget, cReferenceTarget)
	return commitsAhead, commitsBehind, err
}

/*printChanges prints out all changes for the current git repository.
 *If there is an error processing the request, it returns this one.
 */
func (g *GitObject) printChanges() error {
	diff, err := g.getDiffWithWT()
	var buffer bytes.Buffer
	if err != nil {
		return err
	}

	numDeltas, err := diff.NumDeltas()
	if err != nil {
		return err
	}

	headDetached, err := g.repository.IsHeadDetached()
	if err != nil {
		return err
	}
	if headDetached {
		outputHead := fmt.Sprintf("%s", color.RedString("\t/!\\ The repository's HEAD is detached! /!\\\n"))
		buffer.WriteString(outputHead)
	}

	if numDeltas > 0 {
		buffer.WriteString(fmt.Sprintf("%s %9s\t[%d modification(s)]\n", color.RedString("✘"), g.path, numDeltas))
		for i := 0; i < numDeltas; i++ {
			delta, _ := diff.GetDelta(i)
			currentStatus := delta.Status
			newFile := delta.NewFile.Path
			oldFile := delta.OldFile.Path
			switch currentStatus {
			case git.DeltaAdded:
				buffer.WriteString(fmt.Sprintf("\t===> %s has been added!\n", color.MagentaString(newFile)))
			case git.DeltaDeleted:
				buffer.WriteString(fmt.Sprintf("\t===> %s has been deleted!\n", color.MagentaString(newFile)))
			case git.DeltaModified:
				buffer.WriteString(fmt.Sprintf("\t===> %s has been modified!\n", color.MagentaString(newFile)))
			case git.DeltaRenamed:
				buffer.WriteString(fmt.Sprintf("\t===> %s has been renamed to %s!\n", color.MagentaString(oldFile), color.MagentaString(newFile)))
			case git.DeltaUntracked:
				buffer.WriteString(fmt.Sprintf("\t===> %s is untracked - please to add it or update the gitignore file!\n", color.MagentaString(newFile)))
			case git.DeltaTypeChange:
				buffer.WriteString(fmt.Sprintf("\t===> the type of %s has been changed from %d to %d!", color.MagentaString(newFile), delta.OldFile.Mode, delta.NewFile.Mode))
			}
		}
	} else {
		buffer.WriteString(fmt.Sprintf("%s %s\n", color.GreenString("✔"), g.path))
	}

	commitsAhead, commitsBehind, err := g.getCommitsAheadBehind()
	if err == nil {
		if commitsAhead != 0 {
			buffer.WriteString(fmt.Sprintf("\t%s %d commits AHEAD - You need to push your modifications soon\n", color.RedString("⟳"), commitsAhead))
		}
		if commitsBehind != 0 {
			buffer.WriteString(fmt.Sprintf("\t%s %d commits BEHIND - You need to pull the modifications from your remote branch soon\n", color.RedString("⟲"), commitsBehind))
		}
	}

	fmt.Print(buffer.String())
	return nil
}
<commit_msg>Changes the output for ahead/behind messages<commit_after>package gitManip

import (
	"fmt"

	"github.com/fatih/color"

	"bytes"

	"github.com/k0pernicus/goyave/traces"
	git "gopkg.in/libgit2/git2go.v26"
)

/*Map to match the RepositoryState enum type with a string
 */
var repositoryStateToString = map[git.RepositoryState]string{
	git.RepositoryStateNone:                 "None",
	git.RepositoryStateMerge:                "Merge",
	git.RepositoryStateRevert:               "Revert",
	git.RepositoryStateCherrypick:           "Cherrypick",
	git.RepositoryStateBisect:               "Bisect",
	git.RepositoryStateRebase:               "Rebase",
	git.RepositoryStateRebaseInteractive:    "Rebase Interactive",
	git.RepositoryStateRebaseMerge:          "Rebase Merge",
	git.RepositoryStateApplyMailbox:         "Apply Mailbox",
	git.RepositoryStateApplyMailboxOrRebase: "Apply Mailbox or Rebase",
}

/*Global variable to set the StatusOption parameter, in order to list each file status
 */
var statusOption = git.StatusOptions{
	Show:     git.StatusShowIndexAndWorkdir,
	Flags:    git.StatusOptIncludeUntracked,
	Pathspec: []string{},
}

/*GitObject contains informations about the current git repository
 *
 *The structure is:
 *  accessible:
 *		Is the repository still exists in the hard drive?
 *	path:
 *		The path file.
 *	repository:
 *		The object repository.
 */
type GitObject struct {
	accessible error
	path       string
	repository git.Repository
}

/*New is a constructor for GitObject
 *
 * It neeeds:
 *	path:
 *		The path of the current repository.
 */
func New(path string) *GitObject {
	r, err := git.OpenRepository(path)
	return &GitObject{accessible: err, path: path, repository: *r}
}

/*Clone is cloning a given repository, from a public URL
 *
 * It needs:
 * path:
 *		The local path to clone the repository.
 *	URL:
 *		The remote URL to fetch the repository.
 */
func Clone(path, URL string) error {
	_, err := git.Clone(URL, path, &git.CloneOptions{})
	return err
}

/*GetRemoteURL returns the associated remote URL of a given local path repository
 *
 * It needs:
 *	path
 *		The local path of a git repository
 */
func GetRemoteURL(path string) string {
	r, err := git.OpenRepository(path)
	if err != nil {
		fmt.Println("The repository can't be opened")
		return ""
	}
	remoteCollection := r.Remotes
	originRemote, err := remoteCollection.Lookup("origin")
	if err != nil {
		traces.WarningTracer.Printf("can't lookup origin remote URL for %s", path)
		return ""
	}
	return originRemote.Url()
}

/*isAccesible returns the information that is the current git repository is existing or not.
 *This method returns a boolean value: true if the git repository is still accesible (still exists), or false if not.
 */
func (g *GitObject) isAccessible() bool {
	return g.accessible == nil
}

/*Status prints the current status of the repository, accessible via the structure path field.
 *This method works only if the repository is accessible.
 */
func (g *GitObject) Status() {
	if g.isAccessible() {
		if err := g.printChanges(); err != nil {
			color.RedString("Impossible to get stats from %s, due to error %s", g.path, err)
		}
	} else {
		color.RedString("Repository %s not found!", g.path)
	}
}

/*getDiffWithWT returns the difference between the working tree and the index, for the current git repository.
 *If there is an error processing the request, it returns an error.
 */
func (g *GitObject) getDiffWithWT() (*git.Diff, error) {
	// Get the index of the repository
	currentIndex, err := g.repository.Index()
	if err != nil {
		return nil, err
	}
	// Get the default diff options, and add it custom flags
	defaultDiffOptions, err := git.DefaultDiffOptions()
	if err != nil {
		return nil, err
	}
	defaultDiffOptions.Flags = defaultDiffOptions.Flags | git.DiffIncludeUntracked | git.DiffIncludeTypeChange
	// Check the difference between the working directory and the index
	diff, err := g.repository.DiffIndexToWorkdir(currentIndex, &defaultDiffOptions)
	if err != nil {
		return nil, err
	}
	return diff, nil
}

func (g *GitObject) getCommitsAheadBehind() (int, int, error) {
	repositoryHead, err := g.repository.Head()
	// Check upstream branch head
	cBranch := repositoryHead.Branch()
	cReference, err := cBranch.Upstream()
	if err != nil {
		return -1, -1, err
	}
	cReferenceTarget := cReference.Target()
	cRepositoryTarget := repositoryHead.Target()
	commitsAhead, commitsBehind, err := g.repository.AheadBehind(cRepositoryTarget, cReferenceTarget)
	return commitsAhead, commitsBehind, err
}

/*printChanges prints out all changes for the current git repository.
 *If there is an error processing the request, it returns this one.
 */
func (g *GitObject) printChanges() error {
	diff, err := g.getDiffWithWT()
	var buffer bytes.Buffer
	if err != nil {
		return err
	}

	numDeltas, err := diff.NumDeltas()
	if err != nil {
		return err
	}

	headDetached, err := g.repository.IsHeadDetached()
	if err != nil {
		return err
	}
	if headDetached {
		outputHead := fmt.Sprintf("%s", color.RedString("\t/!\\ The repository's HEAD is detached! /!\\\n"))
		buffer.WriteString(outputHead)
	}

	if numDeltas > 0 {
		buffer.WriteString(fmt.Sprintf("%s %9s\t[%d modification(s)]\n", color.RedString("✘"), g.path, numDeltas))
		for i := 0; i < numDeltas; i++ {
			delta, _ := diff.GetDelta(i)
			currentStatus := delta.Status
			newFile := delta.NewFile.Path
			oldFile := delta.OldFile.Path
			switch currentStatus {
			case git.DeltaAdded:
				buffer.WriteString(fmt.Sprintf("\t===> %s has been added!\n", color.MagentaString(newFile)))
			case git.DeltaDeleted:
				buffer.WriteString(fmt.Sprintf("\t===> %s has been deleted!\n", color.MagentaString(newFile)))
			case git.DeltaModified:
				buffer.WriteString(fmt.Sprintf("\t===> %s has been modified!\n", color.MagentaString(newFile)))
			case git.DeltaRenamed:
				buffer.WriteString(fmt.Sprintf("\t===> %s has been renamed to %s!\n", color.MagentaString(oldFile), color.MagentaString(newFile)))
			case git.DeltaUntracked:
				buffer.WriteString(fmt.Sprintf("\t===> %s is untracked - please to add it or update the gitignore file!\n", color.MagentaString(newFile)))
			case git.DeltaTypeChange:
				buffer.WriteString(fmt.Sprintf("\t===> the type of %s has been changed from %d to %d!", color.MagentaString(newFile), delta.OldFile.Mode, delta.NewFile.Mode))
			}
		}
	} else {
		buffer.WriteString(fmt.Sprintf("%s %s\n", color.GreenString("✔"), g.path))
	}

	commitsAhead, commitsBehind, err := g.getCommitsAheadBehind()
	if err == nil {
		if commitsAhead != 0 {
			buffer.WriteString(fmt.Sprintf("\t%s %d commits AHEAD - Soon, you will need to push your modifications\n", color.RedString("⟳"), commitsAhead))
		}
		if commitsBehind != 0 {
			buffer.WriteString(fmt.Sprintf("\t%s %d commits BEHIND - Soon, you will need to pull the modifications from the remote branch\n", color.RedString("⟲"), commitsBehind))
		}
	}

	fmt.Print(buffer.String())
	return nil
}
<|endoftext|>


<commit_before>package google

import (
	"fmt"
	"os"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	"google.golang.org/api/compute/v1"
)

// Add two key value pairs
func TestAccComputeProjectMetadata_basic(t *testing.T) {
	skipIfEnvNotSet(t,
		[]string{
			"GOOGLE_ORG",
			"GOOGLE_BILLING_ACCOUNT",
		}...,
	)

	billingId := os.Getenv("GOOGLE_BILLING_ACCOUNT")
	var project compute.Project
	pid := "terrafom-test-" + acctest.RandString(10)

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckComputeProjectMetadataDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccComputeProject_basic0_metadata(pid, pname, org, billingId),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckComputeProjectExists(
						"google_compute_project_metadata.fizzbuzz", pid, &project),
					testAccCheckComputeProjectMetadataContains(pid, "banana", "orange"),
					testAccCheckComputeProjectMetadataContains(pid, "sofa", "darwinism"),
					testAccCheckComputeProjectMetadataSize(pid, 2),
				),
			},
		},
	})
}

// Add three key value pairs, then replace one and modify a second
func TestAccComputeProjectMetadata_modify_1(t *testing.T) {
	skipIfEnvNotSet(t,
		[]string{
			"GOOGLE_ORG",
			"GOOGLE_BILLING_ACCOUNT",
		}...,
	)

	billingId := os.Getenv("GOOGLE_BILLING_ACCOUNT")
	var project compute.Project
	pid := "terrafom-test-" + acctest.RandString(10)

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckComputeProjectMetadataDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccComputeProject_modify0_metadata(pid, pname, org, billingId),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckComputeProjectExists(
						"google_compute_project_metadata.fizzbuzz", pid, &project),
					testAccCheckComputeProjectMetadataContains(pid, "paper", "pen"),
					testAccCheckComputeProjectMetadataContains(pid, "genghis_khan", "french bread"),
					testAccCheckComputeProjectMetadataContains(pid, "happy", "smiling"),
					testAccCheckComputeProjectMetadataSize(pid, 3),
				),
			},

			resource.TestStep{
				Config: testAccComputeProject_modify1_metadata(pid, pname, org, billingId),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckComputeProjectExists(
						"google_compute_project_metadata.fizzbuzz", pid, &project),
					testAccCheckComputeProjectMetadataContains(pid, "paper", "pen"),
					testAccCheckComputeProjectMetadataContains(pid, "paris", "french bread"),
					testAccCheckComputeProjectMetadataContains(pid, "happy", "laughing"),
					testAccCheckComputeProjectMetadataSize(pid, 3),
				),
			},
		},
	})
}

// Add two key value pairs, and replace both
func TestAccComputeProjectMetadata_modify_2(t *testing.T) {
	skipIfEnvNotSet(t,
		[]string{
			"GOOGLE_ORG",
			"GOOGLE_BILLING_ACCOUNT",
		}...,
	)

	billingId := os.Getenv("GOOGLE_BILLING_ACCOUNT")
	var project compute.Project
	pid := "terraform-test-" + acctest.RandString(10)

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckComputeProjectMetadataDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccComputeProject_basic0_metadata(pid, pname, org, billingId),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckComputeProjectExists(
						"google_compute_project_metadata.fizzbuzz", pid, &project),
					testAccCheckComputeProjectMetadataContains(pid, "banana", "orange"),
					testAccCheckComputeProjectMetadataContains(pid, "sofa", "darwinism"),
					testAccCheckComputeProjectMetadataSize(pid, 2),
				),
			},

			resource.TestStep{
				Config: testAccComputeProject_basic1_metadata(pid, pname, org, billingId),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckComputeProjectExists(
						"google_compute_project_metadata.fizzbuzz", pid, &project),
					testAccCheckComputeProjectMetadataContains(pid, "kiwi", "papaya"),
					testAccCheckComputeProjectMetadataContains(pid, "finches", "darwinism"),
					testAccCheckComputeProjectMetadataSize(pid, 2),
				),
			},
		},
	})
}

func testAccCheckComputeProjectMetadataDestroy(s *terraform.State) error {
	config := testAccProvider.Meta().(*Config)

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "google_compute_project_metadata" {
			continue
		}

		project, err := config.clientCompute.Projects.Get(rs.Primary.ID).Do()
		if err == nil && len(project.CommonInstanceMetadata.Items) > 0 {
			return fmt.Errorf("Error, metadata items still exist in %s", rs.Primary.ID)
		}
	}

	return nil
}

func testAccCheckComputeProjectExists(n, pid string, project *compute.Project) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

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

		config := testAccProvider.Meta().(*Config)

		found, err := config.clientCompute.Projects.Get(
			pid).Do()
		if err != nil {
			return err
		}

		if "common_metadata" != rs.Primary.ID {
			return fmt.Errorf("Common metadata not found, found %s", rs.Primary.ID)
		}

		*project = *found

		return nil
	}
}

func testAccCheckComputeProjectMetadataContains(pid, key, value string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		config := testAccProvider.Meta().(*Config)
		project, err := config.clientCompute.Projects.Get(pid).Do()
		if err != nil {
			return fmt.Errorf("Error, failed to load project service for %s: %s", config.Project, err)
		}

		for _, kv := range project.CommonInstanceMetadata.Items {
			if kv.Key == key {
				if kv.Value != nil && *kv.Value == value {
					return nil
				} else {
					return fmt.Errorf("Error, key value mismatch, wanted (%s, %s), got (%s, %s)",
						key, value, kv.Key, *kv.Value)
				}
			}
		}

		return fmt.Errorf("Error, key %s not present in %s", key, project.SelfLink)
	}
}

func testAccCheckComputeProjectMetadataSize(pid string, size int) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		config := testAccProvider.Meta().(*Config)
		project, err := config.clientCompute.Projects.Get(pid).Do()
		if err != nil {
			return fmt.Errorf("Error, failed to load project service for %s: %s", config.Project, err)
		}

		if size > len(project.CommonInstanceMetadata.Items) {
			return fmt.Errorf("Error, expected at least %d metadata items, got %d", size,
				len(project.CommonInstanceMetadata.Items))
		}

		return nil
	}
}

func testAccComputeProject_basic0_metadata(pid, name, org, billing string) string {
	return fmt.Sprintf(`
resource "google_project" "project" {
  project_id = "%s"
  name = "%s"
  org_id = "%s"
  billing_account = "%s"
}

resource "google_project_services" "services" {
  project = "${google_project.project.project_id}"
  services = ["compute-component.googleapis.com"]
}

resource "google_compute_project_metadata" "fizzbuzz" {
  project = "${google_project.project.project_id}"
  metadata {
    banana = "orange"
    sofa = "darwinism"
  }
  depends_on = ["google_project_services.services"]
}`, pid, name, org, billing)
}

func testAccComputeProject_basic1_metadata(pid, name, org, billing string) string {
	return fmt.Sprintf(`
resource "google_project" "project" {
  project_id = "%s"
  name = "%s"
  org_id = "%s"
  billing_account = "%s"
}

resource "google_project_services" "services" {
  project = "${google_project.project.project_id}"
  services = ["compute-component.googleapis.com"]
}

resource "google_compute_project_metadata" "fizzbuzz" {
  project = "${google_project.project.project_id}"
  metadata {
    kiwi = "papaya"
    finches = "darwinism"
  }
  depends_on = ["google_project_services.services"]
}`, pid, name, org, billing)
}

func testAccComputeProject_modify0_metadata(pid, name, org, billing string) string {
	return fmt.Sprintf(`
resource "google_project" "project" {
  project_id = "%s"
  name = "%s"
  org_id = "%s"
  billing_account = "%s"
}

resource "google_project_services" "services" {
  project = "${google_project.project.project_id}"
  services = ["compute-component.googleapis.com"]
}

resource "google_compute_project_metadata" "fizzbuzz" {
  project = "${google_project.project.project_id}"
  metadata {
    paper = "pen"
    genghis_khan = "french bread"
    happy = "smiling"
  }
  depends_on = ["google_project_services.services"]
}`, pid, name, org, billing)
}

func testAccComputeProject_modify1_metadata(pid, name, org, billing string) string {
	return fmt.Sprintf(`
resource "google_project" "project" {
  project_id = "%s"
  name = "%s"
  org_id = "%s"
  billing_account = "%s"
}

resource "google_project_services" "services" {
  project = "${google_project.project.project_id}"
  services = ["compute-component.googleapis.com"]
}

resource "google_compute_project_metadata" "fizzbuzz" {
  project = "${google_project.project.project_id}"
  metadata {
    paper = "pen"
    paris = "french bread"
    happy = "laughing"
  }
  depends_on = ["google_project_services.services"]
}`, pid, name, org, billing)
}
<commit_msg>Fix both Radek & Dana's comments.<commit_after>package google

import (
	"fmt"
	"os"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	"google.golang.org/api/compute/v1"
)

// Add two key value pairs
func TestAccComputeProjectMetadata_basic(t *testing.T) {
	skipIfEnvNotSet(t,
		[]string{
			"GOOGLE_ORG",
			"GOOGLE_BILLING_ACCOUNT",
		}...,
	)

	billingId := os.Getenv("GOOGLE_BILLING_ACCOUNT")
	var project compute.Project
	projectID := "terrafom-test-" + acctest.RandString(10)

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckComputeProjectMetadataDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccComputeProject_basic0_metadata(projectID, pname, org, billingId),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckComputeProjectExists(
						"google_compute_project_metadata.fizzbuzz", projectID, &project),
					testAccCheckComputeProjectMetadataContains(projectID, "banana", "orange"),
					testAccCheckComputeProjectMetadataContains(projectID, "sofa", "darwinism"),
					testAccCheckComputeProjectMetadataSize(projectID, 2),
				),
			},
		},
	})
}

// Add three key value pairs, then replace one and modify a second
func TestAccComputeProjectMetadata_modify_1(t *testing.T) {
	skipIfEnvNotSet(t,
		[]string{
			"GOOGLE_ORG",
			"GOOGLE_BILLING_ACCOUNT",
		}...,
	)

	billingId := os.Getenv("GOOGLE_BILLING_ACCOUNT")
	var project compute.Project
	projectID := "terrafom-test-" + acctest.RandString(10)

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckComputeProjectMetadataDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccComputeProject_modify0_metadata(projectID, pname, org, billingId),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckComputeProjectExists(
						"google_compute_project_metadata.fizzbuzz", projectID, &project),
					testAccCheckComputeProjectMetadataContains(projectID, "paper", "pen"),
					testAccCheckComputeProjectMetadataContains(projectID, "genghis_khan", "french bread"),
					testAccCheckComputeProjectMetadataContains(projectID, "happy", "smiling"),
					testAccCheckComputeProjectMetadataSize(projectID, 3),
				),
			},

			resource.TestStep{
				Config: testAccComputeProject_modify1_metadata(projectID, pname, org, billingId),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckComputeProjectExists(
						"google_compute_project_metadata.fizzbuzz", projectID, &project),
					testAccCheckComputeProjectMetadataContains(projectID, "paper", "pen"),
					testAccCheckComputeProjectMetadataContains(projectID, "paris", "french bread"),
					testAccCheckComputeProjectMetadataContains(projectID, "happy", "laughing"),
					testAccCheckComputeProjectMetadataSize(projectID, 3),
				),
			},
		},
	})
}

// Add two key value pairs, and replace both
func TestAccComputeProjectMetadata_modify_2(t *testing.T) {
	skipIfEnvNotSet(t,
		[]string{
			"GOOGLE_ORG",
			"GOOGLE_BILLING_ACCOUNT",
		}...,
	)

	billingId := os.Getenv("GOOGLE_BILLING_ACCOUNT")
	var project compute.Project
	projectID := "terraform-test-" + acctest.RandString(10)

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckComputeProjectMetadataDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccComputeProject_basic0_metadata(projectID, pname, org, billingId),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckComputeProjectExists(
						"google_compute_project_metadata.fizzbuzz", projectID, &project),
					testAccCheckComputeProjectMetadataContains(projectID, "banana", "orange"),
					testAccCheckComputeProjectMetadataContains(projectID, "sofa", "darwinism"),
					testAccCheckComputeProjectMetadataSize(projectID, 2),
				),
			},

			resource.TestStep{
				Config: testAccComputeProject_basic1_metadata(projectID, pname, org, billingId),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckComputeProjectExists(
						"google_compute_project_metadata.fizzbuzz", projectID, &project),
					testAccCheckComputeProjectMetadataContains(projectID, "kiwi", "papaya"),
					testAccCheckComputeProjectMetadataContains(projectID, "finches", "darwinism"),
					testAccCheckComputeProjectMetadataSize(projectID, 2),
				),
			},
		},
	})
}

func testAccCheckComputeProjectMetadataDestroy(s *terraform.State) error {
	config := testAccProvider.Meta().(*Config)

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "google_compute_project_metadata" {
			continue
		}

		project, err := config.clientCompute.Projects.Get(rs.Primary.ID).Do()
		if err == nil && len(project.CommonInstanceMetadata.Items) > 0 {
			return fmt.Errorf("Error, metadata items still exist in %s", rs.Primary.ID)
		}
	}

	return nil
}

func testAccCheckComputeProjectExists(n, projectID string, project *compute.Project) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

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

		config := testAccProvider.Meta().(*Config)

		found, err := config.clientCompute.Projects.Get(projectID).Do()
		if err != nil {
			return err
		}

		if "common_metadata" != rs.Primary.ID {
			return fmt.Errorf("Common metadata not found, found %s", rs.Primary.ID)
		}

		*project = *found

		return nil
	}
}

func testAccCheckComputeProjectMetadataContains(projectID, key, value string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		config := testAccProvider.Meta().(*Config)
		project, err := config.clientCompute.Projects.Get(projectID).Do()
		if err != nil {
			return fmt.Errorf("Error, failed to load project service for %s: %s", config.Project, err)
		}

		for _, kv := range project.CommonInstanceMetadata.Items {
			if kv.Key == key {
				if kv.Value != nil && *kv.Value == value {
					return nil
				} else {
					return fmt.Errorf("Error, key value mismatch, wanted (%s, %s), got (%s, %s)",
						key, value, kv.Key, *kv.Value)
				}
			}
		}

		return fmt.Errorf("Error, key %s not present in %s", key, project.SelfLink)
	}
}

func testAccCheckComputeProjectMetadataSize(projectID string, size int) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		config := testAccProvider.Meta().(*Config)
		project, err := config.clientCompute.Projects.Get(projectID).Do()
		if err != nil {
			return fmt.Errorf("Error, failed to load project service for %s: %s", config.Project, err)
		}

		if size > len(project.CommonInstanceMetadata.Items) {
			return fmt.Errorf("Error, expected at least %d metadata items, got %d", size,
				len(project.CommonInstanceMetadata.Items))
		}

		return nil
	}
}

func testAccComputeProject_basic0_metadata(projectID, name, org, billing string) string {
	return fmt.Sprintf(`
resource "google_project" "project" {
  project_id = "%s"
  name = "%s"
  org_id = "%s"
  billing_account = "%s"
}

resource "google_project_services" "services" {
  project = "${google_project.project.project_id}"
  services = ["compute-component.googleapis.com"]
}

resource "google_compute_project_metadata" "fizzbuzz" {
  project = "${google_project.project.project_id}"
  metadata {
    banana = "orange"
    sofa = "darwinism"
  }
  depends_on = ["google_project_services.services"]
}`, projectID, name, org, billing)
}

func testAccComputeProject_basic1_metadata(projectID, name, org, billing string) string {
	return fmt.Sprintf(`
resource "google_project" "project" {
  project_id = "%s"
  name = "%s"
  org_id = "%s"
  billing_account = "%s"
}

resource "google_project_services" "services" {
  project = "${google_project.project.project_id}"
  services = ["compute-component.googleapis.com"]
}

resource "google_compute_project_metadata" "fizzbuzz" {
  project = "${google_project.project.project_id}"
  metadata {
    kiwi = "papaya"
    finches = "darwinism"
  }
  depends_on = ["google_project_services.services"]
}`, projectID, name, org, billing)
}

func testAccComputeProject_modify0_metadata(projectID, name, org, billing string) string {
	return fmt.Sprintf(`
resource "google_project" "project" {
  project_id = "%s"
  name = "%s"
  org_id = "%s"
  billing_account = "%s"
}

resource "google_project_services" "services" {
  project = "${google_project.project.project_id}"
  services = ["compute-component.googleapis.com"]
}

resource "google_compute_project_metadata" "fizzbuzz" {
  project = "${google_project.project.project_id}"
  metadata {
    paper = "pen"
    genghis_khan = "french bread"
    happy = "smiling"
  }
  depends_on = ["google_project_services.services"]
}`, projectID, name, org, billing)
}

func testAccComputeProject_modify1_metadata(projectID, name, org, billing string) string {
	return fmt.Sprintf(`
resource "google_project" "project" {
  project_id = "%s"
  name = "%s"
  org_id = "%s"
  billing_account = "%s"
}

resource "google_project_services" "services" {
  project = "${google_project.project.project_id}"
  services = ["compute-component.googleapis.com"]
}

resource "google_compute_project_metadata" "fizzbuzz" {
  project = "${google_project.project.project_id}"
  metadata {
    paper = "pen"
    paris = "french bread"
    happy = "laughing"
  }
  depends_on = ["google_project_services.services"]
}`, projectID, name, org, billing)
}
<|endoftext|>