<commit_before>// Copyright 2016 PingCAP, 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"time"

	"github.com/gogo/protobuf/proto"
	. "github.com/pingcap/check"
	raftpb "github.com/pingcap/kvproto/pkg/eraftpb"
	"github.com/pingcap/kvproto/pkg/metapb"
	"github.com/pingcap/kvproto/pkg/pdpb"
)

type testOperator struct {
	RegionID uint64
	Kind     ResourceKind
}

func newTestOperator(regionID uint64, kind ResourceKind) Operator {
	region := newRegionInfo(&metapb.Region{Id: regionID}, nil)
	op := &testOperator{RegionID: regionID, Kind: kind}
	return newRegionOperator(region, op)
}

func (op *testOperator) GetRegionID() uint64           { return op.RegionID }
func (op *testOperator) GetResourceKind() ResourceKind { return op.Kind }
func (op *testOperator) Do(region *regionInfo) (*pdpb.RegionHeartbeatResponse, bool) {
	return nil, false
}

var _ = Suite(&testCoordinatorSuite{})

type testCoordinatorSuite struct{}

func (s *testCoordinatorSuite) TestBasic(c *C) {
	cluster := newClusterInfo(newMockIDAllocator())
	_, opt := newTestScheduleConfig()
	co := newCoordinator(cluster, opt)
	l := co.limiter

	op1 := newTestOperator(1, leaderKind)
	co.addOperator(op1)
	c.Assert(l.operatorCount(op1.GetResourceKind()), Equals, uint64(1))
	c.Assert(co.getOperator(1).GetRegionID(), Equals, op1.GetRegionID())

	// Region 1 already has an operator, cannot add another one.
	op2 := newTestOperator(1, regionKind)
	co.addOperator(op2)
	c.Assert(l.operatorCount(op2.GetResourceKind()), Equals, uint64(0))

	// Remove the operator manually, then we can add a new operator.
	co.removeOperator(op1)
	co.addOperator(op2)
	c.Assert(l.operatorCount(op2.GetResourceKind()), Equals, uint64(1))
	c.Assert(co.getOperator(1).GetRegionID(), Equals, op2.GetRegionID())
}

func (s *testCoordinatorSuite) TestDispatch(c *C) {
	cluster := newClusterInfo(newMockIDAllocator())
	tc := newTestClusterInfo(cluster)

	_, opt := newTestScheduleConfig()
	co := newCoordinator(cluster, opt)
	co.run()
	defer co.stop()

	// Transfer peer from store 4 to store 1.
	tc.addRegionStore(4, 4, 0.4)
	tc.addRegionStore(3, 3, 0.3)
	tc.addRegionStore(2, 2, 0.2)
	tc.addRegionStore(1, 1, 0.1)
	tc.addLeaderRegion(1, 2, 3, 4)

	// Transfer leader from store 4 to store 1.
	tc.updateLeaderCount(4, 4, 10)
	tc.updateLeaderCount(3, 3, 10)
	tc.updateLeaderCount(2, 2, 10)
	tc.updateLeaderCount(1, 1, 10)
	tc.addLeaderRegion(2, 4, 1, 2, 3)

	// Wait for schedule and turn off balance.
	time.Sleep(time.Second)
	co.removeScheduler("balance-leader-scheduler")
	co.removeScheduler("balance-storage-scheduler")
	checkTransferPeer(c, co.getOperator(1), 4, 1)
	checkTransferLeader(c, co.getOperator(2), 4, 1)

	// Transfer peer.
	region := cluster.getRegion(1)
	resp := co.dispatch(region)
	checkAddPeerResp(c, resp, 1)
	region.Peers = append(region.Peers, resp.GetChangePeer().GetPeer())
	cluster.putRegion(region)
	resp = co.dispatch(region)
	checkRemovePeerResp(c, resp, 4)

	tc.addLeaderRegion(1, 1, 2, 3)
	region = cluster.getRegion(1)
	c.Assert(co.dispatch(region), IsNil)
	c.Assert(co.getOperator(region.GetId()), IsNil)

	// Transfer leader.
	region = cluster.getRegion(2)
	resp = co.dispatch(region)
	checkTransferLeaderResp(c, resp, 1)
	region.Leader = resp.GetTransferLeader().GetPeer()
	cluster.putRegion(region)
	resp = co.dispatch(region)
	checkRemovePeerResp(c, resp, 4)

	tc.addLeaderRegion(2, 1, 2, 3)
	region = cluster.getRegion(2)
	c.Assert(co.dispatch(region), IsNil)
	c.Assert(co.getOperator(region.GetId()), IsNil)

	// Test replica checker.
	// Peer in store 3 is down.
	tc.setStoreDown(3)
	tc.addLeaderRegion(4, 1, 2, 3)
	region = cluster.getRegion(4)
	downPeer := &pdpb.PeerStats{
		Peer:        region.GetStorePeer(3),
		DownSeconds: proto.Uint64(24 * 60 * 60),
	}
	region.DownPeers = append(region.DownPeers, downPeer)
	resp = co.dispatch(region)
	checkRemovePeerResp(c, resp, 3)
	region.RemoveStorePeer(3)
	region.DownPeers = nil
	cluster.putRegion(region)
	resp = co.dispatch(region)
	checkAddPeerResp(c, resp, 4)
}

func (s *testCoordinatorSuite) TestPeerState(c *C) {
	cluster := newClusterInfo(newMockIDAllocator())
	tc := newTestClusterInfo(cluster)

	_, opt := newTestScheduleConfig()
	co := newCoordinator(cluster, opt)
	co.run()
	defer co.stop()

	// Transfer peer from store 4 to store 1.
	tc.addRegionStore(4, 4, 0.4)
	tc.addRegionStore(3, 3, 0.3)
	tc.addRegionStore(2, 2, 0.2)
	tc.addRegionStore(1, 1, 0.1)
	tc.addLeaderRegion(1, 2, 3, 4)

	// Wait for schedule.
	time.Sleep(time.Second)
	checkTransferPeer(c, co.getOperator(1), 4, 1)

	region := cluster.getRegion(1)

	// Add new peer.
	resp := co.dispatch(region)
	checkAddPeerResp(c, resp, 1)
	newPeer := resp.GetChangePeer().GetPeer()
	region.Peers = append(region.Peers, newPeer)

	// If the new peer is pending, the operator will not finish.
	region.PendingPeers = append(region.PendingPeers, newPeer)
	c.Assert(co.dispatch(region), IsNil)
	c.Assert(co.getOperator(region.GetId()), NotNil)

	// The new peer is not pending now, the operator will finish.
	// And we will proceed to remove peer in store 4.
	region.PendingPeers = nil
	resp = co.dispatch(region)
	checkRemovePeerResp(c, resp, 4)
	tc.addLeaderRegion(1, 1, 2, 3)
	region = cluster.getRegion(1)
	c.Assert(co.dispatch(region), IsNil)
}

func (s *testCoordinatorSuite) TestAddScheduler(c *C) {
	cluster := newClusterInfo(newMockIDAllocator())
	tc := newTestClusterInfo(cluster)

	cfg, opt := newTestScheduleConfig()
	cfg.ReplicaScheduleLimit = 0

	co := newCoordinator(cluster, opt)
	co.run()
	defer co.stop()

	c.Assert(co.schedulers, HasLen, 2)
	c.Assert(co.removeScheduler("balance-leader-scheduler"), IsTrue)
	c.Assert(co.removeScheduler("balance-storage-scheduler"), IsTrue)
	c.Assert(co.schedulers, HasLen, 0)

	// Add stores 1,2,3
	tc.addLeaderStore(1, 1, 1)
	tc.addLeaderStore(2, 1, 1)
	tc.addLeaderStore(3, 1, 1)
	// Add regions 1 with leader in store 1 and followers in stores 2,3
	tc.addLeaderRegion(1, 1, 2, 3)
	// Add regions 2 with leader in store 2 and followers in stores 1,3
	tc.addLeaderRegion(2, 2, 1, 3)
	// Add regions 3 with leader in store 3 and followers in stores 1,2
	tc.addLeaderRegion(3, 3, 1, 2)

	gls := newGrantLeaderScheduler(opt, 1)
	c.Assert(co.removeScheduler(gls.GetName()), IsFalse)
	c.Assert(co.addScheduler(gls), IsTrue)

	// Transfer all leaders to store 1.
	time.Sleep(time.Second)
	region2 := cluster.getRegion(2)
	checkTransferLeaderResp(c, co.dispatch(region2), 1)
	region2.Leader = region2.GetStorePeer(1)
	cluster.putRegion(region2)
	c.Assert(co.dispatch(region2), IsNil)

	time.Sleep(time.Second)
	region3 := cluster.getRegion(3)
	checkTransferLeaderResp(c, co.dispatch(region3), 1)
	region3.Leader = region3.GetStorePeer(1)
	cluster.putRegion(region3)
	c.Assert(co.dispatch(region3), IsNil)
}

var _ = Suite(&testScheduleLimiterSuite{})

type testScheduleLimiterSuite struct{}

func (s *testScheduleLimiterSuite) TestOperatorCount(c *C) {
	l := newScheduleLimiter()
	c.Assert(l.operatorCount(leaderKind), Equals, uint64(0))
	c.Assert(l.operatorCount(regionKind), Equals, uint64(0))

	leaderOP := newTestOperator(1, leaderKind)
	l.addOperator(leaderOP)
	c.Assert(l.operatorCount(leaderKind), Equals, uint64(1))
	l.addOperator(leaderOP)
	c.Assert(l.operatorCount(leaderKind), Equals, uint64(2))
	l.removeOperator(leaderOP)
	c.Assert(l.operatorCount(leaderKind), Equals, uint64(1))

	regionOP := newTestOperator(1, regionKind)
	l.addOperator(regionOP)
	c.Assert(l.operatorCount(regionKind), Equals, uint64(1))
	l.addOperator(regionOP)
	c.Assert(l.operatorCount(regionKind), Equals, uint64(2))
	l.removeOperator(regionOP)
	c.Assert(l.operatorCount(regionKind), Equals, uint64(1))
}

var _ = Suite(&testScheduleControllerSuite{})

type testScheduleControllerSuite struct{}

func (s *testScheduleControllerSuite) TestController(c *C) {
	cluster := newClusterInfo(newMockIDAllocator())
	cfg, opt := newTestScheduleConfig()
	cfg.ScheduleInterval.Duration = time.Minute
	co := newCoordinator(cluster, opt)
	sc := newScheduleController(co, newBalanceLeaderScheduler(opt))

	cfg.LeaderScheduleLimit = 0
	c.Assert(sc.GetInterval(), Equals, time.Minute)

	cfg.LeaderScheduleLimit = 2
	c.Assert(sc.GetInterval(), Equals, time.Second*30)

	// limit = 2
	op := newTestOperator(1, leaderKind)
	// count = 0
	c.Assert(sc.AllowSchedule(), IsTrue)
	sc.limiter.addOperator(op)
	// count = 1
	c.Assert(sc.AllowSchedule(), IsTrue)
	sc.limiter.addOperator(op)
	// count = 2
	c.Assert(sc.AllowSchedule(), IsFalse)
	sc.limiter.removeOperator(op)
	// count = 1
	c.Assert(sc.AllowSchedule(), IsTrue)
}

func checkAddPeerResp(c *C, resp *pdpb.RegionHeartbeatResponse, storeID uint64) {
	changePeer := resp.GetChangePeer()
	c.Assert(changePeer.GetChangeType(), Equals, raftpb.ConfChangeType_AddNode)
	c.Assert(changePeer.GetPeer().GetStoreId(), Equals, storeID)
}

func checkRemovePeerResp(c *C, resp *pdpb.RegionHeartbeatResponse, storeID uint64) {
	changePeer := resp.GetChangePeer()
	c.Assert(changePeer.GetChangeType(), Equals, raftpb.ConfChangeType_RemoveNode)
	c.Assert(changePeer.GetPeer().GetStoreId(), Equals, storeID)
}

func checkTransferLeaderResp(c *C, resp *pdpb.RegionHeartbeatResponse, storeID uint64) {
	c.Assert(resp.GetTransferLeader().GetPeer().GetStoreId(), Equals, storeID)
}

func checkTransferPeerResp(c *C, resp *pdpb.RegionHeartbeatResponse, sourceID, targetID uint64) {
	checkAddPeerResp(c, resp, targetID)
	checkRemovePeerResp(c, resp, sourceID)
}
<commit_msg>server/coordinator: fix some tests (#519)<commit_after>// Copyright 2016 PingCAP, 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"time"

	"github.com/gogo/protobuf/proto"
	. "github.com/pingcap/check"
	raftpb "github.com/pingcap/kvproto/pkg/eraftpb"
	"github.com/pingcap/kvproto/pkg/metapb"
	"github.com/pingcap/kvproto/pkg/pdpb"
)

