<commit_before>package kallax

import (
	"database/sql"
	"testing"

	"github.com/src-d/go-kallax/types"
	"github.com/stretchr/testify/suite"
)

type OpsSuite struct {
	suite.Suite
	db    *sql.DB
	store *Store
}

func (s *OpsSuite) SetupTest() {
	var err error
	s.db, err = openTestDB()
	s.Nil(err)
	_, err = s.db.Exec(`CREATE TABLE model (
		id uuid PRIMARY KEY,
		name varchar(255) not null,
		email varchar(255) not null,
		age int not null
	)`)
	s.Nil(err)
	_, err = s.db.Exec(`CREATE TABLE slices (
		id uuid PRIMARY KEY,
		elems bigint[]
	)`)
	s.Nil(err)
	s.store = NewStore(s.db)
}

func (s *OpsSuite) TearDownTest() {
	_, err := s.db.Exec("DROP TABLE slices")
	s.NoError(err)

	_, err = s.db.Exec("DROP TABLE model")
	s.NoError(err)
}

func (s *OpsSuite) TestOperators() {
	cases := []struct {
		name  string
		cond  Condition
		count int64
	}{
		{"Eq", Eq(f("name"), "Joe"), 1},
		{"Gt", Gt(f("age"), 1), 2},
		{"Lt", Lt(f("age"), 2), 1},
		{"Neq", Neq(f("name"), "Joe"), 2},
		{"GtOrEq", GtOrEq(f("age"), 2), 2},
		{"LtOrEq", LtOrEq(f("age"), 3), 3},
		{"Not", Not(Eq(f("name"), "Joe")), 2},
		{"And", And(Neq(f("name"), "Joe"), Gt(f("age"), 1)), 2},
		{"Or", Or(Neq(f("name"), "Joe"), Eq(f("age"), 1)), 3},
		{"In", In(f("name"), "Joe", "Jane"), 2},
		{"NotIn", NotIn(f("name"), "Joe", "Jane"), 1},
	}

	s.Nil(s.store.Insert(ModelSchema, newModel("Joe", "", 1)))
	s.Nil(s.store.Insert(ModelSchema, newModel("Jane", "", 2)))
	s.Nil(s.store.Insert(ModelSchema, newModel("Anna", "", 2)))

	for _, c := range cases {
		q := NewBaseQuery(ModelSchema)
		q.Where(c.cond)

		s.Equal(s.store.MustCount(q), c.count, c.name)
	}
}

func (s *OpsSuite) TestArrayOperators() {
	f := f("elems")

	cases := []struct {
		name string
		cond Condition
		ok   bool
	}{
		{"ArrayEq", ArrayEq(f, 1, 2, 3), true},
		{"ArrayEq fail", ArrayEq(f, 1, 2, 2), false},
		{"ArrayNotEq", ArrayNotEq(f, 1, 2, 2), true},
		{"ArrayNotEq fail", ArrayNotEq(f, 1, 2, 3), false},
		{"ArrayGt", ArrayGt(f, 1, 2, 2), true},
		{"ArrayGt all eq", ArrayGt(f, 1, 2, 3), false},
		{"ArrayGt some lt", ArrayGt(f, 1, 3, 1), false},
		{"ArrayLt", ArrayLt(f, 1, 2, 4), true},
		{"ArrayLt all eq", ArrayLt(f, 1, 2, 3), false},
		{"ArrayLt some gt", ArrayLt(f, 1, 1, 4), false},
		{"ArrayGtOrEq", ArrayGtOrEq(f, 1, 2, 2), true},
		{"ArrayGtOrEq all eq", ArrayGtOrEq(f, 1, 2, 3), true},
		{"ArrayGtOrEq some lt", ArrayGtOrEq(f, 1, 3, 1), false},
		{"ArrayLtOrEq", ArrayLtOrEq(f, 1, 2, 4), true},
		{"ArrayLtOrEq all eq", ArrayLtOrEq(f, 1, 2, 3), true},
		{"ArrayLtOrEq some gt", ArrayLtOrEq(f, 1, 1, 4), false},
		{"ArrayContains", ArrayContains(f, 1, 2), true},
		{"ArrayContains fail", ArrayContains(f, 5, 6), false},
		{"ArrayContainedBy", ArrayContainedBy(f, 1, 2, 3, 5, 6), true},
		{"ArrayContainedBy fail", ArrayContainedBy(f, 1, 2, 5, 6), false},
		{"ArrayOverlap", ArrayOverlap(f, 5, 1, 7), true},
		{"ArrayOverlap fail", ArrayOverlap(f, 6, 7, 8, 9), false},
	}

	_, err := s.db.Exec("INSERT INTO slices (id,elems) VALUES ($1, $2)", NewID(), types.Slice([]int64{1, 2, 3}))
	s.NoError(err)

	for _, c := range cases {
		q := NewBaseQuery(SlicesSchema)
		q.Where(c.cond)
		cnt, err := s.store.Count(q)
		s.NoError(err, c.name)
		s.Equal(c.ok, cnt > 0, "success: %s", c.name)
	}
}

func TestOperators(t *testing.T) {
	suite.Run(t, new(OpsSuite))
}

var SlicesSchema = &BaseSchema{
	alias: "_sl",
	table: "slices",
	id:    f("id"),
	columns: []SchemaField{
		f("id"),
		f("elems"),
	},
}
<commit_msg>Implement array operators<commit_after>package kallax

import (
	"database/sql"
	"testing"

	"github.com/src-d/go-kallax/types"
	"github.com/stretchr/testify/suite"
)

type OpsSuite struct {
	suite.Suite
	db    *sql.DB
	store *Store
}

func (s *OpsSuite) SetupTest() {
	var err error
	s.db, err = openTestDB()
	s.Nil(err)
	_, err = s.db.Exec(`CREATE TABLE model (
		id uuid PRIMARY KEY,
		name varchar(255) not null,
		email varchar(255) not null,
		age int not null
	)`)
	s.Nil(err)

	_, err = s.db.Exec(`CREATE TABLE slices (
		id uuid PRIMARY KEY,
		elems bigint[]
	)`)
	s.Nil(err)

	s.store = NewStore(s.db)
}

func (s *OpsSuite) TearDownTest() {
	_, err := s.db.Exec("DROP TABLE slices")
	s.NoError(err)

	_, err = s.db.Exec("DROP TABLE model")
	s.NoError(err)
}

func (s *OpsSuite) TestOperators() {
	cases := []struct {
		name  string
		cond  Condition
		count int64
	}{
		{"Eq", Eq(f("name"), "Joe"), 1},
		{"Gt", Gt(f("age"), 1), 2},
		{"Lt", Lt(f("age"), 2), 1},
		{"Neq", Neq(f("name"), "Joe"), 2},
		{"GtOrEq", GtOrEq(f("age"), 2), 2},
		{"LtOrEq", LtOrEq(f("age"), 3), 3},
		{"Not", Not(Eq(f("name"), "Joe")), 2},
		{"And", And(Neq(f("name"), "Joe"), Gt(f("age"), 1)), 2},
		{"Or", Or(Neq(f("name"), "Joe"), Eq(f("age"), 1)), 3},
		{"In", In(f("name"), "Joe", "Jane"), 2},
		{"NotIn", NotIn(f("name"), "Joe", "Jane"), 1},
	}

	s.Nil(s.store.Insert(ModelSchema, newModel("Joe", "", 1)))
	s.Nil(s.store.Insert(ModelSchema, newModel("Jane", "", 2)))
	s.Nil(s.store.Insert(ModelSchema, newModel("Anna", "", 2)))

	for _, c := range cases {
		q := NewBaseQuery(ModelSchema)
		q.Where(c.cond)

		s.Equal(s.store.MustCount(q), c.count, c.name)
	}
}

func (s *OpsSuite) TestArrayOperators() {
	f := f("elems")

	cases := []struct {
		name string
		cond Condition
		ok   bool
	}{
		{"ArrayEq", ArrayEq(f, 1, 2, 3), true},
		{"ArrayEq fail", ArrayEq(f, 1, 2, 2), false},
		{"ArrayNotEq", ArrayNotEq(f, 1, 2, 2), true},
		{"ArrayNotEq fail", ArrayNotEq(f, 1, 2, 3), false},
		{"ArrayGt", ArrayGt(f, 1, 2, 2), true},
		{"ArrayGt all eq", ArrayGt(f, 1, 2, 3), false},
		{"ArrayGt some lt", ArrayGt(f, 1, 3, 1), false},
		{"ArrayLt", ArrayLt(f, 1, 2, 4), true},
		{"ArrayLt all eq", ArrayLt(f, 1, 2, 3), false},
		{"ArrayLt some gt", ArrayLt(f, 1, 1, 4), false},
		{"ArrayGtOrEq", ArrayGtOrEq(f, 1, 2, 2), true},
		{"ArrayGtOrEq all eq", ArrayGtOrEq(f, 1, 2, 3), true},
		{"ArrayGtOrEq some lt", ArrayGtOrEq(f, 1, 3, 1), false},
		{"ArrayLtOrEq", ArrayLtOrEq(f, 1, 2, 4), true},
		{"ArrayLtOrEq all eq", ArrayLtOrEq(f, 1, 2, 3), true},
		{"ArrayLtOrEq some gt", ArrayLtOrEq(f, 1, 1, 4), false},
		{"ArrayContains", ArrayContains(f, 1, 2), true},
		{"ArrayContains fail", ArrayContains(f, 5, 6), false},
		{"ArrayContainedBy", ArrayContainedBy(f, 1, 2, 3, 5, 6), true},
		{"ArrayContainedBy fail", ArrayContainedBy(f, 1, 2, 5, 6), false},
		{"ArrayOverlap", ArrayOverlap(f, 5, 1, 7), true},
		{"ArrayOverlap fail", ArrayOverlap(f, 6, 7, 8, 9), false},
	}

	_, err := s.db.Exec("INSERT INTO slices (id,elems) VALUES ($1, $2)", NewID(), types.Slice([]int64{1, 2, 3}))
	s.NoError(err)

	for _, c := range cases {
		q := NewBaseQuery(SlicesSchema)
		q.Where(c.cond)
		cnt, err := s.store.Count(q)
		s.NoError(err, c.name)
		s.Equal(c.ok, cnt > 0, "success: %s", c.name)
	}
}

func TestOperators(t *testing.T) {
	suite.Run(t, new(OpsSuite))
}

var SlicesSchema = &BaseSchema{
	alias: "_sl",
	table: "slices",
	id:    f("id"),
	columns: []SchemaField{
		f("id"),
		f("elems"),
	},
}
<|endoftext|>


<commit_before>package ccbridge_test

import (
	"fmt"
	"net/http"
	"os"
	"path/filepath"

	"github.com/cloudfoundry-incubator/inigo/fixtures"
	"github.com/cloudfoundry-incubator/inigo/helpers"
	"github.com/cloudfoundry-incubator/inigo/world"
	"github.com/cloudfoundry-incubator/runtime-schema/models/factories"

	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"
	"github.com/tedsuo/ifrit/grouper"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	archive_helper "github.com/pivotal-golang/archiver/extractor/test_helper"
)

