<commit_before>package integration_test

import (
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/http/httptest"
	"path/filepath"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"

	"github.com/rosenhouse/tubes/integration"
)

var _ = Describe("Integration (mocking out AWS)", func() {
	var (
		stackName  string
		envVars    map[string]string
		workingDir string
		fakeAWS    *integration.FakeAWS
		start      func(args ...string) *gexec.Session

		manifestServer *httptest.Server
		boshIOServer   *httptest.Server
	)

	BeforeEach(func() {
		stackName = fmt.Sprintf("tubes-acceptance-test-%x", rand.Int())
		var err error
		workingDir, err = ioutil.TempDir("", "tubes-acceptance-test")
		Expect(err).NotTo(HaveOccurred())

		logger := integration.NewAWSCallLogger(GinkgoWriter)
		fakeAWS = integration.NewFakeAWS(logger)

		concourseManifestTemplate, err := ioutil.ReadFile("fixtures/concourse-template.yml")
		Expect(err).NotTo(HaveOccurred())
		manifestServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.Write(concourseManifestTemplate)
		}))

		boshIOServer = httptest.NewServer(&integration.FakeBoshIO{})

		envVars = map[string]string{
			"AWS_DEFAULT_REGION":                    "us-west-2",
			"AWS_ACCESS_KEY_ID":                     "some-access-key-id",
			"AWS_SECRET_ACCESS_KEY":                 "some-secret-access-key",
			"TUBES_AWS_ENDPOINTS":                   fakeAWS.EndpointOverridesEnvVar(),
			"TUBES_CONCOURSE_MANIFEST_TEMPLATE_URL": manifestServer.URL + "/concourse-template.yml",
			"TUBES_BOSH_IO_URL":                     boshIOServer.URL,
		}

		start = buildStarter(&workingDir, envVars)
	})

	AfterEach(func() {
		fakeAWS.Close()

		if manifestServer != nil {
			manifestServer.Close()
		}

		if boshIOServer != nil {
			boshIOServer.Close()
		}
	})

	const NormalTimeout = "5s"

	It("should support basic environment manipulation", func() {

		By("booting a fresh environment", func() {
			session := start("-n", stackName, "up")

			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("Creating keypair"))
			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("Looking for latest AWS NAT box AMI"))
			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("ami-[a-f0-9]*"))
			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("Upserting stack"))
			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("Stack update complete"))
			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("Generating BOSH init manifest"))
			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("Finished"))
			Eventually(session, NormalTimeout).Should(gexec.Exit(0))
		})

		defaultStateDir := filepath.Join(workingDir, "environments", stackName)
		By("storing the SSH key on the filesystem", func() {
			Expect(ioutil.ReadFile(filepath.Join(defaultStateDir, "ssh-key"))).To(ContainSubstring("RSA PRIVATE KEY"))
		})

		By("exposing the SSH key", func() {
			session := start("-n", stackName, "show")

			Eventually(session, NormalTimeout).Should(gexec.Exit(0))

			pemBlock, _ := pem.Decode(session.Out.Contents())
			Expect(pemBlock).NotTo(BeNil())
			Expect(pemBlock.Type).To(Equal("RSA PRIVATE KEY"))

			_, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
			Expect(err).NotTo(HaveOccurred())
		})

		By("supporting an explicit state directory, rather than the implicit subdirectory of the working directory", func() {
			session := start("-n", stackName, "--state-dir", defaultStateDir, "show")

			Eventually(session, NormalTimeout).Should(gexec.Exit(0))

			pemBlock, _ := pem.Decode(session.Out.Contents())
			Expect(pemBlock).NotTo(BeNil())
			Expect(pemBlock.Type).To(Equal("RSA PRIVATE KEY"))
		})

		By("storing a generated BOSH director manifest in the state directory", func() {
			directorYAMLBytes, err := ioutil.ReadFile(filepath.Join(defaultStateDir, "director.yml"))
			Expect(err).NotTo(HaveOccurred())

			Expect(directorYAMLBytes).To(ContainSubstring("resource_pools:"))

			By("ensuring we create fresh credentials for the BOSH director")
			Expect(directorYAMLBytes).NotTo(ContainSubstring(envVars["AWS_SECRET_ACCESS_KEY"]))
		})

		By("storing a generated Concourse deployment manifest in the state directory", func() {
			concourseYAMLBytes, err := ioutil.ReadFile(filepath.Join(defaultStateDir, "concourse.yml"))
			Expect(err).NotTo(HaveOccurred())

			Expect(concourseYAMLBytes).To(ContainSubstring("network: concourse"))
			Expect(concourseYAMLBytes).To(ContainSubstring("us-west-2"))
		})

		By("tearing down the environment", func() {
			session := start("-n", stackName, "down")

			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("Deleting stack"))
			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("Delete complete"))
			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("Deleting keypair"))
			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("Finished"))
			Eventually(session, NormalTimeout).Should(gexec.Exit(0))
		})
	})

	XIt("should generate a Concourse manifest without any template placeholders", func() {
		session := start("-n", stackName, "up")
		Eventually(session, NormalTimeout).Should(gexec.Exit(0))

		defaultStateDir := filepath.Join(workingDir, "environments", stackName)
		concourseYAMLBytes, err := ioutil.ReadFile(filepath.Join(defaultStateDir, "concourse.yml"))
		Expect(err).NotTo(HaveOccurred())

		Expect(concourseYAMLBytes).NotTo(ContainSubstring("REPLACE_WITH_"))
	})

	Context("invalid user input", func() { // fast failing cases
		const ErrTimeout = "10s"
		Context("no command line argument are provided", func() {
			It("should print a useful error", func() {
				session := start([]string{}...)
				Eventually(session, ErrTimeout).Should(gexec.Exit(1))
				Expect(session.Err.Contents()).To(ContainSubstring("specify one command of: down, show or up"))
			})
		})

		Context("when the action is unknown", func() {
			It("should print a useful error", func() {
				session := start("-n", stackName, "nonsense_action")
				Eventually(session, ErrTimeout).Should(gexec.Exit(1))
				Expect(session.Err.Contents()).To(ContainSubstring("Unknown command"))
				Expect(session.Err.Contents()).To(ContainSubstring("specify one command of: down, show or up"))
			})
		})

		Context("when required env vars are missing", func() {
			It("should print a useful error", func() {
				delete(envVars, "AWS_SECRET_ACCESS_KEY")

				session := start("-n", stackName, "up")

				Eventually(session, ErrTimeout).Should(gexec.Exit(1))
				Expect(session.Err).To(gbytes.Say("missing .* AWS config"))
			})
		})

		Context("when the stack name is invalid", func() {
			It("should return a useful error", func() {
				session := start("-n", "invalid_stack_name", "up")
				Eventually(session, ErrTimeout).Should(gexec.Exit(1))
				Expect(session.Err.Contents()).To(ContainSubstring("invalid name: must match pattern"))
			})
		})
	})
})
<commit_msg>Integration test covers flow of resource IDs & IPS from CloudFormation to BOSH-init manifest<commit_after>package integration_test

import (
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/http/httptest"
	"path/filepath"

	"gopkg.in/yaml.v1"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"

	"github.com/rosenhouse/tubes/integration"
)

var _ = Describe("Integration (mocking out AWS)", func() {
	var (
		stackName  string
		envVars    map[string]string
		workingDir string
		fakeAWS    *integration.FakeAWS
		start      func(args ...string) *gexec.Session

		manifestServer *httptest.Server
		boshIOServer   *httptest.Server
	)

	BeforeEach(func() {
		stackName = fmt.Sprintf("tubes-acceptance-test-%x", rand.Int())
		var err error
		workingDir, err = ioutil.TempDir("", "tubes-acceptance-test")
		Expect(err).NotTo(HaveOccurred())

		logger := integration.NewAWSCallLogger(GinkgoWriter)
		fakeAWS = integration.NewFakeAWS(logger)

		concourseManifestTemplate, err := ioutil.ReadFile("fixtures/concourse-template.yml")
		Expect(err).NotTo(HaveOccurred())
		manifestServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.Write(concourseManifestTemplate)
		}))

		boshIOServer = httptest.NewServer(&integration.FakeBoshIO{})

		envVars = map[string]string{
			"AWS_DEFAULT_REGION":                    "us-west-2",
			"AWS_ACCESS_KEY_ID":                     "some-access-key-id",
			"AWS_SECRET_ACCESS_KEY":                 "some-secret-access-key",
			"TUBES_AWS_ENDPOINTS":                   fakeAWS.EndpointOverridesEnvVar(),
			"TUBES_CONCOURSE_MANIFEST_TEMPLATE_URL": manifestServer.URL + "/concourse-template.yml",
			"TUBES_BOSH_IO_URL":                     boshIOServer.URL,
		}

		start = buildStarter(&workingDir, envVars)
	})

	AfterEach(func() {
		fakeAWS.Close()

		if manifestServer != nil {
			manifestServer.Close()
		}

		if boshIOServer != nil {
			boshIOServer.Close()
		}
	})

	const NormalTimeout = "5s"

	It("should support basic environment manipulation", func() {

		By("booting a fresh environment", func() {
			session := start("-n", stackName, "up")

			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("Creating keypair"))
			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("Looking for latest AWS NAT box AMI"))
			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("ami-[a-f0-9]*"))
			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("Upserting stack"))
			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("Stack update complete"))
			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("Generating BOSH init manifest"))
			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("Finished"))
			Eventually(session, NormalTimeout).Should(gexec.Exit(0))
		})

		defaultStateDir := filepath.Join(workingDir, "environments", stackName)
		By("storing the SSH key on the filesystem", func() {
			Expect(ioutil.ReadFile(filepath.Join(defaultStateDir, "ssh-key"))).To(ContainSubstring("RSA PRIVATE KEY"))
		})

		By("exposing the SSH key", func() {
			session := start("-n", stackName, "show")

			Eventually(session, NormalTimeout).Should(gexec.Exit(0))

			pemBlock, _ := pem.Decode(session.Out.Contents())
			Expect(pemBlock).NotTo(BeNil())
			Expect(pemBlock.Type).To(Equal("RSA PRIVATE KEY"))

			_, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
			Expect(err).NotTo(HaveOccurred())
		})

		By("supporting an explicit state directory, rather than the implicit subdirectory of the working directory", func() {
			session := start("-n", stackName, "--state-dir", defaultStateDir, "show")

			Eventually(session, NormalTimeout).Should(gexec.Exit(0))

			pemBlock, _ := pem.Decode(session.Out.Contents())
			Expect(pemBlock).NotTo(BeNil())
			Expect(pemBlock.Type).To(Equal("RSA PRIVATE KEY"))
		})

		By("storing a generated BOSH director manifest in the state directory", func() {
			directorYAMLBytes, err := ioutil.ReadFile(filepath.Join(defaultStateDir, "director.yml"))
			Expect(err).NotTo(HaveOccurred())

			var directorYAML struct {
				Networks []struct {
					Subnets []struct {
						Cloud_Properties struct {
							Subnet string
						}
					}
				}
				Cloud_Provider struct {
					MBus       string
					SSH_Tunnel struct {
						Host string
					}
					Properties struct {
						AWS struct {
							Access_Key_ID           string
							Secret_Access_Key       string
							Default_Security_Groups []string
						}
					}
				}
			}
			Expect(yaml.Unmarshal(directorYAMLBytes, &directorYAML)).To(Succeed())

			Expect(directorYAML.Networks[0].Subnets[0].Cloud_Properties.Subnet).To(Equal("subnet-12345"))
			Expect(directorYAML.Cloud_Provider.SSH_Tunnel.Host).To(Equal("192.168.12.13"))
			Expect(directorYAML.Cloud_Provider.MBus).To(ContainSubstring("@192.168.12.13:6868"))
			Expect(directorYAML.Cloud_Provider.Properties.AWS.Access_Key_ID).To(Equal("some-access-key"))
			Expect(directorYAML.Cloud_Provider.Properties.AWS.Secret_Access_Key).To(Equal("some-secret-key"))
			Expect(directorYAML.Cloud_Provider.Properties.AWS.Default_Security_Groups[0]).To(Equal("sg-1234"))

			By("ensuring we create fresh credentials for the BOSH director")
			Expect(directorYAMLBytes).NotTo(ContainSubstring(envVars["AWS_SECRET_ACCESS_KEY"]))
		})

		By("storing a generated Concourse deployment manifest in the state directory", func() {
			concourseYAMLBytes, err := ioutil.ReadFile(filepath.Join(defaultStateDir, "concourse.yml"))
			Expect(err).NotTo(HaveOccurred())

			Expect(concourseYAMLBytes).To(ContainSubstring("network: concourse"))
			Expect(concourseYAMLBytes).To(ContainSubstring("us-west-2"))
		})

		By("tearing down the environment", func() {
			session := start("-n", stackName, "down")

			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("Deleting stack"))
			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("Delete complete"))
			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("Deleting keypair"))
			Eventually(session.Err, NormalTimeout).Should(gbytes.Say("Finished"))
			Eventually(session, NormalTimeout).Should(gexec.Exit(0))
		})
	})

	XIt("should generate a Concourse manifest without any template placeholders", func() {
		session := start("-n", stackName, "up")
		Eventually(session, NormalTimeout).Should(gexec.Exit(0))

		defaultStateDir := filepath.Join(workingDir, "environments", stackName)
		concourseYAMLBytes, err := ioutil.ReadFile(filepath.Join(defaultStateDir, "concourse.yml"))
		Expect(err).NotTo(HaveOccurred())

		Expect(concourseYAMLBytes).NotTo(ContainSubstring("REPLACE_WITH_"))
	})

	Context("invalid user input", func() { // fast failing cases
		const ErrTimeout = "10s"
		Context("no command line argument are provided", func() {
			It("should print a useful error", func() {
				session := start([]string{}...)
				Eventually(session, ErrTimeout).Should(gexec.Exit(1))
				Expect(session.Err.Contents()).To(ContainSubstring("specify one command of: down, show or up"))
			})
		})

		Context("when the action is unknown", func() {
			It("should print a useful error", func() {
				session := start("-n", stackName, "nonsense_action")
				Eventually(session, ErrTimeout).Should(gexec.Exit(1))
				Expect(session.Err.Contents()).To(ContainSubstring("Unknown command"))
				Expect(session.Err.Contents()).To(ContainSubstring("specify one command of: down, show or up"))
			})
		})

		Context("when required env vars are missing", func() {
			It("should print a useful error", func() {
				delete(envVars, "AWS_SECRET_ACCESS_KEY")

				session := start("-n", stackName, "up")

				Eventually(session, ErrTimeout).Should(gexec.Exit(1))
				Expect(session.Err).To(gbytes.Say("missing .* AWS config"))
			})
		})

		Context("when the stack name is invalid", func() {
			It("should return a useful error", func() {
				session := start("-n", "invalid_stack_name", "up")
				Eventually(session, ErrTimeout).Should(gexec.Exit(1))
				Expect(session.Err.Contents()).To(ContainSubstring("invalid name: must match pattern"))
			})
		})
	})
})
<|endoftext|>