type testOperator struct {
	RegionID uint64
	Kind     ResourceKind
}

func newTestOperator(regionID uint64, kind ResourceKind) Operator {
	region := newRegionInfo(&metapb.Region{Id: regionID}, nil)
	op := &testOperator{RegionID: regionID, Kind: kind}
	return newRegionOperator(region, op)
}

func (op *testOperator) GetRegionID() uint64           { return op.RegionID }
func (op *testOperator) GetResourceKind() ResourceKind { return op.Kind }
func (op *testOperator) Do(region *regionInfo) (*pdpb.RegionHeartbeatResponse, bool) {
	return nil, false
}

var _ = Suite(&testCoordinatorSuite{})

type testCoordinatorSuite struct{}

func (s *testCoordinatorSuite) TestBasic(c *C) {
	cluster := newClusterInfo(newMockIDAllocator())
	_, opt := newTestScheduleConfig()
	co := newCoordinator(cluster, opt)
	l := co.limiter

	op1 := newTestOperator(1, leaderKind)
	co.addOperator(op1)
	c.Assert(l.operatorCount(op1.GetResourceKind()), Equals, uint64(1))
	c.Assert(co.getOperator(1).GetRegionID(), Equals, op1.GetRegionID())

	// Region 1 already has an operator, cannot add another one.
	op2 := newTestOperator(1, regionKind)
	co.addOperator(op2)
	c.Assert(l.operatorCount(op2.GetResourceKind()), Equals, uint64(0))

	// Remove the operator manually, then we can add a new operator.
	co.removeOperator(op1)
	co.addOperator(op2)
	c.Assert(l.operatorCount(op2.GetResourceKind()), Equals, uint64(1))
	c.Assert(co.getOperator(1).GetRegionID(), Equals, op2.GetRegionID())
}

func (s *testCoordinatorSuite) TestDispatch(c *C) {
	cluster := newClusterInfo(newMockIDAllocator())
	tc := newTestClusterInfo(cluster)

	_, opt := newTestScheduleConfig()
	co := newCoordinator(cluster, opt)
	co.run()
	defer co.stop()

	// Transfer peer from store 4 to store 1.
	tc.addRegionStore(4, 4, 0.4)
	tc.addRegionStore(3, 3, 0.3)
	tc.addRegionStore(2, 2, 0.2)
	tc.addRegionStore(1, 1, 0.1)
	tc.addLeaderRegion(1, 2, 3, 4)

	// Transfer leader from store 4 to store 2.
	tc.updateLeaderCount(4, 4, 10)
	tc.updateLeaderCount(3, 3, 10)
	tc.updateLeaderCount(2, 2, 10)
	tc.updateLeaderCount(1, 1, 10)
	tc.addLeaderRegion(2, 4, 3, 2)

	// Wait for schedule and turn off balance.
	time.Sleep(time.Second)
	checkTransferPeer(c, co.getOperator(1), 4, 1)
	checkTransferLeader(c, co.getOperator(2), 4, 2)

	// Transfer peer.
	region := cluster.getRegion(1)
	resp := co.dispatch(region)
	checkAddPeerResp(c, resp, 1)
	region.Peers = append(region.Peers, resp.GetChangePeer().GetPeer())
	cluster.putRegion(region)
	resp = co.dispatch(region)
	checkRemovePeerResp(c, resp, 4)
	region.RemoveStorePeer(4)
	cluster.putRegion(region)
	c.Assert(co.dispatch(region), IsNil)
	c.Assert(co.getOperator(region.GetId()), IsNil)

	// Transfer leader.
	region = cluster.getRegion(2)
	resp = co.dispatch(region)
	checkTransferLeaderResp(c, resp, 2)
	region.Leader = resp.GetTransferLeader().GetPeer()
	cluster.putRegion(region)
	c.Assert(co.dispatch(region), IsNil)
}

func (s *testCoordinatorSuite) TestReplica(c *C) {
	cluster := newClusterInfo(newMockIDAllocator())
	tc := newTestClusterInfo(cluster)

	// Turn off balance.
	cfg, opt := newTestScheduleConfig()
	cfg.LeaderScheduleLimit = 0
	cfg.RegionScheduleLimit = 0

	co := newCoordinator(cluster, opt)
	co.run()
	defer co.stop()

	tc.addRegionStore(1, 1, 0.1)
	tc.addRegionStore(2, 2, 0.2)
	tc.addRegionStore(3, 3, 0.3)
	tc.addRegionStore(4, 4, 0.4)

	// Add peer to store 1.
	tc.addLeaderRegion(1, 2, 3)
	region := cluster.getRegion(1)
	resp := co.dispatch(region)
	checkAddPeerResp(c, resp, 1)
	region.Peers = append(region.Peers, resp.GetChangePeer().GetPeer())
	c.Assert(co.dispatch(region), IsNil)

	// Peer in store 3 is down, remove peer in store 3 and add peer to store 4.
	tc.setStoreDown(3)
	downPeer := &pdpb.PeerStats{
		Peer:        region.GetStorePeer(3),
		DownSeconds: proto.Uint64(24 * 60 * 60),
	}
	region.DownPeers = append(region.DownPeers, downPeer)
	resp = co.dispatch(region)
	checkRemovePeerResp(c, resp, 3)
	region.RemoveStorePeer(3)
	region.DownPeers = nil
	resp = co.dispatch(region)
	checkAddPeerResp(c, resp, 4)
	region.Peers = append(region.Peers, resp.GetChangePeer().GetPeer())
	c.Assert(co.dispatch(region), IsNil)

	// Remove peer from store 4.
	tc.addLeaderRegion(2, 1, 2, 3, 4)
	region = cluster.getRegion(2)
	resp = co.dispatch(region)
	checkRemovePeerResp(c, resp, 4)
	region.RemoveStorePeer(4)
	c.Assert(co.dispatch(region), IsNil)
}

func (s *testCoordinatorSuite) TestPeerState(c *C) {
	cluster := newClusterInfo(newMockIDAllocator())
	tc := newTestClusterInfo(cluster)

	_, opt := newTestScheduleConfig()
	co := newCoordinator(cluster, opt)
	co.run()
	defer co.stop()

	// Transfer peer from store 4 to store 1.
	tc.addRegionStore(4, 4, 0.4)
	tc.addRegionStore(3, 3, 0.3)
	tc.addRegionStore(2, 2, 0.2)
	tc.addRegionStore(1, 1, 0.1)
	tc.addLeaderRegion(1, 2, 3, 4)

	// Wait for schedule.
	time.Sleep(time.Second)
	checkTransferPeer(c, co.getOperator(1), 4, 1)

	region := cluster.getRegion(1)

	// Add new peer.
	resp := co.dispatch(region)
	checkAddPeerResp(c, resp, 1)
	newPeer := resp.GetChangePeer().GetPeer()
	region.Peers = append(region.Peers, newPeer)

	// If the new peer is pending, the operator will not finish.
	region.PendingPeers = append(region.PendingPeers, newPeer)
	c.Assert(co.dispatch(region), IsNil)
	c.Assert(co.getOperator(region.GetId()), NotNil)

	// The new peer is not pending now, the operator will finish.
	// And we will proceed to remove peer in store 4.
	region.PendingPeers = nil
	resp = co.dispatch(region)
	checkRemovePeerResp(c, resp, 4)
	tc.addLeaderRegion(1, 1, 2, 3)
	region = cluster.getRegion(1)
	c.Assert(co.dispatch(region), IsNil)
}

func (s *testCoordinatorSuite) TestAddScheduler(c *C) {
	cluster := newClusterInfo(newMockIDAllocator())
	tc := newTestClusterInfo(cluster)

	cfg, opt := newTestScheduleConfig()
	cfg.ReplicaScheduleLimit = 0

	co := newCoordinator(cluster, opt)
	co.run()
	defer co.stop()

	c.Assert(co.schedulers, HasLen, 2)
	c.Assert(co.removeScheduler("balance-leader-scheduler"), IsTrue)
	c.Assert(co.removeScheduler("balance-storage-scheduler"), IsTrue)
	c.Assert(co.schedulers, HasLen, 0)

	// Add stores 1,2,3
	tc.addLeaderStore(1, 1, 1)
	tc.addLeaderStore(2, 1, 1)
	tc.addLeaderStore(3, 1, 1)
	// Add regions 1 with leader in store 1 and followers in stores 2,3
	tc.addLeaderRegion(1, 1, 2, 3)
	// Add regions 2 with leader in store 2 and followers in stores 1,3
	tc.addLeaderRegion(2, 2, 1, 3)
	// Add regions 3 with leader in store 3 and followers in stores 1,2
	tc.addLeaderRegion(3, 3, 1, 2)

	gls := newGrantLeaderScheduler(opt, 1)
	c.Assert(co.removeScheduler(gls.GetName()), IsFalse)
	c.Assert(co.addScheduler(gls), IsTrue)

	// Transfer all leaders to store 1.
	time.Sleep(time.Second)
	region2 := cluster.getRegion(2)
	checkTransferLeaderResp(c, co.dispatch(region2), 1)
	region2.Leader = region2.GetStorePeer(1)
	cluster.putRegion(region2)
	c.Assert(co.dispatch(region2), IsNil)

	time.Sleep(time.Second)
	region3 := cluster.getRegion(3)
	checkTransferLeaderResp(c, co.dispatch(region3), 1)
	region3.Leader = region3.GetStorePeer(1)
	cluster.putRegion(region3)
	c.Assert(co.dispatch(region3), IsNil)
}

var _ = Suite(&testScheduleLimiterSuite{})

type testScheduleLimiterSuite struct{}

func (s *testScheduleLimiterSuite) TestOperatorCount(c *C) {
	l := newScheduleLimiter()
	c.Assert(l.operatorCount(leaderKind), Equals, uint64(0))
	c.Assert(l.operatorCount(regionKind), Equals, uint64(0))

	leaderOP := newTestOperator(1, leaderKind)
	l.addOperator(leaderOP)
	c.Assert(l.operatorCount(leaderKind), Equals, uint64(1))
	l.addOperator(leaderOP)
	c.Assert(l.operatorCount(leaderKind), Equals, uint64(2))
	l.removeOperator(leaderOP)
	c.Assert(l.operatorCount(leaderKind), Equals, uint64(1))

	regionOP := newTestOperator(1, regionKind)
	l.addOperator(regionOP)
	c.Assert(l.operatorCount(regionKind), Equals, uint64(1))
	l.addOperator(regionOP)
	c.Assert(l.operatorCount(regionKind), Equals, uint64(2))
	l.removeOperator(regionOP)
	c.Assert(l.operatorCount(regionKind), Equals, uint64(1))
}

var _ = Suite(&testScheduleControllerSuite{})

type testScheduleControllerSuite struct{}

func (s *testScheduleControllerSuite) TestController(c *C) {
	cluster := newClusterInfo(newMockIDAllocator())
	cfg, opt := newTestScheduleConfig()
	cfg.ScheduleInterval.Duration = time.Minute
	co := newCoordinator(cluster, opt)
	sc := newScheduleController(co, newBalanceLeaderScheduler(opt))

	cfg.LeaderScheduleLimit = 0
	c.Assert(sc.GetInterval(), Equals, time.Minute)

	cfg.LeaderScheduleLimit = 2
	c.Assert(sc.GetInterval(), Equals, time.Second*30)

	// limit = 2
	op := newTestOperator(1, leaderKind)
	// count = 0
	c.Assert(sc.AllowSchedule(), IsTrue)
	sc.limiter.addOperator(op)
	// count = 1
	c.Assert(sc.AllowSchedule(), IsTrue)
	sc.limiter.addOperator(op)
	// count = 2
	c.Assert(sc.AllowSchedule(), IsFalse)
	sc.limiter.removeOperator(op)
	// count = 1
	c.Assert(sc.AllowSchedule(), IsTrue)
}

func checkAddPeerResp(c *C, resp *pdpb.RegionHeartbeatResponse, storeID uint64) {
	changePeer := resp.GetChangePeer()
	c.Assert(changePeer.GetChangeType(), Equals, raftpb.ConfChangeType_AddNode)
	c.Assert(changePeer.GetPeer().GetStoreId(), Equals, storeID)
}

func checkRemovePeerResp(c *C, resp *pdpb.RegionHeartbeatResponse, storeID uint64) {
	changePeer := resp.GetChangePeer()
	c.Assert(changePeer.GetChangeType(), Equals, raftpb.ConfChangeType_RemoveNode)
	c.Assert(changePeer.GetPeer().GetStoreId(), Equals, storeID)
}