var _ = Describe("AppRunner", func() {
	var appId string

	var (
		runtime ifrit.Process
		bridge  ifrit.Process
	)

	BeforeEach(func() {
		appId = factories.GenerateGuid()

		fileServer, fileServerStaticDir := componentMaker.FileServer()

		runtime = ginkgomon.Invoke(grouper.NewParallel(os.Kill, grouper.Members{
			{"receptor", componentMaker.Receptor()},
			{"exec", componentMaker.Executor()},
			{"rep", componentMaker.Rep()},
			{"auctioneer", componentMaker.Auctioneer()},
			{"route-emitter", componentMaker.RouteEmitter()},
			{"converger", componentMaker.Converger()},
			{"router", componentMaker.Router()},
			{"file-server", fileServer},
		}))

		bridge = ginkgomon.Invoke(grouper.NewParallel(os.Kill, grouper.Members{
			{"tps", componentMaker.TPS()},
			{"nsync-listener", componentMaker.NsyncListener()},
		}))

		archive_helper.CreateZipArchive(
			filepath.Join(fileServerStaticDir, "droplet.zip"),
			fixtures.HelloWorldIndexApp(),
		)

		helpers.Copy(
			componentMaker.Artifacts.Lifecycles[componentMaker.Stack],
			filepath.Join(fileServerStaticDir, world.LifecycleFilename),
		)
	})

	AfterEach(func() {
		helpers.StopProcesses(runtime, bridge)
	})

	Describe("Running", func() {
		Context("when the running message contains a start_command", func() {
			It("runs the app on the executor, registers routes, and shows that they are running via the tps", func() {
				runningMessage := []byte(
					fmt.Sprintf(
						`
						{
							"process_guid": "process-guid",
							"droplet_uri": "%s",
							"stack": "%s",
							"start_command": "bash server.sh",
							"num_instances": 3,
							"memory_mb": 256,
							"disk_mb": 1024,
							"file_descriptors": 16384,
							"environment":[{"name":"VCAP_APPLICATION", "value":"{}"}],
							"routes": ["route-1", "route-2"],
							"log_guid": "%s"
						}
						`,
						fmt.Sprintf("http://%s/v1/static/%s", componentMaker.Addresses.FileServer, "droplet.zip"),
						componentMaker.Stack,
						appId,
					),
				)

				// publish the app run message
				err := natsClient.Publish("diego.desire.app", runningMessage)
				Ω(err).ShouldNot(HaveOccurred())

				// check lrp instance statuses
				Eventually(helpers.RunningLRPInstancesPoller(componentMaker.Addresses.TPS, "process-guid")).Should(HaveLen(3))

				//both routes should be routable
				Eventually(helpers.ResponseCodeFromHostPoller(componentMaker.Addresses.Router, "route-1")).Should(Equal(http.StatusOK))
				Eventually(helpers.ResponseCodeFromHostPoller(componentMaker.Addresses.Router, "route-2")).Should(Equal(http.StatusOK))

				//a given route should route to all three running instances
				poller := helpers.HelloWorldInstancePoller(componentMaker.Addresses.Router, "route-1")
				Eventually(poller).Should(Equal([]string{"0", "1", "2"}))
			})
		})

		Context("when the start message does not include a start_command", func() {
			It("runs the app, registers a route, and shows running via tps", func() {
				runningMessage := []byte(
					fmt.Sprintf(
						`
						{
							"process_guid": "process-guid",
							"droplet_uri": "%s",
							"stack": "%s",
							"num_instances": 1,
							"memory_mb": 256,
							"disk_mb": 1024,
							"file_descriptors": 16384,
							"environment":[{"name":"VCAP_APPLICATION", "value":"{}"}],
							"routes": ["route-1"],
							"log_guid": "%s"
						}
						`,
						fmt.Sprintf("http://%s/v1/static/%s", componentMaker.Addresses.FileServer, "droplet.zip"),
						componentMaker.Stack,
						appId,
					),
				)

				// publish the app run message
				err := natsClient.Publish("diego.desire.app", runningMessage)
				Ω(err).ShouldNot(HaveOccurred())

				Eventually(helpers.RunningLRPInstancesPoller(componentMaker.Addresses.TPS, "process-guid")).Should(HaveLen(1))
				Eventually(helpers.ResponseCodeFromHostPoller(componentMaker.Addresses.Router, "route-1")).Should(Equal(http.StatusOK))

				//a given route should route to the running instance
				poller := helpers.HelloWorldInstancePoller(componentMaker.Addresses.Router, "route-1")
				Eventually(poller).Should(Equal([]string{"0"}))
			})
		})
	})

	Describe("Stop Index", func() {
		Context("when there is an instance running", func() {
			BeforeEach(func() {
				runningMessage := []byte(
					fmt.Sprintf(
						`
						{
							"process_guid": "process-guid",
							"droplet_uri": "%s",
							"stack": "%s",
							"start_command": "bash server.sh",
							"num_instances": 3,
							"environment":[{"name":"VCAP_APPLICATION", "value":"{}"}],
							"memory_mb": 256,
							"disk_mb": 1024,
							"file_descriptors": 16384,
							"routes": ["route-1", "route-2"],
							"log_guid": "%s"
						}
						`,
						fmt.Sprintf("http://%s/v1/static/%s", componentMaker.Addresses.FileServer, "droplet.zip"),
						componentMaker.Stack,
						appId,
					),
				)

				// publish the app run message
				err := natsClient.Publish("diego.desire.app", runningMessage)
				Ω(err).ShouldNot(HaveOccurred())

				// wait for intances to come up
				Eventually(runningIndexPoller(componentMaker.Addresses.TPS, "process-guid")).Should(ConsistOf(0, 1, 2))
			})

			It("stops the app on the desired index, and then eventually starts it back up", func() {
				stopMessage := []byte(`{"process_guid": "process-guid", "index": 1}`)
				err := natsClient.Publish("diego.stop.index", stopMessage)
				Ω(err).ShouldNot(HaveOccurred())

				// wait for stop to take effect
				Eventually(runningIndexPoller(componentMaker.Addresses.TPS, "process-guid")).Should(ConsistOf(0, 2))

				// wait for system to re-converge on desired state
				Eventually(runningIndexPoller(componentMaker.Addresses.TPS, "process-guid")).Should(ConsistOf(0, 1, 2))
			})
		})
	})
})

func runningIndexPoller(tpsAddr string, guid string) func() []int {
	return func() []int {
		indexes := []int{}
		for _, instance := range helpers.RunningLRPInstances(tpsAddr, guid) {
			indexes = append(indexes, int(instance.Index))
		}
		return indexes
	}
}
<commit_msg>only run 2 instances instead of 3<commit_after>package ccbridge_test

import (
	"fmt"
	"net/http"
	"os"
	"path/filepath"

	"github.com/cloudfoundry-incubator/inigo/fixtures"
	"github.com/cloudfoundry-incubator/inigo/helpers"
	"github.com/cloudfoundry-incubator/inigo/world"
	"github.com/cloudfoundry-incubator/runtime-schema/models/factories"

	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"
	"github.com/tedsuo/ifrit/grouper"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	archive_helper "github.com/pivotal-golang/archiver/extractor/test_helper"
)

var _ = Describe("AppRunner", func() {
	var appId string

	var (
		runtime ifrit.Process
		bridge  ifrit.Process
	)

	BeforeEach(func() {
		appId = factories.GenerateGuid()

		fileServer, fileServerStaticDir := componentMaker.FileServer()

		runtime = ginkgomon.Invoke(grouper.NewParallel(os.Kill, grouper.Members{
			{"receptor", componentMaker.Receptor()},
			{"exec", componentMaker.Executor()},
			{"rep", componentMaker.Rep()},
			{"auctioneer", componentMaker.Auctioneer()},
			{"route-emitter", componentMaker.RouteEmitter()},
			{"converger", componentMaker.Converger()},
			{"router", componentMaker.Router()},
			{"file-server", fileServer},
		}))

		bridge = ginkgomon.Invoke(grouper.NewParallel(os.Kill, grouper.Members{
			{"tps", componentMaker.TPS()},
			{"nsync-listener", componentMaker.NsyncListener()},
		}))

		archive_helper.CreateZipArchive(
			filepath.Join(fileServerStaticDir, "droplet.zip"),
			fixtures.HelloWorldIndexApp(),
		)

		helpers.Copy(
			componentMaker.Artifacts.Lifecycles[componentMaker.Stack],
			filepath.Join(fileServerStaticDir, world.LifecycleFilename),
		)
	})

	AfterEach(func() {
		helpers.StopProcesses(runtime, bridge)
	})

	Describe("Running", func() {
		Context("when the running message contains a start_command", func() {
			It("runs the app on the executor, registers routes, and shows that they are running via the tps", func() {
				runningMessage := []byte(
					fmt.Sprintf(
						`
						{
							"process_guid": "process-guid",
							"droplet_uri": "%s",
							"stack": "%s",
							"start_command": "bash server.sh",
							"num_instances": 2,
							"memory_mb": 256,
							"disk_mb": 1024,
							"file_descriptors": 16384,
							"environment":[{"name":"VCAP_APPLICATION", "value":"{}"}],
							"routes": ["route-1", "route-2"],
							"log_guid": "%s"
						}
						`,
						fmt.Sprintf("http://%s/v1/static/%s", componentMaker.Addresses.FileServer, "droplet.zip"),
						componentMaker.Stack,
						appId,
					),
				)

				// publish the app run message
				err := natsClient.Publish("diego.desire.app", runningMessage)
				Ω(err).ShouldNot(HaveOccurred())

				// check lrp instance statuses
				Eventually(helpers.RunningLRPInstancesPoller(componentMaker.Addresses.TPS, "process-guid")).Should(HaveLen(2))

				//both routes should be routable
				Eventually(helpers.ResponseCodeFromHostPoller(componentMaker.Addresses.Router, "route-1")).Should(Equal(http.StatusOK))
				Eventually(helpers.ResponseCodeFromHostPoller(componentMaker.Addresses.Router, "route-2")).Should(Equal(http.StatusOK))

				//a given route should route to all three running instances
				poller := helpers.HelloWorldInstancePoller(componentMaker.Addresses.Router, "route-1")
				Eventually(poller).Should(Equal([]string{"0", "1"}))
			})
		})

		Context("when the start message does not include a start_command", func() {
			It("runs the app, registers a route, and shows running via tps", func() {
				runningMessage := []byte(
					fmt.Sprintf(
						`
						{
							"process_guid": "process-guid",
							"droplet_uri": "%s",
							"stack": "%s",
							"num_instances": 1,
							"memory_mb": 256,
							"disk_mb": 1024,
							"file_descriptors": 16384,
							"environment":[{"name":"VCAP_APPLICATION", "value":"{}"}],
							"routes": ["route-1"],
							"log_guid": "%s"
						}
						`,
						fmt.Sprintf("http://%s/v1/static/%s", componentMaker.Addresses.FileServer, "droplet.zip"),
						componentMaker.Stack,
						appId,
					),
				)

				// publish the app run message
				err := natsClient.Publish("diego.desire.app", runningMessage)
				Ω(err).ShouldNot(HaveOccurred())

				Eventually(helpers.RunningLRPInstancesPoller(componentMaker.Addresses.TPS, "process-guid")).Should(HaveLen(1))
				Eventually(helpers.ResponseCodeFromHostPoller(componentMaker.Addresses.Router, "route-1")).Should(Equal(http.StatusOK))

				//a given route should route to the running instance
				poller := helpers.HelloWorldInstancePoller(componentMaker.Addresses.Router, "route-1")
				Eventually(poller).Should(Equal([]string{"0"}))
			})
		})
	})

	Describe("Stop Index", func() {
		Context("when there is an instance running", func() {
			BeforeEach(func() {
				runningMessage := []byte(
					fmt.Sprintf(
						`
						{
							"process_guid": "process-guid",
							"droplet_uri": "%s",
							"stack": "%s",
							"start_command": "bash server.sh",
							"num_instances": 2,
							"environment":[{"name":"VCAP_APPLICATION", "value":"{}"}],
							"memory_mb": 256,
							"disk_mb": 1024,
							"file_descriptors": 16384,
							"routes": ["route-1", "route-2"],
							"log_guid": "%s"
						}
						`,
						fmt.Sprintf("http://%s/v1/static/%s", componentMaker.Addresses.FileServer, "droplet.zip"),
						componentMaker.Stack,
						appId,
					),
				)

				// publish the app run message
				err := natsClient.Publish("diego.desire.app", runningMessage)
				Ω(err).ShouldNot(HaveOccurred())

				// wait for intances to come up
				Eventually(runningIndexPoller(componentMaker.Addresses.TPS, "process-guid")).Should(ConsistOf(0, 1))
			})

			It("stops the app on the desired index, and then eventually starts it back up", func() {
				stopMessage := []byte(`{"process_guid": "process-guid", "index": 0}`)
				err := natsClient.Publish("diego.stop.index", stopMessage)
				Ω(err).ShouldNot(HaveOccurred())

				// wait for stop to take effect
				Eventually(runningIndexPoller(componentMaker.Addresses.TPS, "process-guid")).Should(ConsistOf(1))

				// wait for system to re-converge on desired state
				Eventually(runningIndexPoller(componentMaker.Addresses.TPS, "process-guid")).Should(ConsistOf(0, 1))
			})
		})
	})
})

func runningIndexPoller(tpsAddr string, guid string) func() []int {
	return func() []int {
		indexes := []int{}
		for _, instance := range helpers.RunningLRPInstances(tpsAddr, guid) {
			indexes = append(indexes, int(instance.Index))
		}
		return indexes
	}
}
<|endoftext|>


<commit_before>// Package dnsimple implements a DNS provider for solving the DNS-01 challenge
// using dnsimple DNS.
package dnsimple

import (
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/dnsimple/dnsimple-go/dnsimple"
	"github.com/xenolf/lego/acme"
)

// DNSProvider is an implementation of the acme.ChallengeProvider interface.
type DNSProvider struct {
	client *dnsimple.Client
}

// NewDNSProvider returns a DNSProvider instance configured for dnsimple.
// Credentials must be passed in the environment variables: DNSIMPLE_OAUTH_TOKEN.
//
// See: https://developer.dnsimple.com/v2/#authentication
func NewDNSProvider() (*DNSProvider, error) {
	accessToken := os.Getenv("DNSIMPLE_OAUTH_TOKEN")
	baseUrl := os.Getenv("DNSIMPLE_BASE_URL")

	return NewDNSProviderCredentials(accessToken, baseUrl)
}

// NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for dnsimple.
func NewDNSProviderCredentials(accessToken, baseUrl string) (*DNSProvider, error) {
	if accessToken == "" {
		return nil, fmt.Errorf("DNSimple OAuth token is missing")
	}

	client := dnsimple.NewClient(dnsimple.NewOauthTokenCredentials(accessToken))
	client.UserAgent = "lego"

	if baseUrl != "" {
		client.BaseURL = baseUrl
	}

	return &DNSProvider{client: client}, nil
}

// Present creates a TXT record to fulfil the dns-01 challenge.
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
	fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)

	zoneName, err := c.getHostedZone(domain)

	if err != nil {
		return err
	}

	accountID, err := c.getAccountID()
	if err != nil {
		return err
	}

	recordAttributes := c.newTxtRecord(zoneName, fqdn, value, ttl)
	_, err = c.client.Zones.CreateRecord(accountID, zoneName, *recordAttributes)
	if err != nil {
		return fmt.Errorf("DNSimple API call failed: %v", err)
	}

	return nil
}