<commit_before>package irc_test

import (
	. "github.com/jriddick/geoffrey/irc"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Irc", func() {
	var (
		client *IRC
		cfg    Config
		emp    Config
	)

	BeforeEach(func() {
		cfg = Config{
			Hostname:           "localhost",
			Port:               6697,
			Secure:             false,
			InsecureSkipVerify: false,
		}
		emp = Config{}
		client = NewIRC(cfg)
	})

	AfterEach(func() {
		client.Disconnect()
	})

	It("should be able to create an irc client", func() {
		Expect(NewIRC(cfg)).NotTo(BeNil())
	})

	It("should be able to connect", func() {
		Expect(client.Connect()).NotTo(HaveOccurred())
	})

	It("should not be able to connect with an empty configuration", func() {
		Expect(NewIRC(emp).Connect()).To(HaveOccurred())
	})

	It("should be able to read from the server", func(done Done) {
		By("connecting to the server")
		Expect(client.Connect()).NotTo(HaveOccurred())

		By("getting the reader")
		reader := client.Reader()
		Expect(reader).NotTo(BeNil())

		msg := <-reader
		Expect(msg).NotTo(BeNil())
		Expect(msg.String()).NotTo(BeEmpty())

		close(done)
	}, 5)

	It("should be able to register to the server", func(done Done) {
		By("connecting to the server")
		Expect(client.Connect()).NotTo(HaveOccurred())

		By("getting the reader and writer")
		reader := client.Reader()
		Expect(reader).NotTo(BeNil())
		writer := client.Writer()
		Expect(writer).NotTo(BeNil())

		By("sending nick and user")
		writer <- "NICK geoffrey"
		writer <- "USER geoffrey 0 * :geoffrey"

		for {
			line, ok := <-reader
			Expect(ok).To(BeTrue())

			if line.Command == RPL_WELCOME {
				break
			}
		}

		close(done)
	}, 5)

	It("should be able to reconnect to the server", func(done Done) {
		By("connecting to the server")
		Expect(client.Connect()).NotTo(HaveOccurred())

		By("reading from the server")
		Expect(<-client.Reader()).NotTo(BeNil())

		By("reconnecting")
		Expect(client.Reconnect()).NotTo(HaveOccurred())

		By("reading from the server")
		Expect(<-client.Reader()).NotTo(BeNil())

		close(done)
	}, 5)

	It("should not be able to send empty messages", func(done Done) {
		By("connecting to the server")
		Expect(client.Connect()).NotTo(HaveOccurred())

		By("sending an empty message")
		client.Writer() <- ""

		Expect(<-client.Errors()).NotTo(BeNil())

		close(done)
	}, 5)
})
<commit_msg>udpated registration test case<commit_after>package irc_test

import (
	. "github.com/jriddick/geoffrey/irc"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Irc", func() {
	var (
		client *IRC
		cfg    Config
		emp    Config
	)

	BeforeEach(func() {
		cfg = Config{
			Hostname:           "localhost",
			Port:               6697,
			Secure:             false,
			InsecureSkipVerify: false,
		}
		emp = Config{}
		client = NewIRC(cfg)
	})

	AfterEach(func() {
		client.Disconnect()
	})

	It("should be able to create an irc client", func() {
		Expect(NewIRC(cfg)).NotTo(BeNil())
	})

	It("should be able to connect", func() {
		Expect(client.Connect()).NotTo(HaveOccurred())
	})

	It("should not be able to connect with an empty configuration", func() {
		Expect(NewIRC(emp).Connect()).To(HaveOccurred())
	})

	It("should be able to read from the server", func(done Done) {
		By("connecting to the server")
		Expect(client.Connect()).NotTo(HaveOccurred())

		By("getting the reader")
		reader := client.Reader()
		Expect(reader).NotTo(BeNil())

		msg := <-reader
		Expect(msg).NotTo(BeNil())
		Expect(msg.String()).NotTo(BeEmpty())

		close(done)
	}, 5)

	It("should be able to register to the server", func(done Done) {
		By("connecting to the server")
		Expect(client.Connect()).NotTo(HaveOccurred())

		By("getting the reader and writer")
		reader := client.Reader()
		Expect(reader).NotTo(BeNil())
		writer := client.Writer()
		Expect(writer).NotTo(BeNil())

		By("sending nick and user")
		writer <- "NICK geoffrey"
		writer <- "USER geoffrey 0 * :geoffrey"

		for {
			line := <-reader

			if line.Command == RPL_WELCOME {
				break
			}
		}

		close(done)
	}, 5)

	It("should be able to reconnect to the server", func(done Done) {
		By("connecting to the server")
		Expect(client.Connect()).NotTo(HaveOccurred())

		By("reading from the server")
		Expect(<-client.Reader()).NotTo(BeNil())

		By("reconnecting")
		Expect(client.Reconnect()).NotTo(HaveOccurred())

		By("reading from the server")
		Expect(<-client.Reader()).NotTo(BeNil())

		close(done)
	}, 5)

	It("should not be able to send empty messages", func(done Done) {
		By("connecting to the server")
		Expect(client.Connect()).NotTo(HaveOccurred())

		By("sending an empty message")
		client.Writer() <- ""

		Expect(<-client.Errors()).NotTo(BeNil())

		close(done)
	}, 5)
})
<|endoftext|>


<commit_before>// Copyright (c) 2017 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license

package irc

import (
	"fmt"
	"strings"

	"github.com/goshuirc/irc-go/ircfmt"
	"github.com/oragono/oragono/irc/utils"
)

// "enabled" callbacks for specific nickserv commands
func servCmdRequiresAccreg(server *Server) bool {
	return server.AccountConfig().Registration.Enabled
}

func servCmdRequiresAuthEnabled(server *Server) bool {
	return server.AccountConfig().AuthenticationEnabled
}

func nsGroupEnabled(server *Server) bool {
	conf := server.Config()
	return conf.Accounts.AuthenticationEnabled && conf.Accounts.NickReservation.Enabled
}

const nickservHelp = `NickServ lets you register and login to an account.

To see in-depth help for a specific NickServ command, try:
    $b/NS HELP <command>$b

Here are the commands you can use:
%s`

var (
	nickservCommands = map[string]*serviceCommand{
		"drop": {
			handler: nsDropHandler,
			help: `Syntax: $bDROP [nickname]$b

DROP de-links the given (or your current) nickname from your user account.`,
			helpShort:    `$bDROP$b de-links your current (or the given) nickname from your user account.`,
			enabled:      servCmdRequiresAccreg,
			authRequired: true,
		},
		"ghost": {
			handler: nsGhostHandler,
			help: `Syntax: $bGHOST <nickname>$b

GHOST disconnects the given user from the network if they're logged in with the
same user account, letting you reclaim your nickname.`,
			helpShort:    `$bGHOST$b reclaims your nickname.`,
			authRequired: true,
		},
		"group": {
			handler: nsGroupHandler,
			help: `Syntax: $bGROUP$b

GROUP links your current nickname with your logged-in account, preventing other
users from changing to it (or forcing them to rename).`,
			helpShort:    `$bGROUP$b links your current nickname to your user account.`,
			enabled:      nsGroupEnabled,
			authRequired: true,
		},

		"identify": {
			handler: nsIdentifyHandler,
			help: `Syntax: $bIDENTIFY <username> [password]$b

IDENTIFY lets you login to the given username using either password auth, or
certfp (your client certificate) if a password is not given.`,
			helpShort: `$bIDENTIFY$b lets you login to your account.`,
		},
		"info": {
			handler: nsInfoHandler,
			help: `Syntax: $bINFO [username]$b

INFO gives you information about the given (or your own) user account.`,
			helpShort: `$bINFO$b gives you information on a user account.`,
		},
		"register": {
			handler: nsRegisterHandler,
			// TODO: "email" is an oversimplification here; it's actually any callback, e.g.,
			// person@example.com, mailto:person@example.com, tel:16505551234.
			help: `Syntax: $bREGISTER <username> <email> [password]$b

REGISTER lets you register a user account. If the server allows anonymous
registration, you can send an asterisk (*) as the email address.

If the password is left out, your account will be registered to your TLS client
certificate (and you will need to use that certificate to login in future).`,
			helpShort: `$bREGISTER$b lets you register a user account.`,
			enabled:   servCmdRequiresAccreg,
		},
		"sadrop": {
			handler: nsDropHandler,
			help: `Syntax: $bSADROP <nickname>$b

SADROP forcibly de-links the given nickname from the attached user account.`,
			helpShort: `$bSADROP$b forcibly de-links the given nickname from its user account.`,
			capabs:    []string{"unregister"},
			enabled:   servCmdRequiresAccreg,
		},
		"unregister": {
			handler: nsUnregisterHandler,
			help: `Syntax: $bUNREGISTER <username> [code]$b

UNREGISTER lets you delete your user account (or someone else's, if you're an
IRC operator with the correct permissions). To prevent accidental
unregistrations, a verification code is required; invoking the command without
a code will display the necessary code.`,
			helpShort: `$bUNREGISTER$b lets you delete your user account.`,
		},
		"verify": {
			handler: nsVerifyHandler,
			help: `Syntax: $bVERIFY <username> <code>$b

VERIFY lets you complete an account registration, if the server requires email
or other verification.`,
			helpShort: `$bVERIFY$b lets you complete account registration.`,
			enabled:   servCmdRequiresAccreg,
		},
	}
)

// nsNotice sends the client a notice from NickServ
func nsNotice(rb *ResponseBuffer, text string) {
	rb.Add(nil, "NickServ", "NOTICE", rb.target.Nick(), text)
}

func nsDropHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
	sadrop := command == "sadrop"
	nick, _ := utils.ExtractParam(params)

	err := server.accounts.SetNickReserved(client, nick, sadrop, false)
	if err == nil {
		nsNotice(rb, fmt.Sprintf(client.t("Successfully ungrouped nick %s with your account"), nick))
	} else if err == errAccountNotLoggedIn {
		nsNotice(rb, client.t("You're not logged into an account"))
	} else if err == errAccountCantDropPrimaryNick {
		nsNotice(rb, client.t("You can't ungroup your primary nickname (try unregistering your account instead)"))
	} else if err == errNicknameReserved {
		nsNotice(rb, client.t("That nickname is already reserved by someone else"))
	} else {
		nsNotice(rb, client.t("Could not ungroup nick"))
	}
}

func nsGhostHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
	nick, _ := utils.ExtractParam(params)

	ghost := server.clients.Get(nick)
	if ghost == nil {
		nsNotice(rb, client.t("No such nick"))
		return
	} else if ghost == client {
		nsNotice(rb, client.t("You can't GHOST yourself (try /QUIT instead)"))
		return
	}

	authorized := false
	account := client.Account()
	if account != "" {
		// the user must either own the nick, or the target client
		authorized = (server.accounts.NickToAccount(nick) == account) || (ghost.Account() == account)
	}
	if !authorized {
		nsNotice(rb, client.t("You don't own that nick"))
		return
	}

	ghost.Quit(fmt.Sprintf(ghost.t("GHOSTed by %s"), client.Nick()))
	ghost.destroy(false)
}

func nsGroupHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
	nick := client.NickCasefolded()
	err := server.accounts.SetNickReserved(client, nick, false, true)
	if err == nil {
		nsNotice(rb, fmt.Sprintf(client.t("Successfully grouped nick %s with your account"), nick))
	} else if err == errAccountTooManyNicks {
		nsNotice(rb, client.t("You have too many nicks reserved already (you can remove some with /NS DROP)"))
	} else if err == errNicknameReserved {
		nsNotice(rb, client.t("That nickname is already reserved by someone else"))
	} else {
		nsNotice(rb, client.t("Error reserving nickname"))
	}
}

func nsIdentifyHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
	loginSuccessful := false

	username, passphrase := utils.ExtractParam(params)

	// try passphrase
	if username != "" && passphrase != "" {
		err := server.accounts.AuthenticateByPassphrase(client, username, passphrase)
		loginSuccessful = (err == nil)
	}

	// try certfp
	if !loginSuccessful && client.certfp != "" {
		err := server.accounts.AuthenticateByCertFP(client)
		loginSuccessful = (err == nil)
	}

	if loginSuccessful {
		sendSuccessfulSaslAuth(client, rb, true)
	} else {
		nsNotice(rb, client.t("Could not login with your TLS certificate or supplied username/password"))
	}
}

func nsInfoHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
	nick, _ := utils.ExtractParam(params)

	if nick == "" {
		nick = client.Nick()
	}

	accountName := nick
	if server.AccountConfig().NickReservation.Enabled {
		accountName = server.accounts.NickToAccount(nick)
		if accountName == "" {
			nsNotice(rb, client.t("That nickname is not registered"))
			return
		}
	}

	account, err := server.accounts.LoadAccount(accountName)
	if err != nil || !account.Verified {
		nsNotice(rb, client.t("Account does not exist"))
	}

	nsNotice(rb, fmt.Sprintf(client.t("Account: %s"), account.Name))
	registeredAt := account.RegisteredAt.Format("Jan 02, 2006 15:04:05Z")
	nsNotice(rb, fmt.Sprintf(client.t("Registered at: %s"), registeredAt))
	// TODO nicer formatting for this
	for _, nick := range account.AdditionalNicks {
		nsNotice(rb, fmt.Sprintf(client.t("Additional grouped nick: %s"), nick))
	}
}

func nsRegisterHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
	// get params
	username, afterUsername := utils.ExtractParam(params)
	email, passphrase := utils.ExtractParam(afterUsername)

	if !server.AccountConfig().Registration.Enabled {
		nsNotice(rb, client.t("Account registration has been disabled"))
		return
	}

	if username == "" {
		nsNotice(rb, client.t("No username supplied"))
		return
	}

	certfp := client.certfp
	if passphrase == "" && certfp == "" {
		nsNotice(rb, client.t("You need to either supply a passphrase or be connected via TLS with a client cert"))
		return
	}

	if client.LoggedIntoAccount() {
		if server.AccountConfig().Registration.AllowMultiplePerConnection {
			server.accounts.Logout(client)
		} else {
			nsNotice(rb, client.t("You're already logged into an account"))
			return
		}
	}

	config := server.AccountConfig()
	var callbackNamespace, callbackValue string
	noneCallbackAllowed := false
	for _, callback := range config.Registration.EnabledCallbacks {
		if callback == "*" {
			noneCallbackAllowed = true
		}
	}
	// XXX if ACC REGISTER allows registration with the `none` callback, then ignore
	// any callback that was passed here (to avoid confusion in the case where the ircd
	// has no mail server configured). otherwise, register using the provided callback:
	if noneCallbackAllowed {
		callbackNamespace = "*"
	} else {
		callbackNamespace, callbackValue = parseCallback(email, config)
		if callbackNamespace == "" {
			nsNotice(rb, client.t("Registration requires a valid e-mail address"))
			return
		}
	}

	// get and sanitise account name
	account := strings.TrimSpace(username)

	err := server.accounts.Register(client, account, callbackNamespace, callbackValue, passphrase, client.certfp)
	if err == nil {
		if callbackNamespace == "*" {
			err = server.accounts.Verify(client, account, "")
			if err == nil {
				sendSuccessfulRegResponse(client, rb, true)
			}
		} else {
			messageTemplate := client.t("Account created, pending verification; verification code has been sent to %s:%s")
			message := fmt.Sprintf(messageTemplate, callbackNamespace, callbackValue)
			nsNotice(rb, message)
		}
	}

	// details could not be stored and relevant numerics have been dispatched, abort
	if err != nil {
		errMsg := client.t("Could not register")
		if err == errCertfpAlreadyExists {
			errMsg = client.t("An account already exists for your certificate fingerprint")
		} else if err == errAccountAlreadyRegistered {
			errMsg = client.t("Account already exists")
		}
		nsNotice(rb, errMsg)
		return
	}
}

func nsUnregisterHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
	username, verificationCode := utils.ExtractParam(params)

	if !server.AccountConfig().Registration.Enabled {
		nsNotice(rb, client.t("Account registration has been disabled"))
		return
	}

	if username == "" {
		nsNotice(rb, client.t("You must specify an account"))
		return
	}

	account, err := server.accounts.LoadAccount(username)
	if err == errAccountDoesNotExist {
		nsNotice(rb, client.t("Invalid account name"))
		return
	} else if err != nil {
		nsNotice(rb, client.t("Internal error"))
		return
	}

	cfname, _ := CasefoldName(username)
	if !(cfname == client.Account() || client.HasRoleCapabs("accreg")) {
		nsNotice(rb, client.t("Insufficient oper privs"))
		return
	}

	expectedCode := unregisterConfirmationCode(account.Name, account.RegisteredAt)
	if expectedCode != verificationCode {
		nsNotice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this account will remove its stored privileges.$b")))
		nsNotice(rb, fmt.Sprintf(client.t("To confirm account unregistration, type: /NS UNREGISTER %s %s"), cfname, expectedCode))
		return
	}

	if cfname == client.Account() {
		client.server.accounts.Logout(client)
	}

	err = server.accounts.Unregister(cfname)
	if err == errAccountDoesNotExist {
		nsNotice(rb, client.t(err.Error()))
	} else if err != nil {
		nsNotice(rb, client.t("Error while unregistering account"))
	} else {
		nsNotice(rb, fmt.Sprintf(client.t("Successfully unregistered account %s"), cfname))
	}
}

func nsVerifyHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
	username, code := utils.ExtractParam(params)

	err := server.accounts.Verify(client, username, code)

	var errorMessage string
	if err == errAccountVerificationInvalidCode || err == errAccountAlreadyVerified {
		errorMessage = err.Error()
	} else if err != nil {
		errorMessage = errAccountVerificationFailed.Error()
	}

	if errorMessage != "" {
		nsNotice(rb, client.t(errorMessage))
		return
	}

	sendSuccessfulRegResponse(client, rb, true)
}
<commit_msg>unregister capab was renamed to accreg, but i missed this occurrence<commit_after>// Copyright (c) 2017 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license

package irc

import (
	"fmt"
	"strings"

	"github.com/goshuirc/irc-go/ircfmt"
	"github.com/oragono/oragono/irc/utils"
)

// "enabled" callbacks for specific nickserv commands
func servCmdRequiresAccreg(server *Server) bool {
	return server.AccountConfig().Registration.Enabled
}

func servCmdRequiresAuthEnabled(server *Server) bool {
	return server.AccountConfig().AuthenticationEnabled
}

func nsGroupEnabled(server *Server) bool {
	conf := server.Config()
	return conf.Accounts.AuthenticationEnabled && conf.Accounts.NickReservation.Enabled
}

const nickservHelp = `NickServ lets you register and login to an account.

To see in-depth help for a specific NickServ command, try:
    $b/NS HELP <command>$b

Here are the commands you can use:
%s`

var (
	nickservCommands = map[string]*serviceCommand{
		"drop": {
			handler: nsDropHandler,
			help: `Syntax: $bDROP [nickname]$b

DROP de-links the given (or your current) nickname from your user account.`,
			helpShort:    `$bDROP$b de-links your current (or the given) nickname from your user account.`,
			enabled:      servCmdRequiresAccreg,
			authRequired: true,
		},
		"ghost": {
			handler: nsGhostHandler,
			help: `Syntax: $bGHOST <nickname>$b

GHOST disconnects the given user from the network if they're logged in with the
same user account, letting you reclaim your nickname.`,
			helpShort:    `$bGHOST$b reclaims your nickname.`,
			authRequired: true,
		},
		"group": {
			handler: nsGroupHandler,
			help: `Syntax: $bGROUP$b

GROUP links your current nickname with your logged-in account, preventing other
users from changing to it (or forcing them to rename).`,
			helpShort:    `$bGROUP$b links your current nickname to your user account.`,
			enabled:      nsGroupEnabled,
			authRequired: true,
		},

		"identify": {
			handler: nsIdentifyHandler,
			help: `Syntax: $bIDENTIFY <username> [password]$b

IDENTIFY lets you login to the given username using either password auth, or
certfp (your client certificate) if a password is not given.`,
			helpShort: `$bIDENTIFY$b lets you login to your account.`,
		},
		"info": {
			handler: nsInfoHandler,
			help: `Syntax: $bINFO [username]$b

INFO gives you information about the given (or your own) user account.`,
			helpShort: `$bINFO$b gives you information on a user account.`,
		},
		"register": {
			handler: nsRegisterHandler,
			// TODO: "email" is an oversimplification here; it's actually any callback, e.g.,
			// person@example.com, mailto:person@example.com, tel:16505551234.
			help: `Syntax: $bREGISTER <username> <email> [password]$b

REGISTER lets you register a user account. If the server allows anonymous
registration, you can send an asterisk (*) as the email address.

If the password is left out, your account will be registered to your TLS client
certificate (and you will need to use that certificate to login in future).`,
			helpShort: `$bREGISTER$b lets you register a user account.`,
			enabled:   servCmdRequiresAccreg,
		},
		"sadrop": {
			handler: nsDropHandler,
			help: `Syntax: $bSADROP <nickname>$b

SADROP forcibly de-links the given nickname from the attached user account.`,
			helpShort: `$bSADROP$b forcibly de-links the given nickname from its user account.`,
			capabs:    []string{"accreg"},
			enabled:   servCmdRequiresAccreg,
		},
		"unregister": {
			handler: nsUnregisterHandler,
			help: `Syntax: $bUNREGISTER <username> [code]$b

UNREGISTER lets you delete your user account (or someone else's, if you're an
IRC operator with the correct permissions). To prevent accidental
unregistrations, a verification code is required; invoking the command without
a code will display the necessary code.`,
			helpShort: `$bUNREGISTER$b lets you delete your user account.`,
		},
		"verify": {
			handler: nsVerifyHandler,
			help: `Syntax: $bVERIFY <username> <code>$b

VERIFY lets you complete an account registration, if the server requires email
or other verification.`,
			helpShort: `$bVERIFY$b lets you complete account registration.`,
			enabled:   servCmdRequiresAccreg,
		},
	}
)

// nsNotice sends the client a notice from NickServ
func nsNotice(rb *ResponseBuffer, text string) {
	rb.Add(nil, "NickServ", "NOTICE", rb.target.Nick(), text)
}

func nsDropHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
	sadrop := command == "sadrop"
	nick, _ := utils.ExtractParam(params)

	err := server.accounts.SetNickReserved(client, nick, sadrop, false)
	if err == nil {
		nsNotice(rb, fmt.Sprintf(client.t("Successfully ungrouped nick %s with your account"), nick))
	} else if err == errAccountNotLoggedIn {
		nsNotice(rb, client.t("You're not logged into an account"))
	} else if err == errAccountCantDropPrimaryNick {
		nsNotice(rb, client.t("You can't ungroup your primary nickname (try unregistering your account instead)"))
	} else if err == errNicknameReserved {
		nsNotice(rb, client.t("That nickname is already reserved by someone else"))
	} else {
		nsNotice(rb, client.t("Could not ungroup nick"))
	}
}

func nsGhostHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
	nick, _ := utils.ExtractParam(params)

	ghost := server.clients.Get(nick)
	if ghost == nil {
		nsNotice(rb, client.t("No such nick"))
		return
	} else if ghost == client {
		nsNotice(rb, client.t("You can't GHOST yourself (try /QUIT instead)"))
		return
	}

	authorized := false
	account := client.Account()
	if account != "" {
		// the user must either own the nick, or the target client
		authorized = (server.accounts.NickToAccount(nick) == account) || (ghost.Account() == account)
	}
	if !authorized {
		nsNotice(rb, client.t("You don't own that nick"))
		return
	}

	ghost.Quit(fmt.Sprintf(ghost.t("GHOSTed by %s"), client.Nick()))
	ghost.destroy(false)
}

func nsGroupHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
	nick := client.NickCasefolded()
	err := server.accounts.SetNickReserved(client, nick, false, true)
	if err == nil {
		nsNotice(rb, fmt.Sprintf(client.t("Successfully grouped nick %s with your account"), nick))
	} else if err == errAccountTooManyNicks {
		nsNotice(rb, client.t("You have too many nicks reserved already (you can remove some with /NS DROP)"))
	} else if err == errNicknameReserved {
		nsNotice(rb, client.t("That nickname is already reserved by someone else"))
	} else {
		nsNotice(rb, client.t("Error reserving nickname"))
	}
}

func nsIdentifyHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
	loginSuccessful := false

	username, passphrase := utils.ExtractParam(params)

	// try passphrase
	if username != "" && passphrase != "" {
		err := server.accounts.AuthenticateByPassphrase(client, username, passphrase)
		loginSuccessful = (err == nil)
	}

	// try certfp
	if !loginSuccessful && client.certfp != "" {
		err := server.accounts.AuthenticateByCertFP(client)
		loginSuccessful = (err == nil)
	}

	if loginSuccessful {
		sendSuccessfulSaslAuth(client, rb, true)
	} else {
		nsNotice(rb, client.t("Could not login with your TLS certificate or supplied username/password"))
	}
}

func nsInfoHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
	nick, _ := utils.ExtractParam(params)

	if nick == "" {
		nick = client.Nick()
	}

	accountName := nick
	if server.AccountConfig().NickReservation.Enabled {
		accountName = server.accounts.NickToAccount(nick)
		if accountName == "" {
			nsNotice(rb, client.t("That nickname is not registered"))
			return
		}
	}

	account, err := server.accounts.LoadAccount(accountName)
	if err != nil || !account.Verified {
		nsNotice(rb, client.t("Account does not exist"))
	}

	nsNotice(rb, fmt.Sprintf(client.t("Account: %s"), account.Name))
	registeredAt := account.RegisteredAt.Format("Jan 02, 2006 15:04:05Z")
	nsNotice(rb, fmt.Sprintf(client.t("Registered at: %s"), registeredAt))
	// TODO nicer formatting for this
	for _, nick := range account.AdditionalNicks {
		nsNotice(rb, fmt.Sprintf(client.t("Additional grouped nick: %s"), nick))
	}
}

func nsRegisterHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
	// get params
	username, afterUsername := utils.ExtractParam(params)
	email, passphrase := utils.ExtractParam(afterUsername)

	if !server.AccountConfig().Registration.Enabled {
		nsNotice(rb, client.t("Account registration has been disabled"))
		return
	}

	if username == "" {
		nsNotice(rb, client.t("No username supplied"))
		return
	}

	certfp := client.certfp
	if passphrase == "" && certfp == "" {
		nsNotice(rb, client.t("You need to either supply a passphrase or be connected via TLS with a client cert"))
		return
	}

	if client.LoggedIntoAccount() {
		if server.AccountConfig().Registration.AllowMultiplePerConnection {
			server.accounts.Logout(client)
		} else {
			nsNotice(rb, client.t("You're already logged into an account"))
			return
		}
	}

	config := server.AccountConfig()
	var callbackNamespace, callbackValue string
	noneCallbackAllowed := false
	for _, callback := range config.Registration.EnabledCallbacks {
		if callback == "*" {
			noneCallbackAllowed = true
		}
	}
	// XXX if ACC REGISTER allows registration with the `none` callback, then ignore
	// any callback that was passed here (to avoid confusion in the case where the ircd
	// has no mail server configured). otherwise, register using the provided callback:
	if noneCallbackAllowed {
		callbackNamespace = "*"
	} else {
		callbackNamespace, callbackValue = parseCallback(email, config)
		if callbackNamespace == "" {
			nsNotice(rb, client.t("Registration requires a valid e-mail address"))
			return
		}
	}

	// get and sanitise account name
	account := strings.TrimSpace(username)

	err := server.accounts.Register(client, account, callbackNamespace, callbackValue, passphrase, client.certfp)
	if err == nil {
		if callbackNamespace == "*" {
			err = server.accounts.Verify(client, account, "")
			if err == nil {
				sendSuccessfulRegResponse(client, rb, true)
			}
		} else {
			messageTemplate := client.t("Account created, pending verification; verification code has been sent to %s:%s")
			message := fmt.Sprintf(messageTemplate, callbackNamespace, callbackValue)
			nsNotice(rb, message)
		}
	}

	// details could not be stored and relevant numerics have been dispatched, abort
	if err != nil {
		errMsg := client.t("Could not register")
		if err == errCertfpAlreadyExists {
			errMsg = client.t("An account already exists for your certificate fingerprint")
		} else if err == errAccountAlreadyRegistered {
			errMsg = client.t("Account already exists")
		}
		nsNotice(rb, errMsg)
		return
	}
}

func nsUnregisterHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
	username, verificationCode := utils.ExtractParam(params)

	if !server.AccountConfig().Registration.Enabled {
		nsNotice(rb, client.t("Account registration has been disabled"))
		return
	}

	if username == "" {
		nsNotice(rb, client.t("You must specify an account"))
		return
	}

	account, err := server.accounts.LoadAccount(username)
	if err == errAccountDoesNotExist {
		nsNotice(rb, client.t("Invalid account name"))
		return
	} else if err != nil {
		nsNotice(rb, client.t("Internal error"))
		return
	}

	cfname, _ := CasefoldName(username)
	if !(cfname == client.Account() || client.HasRoleCapabs("accreg")) {
		nsNotice(rb, client.t("Insufficient oper privs"))
		return
	}

	expectedCode := unregisterConfirmationCode(account.Name, account.RegisteredAt)
	if expectedCode != verificationCode {
		nsNotice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this account will remove its stored privileges.$b")))
		nsNotice(rb, fmt.Sprintf(client.t("To confirm account unregistration, type: /NS UNREGISTER %s %s"), cfname, expectedCode))
		return
	}

	if cfname == client.Account() {
		client.server.accounts.Logout(client)
	}

	err = server.accounts.Unregister(cfname)
	if err == errAccountDoesNotExist {
		nsNotice(rb, client.t(err.Error()))
	} else if err != nil {
		nsNotice(rb, client.t("Error while unregistering account"))
	} else {
		nsNotice(rb, fmt.Sprintf(client.t("Successfully unregistered account %s"), cfname))
	}
}

func nsVerifyHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
	username, code := utils.ExtractParam(params)

	err := server.accounts.Verify(client, username, code)

	var errorMessage string
	if err == errAccountVerificationInvalidCode || err == errAccountAlreadyVerified {
		errorMessage = err.Error()
	} else if err != nil {
		errorMessage = errAccountVerificationFailed.Error()
	}

	if errorMessage != "" {
		nsNotice(rb, client.t(errorMessage))
		return
	}

	sendSuccessfulRegResponse(client, rb, true)
}
<|endoftext|>


<commit_before>// Copyright © 2017 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package functions

import (
	"testing"
	"time"

	pb_handler "github.com/TheThingsNetwork/ttn/api/handler"
	"github.com/robertkrimen/otto"

	. "github.com/smartystreets/assertions"
)

func TestRunCode(t *testing.T) {
	a := New(t)

	logger := NewEntryLogger()
	foo := 10
	env := map[string]interface{}{
		"foo": foo,
		"bar": "baz",
	}

	code := `
		(function (foo, bar) {
			console.log("hello", foo, bar)
			return foo
		})(foo,bar)
	`

	val, err := RunCode("test", code, env, time.Second, logger)
	a.So(err, ShouldBeNil)
	e, _ := val.Export()
	a.So(e, ShouldEqual, foo)
	a.So(logger.Logs, ShouldResemble, []*pb_handler.LogEntry{
		&pb_handler.LogEntry{
			Function: "test",
			Fields:   []string{`"hello"`, "10", `"baz"`},
		},
	})
}

var result string

func BenchmarkJSON(b *testing.B) {
	v, _ := otto.ToValue("foo")
	var r string
	for n := 0; n < b.N; n++ {
		r = JSON(v)
	}
	result = r
}

func TestRunInvalidCode(t *testing.T) {
	a := New(t)

	logger := NewEntryLogger()
	foo := 10
	env := map[string]interface{}{
		"foo": foo,
		"bar": "baz",
	}

	code := `
		(function (foo, bar) {
			derp
		})(foo,bar)
	`

	_, err := RunCode("test", code, env, time.Second, logger)
	a.So(err, ShouldNotBeNil)
}
<commit_msg>Add test for payload function that throws<commit_after>// Copyright © 2017 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package functions

import (
	"testing"
	"time"

	pb_handler "github.com/TheThingsNetwork/ttn/api/handler"
	"github.com/robertkrimen/otto"

	. "github.com/smartystreets/assertions"
)

func TestRunCode(t *testing.T) {
	a := New(t)

	logger := NewEntryLogger()
	foo := 10
	env := map[string]interface{}{
		"foo": foo,
		"bar": "baz",
	}

	code := `
		(function (foo, bar) {
			console.log("hello", foo, bar)
			return foo
		})(foo,bar)
	`

	val, err := RunCode("test", code, env, time.Second, logger)
	a.So(err, ShouldBeNil)
	e, _ := val.Export()
	a.So(e, ShouldEqual, foo)
	a.So(logger.Logs, ShouldResemble, []*pb_handler.LogEntry{
		&pb_handler.LogEntry{
			Function: "test",
			Fields:   []string{`"hello"`, "10", `"baz"`},
		},
	})
}

func TestRunCodeThrow(t *testing.T) {
	a := New(t)

	logger := NewEntryLogger()
	env := map[string]interface{}{}

	code := `
		(function () {
			throw new Error("This is an error")
			return 10
		})()
	`

	_, err := RunCode("test", code, env, time.Second, logger)
	a.So(err, ShouldNotBeNil)
}

var result string

func BenchmarkJSON(b *testing.B) {
	v, _ := otto.ToValue("foo")
	var r string
	for n := 0; n < b.N; n++ {
		r = JSON(v)
	}
	result = r
}

func TestRunInvalidCode(t *testing.T) {
	a := New(t)

	logger := NewEntryLogger()
	foo := 10
	env := map[string]interface{}{
		"foo": foo,
		"bar": "baz",
	}

	code := `
		(function (foo, bar) {
			derp
		})(foo,bar)
	`

	_, err := RunCode("test", code, env, time.Second, logger)
	a.So(err, ShouldNotBeNil)
}
<|endoftext|>


<commit_before>package generatethumbnail

import (
	"image"
	"image/jpeg"
	"os"
	"os/exec"
	"path"
	"strings"
	"sync"
	"sync/atomic"

	"github.com/kevintavog/findaphoto/common"

	"github.com/ian-kent/go-log/log"
	"github.com/nfnt/resize"
	"github.com/twinj/uuid"
)

var GeneratedImage int64
var FailedImage int64
var GeneratedVideo int64
var FailedVideo int64
var ThumbnailsCreated int64

type ThumbnailInfo struct {
	FullPath    string
	AliasedPath string
	MimeType    string
}

const thumbnailMaxHeightDimension = 170

var queue chan *ThumbnailInfo
var waitGroup sync.WaitGroup

func Start() {
	numConsumers := common.RatioNumCpus(0.5)
	queue = make(chan *ThumbnailInfo, 10000)
	waitGroup.Add(numConsumers)

	log.Info("Thumbnail generation using %d consumers", numConsumers)
	for idx := 0; idx < numConsumers; idx++ {
		go func() {
			dequeue()
			waitGroup.Done()
		}()
	}
}

func Done() {
	close(queue)
}