func checkTransferLeaderResp(c *C, resp *pdpb.RegionHeartbeatResponse, storeID uint64) {
	c.Assert(resp.GetTransferLeader().GetPeer().GetStoreId(), Equals, storeID)
}

func checkTransferPeerResp(c *C, resp *pdpb.RegionHeartbeatResponse, sourceID, targetID uint64) {
	checkAddPeerResp(c, resp, targetID)
	checkRemovePeerResp(c, resp, sourceID)
}
<|endoftext|>


<commit_before>// +build linux,integration

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

Copyright 2015 Intel Corporation

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.
*/

/*
# testing
go test -v github.com/intelsdi-x/pulse-plugin-collector-facter/facter
*/
package facter

import (
	"errors"
	"strings"
	"testing"
	"time"

	"github.com/intelsdi-x/pulse/control/plugin"
	. "github.com/smartystreets/goconvey/convey"
)

// fact expected to be available on every system
// can be allways received from Facter for test purposes
const someFact = "kernel"
const someValue = "linux 1234"

var existingNamespace = []string{vendor, prefix, someFact}

func TestFacterCollectMetrics(t *testing.T) {
	Convey("TestFacterCollect tests", t, func() {

		f := NewFacterCollector()
		// always return at least one metric
		f.getFacts = func(_ []string, _ time.Duration, _ *cmdConfig) (facts, error) {
			return facts{someFact: someValue}, nil
		}

		Convey("asked for nothgin returns nothing", func() {
			metricTypes := []plugin.PluginMetricType{}
			metrics, err := f.CollectMetrics(metricTypes)
			So(err, ShouldBeNil)
			So(metrics, ShouldBeEmpty)
		})

		Convey("asked for somehting returns something", func() {
			metricTypes := []plugin.PluginMetricType{
				plugin.PluginMetricType{
					Namespace_: existingNamespace,
				},
			}
			metrics, err := f.CollectMetrics(metricTypes)
			So(err, ShouldBeNil)
			So(metrics, ShouldNotBeEmpty)
			So(len(metrics), ShouldEqual, 1)

			// check just one metric
			metric := metrics[0]
			So(metric.Namespace()[2], ShouldResemble, someFact)
			So(metric.Data().(string), ShouldEqual, someValue)
		})

		Convey("ask for inappriopriate metrics", func() {
			Convey("wrong number of parts", func() {
				_, err := f.CollectMetrics(
					[]plugin.PluginMetricType{
						plugin.PluginMetricType{
							Namespace_: []string{"where are my other parts"},
						},
					},
				)
				So(err, ShouldNotBeNil)
				So(err.Error(), ShouldContainSubstring, "segments")
			})

			Convey("wrong vendor", func() {
				_, err := f.CollectMetrics(
					[]plugin.PluginMetricType{
						plugin.PluginMetricType{
							Namespace_: []string{"nonintelvendor", prefix, someFact},
						},
					},
				)
				So(err, ShouldNotBeNil)
				So(err.Error(), ShouldContainSubstring, "expected vendor")
			})

			Convey("wrong prefix", func() {
				_, err := f.CollectMetrics(
					[]plugin.PluginMetricType{
						plugin.PluginMetricType{
							Namespace_: []string{vendor, "this is wrong prefix", someFact},
						},
					},
				)
				So(err, ShouldNotBeNil)
				So(err.Error(), ShouldContainSubstring, "expected prefix")
			})

		})
	})
}

func TestFacterInvalidBehavior(t *testing.T) {

	Convey("returns errors as expected when cmd isn't working", t, func() {
		f := NewFacterCollector()
		// mock that getFacts returns error every time
		f.getFacts = func(_ []string, _ time.Duration, _ *cmdConfig) (facts, error) {
			return nil, errors.New("dummy error")
		}

		_, err := f.CollectMetrics([]plugin.PluginMetricType{
			plugin.PluginMetricType{
				Namespace_: existingNamespace,
			},
		},
		)
		So(err, ShouldNotBeNil)

		_, err = f.GetMetricTypes()
		So(err, ShouldNotBeNil)
	})
	Convey("returns not as much values as asked", t, func() {

		f := NewFacterCollector()
		// mock that getFacts returns error every time
		//returns zero elements even when asked for one
		f.getFacts = func(_ []string, _ time.Duration, _ *cmdConfig) (facts, error) {
			return nil, nil
		}

		_, err := f.CollectMetrics([]plugin.PluginMetricType{
			plugin.PluginMetricType{
				Namespace_: existingNamespace,
			},
		},
		)
		So(err, ShouldNotBeNil)
		So(err.Error(), ShouldContainSubstring, "more/less")
	})

}

func TestFacterGetMetricsTypes(t *testing.T) {

	Convey("GetMetricTypes functionallity", t, func() {

		f := NewFacterCollector()

		Convey("GetMetricsTypes returns no error", func() {
			// exectues without error
			metricTypes, err := f.GetMetricTypes()
			So(err, ShouldBeNil)
			Convey("metricTypesReply should contain more than zero metrics", func() {
				So(metricTypes, ShouldNotBeEmpty)
			})

			Convey("at least one metric contains metric namespace \"intel/facter/kernel\"", func() {

				expectedNamespaceStr := strings.Join(existingNamespace, "/")

				found := false
				for _, metricType := range metricTypes {
					// join because we cannot compare slices
					if strings.Join(metricType.Namespace(), "/") == expectedNamespaceStr {
						found = true
						break
					}
				}
				if !found {
					t.Error("It was expected to find at least on intel/facter/kernel metricType (but it wasn't there)")
				}
			})
		})
	})
}
<commit_msg>Update integration tests that call GetMetricTypes()<commit_after>// +build linux,integration

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

Copyright 2015 Intel Corporation

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.
*/

/*
# testing
go test -v github.com/intelsdi-x/pulse-plugin-collector-facter/facter
*/
package facter

import (
	"errors"
	"strings"
	"testing"
	"time"

	"github.com/intelsdi-x/pulse/control/plugin"
	. "github.com/smartystreets/goconvey/convey"
)

// fact expected to be available on every system
// can be allways received from Facter for test purposes
const someFact = "kernel"
const someValue = "linux 1234"

var existingNamespace = []string{vendor, prefix, someFact}

func TestFacterCollectMetrics(t *testing.T) {
	Convey("TestFacterCollect tests", t, func() {

		f := NewFacterCollector()
		// always return at least one metric
		f.getFacts = func(_ []string, _ time.Duration, _ *cmdConfig) (facts, error) {
			return facts{someFact: someValue}, nil
		}

		Convey("asked for nothgin returns nothing", func() {
			metricTypes := []plugin.PluginMetricType{}
			metrics, err := f.CollectMetrics(metricTypes)
			So(err, ShouldBeNil)
			So(metrics, ShouldBeEmpty)
		})

		Convey("asked for somehting returns something", func() {
			metricTypes := []plugin.PluginMetricType{
				plugin.PluginMetricType{
					Namespace_: existingNamespace,
				},
			}
			metrics, err := f.CollectMetrics(metricTypes)
			So(err, ShouldBeNil)
			So(metrics, ShouldNotBeEmpty)
			So(len(metrics), ShouldEqual, 1)

			// check just one metric
			metric := metrics[0]
			So(metric.Namespace()[2], ShouldResemble, someFact)
			So(metric.Data().(string), ShouldEqual, someValue)
		})

		Convey("ask for inappriopriate metrics", func() {
			Convey("wrong number of parts", func() {
				_, err := f.CollectMetrics(
					[]plugin.PluginMetricType{
						plugin.PluginMetricType{
							Namespace_: []string{"where are my other parts"},
						},
					},
				)
				So(err, ShouldNotBeNil)
				So(err.Error(), ShouldContainSubstring, "segments")
			})

			Convey("wrong vendor", func() {
				_, err := f.CollectMetrics(
					[]plugin.PluginMetricType{
						plugin.PluginMetricType{
							Namespace_: []string{"nonintelvendor", prefix, someFact},
						},
					},
				)
				So(err, ShouldNotBeNil)
				So(err.Error(), ShouldContainSubstring, "expected vendor")
			})

			Convey("wrong prefix", func() {
				_, err := f.CollectMetrics(
					[]plugin.PluginMetricType{
						plugin.PluginMetricType{
							Namespace_: []string{vendor, "this is wrong prefix", someFact},
						},
					},
				)
				So(err, ShouldNotBeNil)
				So(err.Error(), ShouldContainSubstring, "expected prefix")
			})

		})
	})
}

func TestFacterInvalidBehavior(t *testing.T) {

	Convey("returns errors as expected when cmd isn't working", t, func() {
		f := NewFacterCollector()
		// mock that getFacts returns error every time
		f.getFacts = func(_ []string, _ time.Duration, _ *cmdConfig) (facts, error) {
			return nil, errors.New("dummy error")
		}

		_, err := f.CollectMetrics([]plugin.PluginMetricType{
			plugin.PluginMetricType{
				Namespace_: existingNamespace,
			},
		},
		)
		So(err, ShouldNotBeNil)

		_, err = f.GetMetricTypes(plugin.PluginConfigType{})
		So(err, ShouldNotBeNil)
	})
	Convey("returns not as much values as asked", t, func() {

		f := NewFacterCollector()
		// mock that getFacts returns error every time
		//returns zero elements even when asked for one
		f.getFacts = func(_ []string, _ time.Duration, _ *cmdConfig) (facts, error) {
			return nil, nil
		}

		_, err := f.CollectMetrics([]plugin.PluginMetricType{
			plugin.PluginMetricType{
				Namespace_: existingNamespace,
			},
		},
		)
		So(err, ShouldNotBeNil)
		So(err.Error(), ShouldContainSubstring, "more/less")
	})

}

func TestFacterGetMetricsTypes(t *testing.T) {

	Convey("GetMetricTypes functionallity", t, func() {

		f := NewFacterCollector()

		Convey("GetMetricsTypes returns no error", func() {
			// exectues without error
			metricTypes, err := f.GetMetricTypes(plugin.PluginConfigType{})
			So(err, ShouldBeNil)
			Convey("metricTypesReply should contain more than zero metrics", func() {
				So(metricTypes, ShouldNotBeEmpty)
			})

			Convey("at least one metric contains metric namespace \"intel/facter/kernel\"", func() {

				expectedNamespaceStr := strings.Join(existingNamespace, "/")

				found := false
				for _, metricType := range metricTypes {
					// join because we cannot compare slices
					if strings.Join(metricType.Namespace(), "/") == expectedNamespaceStr {
						found = true
						break
					}
				}
				if !found {
					t.Error("It was expected to find at least on intel/facter/kernel metricType (but it wasn't there)")
				}
			})
		})
	})
}
<|endoftext|>


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

package monitor

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/golang/protobuf/jsonpb"

	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/common/lhttp"
	"go.chromium.org/luci/common/logging"
	pb "go.chromium.org/luci/common/tsmon/ts_mon_proto"
	"go.chromium.org/luci/common/tsmon/types"
)

var (
	// ProdxmonScopes is the list of oauth2 scopes needed on the http client
	// given to NewHTTPMonitor.
	ProdxmonScopes = []string{"https://www.googleapis.com/auth/prodxmon"}
)

type httpMonitor struct {
	client   *http.Client
	endpoint *url.URL
}

// NewHTTPMonitor creates a new Monitor object that sends metric to an HTTP
// (or HTTPS) endpoint.  The http client should be authenticated as required.
func NewHTTPMonitor(ctx context.Context, client *http.Client, endpoint *url.URL) (Monitor, error) {
	return &httpMonitor{
		client:   client,
		endpoint: endpoint,
	}, nil
}

func (m *httpMonitor) ChunkSize() int {
	return 500
}

func (m *httpMonitor) Send(ctx context.Context, cells []types.Cell) error {
	// Serialize the tsmon cells into protobufs.
	req := &pb.Request{
		Payload: &pb.MetricsPayload{
			MetricsCollection: SerializeCells(cells, clock.Now(ctx)),
		},
	}

	// JSON encode the request.
	encoded := bytes.Buffer{}
	marshaller := jsonpb.Marshaler{}
	if err := marshaller.Marshal(&encoded, req); err != nil {
		return err
	}

	// Make the request.
	status, err := lhttp.NewRequest(ctx, m.client, nil, func() (*http.Request, error) {
		req, err := http.NewRequest("POST", m.endpoint.String(), bytes.NewReader(encoded.Bytes()))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
		return req, nil
	}, func(resp *http.Response) error {
		return resp.Body.Close()
	}, func(resp *http.Response, oErr error) error {
		if resp != nil {
			body, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				logging.WithError(err).Errorf(ctx, "Failed to read error response body")
			} else {
				logging.Errorf(
					ctx, "Monitoring push failed.\nResponse body: %s\nRequest body: %s",
					body, encoded.Bytes())
			}
			resp.Body.Close()
		}
		// On HTTP 429 response (Too many requests) oErr is marked as transient and
		// returning it causes a retry. We don't want to do that. HTTP 429 is
		// received if timestamps in the request body indicate that the sampling
		// period is smaller than the configured retention period. Resending the
		// exact same body with exact same timestamps won't help. Return a fatal
		// error instead.
		if resp != nil && resp.StatusCode == 429 {
			return fmt.Errorf("giving up on HTTP 249 status")
		}
		return oErr
	})()
	if err != nil {
		return err
	}
	if status != http.StatusOK {
		return fmt.Errorf("bad response status %d from endpoint %s", status, m.endpoint)
	}

	logging.Debugf(ctx, "Sent %d tsmon cells to %s", len(cells), m.endpoint)
	return nil
}