// CleanUp removes the TXT record matching the specified parameters.
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
	fqdn, _, _ := acme.DNS01Record(domain, keyAuth)

	records, err := c.findTxtRecords(domain, fqdn)
	if err != nil {
		return err
	}

	accountID, err := c.getAccountID()
	if err != nil {
		return err
	}

	for _, rec := range records {
		_, err := c.client.Zones.DeleteRecord(accountID, rec.ZoneID, rec.ID)
		if err != nil {
			return err
		}
	}

	return nil
}

func (c *DNSProvider) getHostedZone(domain string) (string, error) {
	authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
	if err != nil {
		return "", err
	}

	accountID, err := c.getAccountID()
	if err != nil {
		return "", err
	}

	zoneName := acme.UnFqdn(authZone)

	zones, err := c.client.Zones.ListZones(accountID, &dnsimple.ZoneListOptions{NameLike: zoneName})
	if err != nil {
		return "", fmt.Errorf("DNSimple API call failed: %v", err)
	}

	var hostedZone dnsimple.Zone
	for _, zone := range zones.Data {
		if zone.Name == zoneName {
			hostedZone = zone
		}
	}

	if hostedZone.ID == 0 {
		return "", fmt.Errorf("Zone %s not found in DNSimple for domain %s", authZone, domain)

	}

	return hostedZone.Name, nil
}

func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnsimple.ZoneRecord, error) {
	zoneName, err := c.getHostedZone(domain)
	if err != nil {
		return nil, err
	}

	accountID, err := c.getAccountID()
	if err != nil {
		return nil, err
	}

	recordName := c.extractRecordName(fqdn, zoneName)

	result, err := c.client.Zones.ListRecords(accountID, zoneName, &dnsimple.ZoneRecordListOptions{Name: recordName, Type: "TXT", ListOptions: dnsimple.ListOptions{}})
	if err != nil {
		return []dnsimple.ZoneRecord{}, fmt.Errorf("DNSimple API call has failed: %v", err)
	}

	return result.Data, nil
}

func (c *DNSProvider) newTxtRecord(zoneName, fqdn, value string, ttl int) *dnsimple.ZoneRecord {
	name := c.extractRecordName(fqdn, zoneName)

	return &dnsimple.ZoneRecord{
		Type:    "TXT",
		Name:    name,
		Content: value,
		TTL:     ttl,
	}
}

func (c *DNSProvider) extractRecordName(fqdn, domain string) string {
	name := acme.UnFqdn(fqdn)
	if idx := strings.Index(name, "."+domain); idx != -1 {
		return name[:idx]
	}
	return name
}

func (c *DNSProvider) getAccountID() (string, error) {
	whoamiResponse, err := c.client.Identity.Whoami()
	if err != nil {
		return "", err
	}

	if whoamiResponse.Data.Account == nil {
		return "", fmt.Errorf("DNSimple user tokens are not supported, please use an account token.")
	}

	return strconv.Itoa(whoamiResponse.Data.Account.ID), nil
}
<commit_msg>Fix dnsimple api (#529)<commit_after>// Package dnsimple implements a DNS provider for solving the DNS-01 challenge
// using dnsimple DNS.
package dnsimple

import (
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/dnsimple/dnsimple-go/dnsimple"
	"github.com/xenolf/lego/acme"
)

// DNSProvider is an implementation of the acme.ChallengeProvider interface.
type DNSProvider struct {
	client *dnsimple.Client
}

// NewDNSProvider returns a DNSProvider instance configured for dnsimple.
// Credentials must be passed in the environment variables: DNSIMPLE_OAUTH_TOKEN.
//
// See: https://developer.dnsimple.com/v2/#authentication
func NewDNSProvider() (*DNSProvider, error) {
	accessToken := os.Getenv("DNSIMPLE_OAUTH_TOKEN")
	baseUrl := os.Getenv("DNSIMPLE_BASE_URL")

	return NewDNSProviderCredentials(accessToken, baseUrl)
}

// NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for dnsimple.
func NewDNSProviderCredentials(accessToken, baseUrl string) (*DNSProvider, error) {
	if accessToken == "" {
		return nil, fmt.Errorf("DNSimple OAuth token is missing")
	}

	client := dnsimple.NewClient(dnsimple.NewOauthTokenCredentials(accessToken))
	client.UserAgent = "lego"

	if baseUrl != "" {
		client.BaseURL = baseUrl
	}

	return &DNSProvider{client: client}, nil
}

// Present creates a TXT record to fulfil the dns-01 challenge.
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
	fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)

	zoneName, err := c.getHostedZone(domain)

	if err != nil {
		return err
	}

	accountID, err := c.getAccountID()
	if err != nil {
		return err
	}

	recordAttributes := c.newTxtRecord(zoneName, fqdn, value, ttl)
	_, err = c.client.Zones.CreateRecord(accountID, zoneName, *recordAttributes)
	if err != nil {
		return fmt.Errorf("DNSimple API call failed: %v", err)
	}

	return nil
}

// CleanUp removes the TXT record matching the specified parameters.
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
	fqdn, _, _ := acme.DNS01Record(domain, keyAuth)

	records, err := c.findTxtRecords(domain, fqdn)
	if err != nil {
		return err
	}

	accountID, err := c.getAccountID()
	if err != nil {
		return err
	}

	for _, rec := range records {
		_, err := c.client.Zones.DeleteRecord(accountID, rec.ZoneID, rec.ID)
		if err != nil {
			return err
		}
	}

	return nil
}

func (c *DNSProvider) getHostedZone(domain string) (string, error) {
	authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
	if err != nil {
		return "", err
	}

	accountID, err := c.getAccountID()
	if err != nil {
		return "", err
	}

	zoneName := acme.UnFqdn(authZone)

	zones, err := c.client.Zones.ListZones(accountID, &dnsimple.ZoneListOptions{NameLike: zoneName})
	if err != nil {
		return "", fmt.Errorf("DNSimple API call failed: %v", err)
	}

	var hostedZone dnsimple.Zone
	for _, zone := range zones.Data {
		if zone.Name == zoneName {
			hostedZone = zone
		}
	}

	if hostedZone.ID == 0 {
		return "", fmt.Errorf("Zone %s not found in DNSimple for domain %s", authZone, domain)

	}

	return hostedZone.Name, nil
}

func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnsimple.ZoneRecord, error) {
	zoneName, err := c.getHostedZone(domain)
	if err != nil {
		return nil, err
	}

	accountID, err := c.getAccountID()
	if err != nil {
		return nil, err
	}

	recordName := c.extractRecordName(fqdn, zoneName)

	result, err := c.client.Zones.ListRecords(accountID, zoneName, &dnsimple.ZoneRecordListOptions{Name: recordName, Type: "TXT", ListOptions: dnsimple.ListOptions{}})
	if err != nil {
		return []dnsimple.ZoneRecord{}, fmt.Errorf("DNSimple API call has failed: %v", err)
	}

	return result.Data, nil
}

func (c *DNSProvider) newTxtRecord(zoneName, fqdn, value string, ttl int) *dnsimple.ZoneRecord {
	name := c.extractRecordName(fqdn, zoneName)

	return &dnsimple.ZoneRecord{
		Type:    "TXT",
		Name:    name,
		Content: value,
		TTL:     ttl,
	}
}

func (c *DNSProvider) extractRecordName(fqdn, domain string) string {
	name := acme.UnFqdn(fqdn)
	if idx := strings.Index(name, "."+domain); idx != -1 {
		return name[:idx]
	}
	return name
}

func (c *DNSProvider) getAccountID() (string, error) {
	whoamiResponse, err := c.client.Identity.Whoami()
	if err != nil {
		return "", err
	}

	if whoamiResponse.Data.Account == nil {
		return "", fmt.Errorf("DNSimple user tokens are not supported, please use an account token.")
	}

	return strconv.FormatInt(whoamiResponse.Data.Account.ID, 10), nil
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor:
// - Aaron Meihm ameihm@mozilla.com

// Parse the output of nasltokens to generate scribe checks
package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"scribe"
	"strings"
)

type checkEntry struct {
	dist    string
	pkgname string
	version string
	op      string
}

type releaseInformation struct {
	nasldist   string
	identifier string
	lsbmatch   string
	defid      string
}

var releaseList = []releaseInformation{
	{"UBUNTU14.10", "utopic", "14.10", ""},
	{"UBUNTU15.04", "vivid", "15.04", ""},
	{"UBUNTU14.04", "trusty", "14.04", ""},
	{"UBUNTU12.04", "precise", "12.04", ""},
	{"UBUNTU10.04", "lucid", "10.04", ""},
}

func addReleaseDefinition(o *scribe.Document, rinfo *releaseInformation) {
	identifier := fmt.Sprintf("reldef-%v", rinfo.identifier)
	rinfo.defid = identifier

	obj := scribe.Object{}
	obj.Object = identifier + "-object"
	obj.FileContent.Path = "/etc"
	obj.FileContent.File = "^lsb-release$"
	obj.FileContent.Expression = "DISTRIB_RELEASE=(\\d{1,2}\\.\\d{1,2})"

	test := scribe.Test{}
	test.TestID = identifier + "-test"
	test.Object = obj.Object
	test.EMatch.Value = rinfo.lsbmatch

	o.Tests = append(o.Tests, test)
	o.Objects = append(o.Objects, obj)
}

func addReleaseDefinitions(o *scribe.Document) {
	for x := range releaseList {
		addReleaseDefinition(o, &releaseList[x])
	}
}

func getReleaseDefinition(nasldist string) string {
	for _, x := range releaseList {
		if nasldist == x.nasldist {
			return x.defid
		}
	}
	return ""
}

func addDefinition(o *scribe.Document, prefix string, check checkEntry) {
	// Don't create a definition for anything that is not in our release
	// list.
	reldefid := getReleaseDefinition(check.dist)
	if reldefid == "" {
		return
	}

	// Create an object definition for the package
	objid := fmt.Sprintf("%v-object", prefix)
	obj := scribe.Object{}
	obj.Object = objid
	obj.Package.Name = check.pkgname

	// Create a test
	testid := fmt.Sprintf("%v-test", prefix)
	test := scribe.Test{}
	test.TestID = testid
	test.Object = obj.Object
	test.EVR.Value = check.version
	test.EVR.Operation = check.op
	disttestref := fmt.Sprintf("%v-test", reldefid)
	test.If = append(test.If, disttestref)

	o.Tests = append(o.Tests, test)
	o.Objects = append(o.Objects, obj)
}

func processEntries(fpath string) {
	root := scribe.Document{}

	addReleaseDefinitions(&root)

	fd, err := os.Open(fpath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}
	defer fd.Close()

	scanner := bufio.NewScanner(fd)
	lv := 0
	for scanner.Scan() {
		lv++
		tokens := strings.Split(scanner.Text(), "|")
		ce := checkEntry{}
		ce.dist = strings.Trim(tokens[0], "\"")
		ce.pkgname = strings.Trim(tokens[1], "\"")
		ce.op = tokens[2]
		ce.version = strings.Trim(tokens[3], "\"")
		prefix := fmt.Sprintf("%v-%v", lv, ce.pkgname)
		addDefinition(&root, prefix, ce)
	}
	if err = scanner.Err(); err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}

	buf, err := json.MarshalIndent(&root, "", "    ")
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}
	fmt.Fprintf(os.Stdout, "%v\n", string(buf))
}

func main() {
	flag.Parse()
	args := flag.Args()
	if len(args) < 1 {
		fmt.Fprintf(os.Stderr, "specify path to nasltokens output\n")
		os.Exit(1)
	}

	processEntries(args[0])
}
<commit_msg>resolve syntax issue with some lts dist names<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor:
// - Aaron Meihm ameihm@mozilla.com

// Parse the output of nasltokens to generate scribe checks
package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"scribe"
	"strings"
)

type checkEntry struct {
	dist    string
	pkgname string
	version string
	op      string
}

type releaseInformation struct {
	nasldist   string
	identifier string
	lsbmatch   string
	defid      string
}

var releaseList = []releaseInformation{
	{"UBUNTU14.10", "utopic", "14.10", ""},
	{"UBUNTU15.04", "vivid", "15.04", ""},
	{"UBUNTU14.04 LTS", "trusty", "14.04", ""},
	{"UBUNTU12.04 LTS", "precise", "12.04", ""},
	{"UBUNTU10.04 LTS", "lucid", "10.04", ""},
}

func addReleaseDefinition(o *scribe.Document, rinfo *releaseInformation) {
	identifier := fmt.Sprintf("reldef-%v", rinfo.identifier)
	rinfo.defid = identifier

	obj := scribe.Object{}
	obj.Object = identifier + "-object"
	obj.FileContent.Path = "/etc"
	obj.FileContent.File = "^lsb-release$"
	obj.FileContent.Expression = "DISTRIB_RELEASE=(\\d{1,2}\\.\\d{1,2})"

	test := scribe.Test{}
	test.TestID = identifier + "-test"
	test.Object = obj.Object
	test.EMatch.Value = rinfo.lsbmatch

	o.Tests = append(o.Tests, test)
	o.Objects = append(o.Objects, obj)
}