func Wait() {
	waitGroup.Wait()
}

func Enqueue(fullPath, aliasedPath, mimeType string) {
	thumbnailInfo := &ThumbnailInfo{
		FullPath:    fullPath,
		AliasedPath: aliasedPath,
		MimeType:    mimeType,
	}
	queue <- thumbnailInfo
}

func dequeue() {
	var mediaType []string
	var thumbPath string
	var err error

	for thumbnailInfo := range queue {
		mediaType = strings.Split(thumbnailInfo.MimeType, "/")
		thumbPath = common.ToThumbPath(thumbnailInfo.AliasedPath)
		if len(mediaType) < 1 {
			log.Error("Invalid media type: '%s' for %s", thumbnailInfo.MimeType, thumbnailInfo.FullPath)
			continue
		}

		err = common.CreateDirectory(path.Dir(thumbPath))
		if err != nil {
			log.Error("Unable to create directory for '%s'", thumbPath)
			continue
		}

		switch strings.ToLower(mediaType[0]) {
		case "video":
			generateVideo(thumbnailInfo.FullPath, thumbPath)
		case "image":
			generateImage(thumbnailInfo.FullPath, thumbPath)
		default:
			log.Error("Unhandled mediaType: %s (%s) for %s", thumbnailInfo.MimeType, mediaType, thumbnailInfo.FullPath)
		}

		atomic.AddInt64(&ThumbnailsCreated, 1)
		if ThumbnailsCreated%500 == 0 {
			log.Error("Generated thumbnail %d [%s]", ThumbnailsCreated, thumbnailInfo.FullPath)
		}
	}
}

func generateImage(fullPath, thumbPath string) {
	if err := createThumbnail(fullPath, thumbPath); err != nil {
		log.Error("Failed thumbnail generation: %s", err.Error())
		atomic.AddInt64(&FailedImage, 1)
	} else {
		atomic.AddInt64(&GeneratedImage, 1)
	}
}

func generateVideo(fullPath, thumbPath string) {
	tmpFilename := path.Join(os.TempDir(), "findAPhoto", "thumbnails", uuid.NewV4().String()+".JPG")
	defer os.Remove(tmpFilename)

	err := common.CreateDirectory(path.Dir(tmpFilename))
	if err != nil {
		log.Fatal("Unable to create temporary directory for thumbnail generation (%s): %s", tmpFilename, err.Error())
	}

	out, err := exec.Command(common.FfmpegPath, "-i", fullPath, "-ss", "00:00:01.0", "-vframes", "1", tmpFilename).Output()
	if err != nil {
		atomic.AddInt64(&FailedVideo, 1)
		log.Fatal("Failed executing ffmpeg for '%s': %s (%s)", fullPath, err.Error(), out)
	}

	if exists, _ := common.PathExists(tmpFilename); !exists {
		// The video may not be long enough to grab a frame at the 1 second, so try the first frame
		out, err = exec.Command(common.FfmpegPath, "-i", fullPath, "-ss", "00:00:00.0", "-vframes", "1", tmpFilename).Output()
		if err != nil {
			atomic.AddInt64(&FailedVideo, 1)
			log.Error("Failed executing ffmpeg for '%s': %s (%s)", fullPath, err.Error(), out)
		}
	}

	if err := createThumbnail(tmpFilename, thumbPath); err != nil {
		atomic.AddInt64(&FailedVideo, 1)
	} else {
		atomic.AddInt64(&GeneratedVideo, 1)
	}
}

func createThumbnail(imageFilename, thumbFilename string) error {
	file, err := os.Open(imageFilename)
	if err != nil {
		return err
	}
	defer file.Close()

	image, _, err := image.Decode(file)
	if err != nil {
		return err
	}

	thumb := resize.Resize(0, thumbnailMaxHeightDimension, image, resize.NearestNeighbor)
	savedThumbnailFile, err := os.Create(thumbFilename)
	if err != nil {
		return err
	}
	defer savedThumbnailFile.Close()

	jpeg.Encode(savedThumbnailFile, thumb, &jpeg.Options{Quality: 85})
	return nil
}
<commit_msg>no message<commit_after>package generatethumbnail

import (
	"image"
	"image/jpeg"
	"os"
	"os/exec"
	"path"
	"strings"
	"sync"
	"sync/atomic"

	"github.com/kevintavog/findaphoto/common"

	"github.com/ian-kent/go-log/log"
	"github.com/nfnt/resize"
	"github.com/twinj/uuid"
)

var GeneratedImage int64
var FailedImage int64
var GeneratedVideo int64
var FailedVideo int64
var ThumbnailsCreated int64

type ThumbnailInfo struct {
	FullPath    string
	AliasedPath string
	MimeType    string
}

const thumbnailMaxHeightDimension = 170

var queue chan *ThumbnailInfo
var waitGroup sync.WaitGroup

func Start() {
	numConsumers := common.RatioNumCpus(0.5)
	queue = make(chan *ThumbnailInfo, 10000)
	waitGroup.Add(numConsumers)

	log.Info("Thumbnail generation using %d consumers", numConsumers)
	for idx := 0; idx < numConsumers; idx++ {
		go func() {
			dequeue()
			waitGroup.Done()
		}()
	}
}

func Done() {
	close(queue)
}

func Wait() {
	waitGroup.Wait()
}

func Enqueue(fullPath, aliasedPath, mimeType string) {
	thumbnailInfo := &ThumbnailInfo{
		FullPath:    fullPath,
		AliasedPath: aliasedPath,
		MimeType:    mimeType,
	}
	queue <- thumbnailInfo
}

func dequeue() {
	var mediaType []string
	var thumbPath string
	var err error

	for thumbnailInfo := range queue {
		mediaType = strings.Split(thumbnailInfo.MimeType, "/")
		thumbPath = common.ToThumbPath(thumbnailInfo.AliasedPath)
		if len(mediaType) < 1 {
			log.Error("Invalid media type: '%s' for %s", thumbnailInfo.MimeType, thumbnailInfo.FullPath)
			continue
		}

		err = common.CreateDirectory(path.Dir(thumbPath))
		if err != nil {
			log.Error("Unable to create directory for '%s'", thumbPath)
			continue
		}

		switch strings.ToLower(mediaType[0]) {
		case "video":
			generateVideo(thumbnailInfo.FullPath, thumbPath)
		case "image":
			generateImage(thumbnailInfo.FullPath, thumbPath)
		default:
			log.Error("Unhandled mediaType: %s (%s) for %s", thumbnailInfo.MimeType, mediaType, thumbnailInfo.FullPath)
		}

		atomic.AddInt64(&ThumbnailsCreated, 1)
		if ThumbnailsCreated%500 == 0 {
			log.Error("Generated thumbnail %d [%s]", ThumbnailsCreated, thumbnailInfo.FullPath)
		}
	}
}

func generateImage(fullPath, thumbPath string) {
	if err := createThumbnail(fullPath, thumbPath); err != nil {
		log.Error("Failed thumbnail generation on %s: %s", fullPath, err.Error())
		atomic.AddInt64(&FailedImage, 1)
	} else {
		atomic.AddInt64(&GeneratedImage, 1)
	}
}

func generateVideo(fullPath, thumbPath string) {
	tmpFilename := path.Join(os.TempDir(), "findAPhoto", "thumbnails", uuid.NewV4().String()+".JPG")
	defer os.Remove(tmpFilename)

	err := common.CreateDirectory(path.Dir(tmpFilename))
	if err != nil {
		log.Fatal("Unable to create temporary directory for thumbnail generation (%s): %s", tmpFilename, err.Error())
	}

	out, err := exec.Command(common.FfmpegPath, "-i", fullPath, "-ss", "00:00:01.0", "-vframes", "1", tmpFilename).Output()
	if err != nil {
		atomic.AddInt64(&FailedVideo, 1)
		log.Fatal("Failed executing ffmpeg for '%s': %s (%s)", fullPath, err.Error(), out)
	}

	if exists, _ := common.PathExists(tmpFilename); !exists {
		// The video may not be long enough to grab a frame at the 1 second, so try the first frame
		out, err = exec.Command(common.FfmpegPath, "-i", fullPath, "-ss", "00:00:00.0", "-vframes", "1", tmpFilename).Output()
		if err != nil {
			atomic.AddInt64(&FailedVideo, 1)
			log.Error("Failed executing ffmpeg for '%s': %s (%s)", fullPath, err.Error(), out)
		}
	}

	if err := createThumbnail(tmpFilename, thumbPath); err != nil {
		log.Error("Failed thumbnail generation on %s: %s", tmpFilename, err.Error())
		atomic.AddInt64(&FailedVideo, 1)
	} else {
		atomic.AddInt64(&GeneratedVideo, 1)
	}
}

func createThumbnail(imageFilename, thumbFilename string) error {
	file, err := os.Open(imageFilename)
	if err != nil {
		return err
	}
	defer file.Close()

	image, _, err := image.Decode(file)
	if err != nil {
		return err
	}

	thumb := resize.Resize(0, thumbnailMaxHeightDimension, image, resize.NearestNeighbor)
	savedThumbnailFile, err := os.Create(thumbFilename)
	if err != nil {
		return err
	}
	defer savedThumbnailFile.Close()

	jpeg.Encode(savedThumbnailFile, thumb, &jpeg.Options{Quality: 85})
	return nil
}
<|endoftext|>


<commit_before>package mergo

import (
	"testing"

	"github.com/magiconair/properties/assert"
)

func TestMergoSimpleMap(t *testing.T) {
	dst := map[string]string{"key1": "loosethis", "key2": "keepthis"}
	src := map[string]string{"key1": "key10"}
	exp := map[string]string{"key1": "key10", "key2": "keepthis"}
	Merge(&dst, src, WithAppendSlice, WithOverride)
	assert.Equal(t, dst, exp)
}

type CustomStruct struct {
	SomeMap map[string]string
}

var testDataStructMap = []struct {
	name string
	src  map[string]CustomStruct
	dst  map[string]CustomStruct
	exp  map[string]CustomStruct
}{
	{name: "Normal",
		dst: map[string]CustomStruct{"a": CustomStruct{SomeMap: map[string]string{"key1": "loosethis", "key2": "keepthis"}}},
		src: map[string]CustomStruct{"a": CustomStruct{SomeMap: map[string]string{"key1": "key10"}}},
		exp: map[string]CustomStruct{"a": CustomStruct{SomeMap: map[string]string{"key1": "key10", "key2": "keepthis"}}},
	},
	{name: "Init of struct key", dst: map[string]CustomStruct{"a": CustomStruct{SomeMap: map[string]string{}}},
		src: map[string]CustomStruct{"a": CustomStruct{SomeMap: map[string]string{"key1": "key10"}}},
		exp: map[string]CustomStruct{"a": CustomStruct{SomeMap: map[string]string{"key1": "key10"}}},
	},
	{name: "Not Init of struct key", dst: map[string]CustomStruct{},
		src: map[string]CustomStruct{"a": CustomStruct{SomeMap: map[string]string{"key1": "key10"}}},
		exp: map[string]CustomStruct{"a": CustomStruct{SomeMap: map[string]string{"key1": "key10"}}},
	},
	{name: "Nil struct key", dst: map[string]CustomStruct{"a": CustomStruct{SomeMap: nil}},
		src: map[string]CustomStruct{"a": CustomStruct{SomeMap: map[string]string{"key1": "key10"}}},
		exp: map[string]CustomStruct{"a": CustomStruct{SomeMap: map[string]string{"key1": "key10"}}}},
}

func TestMergoStructMap(t *testing.T) {

	for _, data := range testDataStructMap {
		dst := data.dst
		src := data.src
		exp := data.exp

		Merge(&dst, src, WithAppendSlice, WithOverride)
		assert.Equal(t, dst, exp)
	}
}
<commit_msg>err handling in unit tests<commit_after>package mergo

import (
	"testing"

	"github.com/magiconair/properties/assert"
)

func TestMergoSimpleMap(t *testing.T) {
	dst := map[string]string{"key1": "loosethis", "key2": "keepthis"}
	src := map[string]string{"key1": "key10"}
	exp := map[string]string{"key1": "key10", "key2": "keepthis"}
	err := Merge(&dst, src, WithAppendSlice, WithOverride)
	assert.Equal(t, err, nil)
	assert.Equal(t, dst, exp)
}

type CustomStruct struct {
	SomeMap map[string]string
}