func (m *httpMonitor) Close() error {
	return nil
}
<commit_msg>[ts_mon] Fix a typo<commit_after>// Copyright 2016 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package monitor

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/golang/protobuf/jsonpb"

	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/common/lhttp"
	"go.chromium.org/luci/common/logging"
	pb "go.chromium.org/luci/common/tsmon/ts_mon_proto"
	"go.chromium.org/luci/common/tsmon/types"
)

var (
	// ProdxmonScopes is the list of oauth2 scopes needed on the http client
	// given to NewHTTPMonitor.
	ProdxmonScopes = []string{"https://www.googleapis.com/auth/prodxmon"}
)

type httpMonitor struct {
	client   *http.Client
	endpoint *url.URL
}

// NewHTTPMonitor creates a new Monitor object that sends metric to an HTTP
// (or HTTPS) endpoint.  The http client should be authenticated as required.
func NewHTTPMonitor(ctx context.Context, client *http.Client, endpoint *url.URL) (Monitor, error) {
	return &httpMonitor{
		client:   client,
		endpoint: endpoint,
	}, nil
}

func (m *httpMonitor) ChunkSize() int {
	return 500
}

func (m *httpMonitor) Send(ctx context.Context, cells []types.Cell) error {
	// Serialize the tsmon cells into protobufs.
	req := &pb.Request{
		Payload: &pb.MetricsPayload{
			MetricsCollection: SerializeCells(cells, clock.Now(ctx)),
		},
	}

	// JSON encode the request.
	encoded := bytes.Buffer{}
	marshaller := jsonpb.Marshaler{}
	if err := marshaller.Marshal(&encoded, req); err != nil {
		return err
	}

	// Make the request.
	status, err := lhttp.NewRequest(ctx, m.client, nil, func() (*http.Request, error) {
		req, err := http.NewRequest("POST", m.endpoint.String(), bytes.NewReader(encoded.Bytes()))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
		return req, nil
	}, func(resp *http.Response) error {
		return resp.Body.Close()
	}, func(resp *http.Response, oErr error) error {
		if resp != nil {
			body, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				logging.WithError(err).Errorf(ctx, "Failed to read error response body")
			} else {
				logging.Errorf(
					ctx, "Monitoring push failed.\nResponse body: %s\nRequest body: %s",
					body, encoded.Bytes())
			}
			resp.Body.Close()
		}
		// On HTTP 429 response (Too many requests) oErr is marked as transient and
		// returning it causes a retry. We don't want to do that. HTTP 429 is
		// received if timestamps in the request body indicate that the sampling
		// period is smaller than the configured retention period. Resending the
		// exact same body with exact same timestamps won't help. Return a fatal
		// error instead.
		if resp != nil && resp.StatusCode == 429 {
			return fmt.Errorf("giving up on HTTP 429 status")
		}
		return oErr
	})()
	if err != nil {
		return err
	}
	if status != http.StatusOK {
		return fmt.Errorf("bad response status %d from endpoint %s", status, m.endpoint)
	}

	logging.Debugf(ctx, "Sent %d tsmon cells to %s", len(cells), m.endpoint)
	return nil
}

func (m *httpMonitor) Close() error {
	return nil
}
<|endoftext|>


<commit_before>package app

import (
	"github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/config"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	defaultURL    = "https://github.com/rancher/rancher-charts"
	defaultBranch = "2.0"
)