func addReleaseDefinitions(o *scribe.Document) {
	for x := range releaseList {
		addReleaseDefinition(o, &releaseList[x])
	}
}

func getReleaseDefinition(nasldist string) string {
	for _, x := range releaseList {
		if nasldist == x.nasldist {
			return x.defid
		}
	}
	return ""
}

func addDefinition(o *scribe.Document, prefix string, check checkEntry) {
	// Don't create a definition for anything that is not in our release
	// list.
	reldefid := getReleaseDefinition(check.dist)
	if reldefid == "" {
		return
	}

	// Create an object definition for the package
	objid := fmt.Sprintf("%v-object", prefix)
	obj := scribe.Object{}
	obj.Object = objid
	obj.Package.Name = check.pkgname

	// Create a test
	testid := fmt.Sprintf("%v-test", prefix)
	test := scribe.Test{}
	test.TestID = testid
	test.Object = obj.Object
	test.EVR.Value = check.version
	test.EVR.Operation = check.op
	disttestref := fmt.Sprintf("%v-test", reldefid)
	test.If = append(test.If, disttestref)

	o.Tests = append(o.Tests, test)
	o.Objects = append(o.Objects, obj)
}

func processEntries(fpath string) {
	root := scribe.Document{}

	addReleaseDefinitions(&root)

	fd, err := os.Open(fpath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}
	defer fd.Close()

	scanner := bufio.NewScanner(fd)
	lv := 0
	for scanner.Scan() {
		lv++
		tokens := strings.Split(scanner.Text(), "|")
		ce := checkEntry{}
		ce.dist = strings.Trim(tokens[0], "\"")
		ce.pkgname = strings.Trim(tokens[1], "\"")
		ce.op = tokens[2]
		ce.version = strings.Trim(tokens[3], "\"")
		prefix := fmt.Sprintf("%v-%v", lv, ce.pkgname)
		addDefinition(&root, prefix, ce)
	}
	if err = scanner.Err(); err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}

	buf, err := json.MarshalIndent(&root, "", "    ")
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}
	fmt.Fprintf(os.Stdout, "%v\n", string(buf))
}

func main() {
	flag.Parse()
	args := flag.Args()
	if len(args) < 1 {
		fmt.Fprintf(os.Stderr, "specify path to nasltokens output\n")
		os.Exit(1)
	}

	processEntries(args[0])
}
<|endoftext|>


<commit_before>package ourtest

import (
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
)

// ShouldEqualJSON asserts eqaulity of two JSON bytes or strings by
// their key / value, regardless of the actual position of those
// key-value pairs
func ShouldEqualJSON(actual interface{}, expected ...interface{}) string {
	if len(expected) != 1 {
		return fmt.Sprintf("ShouldEqualJSON receives only one expected argument")
	}

	actualBytes, err := interfaceToByteSlice(actual)
	if err != nil {
		return fmt.Sprintf("%[1]v is %[1]T, not []byte or string", actual)
	}

	expectedBytes, err := interfaceToByteSlice(expected[0])
	if err != nil {
		return fmt.Sprintf("%[1]v is %[1]T, not []byte or string", expected[0])
	}

	actualMap, expectedMap := map[string]interface{}{}, map[string]interface{}{}

	if err := json.Unmarshal(actualBytes, &actualMap); err != nil {
		return fmt.Sprintf("invalid JSON of L.H.S.: %v; actual = \n%v", err, actual)
	}

	if err := json.Unmarshal(expectedBytes, &expectedMap); err != nil {
		return fmt.Sprintf("invalid JSON of R.H.S.: %v; expected = \n%v", err, expected[0])
	}

	if !reflect.DeepEqual(actualMap, expectedMap) {
		return fmt.Sprintf(`Expected: '%s'
Actual:   '%s'`, prettyPrintJSONMap(expectedMap), prettyPrintJSONMap(actualMap))
	}

	return ""
}

func interfaceToByteSlice(i interface{}) ([]byte, error) {
	if b, ok := i.([]byte); ok {
		return b, nil
	}
	if s, ok := i.(string); ok {
		return []byte(s), nil
	}

	return nil, errors.New("cannot convert")
}

func prettyPrintJSONMap(m map[string]interface{}) []byte {
	b, _ := json.Marshal(&m)
	return b
}
<commit_msg>ourtest: fix ShouldEqualJSON not working on non-map value<commit_after>package ourtest

import (
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
)

// ShouldEqualJSON asserts eqaulity of two JSON bytes or strings by
// their key / value, regardless of the actual position of those
// key-value pairs
func ShouldEqualJSON(actual interface{}, expected ...interface{}) string {
	if len(expected) != 1 {
		return fmt.Sprintf("ShouldEqualJSON receives only one expected argument")
	}

	actualBytes, err := interfaceToByteSlice(actual)
	if err != nil {
		return fmt.Sprintf("%[1]v is %[1]T, not []byte or string", actual)
	}

	expectedBytes, err := interfaceToByteSlice(expected[0])
	if err != nil {
		return fmt.Sprintf("%[1]v is %[1]T, not []byte or string", expected[0])
	}

	var actualJSON, expectedJSON interface{}

	if err := json.Unmarshal(actualBytes, &actualJSON); err != nil {
		return fmt.Sprintf("invalid JSON of L.H.S.: %v; actual = \n%v", err, actual)
	}

	if err := json.Unmarshal(expectedBytes, &expectedJSON); err != nil {
		return fmt.Sprintf("invalid JSON of R.H.S.: %v; expected = \n%v", err, expected[0])
	}

	if !reflect.DeepEqual(actualJSON, expectedJSON) {
		return fmt.Sprintf(`Expected: '%s'
Actual:   '%s'`, prettyPrintJSONMap(expectedJSON), prettyPrintJSONMap(actualJSON))
	}

	return ""
}

func interfaceToByteSlice(i interface{}) ([]byte, error) {
	if b, ok := i.([]byte); ok {
		return b, nil
	}
	if s, ok := i.(string); ok {
		return []byte(s), nil
	}

	return nil, errors.New("cannot convert")
}

func prettyPrintJSONMap(i interface{}) []byte {
	b, _ := json.Marshal(&i)
	return b
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"github.com/nu11p01n73R/walker"
	"os"
	"os/exec"
	"strings"
)

const SEARCH = 0
const NORMAL = 1

// Walks the directory.
// Removes the prefix of current working dir,
// for more clear path strings
// Return
//	[]string List of formated file paths.
func intialWalk(dir string) ([]string, error) {
	prefix := dir + "/"
	return walker.Walk(dir, func(files []string) []string {
		for i, file := range files {
			files[i] = strings.TrimPrefix(file, prefix)
		}
		return files
	})
}

func printList(files []string, cursorAt int, searchString string) {
	fmt.Println()
	for i, file := range files {
		cursor := " "
		if cursorAt == i {
			cursor = ">"
		}
		fmt.Printf("%s %s\n", cursor, file)
	}
	fmt.Printf(">> %s", searchString)
}

func clearScreen() {
	cmd := exec.Command("clear")
	cmd.Stdout = os.Stdout
	cmd.Run()
}

func openEditor(file string) error {
	editor := os.Getenv("EDITOR")
	if len(editor) == 0 {
		return errors.New("Cannot open the file because $EDITOR not set")
	}

	cmd := exec.Command(editor, file)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	return cmd.Run()
}

func toggleMode(curr int) int {
	if curr == SEARCH {
		return NORMAL
	} else {
		return SEARCH
	}
}

func normalMode(char string, files []string, size int, cursorAt *int) (bool, error) {
	var err error
	var done bool

	switch char {
	case "j":
		// pressing j
		if *cursorAt == 0 {
			*cursorAt = size - 1
		} else {
			*cursorAt--
		}
		break
	case "k":
		*cursorAt = (*cursorAt + 1) % size
		break
	case "o":
		err = openEditor(files[*cursorAt])
		done = true
		break
	}
	return done, err
}

func contains(str, search string) bool {
	var j int
	for i := 0; i < len(search); i++ {
		for ; j < len(str); j++ {
			if str[j] == search[i] {
				break
			}
		}

		if j == len(str) {
			return false
		}
	}
	return true
}

func filterFiles(files []string, searchString string) []string {
	output := []string{}
	for i := 0; i < len(files); i++ { //&& contains(files[i], searchString); i++ {
		if contains(files[i], searchString) {
			output = append(output, files[i])
		}
	}
	return output
}

func viewPort(files []string) error {
	var err error
	var searchString string
	var done bool

	size := len(files)
	cursorAt := size - 1
	mode := NORMAL
	initialList := files

	exec.Command("stty", "-F", "/dev/tty", "cbreak", "min", "1").Run()
	exec.Command("stty", "-F", "/dev/tty", "-echo").Run()

	char := make([]byte, 1)
userInput:
	for {
		//		clearScreen()
		printList(files, cursorAt, searchString)

		os.Stdin.Read(char)

		fmt.Println("press", char)
		if char[0] == 27 {
			mode = toggleMode(mode)
			continue
		}
		if char[0] == 127 {
			searchString = searchString[:len(searchString)-1]
			files = initialList
		}

		if mode == NORMAL {
			done, err = normalMode(string(char), files, size, &cursorAt)
			if done {
				break userInput
			}
		} else {
			searchString += string(char)
			files = filterFiles(files, searchString)
			size = len(files)
			cursorAt = size - 1
		}

	}
	return err
}

func cleanUp() {
	exec.Command("stty", "-F", "/dev/tty", "echo").Run()
}

// Handles any errors
func handleError(err error) {
	if err != nil {
		cleanUp()
		fmt.Println(err.Error())
		os.Exit(1)
	}
}

func main() {
	pwd, err := os.Getwd()
	handleError(err)

	files, err := intialWalk(pwd)
	handleError(err)

	err = viewPort(files)
	handleError(err)

	cleanUp()
}
<commit_msg>Implement key handler<commit_after>package main

import (
	"errors"
	"fmt"
	"github.com/nu11p01n73R/walker"
	"os"
	"os/exec"
	"strings"
)

const SEARCH = 0
const NORMAL = 1

// Event Type
const (
	NOP       = iota
	UPWARD    = iota
	DONWARD   = iota
	TOGGLE    = iota
	OPEN      = iota
	BACKSPACE = iota
)

// Walks the directory.
// Removes the prefix of current working dir,
// for more clear path strings
// Return
//	[]string List of formated file paths.
func intialWalk(dir string) ([]string, error) {
	prefix := dir + "/"
	return walker.Walk(dir, func(files []string) []string {
		for i, file := range files {
			files[i] = strings.TrimPrefix(file, prefix)
		}
		return files
	})
}

func printList(files []string, cursorAt int, searchString string) {
	fmt.Println()
	for i, file := range files {
		cursor := " "
		if cursorAt == i {
			cursor = ">"
		}
		fmt.Printf("%s %s\n", cursor, file)
	}
	fmt.Printf(">> %s", searchString)
}

func clearScreen() {
	cmd := exec.Command("clear")
	cmd.Stdout = os.Stdout
	cmd.Run()
}

func openEditor(file string) error {
	editor := os.Getenv("EDITOR")
	if len(editor) == 0 {
		return errors.New("Cannot open the file because $EDITOR not set")
	}

	cmd := exec.Command(editor, file)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	return cmd.Run()
}

func toggleMode(curr int) int {
	if curr == SEARCH {
		return NORMAL
	} else {
		return SEARCH
	}
}

func normalMode(char string, files []string, size int, cursorAt *int) (bool, error) {
	var err error
	var done bool

	switch char {
	case "j":
		// pressing j
		if *cursorAt == 0 {
			*cursorAt = size - 1
		} else {
			*cursorAt--
		}
		break
	case "k":
		*cursorAt = (*cursorAt + 1) % size
		break
	case "o":
		err = openEditor(files[*cursorAt])
		done = true
		break
	}
	return done, err
}

func contains(str, search string) bool {
	var j int
	for i := 0; i < len(search); i++ {
		for ; j < len(str); j++ {
			if str[j] == search[i] {
				break
			}
		}

		if j == len(str) {
			return false
		}
	}
	return true
}

func filterFiles(files []string, searchString string) []string {
	output := []string{}
	for i := 0; i < len(files); i++ { //&& contains(files[i], searchString); i++ {
		if contains(files[i], searchString) {
			output = append(output, files[i])
		}
	}
	return output
}

func keyHandler(key byte, mode int) int {
	switch key {
	case 27:
		return TOGGLE
	case 10:
		return OPEN
	}

	if mode == NORMAL {
		switch key {
		case 106:
			return DONWARD
		case 107:
			return UPWARD
		}
	}

	if mode == SEARCH && key == 127 {
		return BACKSPACE
	}

	return NOP
}

func viewPort(files []string) error {
	var err error
	var searchString string
	var done bool

	size := len(files)
	cursorAt := size - 1
	mode := SEARCH
	initialList := files

	exec.Command("stty", "-F", "/dev/tty", "cbreak", "min", "1").Run()
	exec.Command("stty", "-F", "/dev/tty", "-echo").Run()

	char := make([]byte, 1)
userInput:
	for {
		//		clearScreen()
		//printList(files, cursorAt, searchString)

		os.Stdin.Read(char)

		event := keyHandler(char[0], mode)
		fmt.Println(event)
		continue

		fmt.Println("press", char)
		if char[0] == 27 {
			mode = toggleMode(mode)
			continue
		}

		if mode == NORMAL {
			done, err = normalMode(string(char), files, size, &cursorAt)
			if done {
				break userInput
			}
		} else {
			if char[0] == 127 {
				searchString = searchString[:len(searchString)-2]
				files = initialList
			} else {
				searchString += string(char)
			}
			files = filterFiles(files, searchString)
			size = len(files)
			cursorAt = size - 1
		}

	}
	return err
}