var testDataStructMap = []struct {
	name string
	src  map[string]CustomStruct
	dst  map[string]CustomStruct
	exp  map[string]CustomStruct
}{
	{name: "Normal",
		dst: map[string]CustomStruct{"a": CustomStruct{SomeMap: map[string]string{"key1": "loosethis", "key2": "keepthis"}}},
		src: map[string]CustomStruct{"a": CustomStruct{SomeMap: map[string]string{"key1": "key10"}}},
		exp: map[string]CustomStruct{"a": CustomStruct{SomeMap: map[string]string{"key1": "key10", "key2": "keepthis"}}},
	},
	{name: "Init of struct key", dst: map[string]CustomStruct{"a": CustomStruct{SomeMap: map[string]string{}}},
		src: map[string]CustomStruct{"a": CustomStruct{SomeMap: map[string]string{"key1": "key10"}}},
		exp: map[string]CustomStruct{"a": CustomStruct{SomeMap: map[string]string{"key1": "key10"}}},
	},
	{name: "Not Init of struct key", dst: map[string]CustomStruct{},
		src: map[string]CustomStruct{"a": CustomStruct{SomeMap: map[string]string{"key1": "key10"}}},
		exp: map[string]CustomStruct{"a": CustomStruct{SomeMap: map[string]string{"key1": "key10"}}},
	},
	{name: "Nil struct key", dst: map[string]CustomStruct{"a": CustomStruct{SomeMap: nil}},
		src: map[string]CustomStruct{"a": CustomStruct{SomeMap: map[string]string{"key1": "key10"}}},
		exp: map[string]CustomStruct{"a": CustomStruct{SomeMap: map[string]string{"key1": "key10"}}}},
}

func TestMergoStructMap(t *testing.T) {

	for _, data := range testDataStructMap {
		dst := data.dst
		src := data.src
		exp := data.exp

		err := Merge(&dst, src, WithAppendSlice, WithOverride)
		assert.Equal(t, err, nil)
		assert.Equal(t, dst, exp)
	}
}
<|endoftext|>


<commit_before>package daemon

import (
	"errors"
	"fmt"
	"github.com/facebookgo/grace/gracehttp"
	"github.com/whosonfirst/go-webhookd"
	"github.com/whosonfirst/go-webhookd/config"
	"github.com/whosonfirst/go-webhookd/dispatchers"
	"github.com/whosonfirst/go-webhookd/receivers"
	"github.com/whosonfirst/go-webhookd/transformations"
	"github.com/whosonfirst/go-webhookd/webhook"
	_ "log"
	"net/http"
	"sync"
)

type WebhookDaemon struct {
	host     string
	port     int
	webhooks map[string]webhookd.WebhookHandler
}

func NewWebhookDaemonFromConfig(cfg *config.WebhookConfig) (*WebhookDaemon, error) {

	d, err := NewWebhookDaemon(cfg.Daemon.Host, cfg.Daemon.Port)

	if err != nil {
		return nil, err
	}

	err = d.AddWebhooksFromConfig(cfg)

	if err != nil {
		return nil, err
	}

	return d, nil
}

func NewWebhookDaemon(host string, port int) (*WebhookDaemon, error) {

	webhooks := make(map[string]webhookd.WebhookHandler)

	d := WebhookDaemon{
		host:     host,
		port:     port,
		webhooks: webhooks,
	}

	return &d, nil
}

func (d *WebhookDaemon) AddWebhooksFromConfig(cfg *config.WebhookConfig) error {

	if len(cfg.Webhooks) == 0 {
		return errors.New("No webhooks defined")
	}

	for i, hook := range cfg.Webhooks {

		if hook.Endpoint == "" {
			msg := fmt.Sprintf("Missing endpoint at offset %d", i+1)
			return errors.New(msg)
		}

		if hook.Receiver == "" {
			msg := fmt.Sprintf("Missing receiver at offset %d", i+1)
			return errors.New(msg)
		}

		if len(hook.Dispatchers) == 0 {
			msg := fmt.Sprintf("Missing dispatchers at offset %d", i+1)
			return errors.New(msg)
		}

		receiver_config, err := cfg.GetReceiverConfigByName(hook.Receiver)

		if err != nil {
			return err
		}

		receiver, err := receivers.NewReceiverFromConfig(receiver_config)

		if err != nil {
			return err
		}

		var steps []webhookd.WebhookTransformation

		for _, name := range hook.Transformations {

			transformation_config, err := cfg.GetTransformationConfigByName(name)

			if err != nil {
				return err
			}

			step, err := transformations.NewTransformationFromConfig(transformation_config)

			if err != nil {
				return err
			}

			steps = append(steps, step)
		}

		var sendto []webhookd.WebhookDispatcher

		for _, name := range hook.Dispatchers {

			dispatcher_config, err := cfg.GetDispatcherConfigByName(name)

			if err != nil {
				return err
			}

			dispatcher, err := dispatchers.NewDispatcherFromConfig(dispatcher_config)

			if err != nil {
				return err
			}

			sendto = append(sendto, dispatcher)
		}

		wh, err := webhook.NewWebhook(hook.Endpoint, receiver, steps, sendto)

		if err != nil {
			return err
		}

		err = d.AddWebhook(wh)

		if err != nil {
			return err
		}

	}

	return nil
}

func (d *WebhookDaemon) AddWebhook(wh webhook.Webhook) error {

	endpoint := wh.Endpoint()
	_, ok := d.webhooks[endpoint]

	if ok {
		return errors.New("endpoint already configured")
	}

	d.webhooks[endpoint] = wh
	return nil
}

func (d *WebhookDaemon) HandlerFunc() (http.HandlerFunc, error) {

	handler := func(rsp http.ResponseWriter, req *http.Request) {

		endpoint := req.URL.Path

		wh, ok := d.webhooks[endpoint]

		if !ok {
			http.Error(rsp, "404 Not found", http.StatusNotFound)
			return
		}

		rcvr := wh.Receiver()

		body, err := rcvr.Receive(req)

		if err != nil {
			http.Error(rsp, err.Error(), err.Code)
			return
		}

		for _, step := range wh.Transformations() {

			body, err = step.Transform(body)

			if err != nil {
				http.Error(rsp, err.Error(), err.Code)
				return
			}
		}

		wg := new(sync.WaitGroup)
		errors := make([]*webhookd.WebhookError, 0)

		for _, d := range wh.Dispatchers() {

			wg.Add(1)

			go func(d webhookd.WebhookDispatcher, body []byte) {

				defer wg.Done()

				err = d.Dispatch(body)

				if err != nil {
					// FIX ME
				}

			}(d, body)
		}

		wg.Wait()

		if len(errors) > 0 {
			http.Error(rsp, "FIX ME", http.StatusInternalServerError)
			return
		}

		return
	}

	return http.HandlerFunc(handler), nil
}

func (d *WebhookDaemon) Start() error {

	handler, err := d.HandlerFunc()

	if err != nil {
		return err
	}

	addr := fmt.Sprintf("%s:%d", d.host, d.port)

	mux := http.NewServeMux()
	mux.HandleFunc("/", handler)

	err = gracehttp.Serve(&http.Server{Addr: addr, Handler: mux})

	if err != nil {
		return err
	}

	return nil
}
<commit_msg>make error reporting work for dispatchers in goroutines<commit_after>package daemon

import (
	"errors"
	"fmt"
	"github.com/facebookgo/grace/gracehttp"
	"github.com/whosonfirst/go-webhookd"
	"github.com/whosonfirst/go-webhookd/config"
	"github.com/whosonfirst/go-webhookd/dispatchers"
	"github.com/whosonfirst/go-webhookd/receivers"
	"github.com/whosonfirst/go-webhookd/transformations"
	"github.com/whosonfirst/go-webhookd/webhook"
	_ "log"
	"net/http"
	"strings"
	"sync"
)

type WebhookDaemon struct {
	host     string
	port     int
	webhooks map[string]webhookd.WebhookHandler
}

func NewWebhookDaemonFromConfig(cfg *config.WebhookConfig) (*WebhookDaemon, error) {

	d, err := NewWebhookDaemon(cfg.Daemon.Host, cfg.Daemon.Port)

	if err != nil {
		return nil, err
	}

	err = d.AddWebhooksFromConfig(cfg)

	if err != nil {
		return nil, err
	}

	return d, nil
}

func NewWebhookDaemon(host string, port int) (*WebhookDaemon, error) {

	webhooks := make(map[string]webhookd.WebhookHandler)

	d := WebhookDaemon{
		host:     host,
		port:     port,
		webhooks: webhooks,
	}

	return &d, nil
}

func (d *WebhookDaemon) AddWebhooksFromConfig(cfg *config.WebhookConfig) error {

	if len(cfg.Webhooks) == 0 {
		return errors.New("No webhooks defined")
	}

	for i, hook := range cfg.Webhooks {

		if hook.Endpoint == "" {
			msg := fmt.Sprintf("Missing endpoint at offset %d", i+1)
			return errors.New(msg)
		}

		if hook.Receiver == "" {
			msg := fmt.Sprintf("Missing receiver at offset %d", i+1)
			return errors.New(msg)
		}

		if len(hook.Dispatchers) == 0 {
			msg := fmt.Sprintf("Missing dispatchers at offset %d", i+1)
			return errors.New(msg)
		}

		receiver_config, err := cfg.GetReceiverConfigByName(hook.Receiver)

		if err != nil {
			return err
		}

		receiver, err := receivers.NewReceiverFromConfig(receiver_config)

		if err != nil {
			return err
		}

		var steps []webhookd.WebhookTransformation

		for _, name := range hook.Transformations {

			transformation_config, err := cfg.GetTransformationConfigByName(name)

			if err != nil {
				return err
			}

			step, err := transformations.NewTransformationFromConfig(transformation_config)

			if err != nil {
				return err
			}

			steps = append(steps, step)
		}

		var sendto []webhookd.WebhookDispatcher

		for _, name := range hook.Dispatchers {

			dispatcher_config, err := cfg.GetDispatcherConfigByName(name)

			if err != nil {
				return err
			}

			dispatcher, err := dispatchers.NewDispatcherFromConfig(dispatcher_config)

			if err != nil {
				return err
			}

			sendto = append(sendto, dispatcher)
		}

		wh, err := webhook.NewWebhook(hook.Endpoint, receiver, steps, sendto)

		if err != nil {
			return err
		}

		err = d.AddWebhook(wh)

		if err != nil {
			return err
		}

	}

	return nil
}

func (d *WebhookDaemon) AddWebhook(wh webhook.Webhook) error {

	endpoint := wh.Endpoint()
	_, ok := d.webhooks[endpoint]

	if ok {
		return errors.New("endpoint already configured")
	}

	d.webhooks[endpoint] = wh
	return nil
}

func (d *WebhookDaemon) HandlerFunc() (http.HandlerFunc, error) {

	handler := func(rsp http.ResponseWriter, req *http.Request) {

		endpoint := req.URL.Path

		wh, ok := d.webhooks[endpoint]

		if !ok {
			http.Error(rsp, "404 Not found", http.StatusNotFound)
			return
		}

		rcvr := wh.Receiver()

		body, err := rcvr.Receive(req)

		if err != nil {
			http.Error(rsp, err.Error(), err.Code)
			return
		}

		for _, step := range wh.Transformations() {

			body, err = step.Transform(body)

			if err != nil {
				http.Error(rsp, err.Error(), err.Code)
				return
			}
		}

		wg := new(sync.WaitGroup)
		ch := make(chan *webhookd.WebhookError)

		for _, d := range wh.Dispatchers() {

			wg.Add(1)

			go func(d webhookd.WebhookDispatcher, body []byte) {

				defer wg.Done()

				err = d.Dispatch(body)

				if err != nil {
					ch <- err
				}

				// err = &webhookd.WebhookError{Code: 000, Message: "o_O"}
				// ch <- err

			}(d, body)
		}

		errors := make([]string, 0)

		go func() {

			for e := range ch {
				errors = append(errors, e.Error())
			}
		}()

		wg.Wait()

		if len(errors) > 0 {

			msg := strings.Join(errors, "\n\n")
			http.Error(rsp, msg, http.StatusInternalServerError)
			return
		}

		return
	}

	return http.HandlerFunc(handler), nil
}