func addCatalogs(management *config.ManagementContext) error {
	catalogClient := management.Management.Catalogs("")
	_, err := catalogClient.Get("rancher", metav1.GetOptions{})
	if err != nil && !errors.IsNotFound(err) {
		return err
	} else if errors.IsNotFound(err) {
		obj := &v3.Catalog{
			ObjectMeta: metav1.ObjectMeta{
				Name: "rancher",
			},
			Spec: v3.CatalogSpec{
				URL:         defaultURL,
				CatalogKind: "helm",
				Branch:      defaultBranch,
			},
		}
		if _, err := catalogClient.Create(obj); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>Switch to charts master<commit_after>package app

import (
	"github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/config"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	defaultURL    = "https://github.com/rancher/charts"
	defaultBranch = "master"
)

func addCatalogs(management *config.ManagementContext) error {
	catalogClient := management.Management.Catalogs("")
	_, err := catalogClient.Get("rancher", metav1.GetOptions{})
	if err != nil && !errors.IsNotFound(err) {
		return err
	} else if errors.IsNotFound(err) {
		obj := &v3.Catalog{
			ObjectMeta: metav1.ObjectMeta{
				Name: "rancher",
			},
			Spec: v3.CatalogSpec{
				URL:         defaultURL,
				CatalogKind: "helm",
				Branch:      defaultBranch,
			},
		}
		if _, err := catalogClient.Create(obj); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// import github.com/dmcsorley/goblin/gobdocker
package gobdocker

import (
	"bufio"
	"encoding/json"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/events"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/client"
	"golang.org/x/net/context"
	"log"
)

type ExitedBuild struct {
	Id          string
	ContainerId string
	Name        string
	Time        string
	Exit        string
}

func getClient() (*client.Client, error) {
	defaultHeaders := map[string]string{"User-Agent": "engine-api-cli-1.0"}
	return client.NewClient(client.DefaultDockerHost, client.DefaultVersion, nil, defaultHeaders)
}

func ListenForBuildExits(callback func(*ExitedBuild)) {
	cli, err := getClient()
	if err != nil {
		panic(err)
	}

	f := filters.NewArgs()
	f.Add("label", "goblin.build")
	f.Add("event", "die")

	eventstream, err := cli.Events(
		context.Background(),
		types.EventsOptions{
			Filters: f,
		},
	)
	if err != nil {
		panic(err)
	}

	defer eventstream.Close()

	s := bufio.NewScanner(eventstream)
	for s.Scan() {
		event := events.Message{}
		err := json.Unmarshal([]byte(s.Text()), &event)
		if err != nil {
			log.Printf("%v\n", err)
		} else {
			callback(&ExitedBuild{
				Id:          event.Actor.Attributes["goblin.id"],
				ContainerId: event.Actor.ID,
				Name:        event.Actor.Attributes["goblin.name"],
				Time:        event.Actor.Attributes["goblin.time"],
				Exit:        event.Actor.Attributes["exitCode"],
			})
		}
	}
}

func CreateVolume(name string) (string, error) {
	defaultHeaders := map[string]string{"User-Agent": "engine-api-cli-1.0"}
	cli, _ := client.NewClient(client.DefaultDockerHost, client.DefaultVersion, nil, defaultHeaders)

	volume, err := cli.VolumeCreate(
		context.Background(),
		types.VolumeCreateRequest{
			Name: name,
		},
	)

	if err != nil {
		return "", err
	}

	return volume.Name, nil
}

func RemoveVolume(name string) {
	cli, _ := getClient()
	err := cli.VolumeRemove(context.Background(), name, false)
	if err != nil {
		log.Println(err)
	}
}
<commit_msg>match new docker cli.Events usage<commit_after>// import github.com/dmcsorley/goblin/gobdocker
package gobdocker

import (
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/client"
	"golang.org/x/net/context"
	"log"
)

type ExitedBuild struct {
	Id          string
	ContainerId string
	Name        string
	Time        string
	Exit        string
}

func getClient() (*client.Client, error) {
	defaultHeaders := map[string]string{"User-Agent": "engine-api-cli-1.0"}
	return client.NewClient(client.DefaultDockerHost, client.DefaultVersion, nil, defaultHeaders)
}

func ListenForBuildExits(callback func(*ExitedBuild)) {
	cli, err := getClient()
	if err != nil {
		panic(err)
	}

	f := filters.NewArgs()
	f.Add("label", "goblin.build")
	f.Add("event", "die")

	eventchan, errchan := cli.Events(
		context.Background(),
		types.EventsOptions{
			Filters: f,
		},
	)

	for {
	select {
	case event := <- eventchan:
		callback(&ExitedBuild{
			Id:          event.Actor.Attributes["goblin.id"],
			ContainerId: event.Actor.ID,
			Name:        event.Actor.Attributes["goblin.name"],
			Time:        event.Actor.Attributes["goblin.time"],
			Exit:        event.Actor.Attributes["exitCode"],
		})
	case err := <- errchan:
		log.Printf("%v\n", err)
		return
	}
	}
}

func CreateVolume(name string) (string, error) {
	defaultHeaders := map[string]string{"User-Agent": "engine-api-cli-1.0"}
	cli, _ := client.NewClient(client.DefaultDockerHost, client.DefaultVersion, nil, defaultHeaders)

	volume, err := cli.VolumeCreate(
		context.Background(),
		types.VolumeCreateRequest{
			Name: name,
		},
	)

	if err != nil {
		return "", err
	}

	return volume.Name, nil
}

func RemoveVolume(name string) {
	cli, _ := getClient()
	err := cli.VolumeRemove(context.Background(), name, false)
	if err != nil {
		log.Println(err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 The Go 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 godoc

import "testing"

func TestParseVersionRow(t *testing.T) {
	tests := []struct {
		row  string
		want versionedRow
	}{
		{
			row: "# comment",
		},
		{
			row: "",
		},
		{
			row: "pkg archive/tar, type Writer struct",
			want: versionedRow{
				pkg:  "archive/tar",
				kind: "type",
				name: "Writer",
			},
		},
		{
			row: "pkg archive/tar, type Header struct, AccessTime time.Time",
			want: versionedRow{
				pkg:        "archive/tar",
				kind:       "field",
				structName: "Header",
				name:       "AccessTime",
			},
		},
		{
			row: "pkg archive/tar, method (*Reader) Read([]uint8) (int, error)",
			want: versionedRow{
				pkg:  "archive/tar",
				kind: "method",
				name: "Read",
				recv: "*Reader",
			},
		},
		{
			row: "pkg archive/zip, func FileInfoHeader(os.FileInfo) (*FileHeader, error)",
			want: versionedRow{
				pkg:  "archive/zip",
				kind: "func",
				name: "FileInfoHeader",
			},
		},
		{
			row: "pkg encoding/base32, method (Encoding) WithPadding(int32) *Encoding",
			want: versionedRow{
				pkg:  "encoding/base32",
				kind: "method",
				name: "WithPadding",
				recv: "Encoding",
			},
		},
	}

	for i, tt := range tests {
		got, ok := parseRow(tt.row)
		if !ok {
			got = versionedRow{}
		}
		if got != tt.want {
			t.Errorf("%d. parseRow(%q) = %+v; want %+v", i, tt.row, got, tt.want)
		}
	}
}

func TestAPIVersion(t *testing.T) {
	av, err := parsePackageAPIInfo()
	if err != nil {
		t.Fatal(err)
	}
	for _, tc := range []struct {
		kind     string
		pkg      string
		name     string
		receiver string
		want     string
	}{
		// Things that were added post-1.0 should appear
		{"func", "archive/tar", "FileInfoHeader", "", "1.1"},
		{"type", "bufio", "Scanner", "", "1.1"},
		{"method", "bufio", "WriteTo", "*Reader", "1.1"},

		{"func", "bytes", "LastIndexByte", "", "1.5"},
		{"type", "crypto", "Decrypter", "", "1.5"},
		{"method", "crypto/rsa", "Decrypt", "*PrivateKey", "1.5"},
		{"method", "debug/dwarf", "GoString", "Class", "1.5"},

		{"func", "os", "IsTimeout", "", "1.10"},
		{"type", "strings", "Builder", "", "1.10"},
		{"method", "strings", "WriteString", "*Builder", "1.10"},

		// Things from package syscall should never appear
		{"func", "syscall", "FchFlags", "", ""},
		{"type", "syscall", "Inet4Pktinfo", "", ""},

		// Things added in Go 1 should never appear
		{"func", "archive/tar", "NewReader", "", ""},
		{"type", "archive/tar", "Header", "", ""},
		{"method", "archive/tar", "Next", "*Reader", ""},
	} {
		if got := av.sinceVersionFunc(tc.kind, tc.receiver, tc.name, tc.pkg); got != tc.want {
			t.Errorf(`sinceFunc("%s", "%s", "%s", "%s") = "%s"; want "%s"`, tc.kind, tc.receiver, tc.name, tc.pkg, got, tc.want)
		}
	}
}
<commit_msg>godoc: fix tests on Go 1.9<commit_after>// Copyright 2018 The Go 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 godoc

import (
	"go/build"
	"testing"
)

func TestParseVersionRow(t *testing.T) {
	tests := []struct {
		row  string
		want versionedRow
	}{
		{
			row: "# comment",
		},
		{
			row: "",
		},
		{
			row: "pkg archive/tar, type Writer struct",
			want: versionedRow{
				pkg:  "archive/tar",
				kind: "type",
				name: "Writer",
			},
		},
		{
			row: "pkg archive/tar, type Header struct, AccessTime time.Time",
			want: versionedRow{
				pkg:        "archive/tar",
				kind:       "field",
				structName: "Header",
				name:       "AccessTime",
			},
		},
		{
			row: "pkg archive/tar, method (*Reader) Read([]uint8) (int, error)",
			want: versionedRow{
				pkg:  "archive/tar",
				kind: "method",
				name: "Read",
				recv: "*Reader",
			},
		},
		{
			row: "pkg archive/zip, func FileInfoHeader(os.FileInfo) (*FileHeader, error)",
			want: versionedRow{
				pkg:  "archive/zip",
				kind: "func",
				name: "FileInfoHeader",
			},
		},
		{
			row: "pkg encoding/base32, method (Encoding) WithPadding(int32) *Encoding",
			want: versionedRow{
				pkg:  "encoding/base32",
				kind: "method",
				name: "WithPadding",
				recv: "Encoding",
			},
		},
	}

	for i, tt := range tests {
		got, ok := parseRow(tt.row)
		if !ok {
			got = versionedRow{}
		}
		if got != tt.want {
			t.Errorf("%d. parseRow(%q) = %+v; want %+v", i, tt.row, got, tt.want)
		}
	}
}

// hasTag checks whether a given release tag is contained in the current version
// of the go binary.
func hasTag(t string) bool {
	for _, v := range build.Default.ReleaseTags {
		if t == v {
			return true
		}
	}
	return false
}

func TestAPIVersion(t *testing.T) {
	av, err := parsePackageAPIInfo()
	if err != nil {
		t.Fatal(err)
	}
	for _, tc := range []struct {
		kind     string
		pkg      string
		name     string
		receiver string
		want     string
	}{
		// Things that were added post-1.0 should appear
		{"func", "archive/tar", "FileInfoHeader", "", "1.1"},
		{"type", "bufio", "Scanner", "", "1.1"},
		{"method", "bufio", "WriteTo", "*Reader", "1.1"},

		{"func", "bytes", "LastIndexByte", "", "1.5"},
		{"type", "crypto", "Decrypter", "", "1.5"},
		{"method", "crypto/rsa", "Decrypt", "*PrivateKey", "1.5"},
		{"method", "debug/dwarf", "GoString", "Class", "1.5"},

		{"func", "os", "IsTimeout", "", "1.10"},
		{"type", "strings", "Builder", "", "1.10"},
		{"method", "strings", "WriteString", "*Builder", "1.10"},

		// Things from package syscall should never appear
		{"func", "syscall", "FchFlags", "", ""},
		{"type", "syscall", "Inet4Pktinfo", "", ""},

		// Things added in Go 1 should never appear
		{"func", "archive/tar", "NewReader", "", ""},
		{"type", "archive/tar", "Header", "", ""},
		{"method", "archive/tar", "Next", "*Reader", ""},
	} {
		if tc.want != "" && !hasTag("go"+tc.want) {
			continue
		}
		if got := av.sinceVersionFunc(tc.kind, tc.receiver, tc.name, tc.pkg); got != tc.want {
			t.Errorf(`sinceFunc("%s", "%s", "%s", "%s") = "%s"; want "%s"`, tc.kind, tc.receiver, tc.name, tc.pkg, got, tc.want)
		}
	}
}
<|endoftext|>


<commit_before>package compute

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

// Virtual listener types
const (
	// VirtualListenerTypeStandard represents a standard virtual listener.
	VirtualListenerTypeStandard = "STANDARD"

	// VirtualListenerTypePerformanceLayer4 represents a high-performance (layer 4) virtual listener.
	VirtualListenerTypePerformanceLayer4 = "PERFORMANCE_LAYER_4"
)

// Protocols supported by a standard virtual listener
const (
	// Any protocol (as supported by a standard virtual listener).
	VirtualListenerStandardProtocolAny = "ANY"

	// Transmission Control Protocol (as supported by a standard virtual listener).
	VirtualListenerStandardProtocolTCP = "TCP"

	// Uniform Datagram Protocol (as supported by a standard virtual listener).
	VirtualListenerStandardProtocolUDP = "UDP"

	// Hypertext Transfer Protocol (as supported by a standard virtual listener).
	VirtualListenerStandardProtocolHTTP = "HTTP"

	// File Transfer Protocol (as supported by a standard virtual listener).
	VirtualListenerStandardProtocolFTP = "FTP"

	// Simple Mail Transfer Protocol (as supported by a standard virtual listener).
	VirtualListenerStandardProtocolSMTP = "SMTP"
)

// Protocols supported by a high-performance (layer 4) virtual listener
const (
	// Any protocol (as supported by a high-performance layer 4 virtual listener).
	VirtualListenerPerformanceLayer4ProtocolAny = "ANY"

	// Transmission Control Protocol (as supported by a high-performance layer 4 virtual listener).
	VirtualListenerPerformanceLayer4ProtocolTCP = "TCP"

	// Uniform Datagram Protocol (as supported by a high-performance layer 4 virtual listener).
	VirtualListenerPerformanceLayer4ProtocolUDP = "UDP"

	// Hypertext Transfer Protocol (as supported by a high-performance layer 4 virtual listener).
	VirtualListenerPerformanceLayer4ProtocolHTTP = "HTTP"
)

// Options for source-port preservation on a virtual listener.
const (
	// Source port preservation is enabled.
	SourcePortPreservationEnabled = "PRESERVE"

	// Source port preservation is enabled (strict mode).
	SourcePortPreservationEnabledStrict = "PRESERVE_STRICT"

	// Source port preservation is disabled.
	SourcePortPreservationDisabled = "CHANGE"
)

// VirtualListener represents a virtual listener.
type VirtualListener struct {
	ID                         string                    `json:"id"`
	Name                       string                    `json:"name"`
	Description                string                    `json:"description"`
	Type                       string                    `json:"type"`
	Protocol                   string                    `json:"protocol"`
	ListenerIPAddress          string                    `json:"listenerIpAddress"`
	Port                       int                       `json:"port"`
	Enabled                    bool                      `json:"enabled"`
	ConnectionLimit            int                       `json:"connectionLimit"`
	ConnectionRateLimit        int                       `json:"connectionRateLimit"`
	SourcePortPreservation     string                    `json:"sourcePortPreservation"`
	Pool                       VirtualListenerVIPPoolRef `json:"pool"`
	ClientClonePool            VirtualListenerVIPPoolRef `json:"ClientClonePool"`
	PersistenceProfile         EntitySummary             `json:"persistenceProfile"`
	FallbackPersistenceProfile EntitySummary             `json:"fallbackPersistenceProfile"`
	OptimizationProfiles       []EntitySummary           `json:"optimizationProfile"`
	IRules                     []EntitySummary           `json:"irule"`
	State                      string                    `json:"state"`
	CreateTime                 string                    `json:"createTime"`
	NetworkDomainID            string                    `json:"networkDomainId"`
	DataCenterID               string                    `json:"datacenterId"`
}

// GetID returns the virtual listener's Id.
func (virtualListener *VirtualListener) GetID() string {
	return virtualListener.ID
}

// GetResourceType returns the virtual listener's resource type.
func (virtualListener *VirtualListener) GetResourceType() ResourceType {
	return ResourceTypeVirtualListener
}

// GetName returns the virtual listener's name.
func (virtualListener *VirtualListener) GetName() string {
	return virtualListener.Name
}

// GetState returns the virtual listener's current state.
func (virtualListener *VirtualListener) GetState() string {
	return virtualListener.State
}

// IsDeleted determines whether the virtual listener has been deleted (is nil).
func (virtualListener *VirtualListener) IsDeleted() bool {
	return virtualListener == nil
}

var _ Resource = &VirtualListener{}

// VirtualListeners represents a page of VirtualListener results.
type VirtualListeners struct {
	Items []VirtualListener `json:"virtualListener"`

	PagedResult
}

// VirtualListenerVIPPoolRef represents a VirtualListener's reference to a VIP pool
type VirtualListenerVIPPoolRef struct {
	LoadBalanceMethod string          `json:"loadBalanceMethod"`
	ServiceDownAction string          `json:"serviceDownAction"`
	HealthMonitors    []EntitySummary `json:"healthMonitor"`

	EntitySummary
}

// NewVirtualListenerConfiguration represents the configuration for a new virtual listener.
type NewVirtualListenerConfiguration struct {
	Name                         string   `json:"name"`
	Description                  string   `json:"description"`
	Type                         string   `json:"type"`
	Protocol                     string   `json:"protocol"`
	ListenerIPAddress            string   `json:"listenerIpAddress"`
	Port                         int      `json:"port,omitempty"`
	Enabled                      bool     `json:"enabled"`
	ConnectionLimit              int      `json:"connectionLimit"`
	ConnectionRateLimit          int      `json:"connectionRateLimit"`
	SourcePortPreservation       string   `json:"sourcePortPreservation"`
	PoolID                       *string  `json:"poolId,omitempty"`
	ClientClonePoolID            *string  `json:"clientClonePoolId,omitempty"`
	PersistenceProfileID         *string  `json:"persistenceProfileId,omitempty"`
	FallbackPersistenceProfileID *string  `json:"fallbackPersistenceProfileId,omitempty"`
	IRuleIDs                     []string `json:"iruleId"`
	OptimizationProfiles         []string `json:"optimizationProfile"`
	NetworkDomainID              string   `json:"networkDomainId"`
}

// EditVirtualListenerConfiguration represents the configuration for editing a virtual listener.
type EditVirtualListenerConfiguration struct {
	ID                     string    `json:"id"`
	Description            *string   `json:"description,omitempty"`
	Enabled                *bool     `json:"enabled,omitempty"`
	ConnectionLimit        *int      `json:"connectionLimit,omitempty"`
	ConnectionRateLimit    *int      `json:"connectionRateLimit,omitempty"`
	SourcePortPreservation *string   `json:"sourcePortPreservation,omitempty"`
	PoolID                 *string   `json:"poolId,omitempty"`
	PersistenceProfileID   *string   `json:"persistenceProfileId,omitempty"`
	IRuleIDs               *[]string `json:"iruleId,omitempty"`
	OptimizationProfiles   *[]string `json:"optimizationProfile,omitempty"`
}

// Request body for deleting a virtual listener.
type deleteVirtualListener struct {
	// The virtual listener ID.
	ID string `json:"id"`
}

// ListVirtualListenersInNetworkDomain retrieves a list of all virtual listeners in the specified network domain.
func (client *Client) ListVirtualListenersInNetworkDomain(networkDomainID string, paging *Paging) (listeners *VirtualListeners, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return nil, err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/virtualListener?networkDomainId=%s&%s",
		organizationID,
		url.QueryEscape(networkDomainID),
		paging.EnsurePaging().toQueryParameters(),
	)
	request, err := client.newRequestV22(requestURI, http.MethodGet, nil)
	if err != nil {
		return nil, err
	}

	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return nil, err
	}

	if statusCode != http.StatusOK {
		var apiResponse *APIResponseV2

		apiResponse, err = readAPIResponseAsJSON(responseBody, statusCode)
		if err != nil {
			return nil, err
		}

		return nil, apiResponse.ToError("Request to list virtual listeners in network domain '%s' failed with status code %d (%s): %s", networkDomainID, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	listeners = &VirtualListeners{}
	err = json.Unmarshal(responseBody, listeners)
	if err != nil {
		return nil, err
	}

	return listeners, nil
}

// GetVirtualListener retrieves the virtual listener with the specified Id.
// Returns nil if no virtual listener is found with the specified Id.
func (client *Client) GetVirtualListener(id string) (listener *VirtualListener, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return nil, err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/virtualListener/%s", organizationID, id)
	request, err := client.newRequestV22(requestURI, http.MethodGet, nil)
	if err != nil {
		return nil, err
	}
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return nil, err
	}

	if statusCode != http.StatusOK {
		var apiResponse *APIResponseV2

		apiResponse, err = readAPIResponseAsJSON(responseBody, statusCode)
		if err != nil {
			return nil, err
		}

		if apiResponse.ResponseCode == ResponseCodeResourceNotFound {
			return nil, nil // Not an error, but was not found.
		}

		return nil, apiResponse.ToError("Request to retrieve virtual listener with Id '%s' failed with status code %d (%s): %s", id, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	listener = &VirtualListener{}
	err = json.Unmarshal(responseBody, listener)
	if err != nil {
		return nil, err
	}

	return listener, nil
}

// CreateVirtualListener creates a new VIP node.
// Returns the Id of the new node.
func (client *Client) CreateVirtualListener(listenerConfiguration NewVirtualListenerConfiguration) (nodeID string, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return "", err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/createVirtualListener", organizationID)
	request, err := client.newRequestV22(requestURI, http.MethodPost, &listenerConfiguration)
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return "", err
	}

	apiResponse, err := readAPIResponseAsJSON(responseBody, statusCode)
	if err != nil {
		return "", err
	}

	if apiResponse.ResponseCode != ResponseCodeOK {
		return "", apiResponse.ToError("Request to create virtual listener '%s' failed with status code %d (%s): %s", listenerConfiguration.Name, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	// Expected: "info" { "name": "virtualListenerId", "value": "the-Id-of-the-new-virtual-listener" ... }
	virtualListenerIDMessage := apiResponse.GetFieldMessage("virtualListenerId")
	if virtualListenerIDMessage == nil {
		return "", apiResponse.ToError("Received an unexpected response (missing 'virtualListenerId') with status code %d (%s): %s", statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	return *virtualListenerIDMessage, nil
}

// EditVirtualListener updates an existing virtual listener.
func (client *Client) EditVirtualListener(id string, listenerConfiguration EditVirtualListenerConfiguration) error {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return err
	}

	editListenerConfiguration := &listenerConfiguration
	editListenerConfiguration.ID = id

	requestURI := fmt.Sprintf("%s/networkDomainVip/editVirtualListener", organizationID)
	request, err := client.newRequestV22(requestURI, http.MethodPost, editListenerConfiguration)
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return err
	}

	apiResponse, err := readAPIResponseAsJSON(responseBody, statusCode)
	if err != nil {
		return err
	}

	if statusCode != http.StatusOK {
		return apiResponse.ToError("Request to edit virtual listener '%s' failed with status code %d (%s): %s", listenerConfiguration.ID, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	return nil
}

// DeleteVirtualListener deletes an existing virtual listener.
// Returns an error if the operation was not successful.
func (client *Client) DeleteVirtualListener(id string) (err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/deleteVirtualListener", organizationID)
	request, err := client.newRequestV22(requestURI, http.MethodPost, &deleteVirtualListener{id})
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return err
	}

	apiResponse, err := readAPIResponseAsJSON(responseBody, statusCode)
	if err != nil {
		return err
	}

	if apiResponse.ResponseCode != ResponseCodeOK {
		return apiResponse.ToError("Request to delete virtual listener '%s' failed with unexpected status code %d (%s): %s", id, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	return nil
}
<commit_msg>Fix incorrectly-declared data contract for virtual listener.<commit_after>package compute

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

// Virtual listener types
const (
	// VirtualListenerTypeStandard represents a standard virtual listener.
	VirtualListenerTypeStandard = "STANDARD"

	// VirtualListenerTypePerformanceLayer4 represents a high-performance (layer 4) virtual listener.
	VirtualListenerTypePerformanceLayer4 = "PERFORMANCE_LAYER_4"
)

// Protocols supported by a standard virtual listener
const (
	// Any protocol (as supported by a standard virtual listener).
	VirtualListenerStandardProtocolAny = "ANY"

	// Transmission Control Protocol (as supported by a standard virtual listener).
	VirtualListenerStandardProtocolTCP = "TCP"

	// Uniform Datagram Protocol (as supported by a standard virtual listener).
	VirtualListenerStandardProtocolUDP = "UDP"

	// Hypertext Transfer Protocol (as supported by a standard virtual listener).
	VirtualListenerStandardProtocolHTTP = "HTTP"

	// File Transfer Protocol (as supported by a standard virtual listener).
	VirtualListenerStandardProtocolFTP = "FTP"

	// Simple Mail Transfer Protocol (as supported by a standard virtual listener).
	VirtualListenerStandardProtocolSMTP = "SMTP"
)

// Protocols supported by a high-performance (layer 4) virtual listener
const (
	// Any protocol (as supported by a high-performance layer 4 virtual listener).
	VirtualListenerPerformanceLayer4ProtocolAny = "ANY"

	// Transmission Control Protocol (as supported by a high-performance layer 4 virtual listener).
	VirtualListenerPerformanceLayer4ProtocolTCP = "TCP"

	// Uniform Datagram Protocol (as supported by a high-performance layer 4 virtual listener).
	VirtualListenerPerformanceLayer4ProtocolUDP = "UDP"

	// Hypertext Transfer Protocol (as supported by a high-performance layer 4 virtual listener).
	VirtualListenerPerformanceLayer4ProtocolHTTP = "HTTP"
)

// Options for source-port preservation on a virtual listener.
const (
	// Source port preservation is enabled.
	SourcePortPreservationEnabled = "PRESERVE"

	// Source port preservation is enabled (strict mode).
	SourcePortPreservationEnabledStrict = "PRESERVE_STRICT"

	// Source port preservation is disabled.
	SourcePortPreservationDisabled = "CHANGE"
)

// VirtualListener represents a virtual listener.
type VirtualListener struct {
	ID                         string                    `json:"id"`
	Name                       string                    `json:"name"`
	Description                string                    `json:"description"`
	Type                       string                    `json:"type"`
	Protocol                   string                    `json:"protocol"`
	ListenerIPAddress          string                    `json:"listenerIpAddress"`
	Port                       int                       `json:"port"`
	Enabled                    bool                      `json:"enabled"`
	ConnectionLimit            int                       `json:"connectionLimit"`
	ConnectionRateLimit        int                       `json:"connectionRateLimit"`
	SourcePortPreservation     string                    `json:"sourcePortPreservation"`
	Pool                       VirtualListenerVIPPoolRef `json:"pool"`
	ClientClonePool            VirtualListenerVIPPoolRef `json:"ClientClonePool"`
	PersistenceProfile         EntitySummary             `json:"persistenceProfile"`
	FallbackPersistenceProfile EntitySummary             `json:"fallbackPersistenceProfile"`
	OptimizationProfiles       []string                  `json:"optimizationProfile"`
	IRules                     []EntitySummary           `json:"irule"`
	State                      string                    `json:"state"`
	CreateTime                 string                    `json:"createTime"`
	NetworkDomainID            string                    `json:"networkDomainId"`
	DataCenterID               string                    `json:"datacenterId"`
}

// GetID returns the virtual listener's Id.
func (virtualListener *VirtualListener) GetID() string {
	return virtualListener.ID
}

// GetResourceType returns the virtual listener's resource type.
func (virtualListener *VirtualListener) GetResourceType() ResourceType {
	return ResourceTypeVirtualListener
}

// GetName returns the virtual listener's name.
func (virtualListener *VirtualListener) GetName() string {
	return virtualListener.Name
}

// GetState returns the virtual listener's current state.
func (virtualListener *VirtualListener) GetState() string {
	return virtualListener.State
}

// IsDeleted determines whether the virtual listener has been deleted (is nil).
func (virtualListener *VirtualListener) IsDeleted() bool {
	return virtualListener == nil
}

var _ Resource = &VirtualListener{}

// VirtualListeners represents a page of VirtualListener results.
type VirtualListeners struct {
	Items []VirtualListener `json:"virtualListener"`

	PagedResult
}

// VirtualListenerVIPPoolRef represents a VirtualListener's reference to a VIP pool
type VirtualListenerVIPPoolRef struct {
	LoadBalanceMethod string          `json:"loadBalanceMethod"`
	ServiceDownAction string          `json:"serviceDownAction"`
	HealthMonitors    []EntitySummary `json:"healthMonitor"`

	EntitySummary
}

// NewVirtualListenerConfiguration represents the configuration for a new virtual listener.
type NewVirtualListenerConfiguration struct {
	Name                         string   `json:"name"`
	Description                  string   `json:"description"`
	Type                         string   `json:"type"`
	Protocol                     string   `json:"protocol"`
	ListenerIPAddress            *string  `json:"listenerIpAddress,omitempty"`
	Port                         int      `json:"port,omitempty"`
	Enabled                      bool     `json:"enabled"`
	ConnectionLimit              int      `json:"connectionLimit"`
	ConnectionRateLimit          int      `json:"connectionRateLimit"`
	SourcePortPreservation       string   `json:"sourcePortPreservation"`
	PoolID                       *string  `json:"poolId,omitempty"`
	ClientClonePoolID            *string  `json:"clientClonePoolId,omitempty"`
	PersistenceProfileID         *string  `json:"persistenceProfileId,omitempty"`
	FallbackPersistenceProfileID *string  `json:"fallbackPersistenceProfileId,omitempty"`
	IRuleIDs                     []string `json:"iruleId"`
	OptimizationProfiles         []string `json:"optimizationProfile"`
	NetworkDomainID              string   `json:"networkDomainId"`
}

// EditVirtualListenerConfiguration represents the configuration for editing a virtual listener.
type EditVirtualListenerConfiguration struct {
	ID                     string    `json:"id"`
	Description            *string   `json:"description,omitempty"`
	Enabled                *bool     `json:"enabled,omitempty"`
	ConnectionLimit        *int      `json:"connectionLimit,omitempty"`
	ConnectionRateLimit    *int      `json:"connectionRateLimit,omitempty"`
	SourcePortPreservation *string   `json:"sourcePortPreservation,omitempty"`
	PoolID                 *string   `json:"poolId,omitempty"`
	PersistenceProfileID   *string   `json:"persistenceProfileId,omitempty"`
	IRuleIDs               *[]string `json:"iruleId,omitempty"`
	OptimizationProfiles   *[]string `json:"optimizationProfile,omitempty"`
}

// Request body for deleting a virtual listener.
type deleteVirtualListener struct {
	// The virtual listener ID.
	ID string `json:"id"`
}

// ListVirtualListenersInNetworkDomain retrieves a list of all virtual listeners in the specified network domain.
func (client *Client) ListVirtualListenersInNetworkDomain(networkDomainID string, paging *Paging) (listeners *VirtualListeners, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return nil, err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/virtualListener?networkDomainId=%s&%s",
		organizationID,
		url.QueryEscape(networkDomainID),
		paging.EnsurePaging().toQueryParameters(),
	)
	request, err := client.newRequestV22(requestURI, http.MethodGet, nil)
	if err != nil {
		return nil, err
	}

	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return nil, err
	}

	if statusCode != http.StatusOK {
		var apiResponse *APIResponseV2

		apiResponse, err = readAPIResponseAsJSON(responseBody, statusCode)
		if err != nil {
			return nil, err
		}

		return nil, apiResponse.ToError("Request to list virtual listeners in network domain '%s' failed with status code %d (%s): %s", networkDomainID, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	listeners = &VirtualListeners{}
	err = json.Unmarshal(responseBody, listeners)
	if err != nil {
		return nil, err
	}

	return listeners, nil
}

// GetVirtualListener retrieves the virtual listener with the specified Id.
// Returns nil if no virtual listener is found with the specified Id.
func (client *Client) GetVirtualListener(id string) (listener *VirtualListener, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return nil, err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/virtualListener/%s", organizationID, id)
	request, err := client.newRequestV22(requestURI, http.MethodGet, nil)
	if err != nil {
		return nil, err
	}
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return nil, err
	}

	if statusCode != http.StatusOK {
		var apiResponse *APIResponseV2

		apiResponse, err = readAPIResponseAsJSON(responseBody, statusCode)
		if err != nil {
			return nil, err
		}

		if apiResponse.ResponseCode == ResponseCodeResourceNotFound {
			return nil, nil // Not an error, but was not found.
		}

		return nil, apiResponse.ToError("Request to retrieve virtual listener with Id '%s' failed with status code %d (%s): %s", id, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	listener = &VirtualListener{}
	err = json.Unmarshal(responseBody, listener)
	if err != nil {
		return nil, err
	}

	return listener, nil
}

// CreateVirtualListener creates a new VIP node.
// Returns the Id of the new node.
func (client *Client) CreateVirtualListener(listenerConfiguration NewVirtualListenerConfiguration) (nodeID string, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return "", err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/createVirtualListener", organizationID)
	request, err := client.newRequestV22(requestURI, http.MethodPost, &listenerConfiguration)
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return "", err
	}

	apiResponse, err := readAPIResponseAsJSON(responseBody, statusCode)
	if err != nil {
		return "", err
	}

	if apiResponse.ResponseCode != ResponseCodeOK {
		return "", apiResponse.ToError("Request to create virtual listener '%s' failed with status code %d (%s): %s", listenerConfiguration.Name, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	// Expected: "info" { "name": "virtualListenerId", "value": "the-Id-of-the-new-virtual-listener" ... }
	virtualListenerIDMessage := apiResponse.GetFieldMessage("virtualListenerId")
	if virtualListenerIDMessage == nil {
		return "", apiResponse.ToError("Received an unexpected response (missing 'virtualListenerId') with status code %d (%s): %s", statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	return *virtualListenerIDMessage, nil
}

// EditVirtualListener updates an existing virtual listener.
func (client *Client) EditVirtualListener(id string, listenerConfiguration EditVirtualListenerConfiguration) error {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return err
	}

	editListenerConfiguration := &listenerConfiguration
	editListenerConfiguration.ID = id

	requestURI := fmt.Sprintf("%s/networkDomainVip/editVirtualListener", organizationID)
	request, err := client.newRequestV22(requestURI, http.MethodPost, editListenerConfiguration)
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return err
	}

	apiResponse, err := readAPIResponseAsJSON(responseBody, statusCode)
	if err != nil {
		return err
	}

	if statusCode != http.StatusOK {
		return apiResponse.ToError("Request to edit virtual listener '%s' failed with status code %d (%s): %s", listenerConfiguration.ID, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	return nil
}

// DeleteVirtualListener deletes an existing virtual listener.
// Returns an error if the operation was not successful.
func (client *Client) DeleteVirtualListener(id string) (err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return err
	}

	requestURI := fmt.Sprintf("%s/networkDomainVip/deleteVirtualListener", organizationID)
	request, err := client.newRequestV22(requestURI, http.MethodPost, &deleteVirtualListener{id})
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return err
	}

	apiResponse, err := readAPIResponseAsJSON(responseBody, statusCode)
	if err != nil {
		return err
	}

	if apiResponse.ResponseCode != ResponseCodeOK {
		return apiResponse.ToError("Request to delete virtual listener '%s' failed with unexpected status code %d (%s): %s", id, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go 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 goplay

import (
	"bytes"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"io"
	"net/http"

	"appengine"
	"appengine/datastore"
)

const salt = "[replace this with something unique]"

const maxSnippetSize = 64 * 1024

type Snippet struct {
	Body []byte
}

func (s *Snippet) Id() string {
	h := sha1.New()
	io.WriteString(h, salt)
	h.Write(s.Body)
	sum := h.Sum(nil)
	b := make([]byte, base64.URLEncoding.EncodedLen(len(sum)))
	base64.URLEncoding.Encode(b, sum)
	return string(b)[:10]
}

func init() {
	http.Handle("/share", hstsHandler(share))
}

func share(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		status := http.StatusMethodNotAllowed
		http.Error(w, http.StatusText(status), status)
		return
	}
	if !allowShare(r) {
		status := http.StatusUnavailableForLegalReasons
		http.Error(w, http.StatusText(status), status)
		return
	}
	c := appengine.NewContext(r)

	var body bytes.Buffer
	_, err := io.Copy(&body, io.LimitReader(r.Body, maxSnippetSize+1))
	r.Body.Close()
	if err != nil {
		c.Errorf("reading Body: %v", err)
		http.Error(w, "Server Error", http.StatusInternalServerError)
		return
	}
	if body.Len() > maxSnippetSize {
		http.Error(w, "Snippet is too large", http.StatusRequestEntityTooLarge)
		return
	}

	snip := &Snippet{Body: body.Bytes()}
	id := snip.Id()
	key := datastore.NewKey(c, "Snippet", id, 0, nil)
	_, err = datastore.Put(c, key, snip)
	if err != nil {
		c.Errorf("putting Snippet: %v", err)
		http.Error(w, "Server Error", http.StatusInternalServerError)
		return
	}

	fmt.Fprint(w, id)
}

func allowShare(r *http.Request) bool {
	if appengine.IsDevAppServer() {
		return true
	}
	switch r.Header.Get("X-AppEngine-Country") {
	case "", "ZZ", "CN":
		return false
	}
	return true
}
<commit_msg>playground: allow CORS requests to /share<commit_after>// Copyright 2011 The Go 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 goplay

import (
	"bytes"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"io"
	"net/http"

	"appengine"
	"appengine/datastore"
)

const salt = "[replace this with something unique]"

const maxSnippetSize = 64 * 1024

type Snippet struct {
	Body []byte
}

func (s *Snippet) Id() string {
	h := sha1.New()
	io.WriteString(h, salt)
	h.Write(s.Body)
	sum := h.Sum(nil)
	b := make([]byte, base64.URLEncoding.EncodedLen(len(sum)))
	base64.URLEncoding.Encode(b, sum)
	return string(b)[:10]
}

func init() {
	http.Handle("/share", hstsHandler(share))
}

func share(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
	if r.Method == "OPTIONS" {
		// This is likely a pre-flight CORS request.
		return
	}
	if r.Method != "POST" {
		status := http.StatusMethodNotAllowed
		http.Error(w, http.StatusText(status), status)
		return
	}
	if !allowShare(r) {
		status := http.StatusUnavailableForLegalReasons
		http.Error(w, http.StatusText(status), status)
		return
	}
	c := appengine.NewContext(r)

	var body bytes.Buffer
	_, err := io.Copy(&body, io.LimitReader(r.Body, maxSnippetSize+1))
	r.Body.Close()
	if err != nil {
		c.Errorf("reading Body: %v", err)
		http.Error(w, "Server Error", http.StatusInternalServerError)
		return
	}
	if body.Len() > maxSnippetSize {
		http.Error(w, "Snippet is too large", http.StatusRequestEntityTooLarge)
		return
	}

	snip := &Snippet{Body: body.Bytes()}
	id := snip.Id()
	key := datastore.NewKey(c, "Snippet", id, 0, nil)
	_, err = datastore.Put(c, key, snip)
	if err != nil {
		c.Errorf("putting Snippet: %v", err)
		http.Error(w, "Server Error", http.StatusInternalServerError)
		return
	}

	fmt.Fprint(w, id)
}

func allowShare(r *http.Request) bool {
	if appengine.IsDevAppServer() {
		return true
	}
	switch r.Header.Get("X-AppEngine-Country") {
	case "", "ZZ", "CN":
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package handler

import (
	"github.com/dinever/dingo/app/model"
	"github.com/dinever/dingo/app/utils"
	"github.com/dinever/golf"
	"html/template"
	"log"
	"net/url"
	"strconv"
	"strings"
	"time"
)

func RegisterFunctions(app *golf.Application) {
	app.View.FuncMap["Tags"] = getAllTags
	app.View.FuncMap["RecentArticles"] = getRecentPosts
}

func HomeHandler(ctx *golf.Context) {
	p := ctx.Param("page")
	page, _ := strconv.Atoi(p)
	articles, pager, err := model.GetPostList(int64(page), 5, false, true, "published_at DESC")
	if err != nil {
		panic(err)
	}
	// theme := model.GetSetting("site_theme")
	data := map[string]interface{}{
		"Title":    "Home",
		"Articles": articles,
		"Pager":    pager,
	}
	//	updateSidebarData(data)
	ctx.Loader("theme").Render("index.html", data)
}

func ContentHandler(ctx *golf.Context) {
	slug := ctx.Param("slug")
	article, err := model.GetPostBySlug(slug)
	if err != nil {
		log.Printf("[Error]: %v", err)
		ctx.Abort(404)
		return
	}
	article.Hits++
	data := map[string]interface{}{
		"Title":    article.Title,
		"Article":  article,
		"Content":  article,
		"Comments": article.Comments,
	}
	if article.IsPage {
		ctx.Loader("theme").Render("page.html", data)
	} else {
		ctx.Loader("theme").Render("article.html", data)
	}
}

func CommentHandler(ctx *golf.Context) {
	id := ctx.Param("id")
	cid, _ := strconv.Atoi(id)
	post, err := model.GetPostById(int64(cid))
	if cid < 1 || err != nil {
		ctx.JSON(map[string]interface{}{
			"status": "error",
		})
	}
	c := new(model.Comment)
	c.Author = ctx.Request.FormValue("author")
	c.Email = ctx.Request.FormValue("email")
	c.Website = ctx.Request.FormValue("website")
	c.Content = strings.Replace(utils.Html2Str(template.HTMLEscapeString(ctx.Request.FormValue("comment"))), "\n", "
", -1)
	c.Avatar = utils.Gravatar(c.Email, "50")
	c.PostId = post.Id
	pid, _ := strconv.Atoi(ctx.Request.FormValue("pid"))
	c.Parent = int64(pid)
	c.Ip = ctx.Request.RemoteAddr
	c.UserAgent = ctx.Request.UserAgent()
	c.UserId = 0
	createdAt := time.Now()
	c.CreatedAt = &createdAt
	msg := validateComment(c)
	if msg == "" {
		_, err := c.Save()
		if err != nil {
			ctx.JSON(map[string]interface{}{
				"status": "error",
				"msg":    "Can not comment on this post.",
			})
		}
		post.CommentNum++
		err = post.Save()
		if err != nil {
			log.Printf("[Error]: Can not increase comment count for post %v: %v", post.Id, err.Error())
		}
		ctx.JSON(map[string]interface{}{
			"res":     true,
			"comment": c.ToJson(),
		})
		model.NewMessage("comment", c)
	} else {
		ctx.JSON(map[string]interface{}{
			"status": "error",
			"msg":    msg,
		})
	}
}

func validateComment(c *model.Comment) string {
	if utils.IsEmptyString(c.Author) || utils.IsEmptyString(c.Content) {
		return "Name, Email and Content are required fields."
	}
	if !utils.IsEmail(c.Email) {
		return "Email format not valid."
	}
	if !utils.IsEmptyString(c.Website) && !utils.IsURL(c.Website) {
		return "Website URL format not valid."
	}
	return ""
}

func TagHandler(ctx *golf.Context) {
	p := ctx.Param("page")
	page, _ := strconv.Atoi(p)
	t := ctx.Param("tag")
	tagSlug, _ := url.QueryUnescape(t)
	tag, err := model.GetTagBySlug(tagSlug)
	if err != nil {
		NotFoundHandler(ctx)
		return
	}
	posts, pager, err := model.GetPostsByTag(tag.Id, int64(page), 5, true, "published_at DESC")
	data := map[string]interface{}{
		"Articles": posts,
		"Pager":    pager,
		"Tag":      tag,
		"Title":    tag.Name,
	}
	ctx.Loader("theme").Render("tag.html", data)
}

func SiteMapHandler(ctx *golf.Context) {
	baseUrl := model.GetSettingValue("site_url")
	articles, _, _ := model.GetPostList(1, 50, false, true, "published_at DESC")
	navigators := model.GetNavigators()
	now := utils.Now().Format(time.RFC3339)

	articleMap := make([]map[string]string, len(articles))
	for i, a := range articles {
		m := make(map[string]string)
		m["Link"] = strings.Replace(baseUrl+a.Url(), baseUrl+"/", baseUrl, -1)
		m["Created"] = a.PublishedAt.Format(time.RFC3339)
		articleMap[i] = m
	}

	navMap := make([]map[string]string, 0)
	for _, n := range navigators {
		m := make(map[string]string)
		if n.Url == "/" {
			continue
		}
		if strings.HasPrefix(n.Url, "/") {
			m["Link"] = strings.Replace(baseUrl+n.Url, baseUrl+"/", baseUrl, -1)
		} else {
			m["Link"] = n.Url
		}
		m["Created"] = now
		navMap = append(navMap, m)
	}

	ctx.SetHeader("Content-Type", "application/rss+xml;charset=UTF-8")
	ctx.Loader("base").Render("sitemap.xml", map[string]interface{}{
		"Title":      model.GetSettingValue("site_title"),
		"Link":       baseUrl,
		"Created":    now,
		"Articles":   articleMap,
		"Navigators": navMap,
	})
}

func RssHandler(ctx *golf.Context) {
	baseUrl := model.GetSettingValue("site_url")
	articles, _, _ := model.GetPostList(1, 20, false, true, "published_at DESC")
	articleMap := make([]map[string]string, len(articles))
	for i, a := range articles {
		m := make(map[string]string)
		m["Title"] = a.Title
		m["Link"] = a.Url()
		m["Author"] = a.Author.Name
		m["Desc"] = a.Excerpt()
		m["Created"] = a.CreatedAt.Format(time.RFC822)
		articleMap[i] = m
	}

	ctx.SetHeader("Content-Type", "text/xml; charset=utf-8")

	ctx.Loader("base").Loader("base").Render("rss.xml", map[string]interface{}{
		"Title":    model.GetSettingValue("site_title"),
		"Link":     baseUrl,
		"Desc":     model.GetSettingValue("site_description"),
		"Created":  utils.Now().Format(time.RFC822),
		"Articles": articleMap,
	})
}
<commit_msg>[fix] Return 404 is the post is not published yet.<commit_after>package handler

import (
	"github.com/dinever/dingo/app/model"
	"github.com/dinever/dingo/app/utils"
	"github.com/dinever/golf"
	"html/template"
	"log"
	"net/url"
	"strconv"
	"strings"
	"time"
)

func RegisterFunctions(app *golf.Application) {
	app.View.FuncMap["Tags"] = getAllTags
	app.View.FuncMap["RecentArticles"] = getRecentPosts
}

func HomeHandler(ctx *golf.Context) {
	p := ctx.Param("page")
	page, _ := strconv.Atoi(p)
	articles, pager, err := model.GetPostList(int64(page), 5, false, true, "published_at DESC")
	if err != nil {
		panic(err)
	}
	// theme := model.GetSetting("site_theme")
	data := map[string]interface{}{
		"Title":    "Home",
		"Articles": articles,
		"Pager":    pager,
	}
	//	updateSidebarData(data)
	ctx.Loader("theme").Render("index.html", data)
}

func ContentHandler(ctx *golf.Context) {
	slug := ctx.Param("slug")
	post, err := model.GetPostBySlug(slug)
	if !post.IsPublished {
		ctx.Abort(404)
		return
	}
	if err != nil {
		log.Printf("[Error]: %v", err)
		ctx.Abort(404)
		return
	}
	post.Hits++
	data := map[string]interface{}{
		"Title":    post.Title,
		"Article":  post,
		"Content":  post,
		"Comments": post.Comments,
	}
	if post.IsPage {
		ctx.Loader("theme").Render("page.html", data)
	} else {
		ctx.Loader("theme").Render("article.html", data)
	}
}

func CommentHandler(ctx *golf.Context) {
	id := ctx.Param("id")
	cid, _ := strconv.Atoi(id)
	post, err := model.GetPostById(int64(cid))
	if cid < 1 || err != nil {
		ctx.JSON(map[string]interface{}{
			"status": "error",
		})
	}
	c := new(model.Comment)
	c.Author = ctx.Request.FormValue("author")
	c.Email = ctx.Request.FormValue("email")
	c.Website = ctx.Request.FormValue("website")
	c.Content = strings.Replace(utils.Html2Str(template.HTMLEscapeString(ctx.Request.FormValue("comment"))), "\n", "
", -1)
	c.Avatar = utils.Gravatar(c.Email, "50")
	c.PostId = post.Id
	pid, _ := strconv.Atoi(ctx.Request.FormValue("pid"))
	c.Parent = int64(pid)
	c.Ip = ctx.Request.RemoteAddr
	c.UserAgent = ctx.Request.UserAgent()
	c.UserId = 0
	createdAt := time.Now()
	c.CreatedAt = &createdAt
	msg := validateComment(c)
	if msg == "" {
		_, err := c.Save()
		if err != nil {
			ctx.JSON(map[string]interface{}{
				"status": "error",
				"msg":    "Can not comment on this post.",
			})
		}
		post.CommentNum++
		err = post.Save()
		if err != nil {
			log.Printf("[Error]: Can not increase comment count for post %v: %v", post.Id, err.Error())
		}
		ctx.JSON(map[string]interface{}{
			"res":     true,
			"comment": c.ToJson(),
		})
		model.NewMessage("comment", c)
	} else {
		ctx.JSON(map[string]interface{}{
			"status": "error",
			"msg":    msg,
		})
	}
}

func validateComment(c *model.Comment) string {
	if utils.IsEmptyString(c.Author) || utils.IsEmptyString(c.Content) {
		return "Name, Email and Content are required fields."
	}
	if !utils.IsEmail(c.Email) {
		return "Email format not valid."
	}
	if !utils.IsEmptyString(c.Website) && !utils.IsURL(c.Website) {
		return "Website URL format not valid."
	}
	return ""
}

func TagHandler(ctx *golf.Context) {
	p := ctx.Param("page")
	page, _ := strconv.Atoi(p)
	t := ctx.Param("tag")
	tagSlug, _ := url.QueryUnescape(t)
	tag, err := model.GetTagBySlug(tagSlug)
	if err != nil {
		NotFoundHandler(ctx)
		return
	}
	posts, pager, err := model.GetPostsByTag(tag.Id, int64(page), 5, true, "published_at DESC")
	data := map[string]interface{}{
		"Articles": posts,
		"Pager":    pager,
		"Tag":      tag,
		"Title":    tag.Name,
	}
	ctx.Loader("theme").Render("tag.html", data)
}

func SiteMapHandler(ctx *golf.Context) {
	baseUrl := model.GetSettingValue("site_url")
	articles, _, _ := model.GetPostList(1, 50, false, true, "published_at DESC")
	navigators := model.GetNavigators()
	now := utils.Now().Format(time.RFC3339)

	articleMap := make([]map[string]string, len(articles))
	for i, a := range articles {
		m := make(map[string]string)
		m["Link"] = strings.Replace(baseUrl+a.Url(), baseUrl+"/", baseUrl, -1)
		m["Created"] = a.PublishedAt.Format(time.RFC3339)
		articleMap[i] = m
	}

	navMap := make([]map[string]string, 0)
	for _, n := range navigators {
		m := make(map[string]string)
		if n.Url == "/" {
			continue
		}
		if strings.HasPrefix(n.Url, "/") {
			m["Link"] = strings.Replace(baseUrl+n.Url, baseUrl+"/", baseUrl, -1)
		} else {
			m["Link"] = n.Url
		}
		m["Created"] = now
		navMap = append(navMap, m)
	}

	ctx.SetHeader("Content-Type", "application/rss+xml;charset=UTF-8")
	ctx.Loader("base").Render("sitemap.xml", map[string]interface{}{
		"Title":      model.GetSettingValue("site_title"),
		"Link":       baseUrl,
		"Created":    now,
		"Articles":   articleMap,
		"Navigators": navMap,
	})
}

func RssHandler(ctx *golf.Context) {
	baseUrl := model.GetSettingValue("site_url")
	articles, _, _ := model.GetPostList(1, 20, false, true, "published_at DESC")
	articleMap := make([]map[string]string, len(articles))
	for i, a := range articles {
		m := make(map[string]string)
		m["Title"] = a.Title
		m["Link"] = a.Url()
		m["Author"] = a.Author.Name
		m["Desc"] = a.Excerpt()
		m["Created"] = a.CreatedAt.Format(time.RFC822)
		articleMap[i] = m
	}

	ctx.SetHeader("Content-Type", "text/xml; charset=utf-8")

	ctx.Loader("base").Loader("base").Render("rss.xml", map[string]interface{}{
		"Title":    model.GetSettingValue("site_title"),
		"Link":     baseUrl,
		"Desc":     model.GetSettingValue("site_description"),
		"Created":  utils.Now().Format(time.RFC822),
		"Articles": articleMap,
	})
}
<|endoftext|>


<commit_before>package main

import (
	"appstax-cli/appstax/commands"
	"appstax-cli/appstax/log"
	"appstax-cli/appstax/term"
	"fmt"
	"github.com/codegangsta/cli"
	"os"
	"os/signal"
	"strings"
)

func main() {
	setupSignals()
	cliApp := setupCli()
	log.Infof("Command to execute: %s", strings.Join(os.Args, " "))
	term.Section()
	cliApp.Run(os.Args)
	term.PrintSection()
}

func setupCli() *cli.App {
	app := cli.NewApp()
	app.Name = "appstax"
	app.Usage = "command line interface for appstax.com"
	app.Version = "1.2.1"

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "verbose",
			Usage: "enable verbose output",
		},
		cli.StringFlag{
			Name:  "baseurl",
			Usage: "set api base url",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:   "signup",
			Usage:  "Create new account",
			Action: commands.DoSignup,
			Flags:  app.Flags,
		},
		{
			Name:   "login",
			Usage:  "Login",
			Action: commands.DoLogin,
			Flags:  app.Flags,
		},
		{
			Name:   "init",
			Usage:  "Initialize current directory as an appstax app",
			Action: commands.DoInit,
			Flags:  app.Flags,
		},
		{
			Name:   "info",
			Usage:  "Info about app configured in current directory",
			Action: commands.DoInfo,
			Flags:  app.Flags,
		},
		{
			Name:   "server",
			Usage:  "Manage your server code",
			Action: commands.DoServer,
			Flags:  app.Flags,
		},
		{
			Name:   "deploy",
			Usage:  "Deploy files to <yourapp>.appstax.io",
			Action: commands.DoDeploy,
			Flags:  app.Flags,
		},
		{
			Name:   "open",
			Usage:  "Open your browser to the specified destination",
			Action: commands.DoOpen,
			Flags:  app.Flags,
		},
		{
			Name:   "logout",
			Usage:  "Logout of current app session",
			Action: commands.DoLogout,
			Flags:  app.Flags,
		},
		{
			Name:   "serve",
			Usage:  "Run development http server",
			Action: commands.DoServe,
			Flags:  app.Flags,
		},
		{
			Name:   "collection",
			Usage:  "Create and view collections",
			Action: commands.DoCollection,
			Flags:  app.Flags,
		},
		{
			Name:   "find",
			Usage:  "Get objects from a collection",
			Action: commands.DoFind,
			Flags:  app.Flags,
		},
		{
			Name:   "relation",
			Usage:  "Create and view relations",
			Action: commands.DoRelation,
			Flags:  app.Flags,
		},
	}

	return app
}

func setupSignals() {
	c := make(chan os.Signal, 10)
	signal.Notify(c, os.Interrupt)
	go func() {
		for _ = range c {
			fmt.Print("\n")
			os.Exit(-1)
		}
	}()
}
<commit_msg>Version 1.2.2<commit_after>package main

import (
	"appstax-cli/appstax/commands"
	"appstax-cli/appstax/log"
	"appstax-cli/appstax/term"
	"fmt"
	"github.com/codegangsta/cli"
	"os"
	"os/signal"
	"strings"
)

func main() {
	setupSignals()
	cliApp := setupCli()
	log.Infof("Command to execute: %s", strings.Join(os.Args, " "))
	term.Section()
	cliApp.Run(os.Args)
	term.PrintSection()
}

func setupCli() *cli.App {
	app := cli.NewApp()
	app.Name = "appstax"
	app.Usage = "command line interface for appstax.com"
	app.Version = "1.2.2"

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "verbose",
			Usage: "enable verbose output",
		},
		cli.StringFlag{
			Name:  "baseurl",
			Usage: "set api base url",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:   "signup",
			Usage:  "Create new account",
			Action: commands.DoSignup,
			Flags:  app.Flags,
		},
		{
			Name:   "login",
			Usage:  "Login",
			Action: commands.DoLogin,
			Flags:  app.Flags,
		},
		{
			Name:   "init",
			Usage:  "Initialize current directory as an appstax app",
			Action: commands.DoInit,
			Flags:  app.Flags,
		},
		{
			Name:   "info",
			Usage:  "Info about app configured in current directory",
			Action: commands.DoInfo,
			Flags:  app.Flags,
		},
		{
			Name:   "server",
			Usage:  "Manage your server code",
			Action: commands.DoServer,
			Flags:  app.Flags,
		},
		{
			Name:   "deploy",
			Usage:  "Deploy files to <yourapp>.appstax.io",
			Action: commands.DoDeploy,
			Flags:  app.Flags,
		},
		{
			Name:   "open",
			Usage:  "Open your browser to the specified destination",
			Action: commands.DoOpen,
			Flags:  app.Flags,
		},
		{
			Name:   "logout",
			Usage:  "Logout of current app session",
			Action: commands.DoLogout,
			Flags:  app.Flags,
		},
		{
			Name:   "serve",
			Usage:  "Run development http server",
			Action: commands.DoServe,
			Flags:  app.Flags,
		},
		{
			Name:   "collection",
			Usage:  "Create and view collections",
			Action: commands.DoCollection,
			Flags:  app.Flags,
		},
		{
			Name:   "find",
			Usage:  "Get objects from a collection",
			Action: commands.DoFind,
			Flags:  app.Flags,
		},
		{
			Name:   "relation",
			Usage:  "Create and view relations",
			Action: commands.DoRelation,
			Flags:  app.Flags,
		},
	}

	return app
}

func setupSignals() {
	c := make(chan os.Signal, 10)
	signal.Notify(c, os.Interrupt)
	go func() {
		for _ = range c {
			fmt.Print("\n")
			os.Exit(-1)
		}
	}()
}
<|endoftext|>