func cleanUp() {
	exec.Command("stty", "-F", "/dev/tty", "echo").Run()
}

// Handles any errors
func handleError(err error) {
	if err != nil {
		cleanUp()
		fmt.Println(err.Error())
		os.Exit(1)
	}
}

func main() {
	pwd, err := os.Getwd()
	handleError(err)

	files, err := intialWalk(pwd)
	handleError(err)

	err = viewPort(files)
	handleError(err)

	cleanUp()
}
<|endoftext|>


<commit_before>package utils_test

import (
	"errors"
	utils "github.com/joaodias/hugito-app/utils"
	"reflect"
	"testing"
)

// Mock a random reader that returns an error
func randomReaderError(n []byte) (int, error) {
	return 0, errors.New("Some error")
}

// Mock a random reader that returns no error
func randomReaderSuccess(n []byte) (int, error) {
	return len(n), nil
}

func TestContainsSubArray(t *testing.T) {
	type args struct {
		sub       []string
		reference []string
	}
	tests := []struct {
		name string
		args args
		want bool
	}{
		{"one string match", args{[]string{"test"}, []string{"test"}}, true},
		{"one string no match", args{[]string{"test"}, []string{"bla"}}, false},
		{"one string partial match", args{[]string{"test"}, []string{"tes"}}, false},
		{"multiple string match", args{[]string{"one", "two", "three", "four", "five"}, []string{"one", "four"}}, true},
		{"multiple string match", args{[]string{"one", "two", "three", "four", "five"}, []string{"six", "seven"}}, false},
	}
	for _, tt := range tests {
		if got := utils.ContainsSubArray(tt.args.sub, tt.args.reference); got != tt.want {
			t.Errorf("%q. ContainsSubArray() = %v, want %v", tt.name, got, tt.want)
		}
	}
}

func TestAreStringsEqual(t *testing.T) {
	type args struct {
		x string
		y string
	}
	tests := []struct {
		name string
		args args
		want bool
	}{
		{"equal strings", args{"abc", "abc"}, true},
		{"different strings", args{"abc", "xyz"}, false},
	}
	for _, tt := range tests {
		if got := utils.AreStringsEqual(tt.args.x, tt.args.y); got != tt.want {
			t.Errorf("%q. AreStringsEqual() = %v, want %v", tt.name, got, tt.want)
		}
	}
}

func TestGenerateRandomBytes(t *testing.T) {
	type args struct {
		n      int
		reader utils.RandomReader
	}
	tests := []struct {
		name    string
		args    args
		want    []byte
		wantErr bool
	}{
		{"read error", args{16, randomReaderError}, nil, true},
		{"read ok", args{16, randomReaderSuccess}, make([]byte, 16), false},
	}
	for _, tt := range tests {
		got, err := utils.GenerateRandomBytes(tt.args.n, tt.args.reader)
		if (err != nil) != tt.wantErr {
			t.Errorf("%q. GenerateRandomBytes() error = %v, wantErr %v", tt.name, err, tt.wantErr)
			continue
		}
		if !reflect.DeepEqual(got, tt.want) {
			t.Errorf("%q. GenerateRandomBytes() = %v, want %v", tt.name, got, tt.want)
		}
	}
}

func TestGenerateRandomString(t *testing.T) {
	type args struct {
		s            int
		randomReader utils.RandomReader
	}
	tests := []struct {
		name    string
		args    args
		want    string
		wantErr bool
	}{
		{"read error", args{16, randomReaderError}, "", true},
		{"read success", args{16, randomReaderSuccess}, "AAAAAAAAAAAAAAAAAAAAAA==", false},
	}
	for _, tt := range tests {
		got, err := utils.GenerateRandomString(tt.args.s, tt.args.randomReader)
		if (err != nil) != tt.wantErr {
			t.Errorf("%q. GenerateRandomString() error = %v, wantErr %v", tt.name, err, tt.wantErr)
			continue
		}
		if got != tt.want {
			t.Errorf("%q. GenerateRandomString() = %v, want %v", tt.name, got, tt.want)
		}
	}
}
<commit_msg>Modify test strategy<commit_after>package utils_test

import (
	"errors"
	"reflect"
	"testing"

	utils "github.com/joaodias/hugito-app/utils"
)

// Mock a random reader that returns an error
func randomReaderError(n []byte) (int, error) {
	return 0, errors.New("Some error")
}

// Mock a random reader that returns no error
func randomReaderSuccess(n []byte) (int, error) {
	return len(n), nil
}

func TestContainsSubArray(t *testing.T) {
	type args struct {
		sub       []string
		reference []string
	}
	tests := []struct {
		name string
		args args
		want bool
	}{
		{"one string match", args{[]string{"test"}, []string{"test"}}, true},
		{"one string no match", args{[]string{"test"}, []string{"bla"}}, false},
		{"one string partial match", args{[]string{"test"}, []string{"tes"}}, false},
		{"multiple string match", args{[]string{"one", "two", "three", "four", "five"}, []string{"one", "four"}}, true},
		{"multiple string match", args{[]string{"one", "two", "three", "four", "five"}, []string{"six", "seven"}}, false},
	}
	for _, tt := range tests {
		if got := utils.ContainsSubArray(tt.args.sub, tt.args.reference); got != tt.want {
			t.Errorf("%q. ContainsSubArray() = %v, want %v", tt.name, got, tt.want)
		}
	}
}

func TestAreStringsEqual(t *testing.T) {
	type args struct {
		x string
		y string
	}
	tests := []struct {
		name string
		args args
		want bool
	}{
		{"equal strings", args{"abc", "abc"}, true},
		{"different strings", args{"abc", "xyz"}, false},
	}
	for _, tt := range tests {
		if got := utils.AreStringsEqual(tt.args.x, tt.args.y); got != tt.want {
			t.Errorf("%q. AreStringsEqual() = %v, want %v", tt.name, got, tt.want)
		}
	}
}

func TestGenerateRandomBytes(t *testing.T) {
	type args struct {
		n      int
		reader utils.RandomReader
	}
	tests := []struct {
		name    string
		args    args
		want    []byte
		wantErr bool
	}{
		{"read error", args{16, randomReaderError}, nil, true},
		{"read ok", args{16, randomReaderSuccess}, make([]byte, 16), false},
	}
	for _, tt := range tests {
		got, err := utils.GenerateRandomBytes(tt.args.n, tt.args.reader)
		if (err != nil) != tt.wantErr {
			t.Errorf("%q. GenerateRandomBytes() error = %v, wantErr %v", tt.name, err, tt.wantErr)
			continue
		}
		if !reflect.DeepEqual(got, tt.want) {
			t.Errorf("%q. GenerateRandomBytes() = %v, want %v", tt.name, got, tt.want)
		}
	}
}

func TestGenerateRandomString(t *testing.T) {
	type args struct {
		s            int
		randomReader utils.RandomReader
	}
	tests := []struct {
		name    string
		args    args
		want    string
		wantErr bool
	}{
		{"read error", args{16, randomReaderError}, "", true},
		{"read success", args{16, randomReaderSuccess}, "AAAAAAAAAAAAAAAAAAAAAA==", false},
	}
	for _, tt := range tests {
		got, err := utils.GenerateRandomString(tt.args.s, tt.args.randomReader)
		if (err != nil) != tt.wantErr {
			t.Errorf("%q. GenerateRandomString() error = %v, wantErr %v", tt.name, err, tt.wantErr)
			continue
		}
		if got != tt.want {
			t.Errorf("%q. GenerateRandomString() = %v, want %v", tt.name, got, tt.want)
		}
	}
}
<|endoftext|>


<commit_before>package sfml

//#include <SFML/Window.h>
import "C"

type Vector2i struct {
	X int
	Y int
}

type Vector2u struct {
	X uint
	Y uint
}

type Vector2f struct {
	X float32
	Y float32
}

type Vector3f struct {
	X float32
	Y float32
	Z float32
}

func goVector2i(v *C.sfVector2i) *Vector2i {
	return &Vector2i{
		int(v.x),
		int(v.y),
	}
}

func cVector2i(v *Vector2i) C.sfVector2i {
	return C.sfVector2i{
		C.int(v.X),
		C.int(v.Y),
	}
}

func goVector2u(v *C.sfVector2u) *Vector2u {
	return &Vector2u{
		uint(v.x),
		uint(v.y),
	}
}

func cVector2u(v *Vector2u) C.sfVector2u {
	return C.sfVector2u{
		C.uint(v.X),
		C.uint(v.Y),
	}
}

func goVector2f(v *C.sfVector2f) *Vector2f {
	return &Vector2f{
		float32(v.x),
		float32(v.y),
	}
}

func cVector2f(v *Vector2f) C.sfVector2f {
	return C.sfVector2f{
		C.float(v.X),
		C.float(v.Y),
	}
}

func goVector3f(v *C.sfVector3f) *Vector3f {
	return &Vector3f{
		float32(v.x),
		float32(v.y),
		float32(v.z),
	}
}

func cVector3f(v *Vector3f) C.sfVector3f {
	return C.sfVector3f{
		C.float(v.X),
		C.float(v.Y),
		C.float(v.Z),
	}
}
<commit_msg>Update #1<commit_after>package sfml

//#include <SFML/Window.h>
import "C"

type Vector2i struct {
	X int
	Y int
}

type Vector2u struct {
	X uint
	Y uint
}

type Vector2f struct {
	X float32
	Y float32
}

type Vector3f struct {
	X float32
	Y float32
	Z float32
}

func (v Vector2i) IsEqual(vector Vector2i) bool {
	return v.X == vector.X && v.Y == vector.Y
}

func (v Vector2i) Multiply(vector Vector2i) Vector2i {
	return Vector2i{
		v.X * vector.X,
		v.Y * vector.Y,
	}
}

func (v Vector2i) Divide(vector Vector2i) Vector2i {
	return Vector2i{
		v.X / vector.X,
		v.Y / vector.Y,
	}
}

func (v Vector2i) Add(vector Vector2i) Vector2i {
	return Vector2i{
		v.X + vector.X,
		v.Y + vector.Y,
	}
}

func (v Vector2i) Subtract(vector Vector2i) Vector2i {
	return Vector2i{
		v.X - vector.X,
		v.Y - vector.Y,
	}
}

func (v Vector2u) IsEqual(vector Vector2u) bool {
	return v.X == vector.X && v.Y == vector.Y
}

func (v Vector2u) Multiply(vector Vector2u) Vector2u {
	return Vector2u{
		v.X * vector.X,
		v.Y * vector.Y,
	}
}

func (v Vector2u) Divide(vector Vector2u) Vector2u {
	return Vector2u{
		v.X / vector.X,
		v.Y / vector.Y,
	}
}

func (v Vector2u) Add(vector Vector2u) Vector2u {
	return Vector2u{
		v.X + vector.X,
		v.Y + vector.Y,
	}
}

func (v Vector2u) Subtract(vector Vector2u) Vector2u {
	return Vector2u{
		v.X - vector.X,
		v.Y - vector.Y,
	}
}

func (v Vector2f) IsEqual(vector Vector2f) bool {
	return v.X == vector.X && v.Y == vector.Y
}

func (v Vector2f) Multiply(vector Vector2f) Vector2f {
	return Vector2f{
		v.X * vector.X,
		v.Y * vector.Y,
	}
}

func (v Vector2f) Divide(vector Vector2f) Vector2f {
	return Vector2f{
		v.X / vector.X,
		v.Y / vector.Y,
	}
}

func (v Vector2f) Add(vector Vector2f) Vector2f {
	return Vector2f{
		v.X + vector.X,
		v.Y + vector.Y,
	}
}

func (v Vector2f) Subtract(vector Vector2f) Vector2f {
	return Vector2f{
		v.X - vector.X,
		v.Y - vector.Y,
	}
}

func (v Vector3f) IsEqual(vector Vector3f) bool {
	return v.X == vector.X && v.Y == vector.Y && v.Z == vector.Z
}

func (v Vector3f) Multiply(vector Vector3f) Vector3f {
	return Vector3f{
		v.X * vector.X,
		v.Y * vector.Y,
		v.Z * vector.Z,
	}
}

func (v Vector3f) Divide(vector Vector3f) Vector3f {
	return Vector3f{
		v.X / vector.X,
		v.Y / vector.Y,
		v.Z / vector.Z,
	}
}

func (v Vector3f) Add(vector Vector3f) Vector3f {
	return Vector3f{
		v.X + vector.X,
		v.Y + vector.Y,
		v.Z + vector.Z,
	}
}

func (v Vector3f) Subtract(vector Vector3f) Vector3f {
	return Vector3f{
		v.X - vector.X,
		v.Y - vector.Y,
		v.Z - vector.Z,
	}
}

func goVector2i(v *C.sfVector2i) *Vector2i {
	return &Vector2i{
		int(v.x),
		int(v.y),
	}
}