func (d *WebhookDaemon) Start() error {

	handler, err := d.HandlerFunc()

	if err != nil {
		return err
	}

	addr := fmt.Sprintf("%s:%d", d.host, d.port)

	mux := http.NewServeMux()
	mux.HandleFunc("/", handler)

	err = gracehttp.Serve(&http.Server{Addr: addr, Handler: mux})

	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package daemon

import (
    "github.com/sevlyar/go-daemon"
    "github.com/Sirupsen/logrus"
    "github.com/BluePecker/JwtAuth/server"
    "github.com/BluePecker/JwtAuth/server/router/jwt"
)

type TLS struct {
    Cert string
    Key  string
}

type Conf struct {
    Daemon  bool

    PidFile string
    LogFile string

    Port    int
    Host    string
    Tls     TLS
}

type Daemon struct {

}

func (d *Daemon) Start(conf Conf) {
    if (conf.Daemon == true) {
        dCtx := daemon.Context{
            PidFileName: conf.PidFile,
            PidFilePerm: 0644,
            LogFilePerm: 0640,
            Umask:       027,
            WorkDir:     "/",
            LogFileName: conf.LogFile,
        }

        defer dCtx.Release()

        if child, err := dCtx.Reborn(); err != nil {
            logrus.Fatal(err)
        } else if child != nil {
            return
        }
    }

    api := &server.Server{}
    api.AddRouter(jwt.NewRouter(nil))

    api.Accept(server.Options{
        Host: conf.Host,
        Tls: conf.Tls,
        Port: conf.Port,
    })
}<commit_msg>format code<commit_after>package daemon

import (
    "github.com/sevlyar/go-daemon"
    "github.com/Sirupsen/logrus"
    "github.com/BluePecker/JwtAuth/server"
    "github.com/BluePecker/JwtAuth/server/router/jwt"
)

type TLS struct {
    Cert string
    Key  string
}

type Conf struct {
    Daemon  bool

    PidFile string
    LogFile string

    Port    int
    Host    string
    Tls     TLS
}

type Daemon struct {

}

func (d *Daemon) Start(conf Conf) {
    if (conf.Daemon == true) {
        dCtx := daemon.Context{
            PidFileName: conf.PidFile,
            PidFilePerm: 0644,
            LogFilePerm: 0640,
            Umask:       027,
            WorkDir:     "/",
            LogFileName: conf.LogFile,
        }

        defer dCtx.Release()

        if child, err := dCtx.Reborn(); err != nil {
            logrus.Fatal(err)
        } else if child != nil {
            return
        }
    }

    api := &server.Server{}
    api.AddRouter(jwt.NewRouter(nil))

    api.Accept(server.Options{Host: conf.Host, Tls: conf.Tls, Port: conf.Port})
}<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"strings"

	"github.com/hashicorp/errwrap"
	"github.com/labstack/echo"
)

var CheckVersionURL = "https://s3-eu-west-1.amazonaws.com/timeglass/version/VERSION"

type Server struct {
	timer             *Timer
	httpb             string
	router            *echo.Echo
	listener          net.Listener
	mostRecentVersion string

	*http.Server
}

func (s *Server) stop(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Stopping Timer at: %s", s.timer.Time())
	defer s.Stop()
}

func (s *Server) start(c *echo.Context) *echo.HTTPError {
	s.timer.Start()
	return c.String(http.StatusOK, fmt.Sprintf("Started at: %s", s.timer.Time()))
}

func (s *Server) pause(c *echo.Context) *echo.HTTPError {
	s.timer.Stop()
	return c.String(http.StatusOK, fmt.Sprintf("Stopped at: %s", s.timer.Time()))
}

func (s *Server) lap(c *echo.Context) *echo.HTTPError {
	defer s.timer.Reset()
	return c.JSON(http.StatusOK, map[string]interface{}{
		"Time": s.timer.Time().String(),
	})
}

func (s *Server) status(c *echo.Context) *echo.HTTPError {
	data := map[string]interface{}{
		"CurrentVersion":    Version,
		"MostRecentVersion": s.mostRecentVersion,
		"Time":              s.timer.Time().String(),
	}

	//check version without delaying response
	go s.CheckVersion()

	return c.JSON(http.StatusOK, data)
}

func version(c *echo.Context) *echo.HTTPError {
	return c.String(http.StatusOK, fmt.Sprintf("Daemon %s (%s)", Version, Build))
}

func NewServer(httpb string, timer *Timer) (*Server, error) {
	router := echo.New()
	l, err := net.Listen("tcp", httpb)
	if err != nil {
		return nil, errwrap.Wrapf(fmt.Sprintf("Failed to create listener on '%s': {{err}}", httpb), err)
	}

	s := &Server{
		timer:             timer,
		httpb:             httpb,
		router:            router,
		listener:          l,
		mostRecentVersion: Version,
		Server:            &http.Server{Handler: router},
	}

	router.Get("/", version)
	router.Get("/timer.status", s.status)
	router.Get("/timer.pause", s.pause)
	router.Get("/timer.lap", s.lap)
	router.Get("/timer.start", s.start)
	router.Get("/timer.stop", s.stop)

	return s, nil
}

func (s *Server) Addr() string {
	return s.listener.Addr().String()
}

func (s *Server) Stop() {
	s.listener.Close()
}

func (s *Server) Start() error {
	s.timer.Start()
	return s.Server.Serve(s.listener)
}

func (s *Server) CheckVersion() {
	resp, err := http.Get(CheckVersionURL)
	if err == nil {
		defer resp.Body.Close()
		buff := bytes.NewBuffer(nil)
		_, err = io.Copy(buff, resp.Body)
		if err != nil {
			log.Printf("Failed to read response body for version check: %s", err)
		} else {
			s.mostRecentVersion = strings.TrimSpace(buff.String())
		}
	}
}
<commit_msg>added current version to version check [spent 3m30s]<commit_after>package main

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"strings"

	"github.com/hashicorp/errwrap"
	"github.com/labstack/echo"
)

var CheckVersionURL = "https://s3-eu-west-1.amazonaws.com/timeglass/version/VERSION?dversion=" + Version

type Server struct {
	timer             *Timer
	httpb             string
	router            *echo.Echo
	listener          net.Listener
	mostRecentVersion string

	*http.Server
}

func (s *Server) stop(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Stopping Timer at: %s", s.timer.Time())
	defer s.Stop()
}

func (s *Server) start(c *echo.Context) *echo.HTTPError {
	s.timer.Start()
	return c.String(http.StatusOK, fmt.Sprintf("Started at: %s", s.timer.Time()))
}

func (s *Server) pause(c *echo.Context) *echo.HTTPError {
	s.timer.Stop()
	return c.String(http.StatusOK, fmt.Sprintf("Stopped at: %s", s.timer.Time()))
}

func (s *Server) lap(c *echo.Context) *echo.HTTPError {
	defer s.timer.Reset()
	return c.JSON(http.StatusOK, map[string]interface{}{
		"Time": s.timer.Time().String(),
	})
}

func (s *Server) status(c *echo.Context) *echo.HTTPError {
	data := map[string]interface{}{
		"CurrentVersion":    Version,
		"MostRecentVersion": s.mostRecentVersion,
		"Time":              s.timer.Time().String(),
	}

	//check version without delaying response
	go s.CheckVersion()

	return c.JSON(http.StatusOK, data)
}

func version(c *echo.Context) *echo.HTTPError {
	return c.String(http.StatusOK, fmt.Sprintf("Daemon %s (%s)", Version, Build))
}

func NewServer(httpb string, timer *Timer) (*Server, error) {
	router := echo.New()
	l, err := net.Listen("tcp", httpb)
	if err != nil {
		return nil, errwrap.Wrapf(fmt.Sprintf("Failed to create listener on '%s': {{err}}", httpb), err)
	}

	s := &Server{
		timer:             timer,
		httpb:             httpb,
		router:            router,
		listener:          l,
		mostRecentVersion: Version,
		Server:            &http.Server{Handler: router},
	}

	router.Get("/", version)
	router.Get("/timer.status", s.status)
	router.Get("/timer.pause", s.pause)
	router.Get("/timer.lap", s.lap)
	router.Get("/timer.start", s.start)
	router.Get("/timer.stop", s.stop)

	return s, nil
}

func (s *Server) Addr() string {
	return s.listener.Addr().String()
}

func (s *Server) Stop() {
	s.listener.Close()
}

func (s *Server) Start() error {
	s.timer.Start()
	return s.Server.Serve(s.listener)
}

func (s *Server) CheckVersion() {
	resp, err := http.Get(CheckVersionURL)
	if err == nil {
		defer resp.Body.Close()
		buff := bytes.NewBuffer(nil)
		_, err = io.Copy(buff, resp.Body)
		if err != nil {
			log.Printf("Failed to read response body for version check: %s", err)
		} else {
			s.mostRecentVersion = strings.TrimSpace(buff.String())
		}
	}
}
<|endoftext|>


<commit_before>package dal

import (
	"errors"
	"fmt"
	"time"

	"github.com/jmoiron/sqlx"
)

func NewTSMetric(db *sqlx.DB) *TSMetric {
	ts := &TSMetric{}
	ts.db = db
	ts.table = "ts_metrics"

	return ts
}

type HighchartPayload struct {
	Name string          `json:"name"`
	Data [][]interface{} `json:"data"`
}

type TSMetricRow struct {
	ClusterID int64     `db:"cluster_id"`
	MetricID  int64     `db:"metric_id"`
	Created   time.Time `db:"created"`
	Key       string    `db:"key"`
	Host      string    `db:"host"`
	Value     float64   `db:"value"`
}

type TSMetric struct {
	Base
}

func (ts *TSMetric) metricRowsForHighchart(tx *sqlx.Tx, host string, tsMetricRows []*TSMetricRow) (*HighchartPayload, error) {
	hcPayload := &HighchartPayload{}
	hcPayload.Name = host
	hcPayload.Data = make([][]interface{}, len(tsMetricRows))

	for i, tsMetricRow := range tsMetricRows {
		row := make([]interface{}, 2)
		row[0] = tsMetricRow.Created.UnixNano() / 1000000
		row[1] = tsMetricRow.Value

		hcPayload.Data[i] = row
	}

	return hcPayload, nil
}

// Create a new record.
func (ts *TSMetric) Create(tx *sqlx.Tx, clusterID, metricID int64, key, host string, value float64) error {
	insertData := make(map[string]interface{})
	insertData["cluster_id"] = clusterID
	insertData["metric_id"] = metricID
	insertData["key"] = key
	insertData["host"] = host
	insertData["value"] = value

	_, err := ts.InsertIntoTable(tx, insertData)
	return err
}

func (ts *TSMetric) CreateByHostRow(tx *sqlx.Tx, hostRow *HostRow, metricsMap map[string]int64) error {
	// Loop through every host's data and see if they are part of graph metrics.
	// If they are, insert a record in ts_metrics.
	for path, data := range hostRow.DataAsFlatKeyValue() {
		for dataKey, value := range data {
			metricKey := path + "." + dataKey

			if metricID, ok := metricsMap[metricKey]; ok {
				// Deserialized JSON number -> interface{} always have float64 as type.
				if trueValueFloat64, ok := value.(float64); ok {
					// Ignore error for now, there's no need to break the entire loop when one insert fails.
					err := ts.Create(tx, hostRow.ClusterID, metricID, metricKey, hostRow.Name, trueValueFloat64)
					if err != nil {
						println(err.Error())
					}
				}
			}
		}
	}
	return nil
}

func (ts *TSMetric) AllByMetricIDHostAndInterval(tx *sqlx.Tx, clusterID, metricID int64, host string, interval string) ([]*TSMetricRow, error) {
	if interval == "" {
		interval = "1 hour"
	}

	rows := []*TSMetricRow{}
	query := fmt.Sprintf("SELECT * FROM %v WHERE cluster_id=$1 AND metric_id=$2 AND host=$3 AND created >= (NOW() - INTERVAL '%v') ORDER BY cluster_id,metric_id,created ASC", ts.table, interval)
	err := ts.db.Select(&rows, query, clusterID, metricID, host)

	return rows, err
}

func (ts *TSMetric) AllByMetricIDHostAndIntervalForHighchart(tx *sqlx.Tx, clusterID, metricID int64, host string, interval string) (*HighchartPayload, error) {
	tsMetricRows, err := ts.AllByMetricIDHostAndInterval(tx, clusterID, metricID, host, interval)
	if err != nil {
		return nil, err
	}

	return ts.metricRowsForHighchart(tx, host, tsMetricRows)
}

func (ts *TSMetric) AllByMetricIDAndInterval(tx *sqlx.Tx, clusterID, metricID int64, interval string) ([]*TSMetricRow, error) {
	if interval == "" {
		interval = "1 hour"
	}

	rows := []*TSMetricRow{}
	query := fmt.Sprintf("SELECT * FROM %v WHERE cluster_id=$1 AND metric_id=$2 AND created >= (NOW() - INTERVAL '%v') ORDER BY cluster_id,metric_id,created ASC", ts.table, interval)
	err := ts.db.Select(&rows, query, clusterID, metricID)

	return rows, err
}