func cVector2i(v *Vector2i) C.sfVector2i {
	return C.sfVector2i{
		C.int(v.X),
		C.int(v.Y),
	}
}

func goVector2u(v *C.sfVector2u) *Vector2u {
	return &Vector2u{
		uint(v.x),
		uint(v.y),
	}
}

func cVector2u(v *Vector2u) C.sfVector2u {
	return C.sfVector2u{
		C.uint(v.X),
		C.uint(v.Y),
	}
}

func goVector2f(v *C.sfVector2f) *Vector2f {
	return &Vector2f{
		float32(v.x),
		float32(v.y),
	}
}

func cVector2f(v *Vector2f) C.sfVector2f {
	return C.sfVector2f{
		C.float(v.X),
		C.float(v.Y),
	}
}

func goVector3f(v *C.sfVector3f) *Vector3f {
	return &Vector3f{
		float32(v.x),
		float32(v.y),
		float32(v.z),
	}
}

func cVector3f(v *Vector3f) C.sfVector3f {
	return C.sfVector3f{
		C.float(v.X),
		C.float(v.Y),
		C.float(v.Z),
	}
}
<|endoftext|>


<commit_before>package lsproduct

import (
	"errors"
	"fmt"
	"strconv"
)

func LargestSeriesProduct(digits string, span int) (largestProduct int64, e error) {
	if span < 0 {
		return -1, errors.New("span must not be negative")
	}
	if span > len(digits) {
		return -1, errors.New("span must be larger than digits length")
	}
	for i := 0; i <= len(digits)-span; i++ {
		product, err := getProduct(digits[i : i+span])
		if err != nil {
			return -1, err
		}
		if product > largestProduct {
			largestProduct = product
		}
	}
	return largestProduct, nil
}

func getProduct(digits string) (product int64, e error) {
	product = 1
	for _, digit := range digits {
		d, err := strconv.Atoi(string(digit))
		if err != nil {
			return -1, fmt.Errorf("cannot conver %v to int", digit)
		}
		product *= int64(d)
	}
	return product, nil
}
<commit_msg>Fix typo<commit_after>package lsproduct

import (
	"errors"
	"fmt"
	"strconv"
)

func LargestSeriesProduct(digits string, span int) (largestProduct int64, e error) {
	if span < 0 {
		return -1, errors.New("span must not be negative")
	}
	if span > len(digits) {
		return -1, errors.New("span must be larger than digits length")
	}
	for i := 0; i <= len(digits)-span; i++ {
		product, err := getProduct(digits[i : i+span])
		if err != nil {
			return -1, err
		}
		if product > largestProduct {
			largestProduct = product
		}
	}
	return largestProduct, nil
}

func getProduct(digits string) (product int64, e error) {
	product = 1
	for _, digit := range digits {
		d, err := strconv.Atoi(string(digit))
		if err != nil {
			return -1, fmt.Errorf("cannot convert %v to int", digit)
		}
		product *= int64(d)
	}
	return product, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/binary"
	"encoding/gob"
	"log"
	"os"
	"time"

	"github.com/szferi/gomdb"
)

type Storage struct {
	env  *mdb.Env
	dbi  *mdb.DBI
	send AmqpSender

	timerRunning bool
	timer        *time.Timer
	nextSend     time.Time
}

func NewStorage(send AmqpSender) (s *Storage, err error) {
	s = new(Storage)
	s.send = send

	s.env, err = mdb.NewEnv()
	if err != nil {
		return
	}

	storageDir := "db"
	err = os.MkdirAll(storageDir, 0755)
	if err != nil {
		return
	}

	// XXX deal with getting and deleting values with duplicate keys.
	err = s.env.Open(storageDir, 0, 0755)
	if err != nil {
		return
	}

	s.timer = time.NewTimer(time.Duration(24) * time.Hour)

	ok, t, err := s.nextTime()
	if err != nil {
		return
	}

	if ok {
		s.resetTimer(t)
	}

	go func() {
		for {
			// XXX change the timer to run a SEND command through raft, and emit on applying that command.
			t := <-s.timer.C
			entries, err := s.get(t)
			// XXX cleanup and shut down properly
			if err != nil {
				log.Fatal("Could not read entries from db: ", err)
			}

			log.Printf("Sending %d entries\n", len(entries))
			for _, e := range entries {
				s.send.C <- e
				err = s.remove(e)
				if err != nil {
					// XXX abort comitting for raft here, so it can retry.
					log.Fatal("Could not remove entry from db: ", err)
				}
			}

			ok, t, err = s.nextTime()
			if err != nil {
				log.Fatal("Could not read next time from db: ", err)
			}

			s.timerRunning = false
			if ok {
				s.resetTimer(t)
			}
		}
	}()

	return
}

func (s *Storage) Close() {
	s.env.Close()
	s.timer.Stop()
}

func (s *Storage) Add(e Entry, r []byte) error {
	txn, err := s.env.BeginTxn(nil, 0)
	if err != nil {
		return err
	}

	dbi, err := txn.DBIOpen(nil, 0)
	if err != nil {
		return err
	}

	k := uint64ToBytes(uint64(e.SendAt.UnixNano()))
	v := r

	txn.Put(dbi, k, v, 0)

	if !s.timerRunning || e.SendAt.Before(s.nextSend) {
		s.resetTimer(e.SendAt)
	}

	txn.Commit()

	return nil
}

func (s *Storage) get(t time.Time) (entries []Entry, err error) {
	txn, err := s.env.BeginTxn(nil, mdb.RDONLY)
	if err != nil {
		log.Println("Error creating transaction: ", err)
		return
	}
	defer txn.Abort()

	dbi, err := txn.DBIOpen(nil, 0)
	if err != nil {
		log.Println("Error opening dbi: ", err)
		return
	}
	defer s.env.DBIClose(dbi)

	cursor, err := txn.CursorOpen(dbi)
	if err != nil {
		log.Println("Error getting cursor: ", err)
		return
	}
	defer cursor.Close()

	sk := uint64(t.UnixNano())
	log.Println("Looking for: ", t, t.UnixNano())

	for {
		k, v, err := cursor.Get(nil, mdb.NEXT)
		if err == mdb.NotFound {
			break
		}
		if err != nil {
			panic(err)
		}

		kt := bytesToUint64(k)
		if kt > sk {
			break
		}

		entry := Entry{}
		dec := gob.NewDecoder(bytes.NewBuffer(v))
		err = dec.Decode(&entry)
		if err != nil {
			panic(err)
		}

		entries = append(entries, entry)
	}

	return
}

// get the next entry send time from the db
func (s *Storage) nextTime() (ok bool, t time.Time, err error) {
	ok = true
	txn, err := s.env.BeginTxn(nil, mdb.RDONLY)
	if err != nil {
		log.Println("Error creating transaction: ", err)
		return
	}
	defer txn.Abort()

	dbi, err := txn.DBIOpen(nil, 0)
	if err != nil {
		log.Println("Error opening dbi: ", err)
		return
	}
	defer s.env.DBIClose(dbi)

	cursor, err := txn.CursorOpen(dbi)
	if err != nil {
		log.Println("Error getting cursor: ", err)
		return
	}
	defer cursor.Close()

	k, _, err := cursor.Get(nil, mdb.FIRST)
	if err == mdb.NotFound {
		err = nil
		ok = false
		return
	}
	if err != nil {
		log.Println("Error reading next time from db: ", err)
		return
	}

	t = time.Unix(0, int64(bytesToUint64(k)))

	return
}

// Remove an emitted entry from the db
func (s *Storage) remove(e Entry) error {
	txn, err := s.env.BeginTxn(nil, 0)
	if err != nil {
		return err
	}

	dbi, err := txn.DBIOpen(nil, 0)
	if err != nil {
		return err
	}

	k := uint64ToBytes(uint64(e.SendAt.UnixNano()))

	err = txn.Del(dbi, k, nil)
	if err != nil {
		return err
	}

	txn.Commit()

	return nil
}

func (s *Storage) resetTimer(t time.Time) {
	if t.Before(time.Now()) {
		t = time.Now()
	}
	log.Println("Setting next timer for: ", t)
	s.timerRunning = true
	s.timer.Reset(t.Sub(time.Now()))
	s.nextSend = t
}

// Converts bytes to an integer
func bytesToUint64(b []byte) uint64 {
	return binary.BigEndian.Uint64(b)
}

// Converts a uint to a byte slice
func uint64ToBytes(u uint64) []byte {
	buf := make([]byte, 8)
	binary.BigEndian.PutUint64(buf, u)
	return buf
}
<commit_msg>Use sub databases for indicies<commit_after>package main

import (
	"bytes"
	"crypto/rand"
	"encoding/binary"
	"encoding/gob"
	"errors"
	"io"
	"log"
	"os"
	"time"

	"github.com/szferi/gomdb"
)

const (
	timeDB  = "time"    // map of emit time to entry uuid
	keyDB   = "keys"    // map of user provided key to entry uuid. optional
	entryDB = "entries" // map of uuid to entry
)

type Storage struct {
	env  *mdb.Env
	dbi  *mdb.DBI
	send AmqpSender

	timerRunning bool
	timer        *time.Timer
	nextSend     time.Time
}

func NewStorage(send AmqpSender) (s *Storage, err error) {
	s = new(Storage)
	s.send = send

	s.env, err = mdb.NewEnv()
	if err != nil {
		return
	}

	storageDir := "db"
	err = os.MkdirAll(storageDir, 0755)
	if err != nil {
		return
	}

	// 3 sub dbs: Entries, time index, and key index
	err = s.env.SetMaxDBs(mdb.DBI(3))
	if err != nil {
		return
	}

	// XXX deal with getting and deleting values with duplicate keys.
	err = s.env.Open(storageDir, 0, 0755)
	if err != nil {
		return
	}

	// Initialize sub dbs
	txn, dbis, err := s.startTxn(false, timeDB, entryDB)
	if err != nil {
		s.abortTxn(txn, dbis)
		return
	}

	err = txn.Commit()
	if err != nil {
		s.closeDBIs(dbis)
		return
	}

	s.timer = time.NewTimer(time.Duration(24) * time.Hour)

	ok, t, err := s.nextTime()
	if err != nil {
		return
	}

	if ok {
		s.resetTimer(t)
	}

	go s.startTimerLoop()
	return
}

func (s *Storage) Close() {
	s.env.Close()
	s.timer.Stop()
}

func (s *Storage) startTimerLoop() {
	for {
		// XXX change the timer to run a SEND command through raft, and emit on applying that command.
		t := <-s.timer.C
		entries, err := s.get(t)
		// XXX cleanup and shut down properly
		if err != nil {
			log.Fatal("Could not read entries from db: ", err)
		}

		log.Printf("Sending %d entries\n", len(entries))
		for _, e := range entries {
			s.send.C <- e
			err = s.remove(e)
			if err != nil {
				// XXX abort comitting for raft here, so it can retry.
				log.Fatal("Could not remove entry from db: ", err)
			}
		}

		ok, t, err := s.nextTime()
		if err != nil {
			log.Fatal("Could not read next time from db: ", err)
		}

		s.timerRunning = false
		if ok {
			s.resetTimer(t)
		}
	}
}

// startTxn is used to start a transaction and open all the associated sub-databases
func (s *Storage) startTxn(readonly bool, open ...string) (*mdb.Txn, []mdb.DBI, error) {
	var txnFlags uint = 0
	var dbiFlags uint = 0
	if readonly {
		txnFlags |= mdb.RDONLY
	} else {
		dbiFlags |= mdb.CREATE
	}

	txn, err := s.env.BeginTxn(nil, txnFlags)
	if err != nil {
		return nil, nil, err
	}

	var dbs []mdb.DBI
	for _, name := range open {
		dbi, err := txn.DBIOpen(&name, dbiFlags)
		if err != nil {
			txn.Abort()
			return nil, nil, err
		}
		dbs = append(dbs, dbi)
	}

	return txn, dbs, nil
}

func (s *Storage) closeDBIs(dbis []mdb.DBI) {
	for _, dbi := range dbis {
		s.env.DBIClose(dbi)
	}
}

func (s *Storage) abortTxn(txn *mdb.Txn, dbis []mdb.DBI) {
	txn.Abort()
	s.closeDBIs(dbis)
}

func (s *Storage) Add(e Entry, r []byte) (err error) {
	txn, dbis, err := s.startTxn(false, timeDB, entryDB)
	if err != nil {
		return
	}
	defer s.closeDBIs(dbis)

	uuid, err := newUUID()
	if err != nil {
		txn.Abort()
		return
	}

	k := uint64ToBytes(uint64(e.SendAt.UnixNano()))

	err = txn.Put(dbis[0], k, uuid, 0)
	if err != nil {
		txn.Abort()
		return
	}
	err = txn.Put(dbis[1], uuid, r, 0)

	if !s.timerRunning || e.SendAt.Before(s.nextSend) {
		s.resetTimer(e.SendAt)
	}

	err = txn.Commit()
	return
}

func (s *Storage) get(t time.Time) (entries []Entry, err error) {
	txn, dbis, err := s.startTxn(true, timeDB, entryDB)
	if err != nil {
		log.Println("Error creating transaction: ", err)
		return
	}
	defer s.abortTxn(txn, dbis)

	cursor, err := txn.CursorOpen(dbis[0])
	if err != nil {
		log.Println("Error getting cursor: ", err)
		return
	}
	defer cursor.Close()

	sk := uint64(t.UnixNano())
	log.Println("Looking for: ", t, t.UnixNano())

	for {
		k, uuid, err := cursor.Get(nil, mdb.NEXT)
		if err == mdb.NotFound {
			break
		}
		if err != nil {
			// XXX don't panic
			panic(err)
		}

		kt := bytesToUint64(k)
		if kt > sk {
			break
		}

		v, err := txn.Get(dbis[1], uuid)
		if err != nil {
			// XXX don't panic
			panic(err)
		}

		entry := Entry{}
		dec := gob.NewDecoder(bytes.NewBuffer(v))
		err = dec.Decode(&entry)
		if err != nil {
			// XXX don't panic
			panic(err)
		}

		entries = append(entries, entry)
	}

	return
}

// get the next entry send time from the db
func (s *Storage) nextTime() (ok bool, t time.Time, err error) {
	ok = true
	txn, dbis, err := s.startTxn(true, timeDB)
	if err != nil {
		log.Println("Error creating transaction: ", err)
		return
	}
	defer s.abortTxn(txn, dbis)

	cursor, err := txn.CursorOpen(dbis[0])
	if err != nil {
		log.Println("Error getting cursor: ", err)
		return
	}
	defer cursor.Close()

	k, _, err := cursor.Get(nil, mdb.FIRST)
	if err == mdb.NotFound {
		err = nil
		ok = false
		return
	}
	if err != nil {
		log.Println("Error reading next time from db: ", err)
		return
	}

	t = time.Unix(0, int64(bytesToUint64(k)))

	return
}

// Remove an emitted entry from the db
func (s *Storage) remove(e Entry) (err error) {
	txn, dbis, err := s.startTxn(false, timeDB, entryDB)
	if err != nil {
		return
	}
	defer s.closeDBIs(dbis)

	k := uint64ToBytes(uint64(e.SendAt.UnixNano()))

	uuid, err := txn.Get(dbis[0], k)
	if err != nil {
		return
	}

	err = txn.Del(dbis[0], k, nil)
	if err != nil {
		return
	}

	err = txn.Del(dbis[1], uuid, nil)
	if err != nil {
		return
	}

	err = txn.Commit()
	return
}

func (s *Storage) resetTimer(t time.Time) {
	if t.Before(time.Now()) {
		t = time.Now()
	}
	log.Println("Setting next timer for: ", t)
	s.timerRunning = true
	s.timer.Reset(t.Sub(time.Now()))
	s.nextSend = t
}

// Converts bytes to an integer
func bytesToUint64(b []byte) uint64 {
	return binary.BigEndian.Uint64(b)
}

// Converts a uint to a byte slice
func uint64ToBytes(u uint64) []byte {
	buf := make([]byte, 8)
	binary.BigEndian.PutUint64(buf, u)
	return buf
}

func newUUID() (uuid []byte, err error) {
	uuid = make([]byte, 16)
	n, err := io.ReadFull(rand.Reader, uuid)
	if n != len(uuid) {
		err = errors.New("Could not create uuid")
	}
	if err != nil {
		return
	}

	// variant bits; see section 4.1.1
	uuid[8] = uuid[8]&^0xc0 | 0x80
	// version 4 (pseudo-random); see section 4.1.3
	uuid[6] = uuid[6]&^0xf0 | 0x40
	return
}
<|endoftext|>


<commit_before>package p2p

import (
	"fmt"
)

const (
	errMagicTokenMismatch = iota
	errRead
	errWrite
	errMisc
	errInvalidMsgCode
	errInvalidMsg
	errP2PVersionMismatch
	errPubkeyInvalid
	errPubkeyForbidden
	errProtocolBreach
	errPingTimeout
	errInvalidNetworkId
	errInvalidProtocolVersion
)

var errorToString = map[int]string{
	errMagicTokenMismatch:     "magic token mismatch",
	errRead:                   "read error",
	errWrite:                  "write error",
	errMisc:                   "misc error",
	errInvalidMsgCode:         "invalid message code",
	errInvalidMsg:             "invalid message",
	errP2PVersionMismatch:     "P2P Version Mismatch",
	errPubkeyInvalid:          "public key invalid",
	errPubkeyForbidden:        "public key forbidden",
	errProtocolBreach:         "protocol Breach",
	errPingTimeout:            "ping timeout",
	errInvalidNetworkId:       "invalid network id",
	errInvalidProtocolVersion: "invalid protocol version",
}

type peerError struct {
	Code    int
	message string
}

func newPeerError(code int, format string, v ...interface{}) *peerError {
	desc, ok := errorToString[code]
	if !ok {
		panic("invalid error code")
	}
	err := &peerError{code, desc}
	if format != "" {
		err.message += ": " + fmt.Sprintf(format, v...)
	}
	return err
}

func (self *peerError) Error() string {
	return self.message
}

type DiscReason byte

const (
	DiscRequested DiscReason = iota
	DiscNetworkError
	DiscProtocolError
	DiscUselessPeer
	DiscTooManyPeers
	DiscAlreadyConnected
	DiscIncompatibleVersion
	DiscInvalidIdentity
	DiscQuitting
	DiscUnexpectedIdentity
	DiscSelf
	DiscReadTimeout
	DiscSubprotocolError
)

var discReasonToString = [...]string{
	DiscRequested:           "Disconnect requested",
	DiscNetworkError:        "Network error",
	DiscProtocolError:       "Breach of protocol",
	DiscUselessPeer:         "Useless peer",
	DiscTooManyPeers:        "Too many peers",
	DiscAlreadyConnected:    "Already connected",
	DiscIncompatibleVersion: "Incompatible P2P protocol version",
	DiscInvalidIdentity:     "Invalid node identity",
	DiscQuitting:            "Client quitting",
	DiscUnexpectedIdentity:  "Unexpected identity",
	DiscSelf:                "Connected to self",
	DiscReadTimeout:         "Read timeout",
	DiscSubprotocolError:    "Subprotocol error",
}

func (d DiscReason) String() string {
	if len(discReasonToString) < int(d) {
		return fmt.Sprintf("Unknown Reason(%d)", d)
	}
	return discReasonToString[d]
}

func (d DiscReason) Error() string {
	return d.String()
}

func discReasonForError(err error) DiscReason {
	if reason, ok := err.(DiscReason); ok {
		return reason
	}
	peerError, ok := err.(*peerError)
	if !ok {
		return DiscSubprotocolError
	}
	switch peerError.Code {
	case errP2PVersionMismatch:
		return DiscIncompatibleVersion
	case errPubkeyInvalid:
		return DiscInvalidIdentity
	case errPubkeyForbidden:
		return DiscUselessPeer
	case errInvalidMsgCode, errMagicTokenMismatch, errProtocolBreach:
		return DiscProtocolError
	case errPingTimeout:
		return DiscReadTimeout
	case errRead, errWrite:
		return DiscNetworkError
	default:
		return DiscSubprotocolError
	}
}
<commit_msg>p2p: make DiscReason bigger than byte<commit_after>package p2p

import (
	"fmt"
)

const (
	errMagicTokenMismatch = iota
	errRead
	errWrite
	errMisc
	errInvalidMsgCode
	errInvalidMsg
	errP2PVersionMismatch
	errPubkeyInvalid
	errPubkeyForbidden
	errProtocolBreach
	errPingTimeout
	errInvalidNetworkId
	errInvalidProtocolVersion
)

var errorToString = map[int]string{
	errMagicTokenMismatch:     "magic token mismatch",
	errRead:                   "read error",
	errWrite:                  "write error",
	errMisc:                   "misc error",
	errInvalidMsgCode:         "invalid message code",
	errInvalidMsg:             "invalid message",
	errP2PVersionMismatch:     "P2P Version Mismatch",
	errPubkeyInvalid:          "public key invalid",
	errPubkeyForbidden:        "public key forbidden",
	errProtocolBreach:         "protocol Breach",
	errPingTimeout:            "ping timeout",
	errInvalidNetworkId:       "invalid network id",
	errInvalidProtocolVersion: "invalid protocol version",
}

type peerError struct {
	Code    int
	message string
}

func newPeerError(code int, format string, v ...interface{}) *peerError {
	desc, ok := errorToString[code]
	if !ok {
		panic("invalid error code")
	}
	err := &peerError{code, desc}
	if format != "" {
		err.message += ": " + fmt.Sprintf(format, v...)
	}
	return err
}

func (self *peerError) Error() string {
	return self.message
}

type DiscReason uint

const (
	DiscRequested DiscReason = iota
	DiscNetworkError
	DiscProtocolError
	DiscUselessPeer
	DiscTooManyPeers
	DiscAlreadyConnected
	DiscIncompatibleVersion
	DiscInvalidIdentity
	DiscQuitting
	DiscUnexpectedIdentity
	DiscSelf
	DiscReadTimeout
	DiscSubprotocolError
)

var discReasonToString = [...]string{
	DiscRequested:           "Disconnect requested",
	DiscNetworkError:        "Network error",
	DiscProtocolError:       "Breach of protocol",
	DiscUselessPeer:         "Useless peer",
	DiscTooManyPeers:        "Too many peers",
	DiscAlreadyConnected:    "Already connected",
	DiscIncompatibleVersion: "Incompatible P2P protocol version",
	DiscInvalidIdentity:     "Invalid node identity",
	DiscQuitting:            "Client quitting",
	DiscUnexpectedIdentity:  "Unexpected identity",
	DiscSelf:                "Connected to self",
	DiscReadTimeout:         "Read timeout",
	DiscSubprotocolError:    "Subprotocol error",
}

func (d DiscReason) String() string {
	if len(discReasonToString) < int(d) {
		return fmt.Sprintf("Unknown Reason(%d)", d)
	}
	return discReasonToString[d]
}

func (d DiscReason) Error() string {
	return d.String()
}

func discReasonForError(err error) DiscReason {
	if reason, ok := err.(DiscReason); ok {
		return reason
	}
	peerError, ok := err.(*peerError)
	if !ok {
		return DiscSubprotocolError
	}
	switch peerError.Code {
	case errP2PVersionMismatch:
		return DiscIncompatibleVersion
	case errPubkeyInvalid:
		return DiscInvalidIdentity
	case errPubkeyForbidden:
		return DiscUselessPeer
	case errInvalidMsgCode, errMagicTokenMismatch, errProtocolBreach:
		return DiscProtocolError
	case errPingTimeout:
		return DiscReadTimeout
	case errRead, errWrite:
		return DiscNetworkError
	default:
		return DiscSubprotocolError
	}
}
<|endoftext|>


<commit_before>package promgrpc

import (
	"context"
	"fmt"
	"strconv"

	"github.com/prometheus/client_golang/prometheus"
	"google.golang.org/grpc/stats"
)

// StatsHandlerCollector is a simple wrapper for stats Handler and prometheus Collector interfaces.
type StatsHandlerCollector interface {
	stats.Handler
	prometheus.Collector
}

var _ StatsHandlerCollector = &StatsHandler{}

// StatsHandler wraps set of stats handlers and coordinate their execution.
// Additionally, it tags RPC requests with a common set of labels.
// That way it reduces context manipulation overhead and improves overall performance.
type StatsHandler struct {
	handlers []StatsHandlerCollector
}

// NewStatsHandler allocates a new coordinator.
// It allows passing a various number of handlers that later it will iterate through.
func NewStatsHandler(handlers ...StatsHandlerCollector) *StatsHandler {
	return &StatsHandler{
		handlers: handlers,
	}
}

// ClientStatsHandler instantiates a default client-side coordinator together with every metric specific stats handler provided by this package.
func ClientStatsHandler(opts ...ShareableOption) *StatsHandler {
	collectorOpts, statsHandlerOpts := optionsSplit(opts...)

	return NewStatsHandler(
		NewClientConnectionsStatsHandler(NewClientConnectionsGaugeVec(collectorOpts...)),
		NewClientRequestsTotalStatsHandler(NewClientRequestsTotalCounterVec(collectorOpts...), statsHandlerOpts...),
		NewClientRequestsInFlightStatsHandler(NewClientRequestsInFlightGaugeVec(collectorOpts...), statsHandlerOpts...),
		NewClientRequestDurationStatsHandler(NewClientRequestDurationHistogramVec(collectorOpts...), statsHandlerOpts...),
		NewClientResponsesTotalStatsHandler(NewClientResponsesTotalCounterVec(collectorOpts...), statsHandlerOpts...),
		NewClientMessagesReceivedTotalStatsHandler(NewClientMessagesReceivedTotalCounterVec(collectorOpts...), statsHandlerOpts...),
		NewClientMessagesSentTotalStatsHandler(NewClientMessagesSentTotalCounterVec(collectorOpts...), statsHandlerOpts...),
		NewClientMessageSentSizeStatsHandler(NewClientMessageSentSizeHistogramVec(collectorOpts...), statsHandlerOpts...),
		NewClientMessageReceivedSizeStatsHandler(NewClientMessageReceivedSizeHistogramVec(collectorOpts...), statsHandlerOpts...),
	)
}