func (ts *TSMetric) AllByMetricIDAndIntervalForHighchart(tx *sqlx.Tx, clusterID, metricID int64, interval string) ([]*HighchartPayload, error) {
	tsMetricRows, err := ts.AllByMetricIDAndInterval(tx, clusterID, metricID, interval)
	if err != nil {
		return nil, err
	}

	// Group all TSMetricRows per host
	mapHostsAndMetrics := make(map[string][]*TSMetricRow)

	for _, tsMetricRow := range tsMetricRows {
		host := tsMetricRow.Host

		if _, ok := mapHostsAndMetrics[host]; !ok {
			mapHostsAndMetrics[host] = make([]*TSMetricRow, 0)
		}

		mapHostsAndMetrics[host] = append(mapHostsAndMetrics[host], tsMetricRow)
	}

	// Then generate multiple Highchart payloads per all these hosts.
	highChartPayloads := make([]*HighchartPayload, 0)

	for host, tsMetricRows := range mapHostsAndMetrics {
		highChartPayload, err := ts.metricRowsForHighchart(tx, host, tsMetricRows)
		if err != nil {
			return nil, err
		}
		highChartPayloads = append(highChartPayloads, highChartPayload)
	}

	return highChartPayloads, nil
}

// DeleteByDayInterval deletes all record older than x days ago.
func (ts *TSMetric) DeleteByDayInterval(tx *sqlx.Tx, dayInterval int) error {
	if ts.table == "" {
		return errors.New("Table must not be empty.")
	}

	tx, wrapInSingleTransaction, err := ts.newTransactionIfNeeded(tx)
	if tx == nil {
		return errors.New("Transaction struct must not be empty.")
	}
	if err != nil {
		return err
	}

	query := fmt.Sprintf("DELETE FROM %v WHERE created < (NOW() - INTERVAL '%v day')", ts.table, dayInterval)

	_, err = tx.Exec(query)

	if wrapInSingleTransaction == true {
		err = tx.Commit()
	}

	return err
}
<commit_msg>Aggregate every x interval<commit_after>package dal

import (
	"errors"
	"fmt"
	"time"

	"github.com/jmoiron/sqlx"
)

func NewTSMetric(db *sqlx.DB) *TSMetric {
	ts := &TSMetric{}
	ts.db = db
	ts.table = "ts_metrics"

	return ts
}

type HighchartPayload struct {
	Name string          `json:"name"`
	Data [][]interface{} `json:"data"`
}

type TSMetricAggregateRow struct {
	CreatedUnix int64   `db:"created_unix"`
	Key         string  `db:"key"`
	Avg         float64 `db:"avg"`
	Max         float64 `db:"max"`
	Min         float64 `db:"min"`
	Sum         float64 `db:"sum"`
}

type TSMetricRow struct {
	ClusterID int64     `db:"cluster_id"`
	MetricID  int64     `db:"metric_id"`
	Created   time.Time `db:"created"`
	Key       string    `db:"key"`
	Host      string    `db:"host"`
	Value     float64   `db:"value"`
}

type TSMetric struct {
	Base
}

func (ts *TSMetric) metricRowsForHighchart(tx *sqlx.Tx, host string, tsMetricRows []*TSMetricRow) (*HighchartPayload, error) {
	hcPayload := &HighchartPayload{}
	hcPayload.Name = host
	hcPayload.Data = make([][]interface{}, len(tsMetricRows))

	for i, tsMetricRow := range tsMetricRows {
		row := make([]interface{}, 2)
		row[0] = tsMetricRow.Created.UnixNano() / 1000000
		row[1] = tsMetricRow.Value

		hcPayload.Data[i] = row
	}

	return hcPayload, nil
}

// Create a new record.
func (ts *TSMetric) Create(tx *sqlx.Tx, clusterID, metricID int64, key, host string, value float64) error {
	insertData := make(map[string]interface{})
	insertData["cluster_id"] = clusterID
	insertData["metric_id"] = metricID
	insertData["key"] = key
	insertData["host"] = host
	insertData["value"] = value

	_, err := ts.InsertIntoTable(tx, insertData)
	return err
}

func (ts *TSMetric) CreateByHostRow(tx *sqlx.Tx, hostRow *HostRow, metricsMap map[string]int64) error {
	// Loop through every host's data and see if they are part of graph metrics.
	// If they are, insert a record in ts_metrics.
	for path, data := range hostRow.DataAsFlatKeyValue() {
		for dataKey, value := range data {
			metricKey := path + "." + dataKey

			if metricID, ok := metricsMap[metricKey]; ok {
				// Deserialized JSON number -> interface{} always have float64 as type.
				if trueValueFloat64, ok := value.(float64); ok {
					// Ignore error for now, there's no need to break the entire loop when one insert fails.
					err := ts.Create(tx, hostRow.ClusterID, metricID, metricKey, hostRow.Name, trueValueFloat64)
					if err != nil {
						println(err.Error())
					}
				}
			}
		}
	}
	return nil
}

func (ts *TSMetric) AggregateEvery(tx *sqlx.Tx, clusterID int64, interval string) ([]*TSMetricAggregateRow, error) {
	if interval == "" {
		interval = "15 minute"
	}

	rows := []*TSMetricAggregateRow{}
	query := fmt.Sprintf("SELECT cluster_id,CEILING(extract('epoch' from created)/900)*900 AS created_unix, key, avg(value) as avg, max(value) as max, min(value) as min, sum(value) as sum FROM %v WHERE cluster_id=$1 AND created >= (NOW() - INTERVAL '%v') GROUP BY cluster_id, created_unix, key ORDER BY created_unix ASC", ts.table, interval)
	err := ts.db.Select(&rows, query, clusterID)

	return rows, err
}

func (ts *TSMetric) AllByMetricIDHostAndInterval(tx *sqlx.Tx, clusterID, metricID int64, host string, interval string) ([]*TSMetricRow, error) {
	if interval == "" {
		interval = "1 hour"
	}

	rows := []*TSMetricRow{}
	query := fmt.Sprintf("SELECT * FROM %v WHERE cluster_id=$1 AND metric_id=$2 AND host=$3 AND created >= (NOW() - INTERVAL '%v') ORDER BY cluster_id,metric_id,created ASC", ts.table, interval)
	err := ts.db.Select(&rows, query, clusterID, metricID, host)

	return rows, err
}

func (ts *TSMetric) AllByMetricIDHostAndIntervalForHighchart(tx *sqlx.Tx, clusterID, metricID int64, host string, interval string) (*HighchartPayload, error) {
	tsMetricRows, err := ts.AllByMetricIDHostAndInterval(tx, clusterID, metricID, host, interval)
	if err != nil {
		return nil, err
	}

	return ts.metricRowsForHighchart(tx, host, tsMetricRows)
}

func (ts *TSMetric) AllByMetricIDAndInterval(tx *sqlx.Tx, clusterID, metricID int64, interval string) ([]*TSMetricRow, error) {
	if interval == "" {
		interval = "1 hour"
	}

	rows := []*TSMetricRow{}
	query := fmt.Sprintf("SELECT * FROM %v WHERE cluster_id=$1 AND metric_id=$2 AND created >= (NOW() - INTERVAL '%v') ORDER BY cluster_id,metric_id,created ASC", ts.table, interval)
	err := ts.db.Select(&rows, query, clusterID, metricID)

	return rows, err
}

func (ts *TSMetric) AllByMetricIDAndIntervalForHighchart(tx *sqlx.Tx, clusterID, metricID int64, interval string) ([]*HighchartPayload, error) {
	tsMetricRows, err := ts.AllByMetricIDAndInterval(tx, clusterID, metricID, interval)
	if err != nil {
		return nil, err
	}

	// Group all TSMetricRows per host
	mapHostsAndMetrics := make(map[string][]*TSMetricRow)

	for _, tsMetricRow := range tsMetricRows {
		host := tsMetricRow.Host

		if _, ok := mapHostsAndMetrics[host]; !ok {
			mapHostsAndMetrics[host] = make([]*TSMetricRow, 0)
		}

		mapHostsAndMetrics[host] = append(mapHostsAndMetrics[host], tsMetricRow)
	}

	// Then generate multiple Highchart payloads per all these hosts.
	highChartPayloads := make([]*HighchartPayload, 0)

	for host, tsMetricRows := range mapHostsAndMetrics {
		highChartPayload, err := ts.metricRowsForHighchart(tx, host, tsMetricRows)
		if err != nil {
			return nil, err
		}
		highChartPayloads = append(highChartPayloads, highChartPayload)
	}

	return highChartPayloads, nil
}

// DeleteByDayInterval deletes all record older than x days ago.
func (ts *TSMetric) DeleteByDayInterval(tx *sqlx.Tx, dayInterval int) error {
	if ts.table == "" {
		return errors.New("Table must not be empty.")
	}

	tx, wrapInSingleTransaction, err := ts.newTransactionIfNeeded(tx)
	if tx == nil {
		return errors.New("Transaction struct must not be empty.")
	}
	if err != nil {
		return err
	}

	query := fmt.Sprintf("DELETE FROM %v WHERE created < (NOW() - INTERVAL '%v day')", ts.table, dayInterval)

	_, err = tx.Exec(query)

	if wrapInSingleTransaction == true {
		err = tx.Commit()
	}

	return err
}
<|endoftext|>


<commit_before>// service for exchanging current user settings with UI
package settings

import (
	"net/http"
	"sync"

	"github.com/getlantern/flashlight/analytics"
	"github.com/getlantern/flashlight/config"

	"github.com/getlantern/flashlight/ui"
	"github.com/getlantern/golog"
)

const (
	messageType = `Settings`
)

var (
	log          = golog.LoggerFor("flashlight.settings")
	service      *ui.Service
	cfgMutex     sync.Mutex
	baseSettings *Settings
	httpClient   *http.Client
)

type Settings struct {
	Version    string
	BuildDate  string
	AutoReport bool
	mutex      sync.Mutex
}

func Configure(cfg *config.Config, version, buildDate string) {

	cfgMutex.Lock()
	defer cfgMutex.Unlock()

	// base settings are always written
	baseSettings = &Settings{
		Version:    version,
		BuildDate:  buildDate,
		AutoReport: *cfg.AutoReport,
	}

	if service == nil {
		err := start(baseSettings)
		if err != nil {
			log.Errorf("Unable to register settings service: %q", err)
		}
	}
}

func start(baseSettings *Settings) error {
	var err error

	helloFn := func(write func(interface{}) error) error {
		log.Debugf("Sending Lantern settings to new client")
		return write(baseSettings)
	}

	service, err = ui.Register(messageType, nil, helloFn)
	go read()
	return err
}

func read() {
	for msg := range service.In {
		settings := (msg).(map[string]interface{})
		baseSettings.mutex.Lock()
		config.Update(func(updated *config.Config) error {
			autoReport := settings["autoReport"].(bool)
			if autoReport {
				analytics.StartService()
			} else {
				analytics.StopService()
			}
			baseSettings.AutoReport = autoReport
			*updated.AutoReport = autoReport
			return nil
		})
		baseSettings.mutex.Unlock()
	}
}
<commit_msg>settings needed mutex<commit_after>// service for exchanging current user settings with UI
package settings

import (
	"net/http"
	"sync"

	"github.com/getlantern/flashlight/analytics"
	"github.com/getlantern/flashlight/config"

	"github.com/getlantern/flashlight/ui"
	"github.com/getlantern/golog"
)

const (
	messageType = `Settings`
)

var (
	log          = golog.LoggerFor("flashlight.settings")
	service      *ui.Service
	cfgMutex     sync.Mutex
	baseSettings *Settings
	httpClient   *http.Client
)

type Settings struct {
	Version    string
	BuildDate  string
	AutoReport bool
}

func Configure(cfg *config.Config, version, buildDate string) {

	cfgMutex.Lock()
	defer cfgMutex.Unlock()

	// base settings are always written
	baseSettings = &Settings{
		Version:    version,
		BuildDate:  buildDate,
		AutoReport: *cfg.AutoReport,
	}

	if service == nil {
		err := start(baseSettings)
		if err != nil {
			log.Errorf("Unable to register settings service: %q", err)
		}
	}
}

func start(baseSettings *Settings) error {
	var err error

	helloFn := func(write func(interface{}) error) error {
		log.Debugf("Sending Lantern settings to new client")
		return write(baseSettings)
	}

	service, err = ui.Register(messageType, nil, helloFn)
	go read()
	return err
}

func read() {
	for msg := range service.In {
		settings := (msg).(map[string]interface{})
		config.Update(func(updated *config.Config) error {
			autoReport := settings["autoReport"].(bool)
			if autoReport {
				analytics.StartService()
			} else {
				analytics.StopService()
			}
			baseSettings.AutoReport = autoReport
			*updated.AutoReport = autoReport
			return nil
		})
	}
}
<|endoftext|>