// ClientStatsHandler instantiates a default server-side coordinator together with every metric specific stats handler provided by this package.
func ServerStatsHandler(opts ...ShareableOption) *StatsHandler {
	collectorOpts, statsHandlerOpts := optionsSplit(opts...)

	return NewStatsHandler(
		NewServerConnectionsStatsHandler(NewServerConnectionsGaugeVec(collectorOpts...)),
		NewServerRequestsTotalStatsHandler(NewServerRequestsTotalCounterVec(collectorOpts...), statsHandlerOpts...),
		NewServerRequestsInFlightStatsHandler(NewServerRequestsInFlightGaugeVec(collectorOpts...), statsHandlerOpts...),
		NewServerRequestDurationStatsHandler(NewServerRequestDurationHistogramVec(collectorOpts...), statsHandlerOpts...),
		NewServerResponsesTotalStatsHandler(NewServerResponsesTotalCounterVec(collectorOpts...), statsHandlerOpts...),
		NewServerMessagesReceivedTotalStatsHandler(NewServerMessagesReceivedTotalCounterVec(collectorOpts...), statsHandlerOpts...),
		NewServerMessagesSentTotalStatsHandler(NewServerMessagesSentTotalCounterVec(collectorOpts...), statsHandlerOpts...),
		NewServerMessageSentSizeStatsHandler(NewServerMessageSentSizeHistogramVec(collectorOpts...), statsHandlerOpts...),
		NewServerMessageReceivedSizeStatsHandler(NewServerMessageReceivedSizeHistogramVec(collectorOpts...), statsHandlerOpts...),
	)
}

// TagRPC implements stats Handler interface.
func (h *StatsHandler) TagRPC(ctx context.Context, inf *stats.RPCTagInfo) context.Context {
	service, method := split(inf.FullMethodName)

	ctx = context.WithValue(ctx, tagRPCKey, rpcTagLabels{
		isFailFast:      strconv.FormatBool(inf.FailFast),
		service:         service,
		method:          method,
		clientUserAgent: userAgentOnServerSide(ctx, inf),
	})

	for _, c := range h.handlers {
		ctx = c.TagRPC(ctx, inf)
	}
	return ctx
}

// HandleRPC implements stats Handler interface.
func (h *StatsHandler) HandleRPC(ctx context.Context, sts stats.RPCStats) {
	for _, c := range h.handlers {
		c.HandleRPC(ctx, sts)
	}
}

// TagConn implements stats Handler interface.
func (h *StatsHandler) TagConn(ctx context.Context, inf *stats.ConnTagInfo) context.Context {
	for _, c := range h.handlers {
		ctx = c.TagConn(ctx, inf)
	}
	return ctx
}

// HandleConn implements stats Handler interface.
func (h *StatsHandler) HandleConn(ctx context.Context, sts stats.ConnStats) {
	for _, c := range h.handlers {
		c.HandleConn(ctx, sts)
	}
}

// Describe implements prometheus Collector interface.
func (h *StatsHandler) Describe(in chan<- *prometheus.Desc) {
	for _, c := range h.handlers {
		c.Describe(in)
	}
}

// Collect implements prometheus Collector interface.
func (h *StatsHandler) Collect(in chan<- prometheus.Metric) {
	for _, c := range h.handlers {
		c.Collect(in)
	}
}

type baseStatsHandler struct {
	collector prometheus.Collector
	options   statsHandlerOptions
}

// TagRPC implements stats Handler interface.
func (h *baseStatsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
	if h.options.tagRPCLabelFn != nil {
		return h.options.tagRPCLabelFn(ctx, info)
	}
	return ctx
}

// TagConn implements stats Handler interface.
func (h *baseStatsHandler) TagConn(ctx context.Context, info *stats.ConnTagInfo) context.Context {
	return context.WithValue(ctx, tagConnKey, connTagLabels{
		remoteAddr:      info.RemoteAddr.String(),
		localAddr:       info.LocalAddr.String(),
		clientUserAgent: userAgentOnServerSide(ctx, &stats.RPCTagInfo{}),
	})
}

// HandleConn implements stats Handler interface.
func (h *baseStatsHandler) HandleConn(_ context.Context, _ stats.ConnStats) {
}

// HandleRPC implements stats Handler interface.
func (h *baseStatsHandler) HandleRPC(_ context.Context, _ stats.RPCStats) {
}

// Describe implements prometheus Collector interface.
func (h *baseStatsHandler) Describe(in chan<- *prometheus.Desc) {
	h.collector.Describe(in)
}

// Collect implements prometheus Collector interface.
func (h *baseStatsHandler) Collect(in chan<- prometheus.Metric) {
	h.collector.Collect(in)
}

func (h *baseStatsHandler) applyOpts(opts ...StatsHandlerOption) {
	for _, opt := range opts {
		opt.apply(&h.options)
	}
}

func optionsSplit(opts ...ShareableOption) ([]CollectorOption, []StatsHandlerOption) {
	var (
		collectorOpts    []CollectorOption
		statsHandlerOpts []StatsHandlerOption
	)

	for _, opt := range opts {
		switch val := opt.(type) {
		case StatsHandlerOption:
			statsHandlerOpts = append(statsHandlerOpts, val)
		case CollectorOption:
			collectorOpts = append(collectorOpts, val)
		default:
			panic(fmt.Sprintf("shareable option does not implement any known type: %T", opt))
		}
	}

	return collectorOpts, statsHandlerOpts
}
<commit_msg>baseStatsHandler.TagConn - port removed from remote address tag to reduce cardinality.<commit_after>package promgrpc

import (
	"context"
	"fmt"
	"net"
	"strconv"

	"github.com/prometheus/client_golang/prometheus"
	"google.golang.org/grpc/stats"
)

// StatsHandlerCollector is a simple wrapper for stats Handler and prometheus Collector interfaces.
type StatsHandlerCollector interface {
	stats.Handler
	prometheus.Collector
}

var _ StatsHandlerCollector = &StatsHandler{}

// StatsHandler wraps set of stats handlers and coordinate their execution.
// Additionally, it tags RPC requests with a common set of labels.
// That way it reduces context manipulation overhead and improves overall performance.
type StatsHandler struct {
	handlers []StatsHandlerCollector
}

// NewStatsHandler allocates a new coordinator.
// It allows passing a various number of handlers that later it will iterate through.
func NewStatsHandler(handlers ...StatsHandlerCollector) *StatsHandler {
	return &StatsHandler{
		handlers: handlers,
	}
}

// ClientStatsHandler instantiates a default client-side coordinator together with every metric specific stats handler provided by this package.
func ClientStatsHandler(opts ...ShareableOption) *StatsHandler {
	collectorOpts, statsHandlerOpts := optionsSplit(opts...)

	return NewStatsHandler(
		NewClientConnectionsStatsHandler(NewClientConnectionsGaugeVec(collectorOpts...)),
		NewClientRequestsTotalStatsHandler(NewClientRequestsTotalCounterVec(collectorOpts...), statsHandlerOpts...),
		NewClientRequestsInFlightStatsHandler(NewClientRequestsInFlightGaugeVec(collectorOpts...), statsHandlerOpts...),
		NewClientRequestDurationStatsHandler(NewClientRequestDurationHistogramVec(collectorOpts...), statsHandlerOpts...),
		NewClientResponsesTotalStatsHandler(NewClientResponsesTotalCounterVec(collectorOpts...), statsHandlerOpts...),
		NewClientMessagesReceivedTotalStatsHandler(NewClientMessagesReceivedTotalCounterVec(collectorOpts...), statsHandlerOpts...),
		NewClientMessagesSentTotalStatsHandler(NewClientMessagesSentTotalCounterVec(collectorOpts...), statsHandlerOpts...),
		NewClientMessageSentSizeStatsHandler(NewClientMessageSentSizeHistogramVec(collectorOpts...), statsHandlerOpts...),
		NewClientMessageReceivedSizeStatsHandler(NewClientMessageReceivedSizeHistogramVec(collectorOpts...), statsHandlerOpts...),
	)
}

// ClientStatsHandler instantiates a default server-side coordinator together with every metric specific stats handler provided by this package.
func ServerStatsHandler(opts ...ShareableOption) *StatsHandler {
	collectorOpts, statsHandlerOpts := optionsSplit(opts...)

	return NewStatsHandler(
		NewServerConnectionsStatsHandler(NewServerConnectionsGaugeVec(collectorOpts...)),
		NewServerRequestsTotalStatsHandler(NewServerRequestsTotalCounterVec(collectorOpts...), statsHandlerOpts...),
		NewServerRequestsInFlightStatsHandler(NewServerRequestsInFlightGaugeVec(collectorOpts...), statsHandlerOpts...),
		NewServerRequestDurationStatsHandler(NewServerRequestDurationHistogramVec(collectorOpts...), statsHandlerOpts...),
		NewServerResponsesTotalStatsHandler(NewServerResponsesTotalCounterVec(collectorOpts...), statsHandlerOpts...),
		NewServerMessagesReceivedTotalStatsHandler(NewServerMessagesReceivedTotalCounterVec(collectorOpts...), statsHandlerOpts...),
		NewServerMessagesSentTotalStatsHandler(NewServerMessagesSentTotalCounterVec(collectorOpts...), statsHandlerOpts...),
		NewServerMessageSentSizeStatsHandler(NewServerMessageSentSizeHistogramVec(collectorOpts...), statsHandlerOpts...),
		NewServerMessageReceivedSizeStatsHandler(NewServerMessageReceivedSizeHistogramVec(collectorOpts...), statsHandlerOpts...),
	)
}

// TagRPC implements stats Handler interface.
func (h *StatsHandler) TagRPC(ctx context.Context, inf *stats.RPCTagInfo) context.Context {
	service, method := split(inf.FullMethodName)

	ctx = context.WithValue(ctx, tagRPCKey, rpcTagLabels{
		isFailFast:      strconv.FormatBool(inf.FailFast),
		service:         service,
		method:          method,
		clientUserAgent: userAgentOnServerSide(ctx, inf),
	})

	for _, c := range h.handlers {
		ctx = c.TagRPC(ctx, inf)
	}
	return ctx
}

// HandleRPC implements stats Handler interface.
func (h *StatsHandler) HandleRPC(ctx context.Context, sts stats.RPCStats) {
	for _, c := range h.handlers {
		c.HandleRPC(ctx, sts)
	}
}

// TagConn implements stats Handler interface.
func (h *StatsHandler) TagConn(ctx context.Context, inf *stats.ConnTagInfo) context.Context {
	for _, c := range h.handlers {
		ctx = c.TagConn(ctx, inf)
	}
	return ctx
}

// HandleConn implements stats Handler interface.
func (h *StatsHandler) HandleConn(ctx context.Context, sts stats.ConnStats) {
	for _, c := range h.handlers {
		c.HandleConn(ctx, sts)
	}
}

// Describe implements prometheus Collector interface.
func (h *StatsHandler) Describe(in chan<- *prometheus.Desc) {
	for _, c := range h.handlers {
		c.Describe(in)
	}
}

// Collect implements prometheus Collector interface.
func (h *StatsHandler) Collect(in chan<- prometheus.Metric) {
	for _, c := range h.handlers {
		c.Collect(in)
	}
}

type baseStatsHandler struct {
	collector prometheus.Collector
	options   statsHandlerOptions
}

// TagRPC implements stats Handler interface.
func (h *baseStatsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
	if h.options.tagRPCLabelFn != nil {
		return h.options.tagRPCLabelFn(ctx, info)
	}
	return ctx
}

// TagConn implements stats Handler interface.
func (h *baseStatsHandler) TagConn(ctx context.Context, info *stats.ConnTagInfo) context.Context {
	remoteAddr, _, _ := net.SplitHostPort(info.RemoteAddr.String())

	return context.WithValue(ctx, tagConnKey, connTagLabels{
		remoteAddr:      remoteAddr,
		localAddr:       info.LocalAddr.String(),
		clientUserAgent: userAgentOnServerSide(ctx, &stats.RPCTagInfo{}),
	})
}

// HandleConn implements stats Handler interface.
func (h *baseStatsHandler) HandleConn(_ context.Context, _ stats.ConnStats) {
}

// HandleRPC implements stats Handler interface.
func (h *baseStatsHandler) HandleRPC(_ context.Context, _ stats.RPCStats) {
}

// Describe implements prometheus Collector interface.
func (h *baseStatsHandler) Describe(in chan<- *prometheus.Desc) {
	h.collector.Describe(in)
}

// Collect implements prometheus Collector interface.
func (h *baseStatsHandler) Collect(in chan<- prometheus.Metric) {
	h.collector.Collect(in)
}

func (h *baseStatsHandler) applyOpts(opts ...StatsHandlerOption) {
	for _, opt := range opts {
		opt.apply(&h.options)
	}
}

func optionsSplit(opts ...ShareableOption) ([]CollectorOption, []StatsHandlerOption) {
	var (
		collectorOpts    []CollectorOption
		statsHandlerOpts []StatsHandlerOption
	)

	for _, opt := range opts {
		switch val := opt.(type) {
		case StatsHandlerOption:
			statsHandlerOpts = append(statsHandlerOpts, val)
		case CollectorOption:
			collectorOpts = append(collectorOpts, val)
		default:
			panic(fmt.Sprintf("shareable option does not implement any known type: %T", opt))
		}
	}

	return collectorOpts, statsHandlerOpts
}
<|endoftext|>