<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"sync"
	"testing"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/patrobinson/go-fish/output"
)

type testInput struct {
	value string
}

func (t testInput) Init() error {
	return nil
}

func (t *testInput) Retrieve(out *chan []byte) {
	defer close(*out)
	*out <- []byte(t.value)
}

type testOutput struct {
	c *chan bool
}

func (t *testOutput) Sink(in *chan interface{}, wg *sync.WaitGroup) {
	defer (*wg).Done()
	for msg := range *in {
		log.Info("Input received")
		*t.c <- msg.(bool)
	}
	log.Info("Input closed")
}

func TestSuccessfulRun(t *testing.T) {
	output := make(chan bool)
	out := &testOutput{c: &output}
	in := &testInput{value: "a"}
	go run("testdata/rules", "testdata/eventTypes", in, out)
	r1 := <-output
	fmt.Print("Received 1 output\n")
	r2 := <-output
	fmt.Print("Received 2 output\n")
	if !r1 || !r2 {
		t.Errorf("Rules did not match %v %v", r1, r2)
	}
}

func TestFailRun(t *testing.T) {
	output := make(chan bool)
	out := &testOutput{c: &output}
	in := &testInput{value: "abc"}
	go run("testdata/rules", "testdata/eventTypes", in, out)
	if r1, r2 := <-output, <-output; r1 || r2 {
		t.Errorf("Rules did not match %v %v", r1, r2)
	}
}

type benchmarkInput struct {
	input *chan []byte
}

func (t benchmarkInput) Init() error {
	return nil
}

func (t *benchmarkInput) Retrieve(out *chan []byte) {
	defer close(*out)
	for in := range *t.input {
		*out <- in
	}
}

func BenchmarkRun(b *testing.B) {
	log.SetLevel(log.WarnLevel)
	output := make(chan bool)
	out := &testOutput{c: &output}

	input := make(chan []byte)
	in := &benchmarkInput{input: &input}
	var r1 bool
	var r2 bool
	b.ResetTimer()
	go run("testdata/rules", "testdata/eventTypes", in, out)
	for i := 0; i < b.N; i++ {
		bs := make([]byte, 1)
		bs[0] = byte(i)
		input <- bs
		r1 = <-output
		r2 = <-output
	}
	r := r1 || r2
	fmt.Printf("%v\n", r)
}

// +build integration

func TestStreamToStreamStateIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping integration test")
	}
	expectedEvent := output.OutputEvent{
		Source:    "CloudTrail",
		EventTime: time.Date(2016, 11, 14, 17, 25, 45, 0, &time.Location{}).UTC(),
		EventType: "UserCreated",
		Name:      "IAMUserCreated",
		Level:     output.WarnLevel,
		EventId:   "dEXAMPLE-265a-41e0-9352-4401bEXAMPLE",
		Entity:    "user/Bob",
		SourceIP:  "192.0.2.1",
		Body: map[string]interface{}{
			"AccountID":   "777788889999",
			"UserCreated": "god_user",
		},
		Occurrences: 1,
	}

	outChan := make(chan interface{})
	out := &testStatefulOutput{c: &outChan}
	inChan := make(chan []byte)
	in := &testStatefulInput{
		channel: &inChan,
		inputs:  2,
	}
	go run("testdata/statefulIntegrationTests/s2s_rules", "testdata/statefulIntegrationTests/eventTypes", in, out)

	assumeRoleEvent, _ := ioutil.ReadFile("testdata/statefulIntegrationTests/assumeRoleEvent.json")
	inChan <- assumeRoleEvent

	r2 := <-outChan
	fmt.Print("Received 1 output\n")

	createUserEvent, _ := ioutil.ReadFile("testdata/statefulIntegrationTests/createUserEvent.json")
	inChan <- createUserEvent

	r2 = <-outChan
	fmt.Print("Received 2 output\n")
	if !reflect.DeepEqual(r2.(output.OutputEvent), expectedEvent) {
		t.Errorf("Expected %v\nGot %v\n", expectedEvent, r2)
		event := r2.(output.OutputEvent)
		fmt.Printf("Source: %v\n", event.Source == expectedEvent.Source)
		fmt.Printf("EventTime: %v\n", event.EventTime == expectedEvent.EventTime)
		fmt.Printf("EventType: %v\n", event.EventType == expectedEvent.EventType)
		fmt.Printf("Name: %v\n", event.Name == expectedEvent.Name)
		fmt.Printf("Level: %v\n", event.Level == expectedEvent.Level)
		fmt.Printf("EventId: %v\n", event.EventId == expectedEvent.EventId)
		fmt.Printf("Entity: %v\n", event.Entity == expectedEvent.Entity)
		fmt.Printf("SourceIP: %v\n", event.EventId == expectedEvent.SourceIP)
		fmt.Printf("Body: %v\n", reflect.DeepEqual(event.Body, expectedEvent.Body))
	}

	os.Remove("assumeRoleEnrichment")
}

type testStatefulInput struct {
	channel *chan []byte
	inputs  int
}

func (t testStatefulInput) Init() error {
	return nil
}

func (t *testStatefulInput) Retrieve(out *chan []byte) {
	defer close(*out)
	for i := 0; i < t.inputs; i++ {
		output := <-*t.channel
		*out <- output
	}
}

type testStatefulOutput struct {
	c *chan interface{}
}

func (t *testStatefulOutput) Sink(in *chan interface{}, wg *sync.WaitGroup) {
	defer (*wg).Done()
	for msg := range *in {
		log.Info("Input received")
		*t.c <- msg
	}
	log.Info("Input closed")
}

func TestAggregateStateIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping integration test")
	}
	expectedEvent := output.OutputEvent{
		Source:    "CloudTrail",
		EventTime: time.Date(2016, 11, 14, 17, 25, 45, 0, &time.Location{}).UTC(),
		EventType: "NoMFA",
		Name:      "NoMFA",
		Level:     output.WarnLevel,
		EventId:   "dEXAMPLE-265a-41e0-9352-4401bEXAMPLE",
		Entity:    "role/AssumeNothing",
		SourceIP:  "192.0.2.1",
		Body: map[string]interface{}{
			"AccountID": "777788889999",
		},
		Occurrences: 3,
	}

	outChan := make(chan interface{})
	outProcessor := &testStatefulOutput{c: &outChan}
	inChan := make(chan []byte)
	in := &testStatefulInput{
		channel: &inChan,
		inputs:  4,
	}
	go run("testdata/statefulIntegrationTests/agg_rules", "testdata/statefulIntegrationTests/eventTypes", in, outProcessor)

	createUserEvent, _ := ioutil.ReadFile("testdata/statefulIntegrationTests/createUserEvent.json")
	inChan <- createUserEvent
	out := <-outChan
	if out != nil {
		t.Errorf("Expected %v\nGot %v\n", nil, out)
	}

	inChan <- createUserEvent
	out = <-outChan
	if out != nil {
		t.Errorf("Expected %v\nGot %v\n", nil, out)
	}

	inChan <- createUserEvent
	out = <-outChan
	if out != nil {
		t.Errorf("Expected %v\nGot %v\n", nil, out)
	}

	out = <-outChan

	if !reflect.DeepEqual(out.(output.OutputEvent), expectedEvent) {
		t.Errorf("Expected %v\nGot %v\n", expectedEvent, out)
		event := out.(output.OutputEvent)
		fmt.Printf("Source: %v\n", event.Source == expectedEvent.Source)
		fmt.Printf("EventTime: %v\n", event.EventTime == expectedEvent.EventTime)
		fmt.Printf("EventType: %v\n", event.EventType == expectedEvent.EventType)
		fmt.Printf("Name: %v\n", event.Name == expectedEvent.Name)
		fmt.Printf("Level: %v\n", event.Level == expectedEvent.Level)
		fmt.Printf("EventId: %v\n", event.EventId == expectedEvent.EventId)
		fmt.Printf("Entity: %v\n", event.Entity == expectedEvent.Entity)
		fmt.Printf("SourceIP: %v\n", event.EventId == expectedEvent.SourceIP)
		fmt.Printf("Body: %v\n", reflect.DeepEqual(event.Body, expectedEvent.Body))
	}

	os.Remove("aggregateEvent")
}
<commit_msg>Ensure hygene of test environment<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"sync"
	"testing"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/patrobinson/go-fish/output"
)

type testInput struct {
	value string
}

func (t testInput) Init() error {
	return nil
}

func (t *testInput) Retrieve(out *chan []byte) {
	defer close(*out)
	*out <- []byte(t.value)
}

type testOutput struct {
	c *chan bool
}

func (t *testOutput) Sink(in *chan interface{}, wg *sync.WaitGroup) {
	defer (*wg).Done()
	for msg := range *in {
		log.Info("Input received")
		*t.c <- msg.(bool)
	}
	log.Info("Input closed")
}

func TestSuccessfulRun(t *testing.T) {
	output := make(chan bool)
	out := &testOutput{c: &output}
	in := &testInput{value: "a"}
	go run("testdata/rules", "testdata/eventTypes", in, out)
	r1 := <-output
	fmt.Print("Received 1 output\n")
	r2 := <-output
	fmt.Print("Received 2 output\n")
	if !r1 || !r2 {
		t.Errorf("Rules did not match %v %v", r1, r2)
	}
}

func TestFailRun(t *testing.T) {
	output := make(chan bool)
	out := &testOutput{c: &output}
	in := &testInput{value: "abc"}
	go run("testdata/rules", "testdata/eventTypes", in, out)
	if r1, r2 := <-output, <-output; r1 || r2 {
		t.Errorf("Rules did not match %v %v", r1, r2)
	}
}

type benchmarkInput struct {
	input *chan []byte
}

func (t benchmarkInput) Init() error {
	return nil
}

func (t *benchmarkInput) Retrieve(out *chan []byte) {
	defer close(*out)
	for in := range *t.input {
		*out <- in
	}
}

func BenchmarkRun(b *testing.B) {
	log.SetLevel(log.WarnLevel)
	output := make(chan bool)
	out := &testOutput{c: &output}

	input := make(chan []byte)
	in := &benchmarkInput{input: &input}
	var r1 bool
	var r2 bool
	b.ResetTimer()
	go run("testdata/rules", "testdata/eventTypes", in, out)
	for i := 0; i < b.N; i++ {
		bs := make([]byte, 1)
		bs[0] = byte(i)
		input <- bs
		r1 = <-output
		r2 = <-output
	}
	r := r1 || r2
	fmt.Printf("%v\n", r)
}

// +build integration

func TestStreamToStreamStateIntegration(t *testing.T) {
	defer os.Remove("assumeRoleEnrichment")
	if testing.Short() {
		t.Skip("skipping integration test")
	}
	expectedEvent := output.OutputEvent{
		Source:    "CloudTrail",
		EventTime: time.Date(2016, 11, 14, 17, 25, 45, 0, &time.Location{}).UTC(),
		EventType: "UserCreated",
		Name:      "IAMUserCreated",
		Level:     output.WarnLevel,
		EventId:   "dEXAMPLE-265a-41e0-9352-4401bEXAMPLE",
		Entity:    "user/Bob",
		SourceIP:  "192.0.2.1",
		Body: map[string]interface{}{
			"AccountID":   "777788889999",
			"UserCreated": "god_user",
		},
		Occurrences: 1,
	}

	outChan := make(chan interface{})
	out := &testStatefulOutput{c: &outChan}
	inChan := make(chan []byte)
	in := &testStatefulInput{
		channel: &inChan,
		inputs:  2,
	}
	go run("testdata/statefulIntegrationTests/s2s_rules", "testdata/statefulIntegrationTests/eventTypes", in, out)

	assumeRoleEvent, _ := ioutil.ReadFile("testdata/statefulIntegrationTests/assumeRoleEvent.json")
	inChan <- assumeRoleEvent

	r2 := <-outChan
	fmt.Print("Received 1 output\n")

	createUserEvent, _ := ioutil.ReadFile("testdata/statefulIntegrationTests/createUserEvent.json")
	inChan <- createUserEvent

	r2 = <-outChan
	fmt.Print("Received 2 output\n")
	if !reflect.DeepEqual(r2.(output.OutputEvent), expectedEvent) {
		t.Errorf("Expected %v\nGot %v\n", expectedEvent, r2)
		event := r2.(output.OutputEvent)
		fmt.Printf("Source: %v\n", event.Source == expectedEvent.Source)
		fmt.Printf("EventTime: %v\n", event.EventTime == expectedEvent.EventTime)
		fmt.Printf("EventType: %v\n", event.EventType == expectedEvent.EventType)
		fmt.Printf("Name: %v\n", event.Name == expectedEvent.Name)
		fmt.Printf("Level: %v\n", event.Level == expectedEvent.Level)
		fmt.Printf("EventId: %v\n", event.EventId == expectedEvent.EventId)
		fmt.Printf("Entity: %v\n", event.Entity == expectedEvent.Entity)
		fmt.Printf("SourceIP: %v\n", event.EventId == expectedEvent.SourceIP)
		fmt.Printf("Body: %v\n", reflect.DeepEqual(event.Body, expectedEvent.Body))
	}
}

type testStatefulInput struct {
	channel *chan []byte
	inputs  int
}

func (t testStatefulInput) Init() error {
	return nil
}

func (t *testStatefulInput) Retrieve(out *chan []byte) {
	defer close(*out)
	for i := 0; i < t.inputs; i++ {
		output := <-*t.channel
		*out <- output
	}
}

type testStatefulOutput struct {
	c *chan interface{}
}

func (t *testStatefulOutput) Sink(in *chan interface{}, wg *sync.WaitGroup) {
	defer (*wg).Done()
	for msg := range *in {
		log.Info("Input received")
		*t.c <- msg
	}
	log.Info("Input closed")
}

func TestAggregateStateIntegration(t *testing.T) {
	defer os.Remove("aggregateEvent")
	if testing.Short() {
		t.Skip("skipping integration test")
	}
	expectedEvent := output.OutputEvent{
		Source:    "CloudTrail",
		EventTime: time.Date(2016, 11, 14, 17, 25, 45, 0, &time.Location{}).UTC(),
		EventType: "NoMFA",
		Name:      "NoMFA",
		Level:     output.WarnLevel,
		EventId:   "dEXAMPLE-265a-41e0-9352-4401bEXAMPLE",
		Entity:    "role/AssumeNothing",
		SourceIP:  "192.0.2.1",
		Body: map[string]interface{}{
			"AccountID": "777788889999",
		},
		Occurrences: 3,
	}

	outChan := make(chan interface{})
	outProcessor := &testStatefulOutput{c: &outChan}
	inChan := make(chan []byte)
	in := &testStatefulInput{
		channel: &inChan,
		inputs:  4,
	}
	go run("testdata/statefulIntegrationTests/agg_rules", "testdata/statefulIntegrationTests/eventTypes", in, outProcessor)

	createUserEvent, _ := ioutil.ReadFile("testdata/statefulIntegrationTests/createUserEvent.json")
	inChan <- createUserEvent
	out := <-outChan
	if out != nil {
		t.Errorf("Expected %v\nGot %v\n", nil, out)
	}

	inChan <- createUserEvent
	out = <-outChan
	if out != nil {
		t.Errorf("Expected %v\nGot %v\n", nil, out)
	}

	inChan <- createUserEvent
	out = <-outChan
	if out != nil {
		t.Errorf("Expected %v\nGot %v\n", nil, out)
	}

	out = <-outChan

	if !reflect.DeepEqual(out.(output.OutputEvent), expectedEvent) {
		t.Errorf("Expected %v\nGot %v\n", expectedEvent, out)
		event := out.(output.OutputEvent)
		fmt.Printf("Source: %v\n", event.Source == expectedEvent.Source)
		fmt.Printf("EventTime: %v\n", event.EventTime == expectedEvent.EventTime)
		fmt.Printf("EventType: %v\n", event.EventType == expectedEvent.EventType)
		fmt.Printf("Name: %v\n", event.Name == expectedEvent.Name)
		fmt.Printf("Level: %v\n", event.Level == expectedEvent.Level)
		fmt.Printf("EventId: %v\n", event.EventId == expectedEvent.EventId)
		fmt.Printf("Entity: %v\n", event.Entity == expectedEvent.Entity)
		fmt.Printf("SourceIP: %v\n", event.EventId == expectedEvent.SourceIP)
		fmt.Printf("Body: %v\n", reflect.DeepEqual(event.Body, expectedEvent.Body))
	}
}
<|endoftext|>


<commit_before>package main_test

import (
	"github.com/cloudfoundry-incubator/candiedyaml"
	"github.com/cloudfoundry/gorouter/config"
	"github.com/cloudfoundry/gorouter/route"
	"github.com/cloudfoundry/gorouter/test"
	"github.com/cloudfoundry/gorouter/test_util"
	"github.com/cloudfoundry/gunk/natsrunner"
	"github.com/cloudfoundry/yagnats"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
	"github.com/pivotal-golang/localip"

	"io"
	"net"
	"net/url"
	"syscall"

	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"time"
)

var _ = Describe("Router Integration", func() {
	var tmpdir string

	var natsPort uint16
	var natsRunner *natsrunner.NATSRunner

	var gorouterSession *Session

	writeConfig := func(config *config.Config, cfgFile string) {
		cfgBytes, err := candiedyaml.Marshal(config)
		Ω(err).ShouldNot(HaveOccurred())
		ioutil.WriteFile(cfgFile, cfgBytes, os.ModePerm)
	}

	createConfig := func(cfgFile string, statusPort, proxyPort uint16) *config.Config {
		config := test_util.SpecConfig(natsPort, statusPort, proxyPort)

		// ensure the threshold is longer than the interval that we check,
		// because we set the route's timestamp to time.Now() on the interval
		// as part of pausing
		config.PruneStaleDropletsIntervalInSeconds = 1
		config.DropletStaleThresholdInSeconds = 2
		config.StartResponseDelayIntervalInSeconds = 1
		config.EndpointTimeoutInSeconds = 5
		config.DrainTimeoutInSeconds = 1

		writeConfig(config, cfgFile)
		return config
	}

	startGorouterSession := func(cfgFile string) *Session {
		gorouterCmd := exec.Command(gorouterPath, "-c", cfgFile)
		session, err := Start(gorouterCmd, GinkgoWriter, GinkgoWriter)
		Ω(err).ShouldNot(HaveOccurred())
		Eventually(session, 5).Should(Say("gorouter.started"))
		gorouterSession = session

		return session
	}

	stopGorouter := func(gorouterSession *Session) {
		err := gorouterSession.Command.Process.Signal(syscall.SIGTERM)
		Ω(err).ShouldNot(HaveOccurred())
		Expect(gorouterSession.Wait(5 * time.Second)).Should(Exit(0))
	}

	BeforeEach(func() {
		var err error
		tmpdir, err = ioutil.TempDir("", "gorouter")
		Ω(err).ShouldNot(HaveOccurred())

		natsPort = test_util.NextAvailPort()
		natsRunner = natsrunner.NewNATSRunner(int(natsPort))
		natsRunner.Start()
	})

	AfterEach(func() {
		if natsRunner != nil {
			natsRunner.Stop()
		}

		os.RemoveAll(tmpdir)

		if gorouterSession != nil {
			stopGorouter(gorouterSession)
		}
	})

	Context("Drain", func() {
		var config *config.Config
		var localIP string
		var statusPort uint16
		var proxyPort uint16

		BeforeEach(func() {
			var err error
			localIP, err = localip.LocalIP()
			Ω(err).ShouldNot(HaveOccurred())

			statusPort = test_util.NextAvailPort()
			proxyPort = test_util.NextAvailPort()

			cfgFile := filepath.Join(tmpdir, "config.yml")
			config = createConfig(cfgFile, statusPort, proxyPort)

			gorouterSession = startGorouterSession(cfgFile)
		})

		It("waits for all requests to finish", func() {
			mbusClient, err := newMessageBus(config)
			Ω(err).ShouldNot(HaveOccurred())

			blocker := make(chan bool)
			longApp := test.NewTestApp([]route.Uri{"longapp.vcap.me"}, proxyPort, mbusClient, nil)
			longApp.AddHandler("/", func(w http.ResponseWriter, r *http.Request) {
				blocker <- true
				_, err := ioutil.ReadAll(r.Body)
				defer r.Body.Close()
				Ω(err).ShouldNot(HaveOccurred())
				w.WriteHeader(http.StatusNoContent)
			})
			longApp.Listen()
			routesUri := fmt.Sprintf("http://%s:%s@%s:%d/routes", config.Status.User, config.Status.Pass, localIP, statusPort)
			Ω(waitAppRegistered(routesUri, longApp, 2*time.Second)).To(BeTrue())

			go func() {
				defer GinkgoRecover()
				resp, err := http.Get(longApp.Endpoint())
				Ω(err).ShouldNot(HaveOccurred())
				Ω(resp.StatusCode).Should(Equal(http.StatusNoContent))
				ioutil.ReadAll(resp.Body)
				resp.Body.Close()
			}()

			<-blocker

			grouter := gorouterSession
			gorouterSession = nil
			err = grouter.Command.Process.Signal(syscall.SIGUSR1)
			Ω(err).ShouldNot(HaveOccurred())
			Eventually(grouter, 5).Should(Exit(0))
		})

		It("will timeout if requests take too long", func() {
			mbusClient, err := newMessageBus(config)
			Ω(err).ShouldNot(HaveOccurred())

			blocker := make(chan bool)
			resultCh := make(chan error, 1)
			timeoutApp := test.NewTestApp([]route.Uri{"timeout.vcap.me"}, proxyPort, mbusClient, nil)
			timeoutApp.AddHandler("/", func(w http.ResponseWriter, r *http.Request) {
				blocker <- true
				<-blocker
			})
			timeoutApp.Listen()
			routesUri := fmt.Sprintf("http://%s:%s@%s:%d/routes", config.Status.User, config.Status.Pass, localIP, statusPort)
			Ω(waitAppRegistered(routesUri, timeoutApp, 2*time.Second)).To(BeTrue())

			go func() {
				_, err := http.Get(timeoutApp.Endpoint())
				resultCh <- err
			}()

			<-blocker
			defer func() {
				blocker <- true
			}()

			grouter := gorouterSession
			gorouterSession = nil
			err = grouter.Command.Process.Signal(syscall.SIGUSR1)
			Ω(err).ShouldNot(HaveOccurred())
			Eventually(grouter, 5).Should(Exit(0))

			var result error
			Eventually(resultCh, 5).Should(Receive(&result))
			Ω(result).Should(BeAssignableToTypeOf(&url.Error{}))
			urlErr := result.(*url.Error)
			Ω(urlErr.Err).Should(Equal(io.EOF))
		})

		It("prevents new connections", func() {
			mbusClient, err := newMessageBus(config)

			blocker := make(chan bool)
			timeoutApp := test.NewTestApp([]route.Uri{"timeout.vcap.me"}, proxyPort, mbusClient, nil)
			timeoutApp.AddHandler("/", func(w http.ResponseWriter, r *http.Request) {
				blocker <- true
				<-blocker
			})
			timeoutApp.Listen()
			routesUri := fmt.Sprintf("http://%s:%s@%s:%d/routes", config.Status.User, config.Status.Pass, localIP, statusPort)
			Ω(waitAppRegistered(routesUri, timeoutApp, 2*time.Second)).To(BeTrue())

			go func() {
				http.Get(timeoutApp.Endpoint())
			}()

			<-blocker
			defer func() {
				blocker <- true
			}()

			grouter := gorouterSession
			gorouterSession = nil
			err = grouter.Command.Process.Signal(syscall.SIGUSR1)
			Ω(err).ShouldNot(HaveOccurred())
			Eventually(grouter, 5).Should(Exit(0))

			_, err = http.Get(timeoutApp.Endpoint())
			Ω(err).Should(HaveOccurred())
			urlErr := err.(*url.Error)
			opErr := urlErr.Err.(*net.OpError)
			Ω(opErr.Op).Should(Equal("dial"))
		})
	})

	Context("When Dropsonde is misconfigured", func() {
		It("fails to start", func() {
			statusPort := test_util.NextAvailPort()
			proxyPort := test_util.NextAvailPort()

			cfgFile := filepath.Join(tmpdir, "config.yml")
			config := createConfig(cfgFile, statusPort, proxyPort)
			config.Logging.MetronAddress = ""
			writeConfig(config, cfgFile)

			gorouterCmd := exec.Command(gorouterPath, "-c", cfgFile)
			session, _ := Start(gorouterCmd, GinkgoWriter, GinkgoWriter)
			Eventually(session, 5).Should(Exit(1))
		})
	})

	It("has Nats connectivity", func() {
		localIP, err := localip.LocalIP()
		Ω(err).ShouldNot(HaveOccurred())

		statusPort := test_util.NextAvailPort()
		proxyPort := test_util.NextAvailPort()

		cfgFile := filepath.Join(tmpdir, "config.yml")
		config := createConfig(cfgFile, statusPort, proxyPort)

		gorouterSession = startGorouterSession(cfgFile)

		mbusClient, err := newMessageBus(config)

		zombieApp := test.NewGreetApp([]route.Uri{"zombie.vcap.me"}, proxyPort, mbusClient, nil)
		zombieApp.Listen()

		runningApp := test.NewGreetApp([]route.Uri{"innocent.bystander.vcap.me"}, proxyPort, mbusClient, nil)
		runningApp.Listen()

		routesUri := fmt.Sprintf("http://%s:%s@%s:%d/routes", config.Status.User, config.Status.Pass, localIP, statusPort)

		Ω(waitAppRegistered(routesUri, zombieApp, 2*time.Second)).To(BeTrue())
		Ω(waitAppRegistered(routesUri, runningApp, 2*time.Second)).To(BeTrue())

		heartbeatInterval := 200 * time.Millisecond
		zombieTicker := time.NewTicker(heartbeatInterval)
		runningTicker := time.NewTicker(heartbeatInterval)

		go func() {
			for {
				select {
				case <-zombieTicker.C:
					zombieApp.Register()
				case <-runningTicker.C:
					runningApp.Register()
				}
			}
		}()

		zombieApp.VerifyAppStatus(200)

		// Give enough time to register multiple times
		time.Sleep(heartbeatInterval * 3)

		// kill registration ticker => kill app (must be before stopping NATS since app.Register is fake and queues messages in memory)
		zombieTicker.Stop()

		natsRunner.Stop()

		staleCheckInterval := config.PruneStaleDropletsInterval
		staleThreshold := config.DropletStaleThreshold
		// Give router time to make a bad decision (i.e. prune routes)
		time.Sleep(staleCheckInterval + staleThreshold + 250*time.Millisecond)

		// While NATS is down no routes should go down
		zombieApp.VerifyAppStatus(200)
		runningApp.VerifyAppStatus(200)

		natsRunner.Start()

		// Right after NATS starts up all routes should stay up
		zombieApp.VerifyAppStatus(200)
		runningApp.VerifyAppStatus(200)

		zombieGone := make(chan bool)

		go func() {
			for {
				// Finally the zombie is cleaned up. Maybe proactively enqueue Unregister events in DEA's.
				err := zombieApp.CheckAppStatus(404)
				if err != nil {
					time.Sleep(100 * time.Millisecond)
					continue
				}

				err = runningApp.CheckAppStatus(200)
				if err != nil {
					time.Sleep(100 * time.Millisecond)
					continue
				}

				zombieGone <- true

				break
			}
		}()

		waitTime := staleCheckInterval + staleThreshold + 5*time.Second
		Eventually(zombieGone, waitTime.Seconds()).Should(Receive())
	})
})

func newMessageBus(c *config.Config) (yagnats.NATSConn, error) {
	natsMembers := make([]string, len(c.Nats))
	for _, info := range c.Nats {
		uri := url.URL{
			Scheme: "nats",
			User:   url.UserPassword(info.User, info.Pass),
			Host:   fmt.Sprintf("%s:%d", info.Host, info.Port),
		}
		natsMembers = append(natsMembers, uri.String())
	}

	return yagnats.Connect(natsMembers)
}

func waitAppRegistered(routesUri string, app *test.TestApp, timeout time.Duration) bool {
	return waitMsgReceived(routesUri, app, true, timeout)
}

func waitAppUnregistered(routesUri string, app *test.TestApp, timeout time.Duration) bool {
	return waitMsgReceived(routesUri, app, false, timeout)
}

func waitMsgReceived(uri string, app *test.TestApp, expectedToBeFound bool, timeout time.Duration) bool {
	interval := time.Millisecond * 50
	repetitions := int(timeout / interval)

	for j := 0; j < repetitions; j++ {
		resp, err := http.Get(uri)
		if err == nil {
			switch resp.StatusCode {
			case http.StatusOK:
				bytes, err := ioutil.ReadAll(resp.Body)
				resp.Body.Close()
				Ω(err).ShouldNot(HaveOccurred())
				routes := make(map[string][]string)
				err = json.Unmarshal(bytes, &routes)
				Ω(err).ShouldNot(HaveOccurred())
				route := routes[string(app.Urls()[0])]
				if expectedToBeFound {
					if route != nil {
						return true
					}
				} else {
					if route == nil {
						return true
					}
				}
			default:
				println("Failed to receive routes: ", resp.StatusCode, uri)
			}
		}

		time.Sleep(interval)
	}

	return false
}
<commit_msg>Only stop gorouter session if it has not exited yet.<commit_after>package main_test

import (
	"github.com/cloudfoundry-incubator/candiedyaml"
	"github.com/cloudfoundry/gorouter/config"
	"github.com/cloudfoundry/gorouter/route"
	"github.com/cloudfoundry/gorouter/test"
	"github.com/cloudfoundry/gorouter/test_util"
	"github.com/cloudfoundry/gunk/natsrunner"
	"github.com/cloudfoundry/yagnats"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
	"github.com/pivotal-golang/localip"

	"io"
	"net"
	"net/url"
	"syscall"

	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"time"
)

var _ = Describe("Router Integration", func() {
	var tmpdir string

	var natsPort uint16
	var natsRunner *natsrunner.NATSRunner

	var gorouterSession *Session

	writeConfig := func(config *config.Config, cfgFile string) {
		cfgBytes, err := candiedyaml.Marshal(config)
		Ω(err).ShouldNot(HaveOccurred())
		ioutil.WriteFile(cfgFile, cfgBytes, os.ModePerm)
	}

	createConfig := func(cfgFile string, statusPort, proxyPort uint16) *config.Config {
		config := test_util.SpecConfig(natsPort, statusPort, proxyPort)

		// ensure the threshold is longer than the interval that we check,
		// because we set the route's timestamp to time.Now() on the interval
		// as part of pausing
		config.PruneStaleDropletsIntervalInSeconds = 1
		config.DropletStaleThresholdInSeconds = 2
		config.StartResponseDelayIntervalInSeconds = 1
		config.EndpointTimeoutInSeconds = 5
		config.DrainTimeoutInSeconds = 1

		writeConfig(config, cfgFile)
		return config
	}

	startGorouterSession := func(cfgFile string) *Session {
		gorouterCmd := exec.Command(gorouterPath, "-c", cfgFile)
		session, err := Start(gorouterCmd, GinkgoWriter, GinkgoWriter)
		Ω(err).ShouldNot(HaveOccurred())
		Eventually(session, 5).Should(Say("gorouter.started"))
		gorouterSession = session

		return session
	}

	stopGorouter := func(gorouterSession *Session) {
		err := gorouterSession.Command.Process.Signal(syscall.SIGTERM)
		Ω(err).ShouldNot(HaveOccurred())
		Expect(gorouterSession.Wait(5 * time.Second)).Should(Exit(0))
	}

	BeforeEach(func() {
		var err error
		tmpdir, err = ioutil.TempDir("", "gorouter")
		Ω(err).ShouldNot(HaveOccurred())

		natsPort = test_util.NextAvailPort()
		natsRunner = natsrunner.NewNATSRunner(int(natsPort))
		natsRunner.Start()
	})

	AfterEach(func() {
		if natsRunner != nil {
			natsRunner.Stop()
		}

		os.RemoveAll(tmpdir)

		if gorouterSession != nil && gorouterSession.ExitCode() == -1 {
			stopGorouter(gorouterSession)
		}
	})

	Context("Drain", func() {
		var config *config.Config
		var localIP string
		var statusPort uint16
		var proxyPort uint16

		BeforeEach(func() {
			var err error
			localIP, err = localip.LocalIP()
			Ω(err).ShouldNot(HaveOccurred())

			statusPort = test_util.NextAvailPort()
			proxyPort = test_util.NextAvailPort()

			cfgFile := filepath.Join(tmpdir, "config.yml")
			config = createConfig(cfgFile, statusPort, proxyPort)

			gorouterSession = startGorouterSession(cfgFile)
		})

		It("waits for all requests to finish", func() {
			mbusClient, err := newMessageBus(config)
			Ω(err).ShouldNot(HaveOccurred())

			blocker := make(chan bool)
			longApp := test.NewTestApp([]route.Uri{"longapp.vcap.me"}, proxyPort, mbusClient, nil)
			longApp.AddHandler("/", func(w http.ResponseWriter, r *http.Request) {
				blocker <- true
				_, err := ioutil.ReadAll(r.Body)
				defer r.Body.Close()
				Ω(err).ShouldNot(HaveOccurred())
				w.WriteHeader(http.StatusNoContent)
			})
			longApp.Listen()
			routesUri := fmt.Sprintf("http://%s:%s@%s:%d/routes", config.Status.User, config.Status.Pass, localIP, statusPort)
			Ω(waitAppRegistered(routesUri, longApp, 2*time.Second)).To(BeTrue())

			go func() {
				defer GinkgoRecover()
				resp, err := http.Get(longApp.Endpoint())
				Ω(err).ShouldNot(HaveOccurred())
				Ω(resp.StatusCode).Should(Equal(http.StatusNoContent))
				ioutil.ReadAll(resp.Body)
				resp.Body.Close()
			}()

			<-blocker

			grouter := gorouterSession
			gorouterSession = nil
			err = grouter.Command.Process.Signal(syscall.SIGUSR1)
			Ω(err).ShouldNot(HaveOccurred())
			Eventually(grouter, 5).Should(Exit(0))
		})

		It("will timeout if requests take too long", func() {
			mbusClient, err := newMessageBus(config)
			Ω(err).ShouldNot(HaveOccurred())

			blocker := make(chan bool)
			resultCh := make(chan error, 1)
			timeoutApp := test.NewTestApp([]route.Uri{"timeout.vcap.me"}, proxyPort, mbusClient, nil)
			timeoutApp.AddHandler("/", func(w http.ResponseWriter, r *http.Request) {
				blocker <- true
				<-blocker
			})
			timeoutApp.Listen()
			routesUri := fmt.Sprintf("http://%s:%s@%s:%d/routes", config.Status.User, config.Status.Pass, localIP, statusPort)
			Ω(waitAppRegistered(routesUri, timeoutApp, 2*time.Second)).To(BeTrue())

			go func() {
				_, err := http.Get(timeoutApp.Endpoint())
				resultCh <- err
			}()

			<-blocker
			defer func() {
				blocker <- true
			}()

			grouter := gorouterSession
			gorouterSession = nil
			err = grouter.Command.Process.Signal(syscall.SIGUSR1)
			Ω(err).ShouldNot(HaveOccurred())
			Eventually(grouter, 5).Should(Exit(0))

			var result error
			Eventually(resultCh, 5).Should(Receive(&result))
			Ω(result).Should(BeAssignableToTypeOf(&url.Error{}))
			urlErr := result.(*url.Error)
			Ω(urlErr.Err).Should(Equal(io.EOF))
		})

		It("prevents new connections", func() {
			mbusClient, err := newMessageBus(config)

			blocker := make(chan bool)
			timeoutApp := test.NewTestApp([]route.Uri{"timeout.vcap.me"}, proxyPort, mbusClient, nil)
			timeoutApp.AddHandler("/", func(w http.ResponseWriter, r *http.Request) {
				blocker <- true
				<-blocker
			})
			timeoutApp.Listen()
			routesUri := fmt.Sprintf("http://%s:%s@%s:%d/routes", config.Status.User, config.Status.Pass, localIP, statusPort)
			Ω(waitAppRegistered(routesUri, timeoutApp, 2*time.Second)).To(BeTrue())

			go func() {
				http.Get(timeoutApp.Endpoint())
			}()

			<-blocker
			defer func() {
				blocker <- true
			}()

			grouter := gorouterSession
			gorouterSession = nil
			err = grouter.Command.Process.Signal(syscall.SIGUSR1)
			Ω(err).ShouldNot(HaveOccurred())
			Eventually(grouter, 5).Should(Exit(0))

			_, err = http.Get(timeoutApp.Endpoint())
			Ω(err).Should(HaveOccurred())
			urlErr := err.(*url.Error)
			opErr := urlErr.Err.(*net.OpError)
			Ω(opErr.Op).Should(Equal("dial"))
		})
	})

	Context("When Dropsonde is misconfigured", func() {
		It("fails to start", func() {
			statusPort := test_util.NextAvailPort()
			proxyPort := test_util.NextAvailPort()

			cfgFile := filepath.Join(tmpdir, "config.yml")
			config := createConfig(cfgFile, statusPort, proxyPort)
			config.Logging.MetronAddress = ""
			writeConfig(config, cfgFile)

			gorouterCmd := exec.Command(gorouterPath, "-c", cfgFile)
			gorouterSession, _ = Start(gorouterCmd, GinkgoWriter, GinkgoWriter)
			Eventually(gorouterSession, 5).Should(Exit(1))
		})
	})

	It("has Nats connectivity", func() {
		localIP, err := localip.LocalIP()
		Ω(err).ShouldNot(HaveOccurred())

		statusPort := test_util.NextAvailPort()
		proxyPort := test_util.NextAvailPort()

		cfgFile := filepath.Join(tmpdir, "config.yml")
		config := createConfig(cfgFile, statusPort, proxyPort)

		gorouterSession = startGorouterSession(cfgFile)

		mbusClient, err := newMessageBus(config)

		zombieApp := test.NewGreetApp([]route.Uri{"zombie.vcap.me"}, proxyPort, mbusClient, nil)
		zombieApp.Listen()

		runningApp := test.NewGreetApp([]route.Uri{"innocent.bystander.vcap.me"}, proxyPort, mbusClient, nil)
		runningApp.Listen()

		routesUri := fmt.Sprintf("http://%s:%s@%s:%d/routes", config.Status.User, config.Status.Pass, localIP, statusPort)

		Ω(waitAppRegistered(routesUri, zombieApp, 2*time.Second)).To(BeTrue())
		Ω(waitAppRegistered(routesUri, runningApp, 2*time.Second)).To(BeTrue())

		heartbeatInterval := 200 * time.Millisecond
		zombieTicker := time.NewTicker(heartbeatInterval)
		runningTicker := time.NewTicker(heartbeatInterval)

		go func() {
			for {
				select {
				case <-zombieTicker.C:
					zombieApp.Register()
				case <-runningTicker.C:
					runningApp.Register()
				}
			}
		}()

		zombieApp.VerifyAppStatus(200)

		// Give enough time to register multiple times
		time.Sleep(heartbeatInterval * 3)

		// kill registration ticker => kill app (must be before stopping NATS since app.Register is fake and queues messages in memory)
		zombieTicker.Stop()

		natsRunner.Stop()

		staleCheckInterval := config.PruneStaleDropletsInterval
		staleThreshold := config.DropletStaleThreshold
		// Give router time to make a bad decision (i.e. prune routes)
		time.Sleep(staleCheckInterval + staleThreshold + 250*time.Millisecond)

		// While NATS is down no routes should go down
		zombieApp.VerifyAppStatus(200)
		runningApp.VerifyAppStatus(200)

		natsRunner.Start()

		// Right after NATS starts up all routes should stay up
		zombieApp.VerifyAppStatus(200)
		runningApp.VerifyAppStatus(200)

		zombieGone := make(chan bool)

		go func() {
			for {
				// Finally the zombie is cleaned up. Maybe proactively enqueue Unregister events in DEA's.
				err := zombieApp.CheckAppStatus(404)
				if err != nil {
					time.Sleep(100 * time.Millisecond)
					continue
				}

				err = runningApp.CheckAppStatus(200)
				if err != nil {
					time.Sleep(100 * time.Millisecond)
					continue
				}

				zombieGone <- true

				break
			}
		}()

		waitTime := staleCheckInterval + staleThreshold + 5*time.Second
		Eventually(zombieGone, waitTime.Seconds()).Should(Receive())
	})
})

func newMessageBus(c *config.Config) (yagnats.NATSConn, error) {
	natsMembers := make([]string, len(c.Nats))
	for _, info := range c.Nats {
		uri := url.URL{
			Scheme: "nats",
			User:   url.UserPassword(info.User, info.Pass),
			Host:   fmt.Sprintf("%s:%d", info.Host, info.Port),
		}
		natsMembers = append(natsMembers, uri.String())
	}

	return yagnats.Connect(natsMembers)
}

func waitAppRegistered(routesUri string, app *test.TestApp, timeout time.Duration) bool {
	return waitMsgReceived(routesUri, app, true, timeout)
}

func waitAppUnregistered(routesUri string, app *test.TestApp, timeout time.Duration) bool {
	return waitMsgReceived(routesUri, app, false, timeout)
}

func waitMsgReceived(uri string, app *test.TestApp, expectedToBeFound bool, timeout time.Duration) bool {
	interval := time.Millisecond * 50
	repetitions := int(timeout / interval)

	for j := 0; j < repetitions; j++ {
		resp, err := http.Get(uri)
		if err == nil {
			switch resp.StatusCode {
			case http.StatusOK:
				bytes, err := ioutil.ReadAll(resp.Body)
				resp.Body.Close()
				Ω(err).ShouldNot(HaveOccurred())
				routes := make(map[string][]string)
				err = json.Unmarshal(bytes, &routes)
				Ω(err).ShouldNot(HaveOccurred())
				route := routes[string(app.Urls()[0])]
				if expectedToBeFound {
					if route != nil {
						return true
					}
				} else {
					if route == nil {
						return true
					}
				}
			default:
				println("Failed to receive routes: ", resp.StatusCode, uri)
			}
		}

		time.Sleep(interval)
	}

	return false
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"os"
	"strings"
	"syscall"
	"testing"
)

func TestBadEnv(t *testing.T) {
	buff := new(bytes.Buffer)
	stderr = buff
	var code int
	exit = func(c int) {
		code = c
	}
	main()
	if code != 1 {
		t.Error("expected exit status 1")
	}
	if !strings.Contains(buff.String(), "Bad COFFEE_SHOP_CLOSE_TIME") {
		t.Error("Didn't provide close time, but didn't error")
	}
	code = 0
	os.Setenv("COFFEE_SHOP_CLOSE_TIME", "1")
	main()
	if code != 1 {
		t.Error("expected exit status 1")
	}
	if !strings.Contains(buff.String(), "Bad COFFEE_SHOP_SHUTDOWN") {
		t.Error("Didn't provide shutdown time, but didn't error")
	}
	code = 0
	os.Setenv("COFFEE_SHOP_SHUTDOWN", "1")
	main()
	if code != 1 {
		t.Error("expected exit status 1")
	}
	if !strings.Contains(buff.String(), "Bad COFFEE_SHOP_CUSTOMERS") {
		t.Error("Didn't provide number of customers, but didn't error")
	}
	code = 0
	os.Setenv("COFFEE_SHOP_CUSTOMERS", "1")
	main()
	if code != 1 {
		t.Error("expected exit status 1")
	}
	if !strings.Contains(buff.String(), "Bad COFFEE_SHOP_BARISTAS") {
		t.Error("Didn't provide number of baristas, but didn't error")
	}
}

func TestStore(t *testing.T) {
	buff := new(bytes.Buffer)
	stderr = buff
	code := -1
	exit = func(c int) {
		code = c
	}
	os.Setenv("COFFEE_SHOP_CLOSE_TIME", "1")
	os.Setenv("COFFEE_SHOP_SHUTDOWN", "0")
	os.Setenv("COFFEE_SHOP_CUSTOMERS", "1")
	os.Setenv("COFFEE_SHOP_BARISTAS", "1")
	main()
	if code != 0 {
		t.Error("Expected 0 exit status")
	}
	if s := buff.String(); !strings.Contains(s, "Store is closing") || !strings.Contains(s, "Customer 1 says Yum and thanks to Barista 1") {
		t.Errorf("Output was not expected got %v", s)
	}
}

func TestSignalStore(t *testing.T) {
	buff := new(bytes.Buffer)
	stderr = buff
	code := -1
	exit = func(c int) {
		code = c
	}
	stop = make(chan os.Signal, 1)
	os.Setenv("COFFEE_SHOP_CLOSE_TIME", "1")
	os.Setenv("COFFEE_SHOP_SHUTDOWN", "2")
	os.Setenv("COFFEE_SHOP_CUSTOMERS", "1")
	os.Setenv("COFFEE_SHOP_BARISTAS", "1")
	stop <- syscall.SIGINT
	main()
	if code != 0 {
		t.Error("Expected 0 exit code")
	}
	if s := buff.String(); !strings.Contains(s, "I received a signal to close the store") || !strings.Contains(s, "Store closed") {
		t.Error("Expected a store to close with all customers served")
	}
}

func TestSignalStoreShutdownTimeout(t *testing.T) {
	buff := new(bytes.Buffer)
	stderr = buff
	code := -1
	exit = func(c int) {
		code = c
	}
	stop = make(chan os.Signal, 1)
	os.Setenv("COFFEE_SHOP_CLOSE_TIME", "1")
	os.Setenv("COFFEE_SHOP_SHUTDOWN", "1")
	os.Setenv("COFFEE_SHOP_CUSTOMERS", "1")
	os.Setenv("COFFEE_SHOP_BARISTAS", "1")
	stop <- syscall.SIGINT
	main()
	if code != 0 {
		t.Error("Expected 0 exit code")
	}
	if s := buff.String(); !strings.Contains(s, "I received a signal to close the store") || !strings.Contains(s, "Shutdown time reached") {
		t.Error("Expected a store to close with all customers served")
	}
}

func TestCantOpenStore(t *testing.T) {
	buff := new(bytes.Buffer)
	stderr = buff
	exit = func(code int) {}
	os.Setenv("COFFEE_SHOP_CLOSE_TIME", "0")
	os.Setenv("COFFEE_SHOP_SHUTDOWN", "0")
	os.Setenv("COFFEE_SHOP_CUSTOMERS", "0")
	os.Setenv("COFFEE_SHOP_BARISTAS", "0")
	main()
}
<commit_msg>make coffeshop shutdown timeout immediate<commit_after>package main

import (
	"bytes"
	"os"
	"strings"
	"syscall"
	"testing"
)

func TestBadEnv(t *testing.T) {
	buff := new(bytes.Buffer)
	stderr = buff
	var code int
	exit = func(c int) {
		code = c
	}
	main()
	if code != 1 {
		t.Error("expected exit status 1")
	}
	if !strings.Contains(buff.String(), "Bad COFFEE_SHOP_CLOSE_TIME") {
		t.Error("Didn't provide close time, but didn't error")
	}
	code = 0
	os.Setenv("COFFEE_SHOP_CLOSE_TIME", "1")
	main()
	if code != 1 {
		t.Error("expected exit status 1")
	}
	if !strings.Contains(buff.String(), "Bad COFFEE_SHOP_SHUTDOWN") {
		t.Error("Didn't provide shutdown time, but didn't error")
	}
	code = 0
	os.Setenv("COFFEE_SHOP_SHUTDOWN", "1")
	main()
	if code != 1 {
		t.Error("expected exit status 1")
	}
	if !strings.Contains(buff.String(), "Bad COFFEE_SHOP_CUSTOMERS") {
		t.Error("Didn't provide number of customers, but didn't error")
	}
	code = 0
	os.Setenv("COFFEE_SHOP_CUSTOMERS", "1")
	main()
	if code != 1 {
		t.Error("expected exit status 1")
	}
	if !strings.Contains(buff.String(), "Bad COFFEE_SHOP_BARISTAS") {
		t.Error("Didn't provide number of baristas, but didn't error")
	}
}

func TestStore(t *testing.T) {
	buff := new(bytes.Buffer)
	stderr = buff
	code := -1
	exit = func(c int) {
		code = c
	}
	os.Setenv("COFFEE_SHOP_CLOSE_TIME", "1")
	os.Setenv("COFFEE_SHOP_SHUTDOWN", "0")
	os.Setenv("COFFEE_SHOP_CUSTOMERS", "1")
	os.Setenv("COFFEE_SHOP_BARISTAS", "1")
	main()
	if code != 0 {
		t.Error("Expected 0 exit status")
	}
	if s := buff.String(); !strings.Contains(s, "Store is closing") || !strings.Contains(s, "Customer 1 says Yum and thanks to Barista 1") {
		t.Errorf("Output was not expected got %v", s)
	}
}

func TestSignalStore(t *testing.T) {
	buff := new(bytes.Buffer)
	stderr = buff
	code := -1
	exit = func(c int) {
		code = c
	}
	stop = make(chan os.Signal, 1)
	os.Setenv("COFFEE_SHOP_CLOSE_TIME", "1")
	os.Setenv("COFFEE_SHOP_SHUTDOWN", "2")
	os.Setenv("COFFEE_SHOP_CUSTOMERS", "1")
	os.Setenv("COFFEE_SHOP_BARISTAS", "1")
	stop <- syscall.SIGINT
	main()
	if code != 0 {
		t.Error("Expected 0 exit code")
	}
	if s := buff.String(); !strings.Contains(s, "I received a signal to close the store") || !strings.Contains(s, "Store closed") {
		t.Error("Expected a store to close with all customers served")
	}
}

func TestSignalStoreShutdownTimeout(t *testing.T) {
	buff := new(bytes.Buffer)
	stderr = buff
	code := -1
	exit = func(c int) {
		code = c
	}
	stop = make(chan os.Signal, 1)
	os.Setenv("COFFEE_SHOP_CLOSE_TIME", "1")
	os.Setenv("COFFEE_SHOP_SHUTDOWN", "0")
	os.Setenv("COFFEE_SHOP_CUSTOMERS", "1")
	os.Setenv("COFFEE_SHOP_BARISTAS", "1")
	stop <- syscall.SIGINT
	main()
	if code != 0 {
		t.Error("Expected 0 exit code")
	}
	if s := buff.String(); !strings.Contains(s, "I received a signal to close the store") || !strings.Contains(s, "Shutdown time reached") {
		t.Error("Expected a store to close with all customers served")
	}
}

func TestCantOpenStore(t *testing.T) {
	buff := new(bytes.Buffer)
	stderr = buff
	exit = func(code int) {}
	os.Setenv("COFFEE_SHOP_CLOSE_TIME", "0")
	os.Setenv("COFFEE_SHOP_SHUTDOWN", "0")
	os.Setenv("COFFEE_SHOP_CUSTOMERS", "0")
	os.Setenv("COFFEE_SHOP_BARISTAS", "0")
	main()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"testing"

	"github.com/streadway/amqp"
)

const (
	testAmqpURI      = "amqp://guest:guest@127.0.0.1:5872/"
	testQueueName    = "test-rabbitmq-dump-queue"
	testExchangeName = "test-rabbitmq-dump-exchange"
	testRoutingKey   = "test-rabbitmq-dump-routing-key"
)

func makeAmqpMessage(i int) amqp.Publishing {
	headers := make(amqp.Table)
	headers["my-header"] = fmt.Sprintf("my-value-%d", i)
	return amqp.Publishing{
		Headers:     headers,
		ContentType: "text/plain",
		Priority:    4,
		MessageId:   fmt.Sprintf("msgid-%d", i),
		Body:        []byte(fmt.Sprintf("message-%d-body", i)),
	}
}

// Publish 10 messages to the queue
func populateTestQueue(t *testing.T, messagesToPublish int, exchange ...string) {
	conn, err := amqp.Dial(testAmqpURI)
	if err != nil {
		t.Fatalf("Dial: %s", err)
	}
	defer conn.Close()

	channel, err := conn.Channel()
	if err != nil {
		t.Fatalf("Channel: %s", err)
	}

	_, err = channel.QueueDeclare(testQueueName, true, false, false, false, nil)
	if err != nil {
		t.Fatalf("QueueDeclare: %s", err)
	}

	_, err = channel.QueuePurge(testQueueName, false)
	if err != nil {
		t.Fatalf("QueuePurge: %s", err)
	}

	exchangeToPublish := ""
	queueToPublish := testQueueName

	if len(exchange) > 0 {
		exchangeToPublish = exchange[0]
		queueToPublish = testRoutingKey

		err = channel.ExchangeDeclare(
			exchangeToPublish, // name
			"topic",           // type
			true,              // durable
			false,             // auto-deleted
			false,             // internal
			false,             // no-wait
			nil,               // arguments
		)

		if err != nil {
			t.Fatalf("Failed to declare exchange: %s", err)
		}

		err = channel.QueueBind(
			testQueueName,    // queue name
			testRoutingKey,   // routing key
			testExchangeName, // exchange
			false,
			nil)
	}

	for i := 0; i < messagesToPublish; i++ {
		err = channel.Publish(exchangeToPublish, queueToPublish, false, false, makeAmqpMessage(i))
		if err != nil {
			t.Fatalf("Publish: %s", err)
		}
	}
}

func getMetadataFromFile(t *testing.T, headerFileToLoad string) (map[string]interface{}, map[string]interface{}) {
	jsonContent, err := ioutil.ReadFile(headerFileToLoad)

	if err != nil {
		t.Fatalf("Error reading %s: %s", headerFileToLoad, err)
	}

	var v map[string]interface{}

	err = json.Unmarshal(jsonContent, &v)

	if err != nil {
		t.Fatalf("Error unmarshaling JSON: %s", err)
	}

	headers, ok := v["headers"].(map[string]interface{})

	if !ok {
		t.Fatalf("Wrong data type for 'headers' in JSON")
	}

	properties, ok := v["properties"].(map[string]interface{})

	if !ok {
		t.Fatalf("Wrong data type for 'properties' in JSON")
	}

	return headers, properties
}

func deleteTestQueue(t *testing.T) {
	conn, err := amqp.Dial(testAmqpURI)
	if err != nil {
		t.Fatalf("Dial: %s", err)
	}
	defer conn.Close()

	channel, err := conn.Channel()
	if err != nil {
		t.Fatalf("Channel: %s", err)
	}

	_, err = channel.QueueDelete(testQueueName, false, false, false)
	if err != nil {
		t.Fatalf("QueueDelete: %s", err)
	}
}

func getTestQueueLength(t *testing.T) int {
	conn, err := amqp.Dial(testAmqpURI)
	if err != nil {
		t.Fatalf("Dial: %s", err)
	}
	defer conn.Close()

	channel, err := conn.Channel()
	if err != nil {
		t.Fatalf("Channel: %s", err)
	}

	queue, err := channel.QueueInspect(testQueueName)
	if err != nil {
		t.Fatalf("QueueInspect: %s", err)
	}

	return queue.Messages
}

func run(t *testing.T, commandLine string) string {
	queueLengthBeforeDump := getTestQueueLength(t)
	args := strings.Split(commandLine, " ")
	output, err := exec.Command("./rabbitmq-dump-queue", args...).CombinedOutput()
	if err != nil {
		t.Fatalf("run: %s: %s", err, string(output))
	}
	queueLengthAfterDump := getTestQueueLength(t)
	if queueLengthAfterDump != queueLengthBeforeDump {
		t.Errorf("Queue length changed after rabbitmq-dump-queue: expected %d but got %d", queueLengthBeforeDump, queueLengthAfterDump)
	}
	return string(output)
}

func verifyOutput(t *testing.T) {
	output := run(t, "-uri="+testAmqpURI+" -queue="+testQueueName+" -max-messages=3 -output-dir=tmp-test -full")

	expectedOutput := "tmp-test/msg-0000\n" +
		"tmp-test/msg-0000-headers+properties.json\n" +
		"tmp-test/msg-0001\n" +
		"tmp-test/msg-0001-headers+properties.json\n" +
		"tmp-test/msg-0002\n" +
		"tmp-test/msg-0002-headers+properties.json\n"

	if output != expectedOutput {
		t.Errorf("Wrong output: expected '%s' but got '%s'", expectedOutput, output)
	}
}

func verifyFileContent(t *testing.T, filename, expectedContent string) {
	content, err := ioutil.ReadFile(filename)
	if err != nil {
		t.Fatalf("Error reading %s: %s", filename, err)
	}
	if expectedContent != string(content) {
		t.Errorf("Wrong content for %s: expected '%s', got '%s'", filename, expectedContent, string(content))
	}
}

func verifyAndGetDefaultMetadata(t *testing.T) (map[string]interface{}, map[string]interface{}) {
	headers, properties := getMetadataFromFile(t, "tmp-test/msg-0000-headers+properties.json")

	if properties["priority"] != 4.0 || // JSON numbers are floats
		properties["content_type"] != "text/plain" ||
		properties["message_id"] != "msgid-0" {
		t.Errorf("Wrong property value: properties = %#v", properties)
	}

	if headers["my-header"] != "my-value-0" {
		t.Errorf("Wrong header value: header = %#v", headers)
	}

	return headers, properties
}

func TestAcknowledge(t *testing.T) {
	os.MkdirAll("tmp-test", 0775)
	defer os.RemoveAll("tmp-test")
	populateTestQueue(t, 10)
	defer deleteTestQueue(t)
	output, err := exec.Command("./rabbitmq-dump-queue", "-uri="+testAmqpURI, "-queue="+testQueueName, "-max-messages=3", "-output-dir=tmp-test", "-ack=true").CombinedOutput()
	if err != nil {
		t.Fatalf("run: %s: %s", err, string(output))
	}
	expectedOutput := "tmp-test/msg-0000\n" +
		"tmp-test/msg-0001\n" +
		"tmp-test/msg-0002\n"
	if string(output) != expectedOutput {
		t.Errorf("Wrong output: expected '%s' but got '%s'", expectedOutput, output)
	}
	output2, err2 := exec.Command("./rabbitmq-dump-queue", "-uri="+testAmqpURI, "-queue="+testQueueName, "-max-messages=10", "-output-dir=tmp-test", "-ack=true").CombinedOutput()
	if err2 != nil {
		t.Fatalf("run: %s: %s", err, string(output))
	}
	expectedOutput2 := "tmp-test/msg-0000\n" +
		"tmp-test/msg-0001\n" +
		"tmp-test/msg-0002\n" +
		"tmp-test/msg-0003\n" +
		"tmp-test/msg-0004\n" +
		"tmp-test/msg-0005\n" +
		"tmp-test/msg-0006\n"
	if string(output2) != expectedOutput2 {
		t.Errorf("Wrong output: expected '%s' but got '%s'", expectedOutput2, output2)
	}
}

func TestNormal(t *testing.T) {
	os.MkdirAll("tmp-test", 0775)
	defer os.RemoveAll("tmp-test")
	populateTestQueue(t, 10)
	defer deleteTestQueue(t)
	output := run(t, "-uri="+testAmqpURI+" -queue="+testQueueName+" -max-messages=3 -output-dir=tmp-test")
	expectedOutput := "tmp-test/msg-0000\n" +
		"tmp-test/msg-0001\n" +
		"tmp-test/msg-0002\n"
	if output != expectedOutput {
		t.Errorf("Wrong output: expected '%s' but got '%s'", expectedOutput, output)
	}
	verifyFileContent(t, "tmp-test/msg-0000", "message-0-body")
	verifyFileContent(t, "tmp-test/msg-0001", "message-1-body")
	verifyFileContent(t, "tmp-test/msg-0002", "message-2-body")
	_, err := os.Stat("tmp-test/msg-0003")
	if !os.IsNotExist(err) {
		t.Errorf("Expected msg-0003 to not exist: %v", err)
	}
}

func TestEmptyQueue(t *testing.T) {
	os.MkdirAll("tmp-test", 0775)
	defer os.RemoveAll("tmp-test")
	populateTestQueue(t, 0)
	defer deleteTestQueue(t)
	output := run(t, "-uri="+testAmqpURI+" -queue="+testQueueName+" -max-messages=3 -output-dir=tmp-test")
	expectedOutput := ""
	if output != expectedOutput {
		t.Errorf("Wrong output: expected '%s' but got '%s'", expectedOutput, output)
	}
}

func TestMaxMessagesLargerThanQueueLength(t *testing.T) {
	os.MkdirAll("tmp-test", 0775)
	defer os.RemoveAll("tmp-test")
	populateTestQueue(t, 3)
	defer deleteTestQueue(t)
	output := run(t, "-uri="+testAmqpURI+" -queue="+testQueueName+" -max-messages=9 -output-dir=tmp-test")
	expectedOutput := "tmp-test/msg-0000\n" +
		"tmp-test/msg-0001\n" +
		"tmp-test/msg-0002\n"
	if output != expectedOutput {
		t.Errorf("Wrong output: expected '%s' but got '%s'", expectedOutput, output)
	}
}

func TestFull(t *testing.T) {
	os.MkdirAll("tmp-test", 0775)
	defer os.RemoveAll("tmp-test")
	populateTestQueue(t, 10)
	defer deleteTestQueue(t)

	verifyOutput(t)
	verifyFileContent(t, "tmp-test/msg-0000", "message-0-body")

	verifyAndGetDefaultMetadata(t)
}

func TestFullRouted(t *testing.T) {
	os.MkdirAll("tmp-test", 0775)
	defer os.RemoveAll("tmp-test")
	populateTestQueue(t, 10, testExchangeName)
	defer deleteTestQueue(t)

	verifyOutput(t)
	verifyFileContent(t, "tmp-test/msg-0000", "message-0-body")

	_, properties := verifyAndGetDefaultMetadata(t)

	//Extended properties, only available when published through exchange
	if properties["exchange"] != testExchangeName ||
		properties["routing_key"] != testRoutingKey {
		t.Errorf("Wrong property value: properties = %#v", properties)
	}
}
<commit_msg>Revert the RabbitMQ port<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"testing"

	"github.com/streadway/amqp"
)

const (
	testAmqpURI      = "amqp://guest:guest@127.0.0.1:5672/"
	testQueueName    = "test-rabbitmq-dump-queue"
	testExchangeName = "test-rabbitmq-dump-exchange"
	testRoutingKey   = "test-rabbitmq-dump-routing-key"
)

func makeAmqpMessage(i int) amqp.Publishing {
	headers := make(amqp.Table)
	headers["my-header"] = fmt.Sprintf("my-value-%d", i)
	return amqp.Publishing{
		Headers:     headers,
		ContentType: "text/plain",
		Priority:    4,
		MessageId:   fmt.Sprintf("msgid-%d", i),
		Body:        []byte(fmt.Sprintf("message-%d-body", i)),
	}
}

// Publish 10 messages to the queue
func populateTestQueue(t *testing.T, messagesToPublish int, exchange ...string) {
	conn, err := amqp.Dial(testAmqpURI)
	if err != nil {
		t.Fatalf("Dial: %s", err)
	}
	defer conn.Close()

	channel, err := conn.Channel()
	if err != nil {
		t.Fatalf("Channel: %s", err)
	}

	_, err = channel.QueueDeclare(testQueueName, true, false, false, false, nil)
	if err != nil {
		t.Fatalf("QueueDeclare: %s", err)
	}

	_, err = channel.QueuePurge(testQueueName, false)
	if err != nil {
		t.Fatalf("QueuePurge: %s", err)
	}

	exchangeToPublish := ""
	queueToPublish := testQueueName

	if len(exchange) > 0 {
		exchangeToPublish = exchange[0]
		queueToPublish = testRoutingKey

		err = channel.ExchangeDeclare(
			exchangeToPublish, // name
			"topic",           // type
			true,              // durable
			false,             // auto-deleted
			false,             // internal
			false,             // no-wait
			nil,               // arguments
		)

		if err != nil {
			t.Fatalf("Failed to declare exchange: %s", err)
		}

		err = channel.QueueBind(
			testQueueName,    // queue name
			testRoutingKey,   // routing key
			testExchangeName, // exchange
			false,
			nil)
	}

	for i := 0; i < messagesToPublish; i++ {
		err = channel.Publish(exchangeToPublish, queueToPublish, false, false, makeAmqpMessage(i))
		if err != nil {
			t.Fatalf("Publish: %s", err)
		}
	}
}

func getMetadataFromFile(t *testing.T, headerFileToLoad string) (map[string]interface{}, map[string]interface{}) {
	jsonContent, err := ioutil.ReadFile(headerFileToLoad)

	if err != nil {
		t.Fatalf("Error reading %s: %s", headerFileToLoad, err)
	}

	var v map[string]interface{}

	err = json.Unmarshal(jsonContent, &v)

	if err != nil {
		t.Fatalf("Error unmarshaling JSON: %s", err)
	}

	headers, ok := v["headers"].(map[string]interface{})

	if !ok {
		t.Fatalf("Wrong data type for 'headers' in JSON")
	}

	properties, ok := v["properties"].(map[string]interface{})

	if !ok {
		t.Fatalf("Wrong data type for 'properties' in JSON")
	}

	return headers, properties
}

func deleteTestQueue(t *testing.T) {
	conn, err := amqp.Dial(testAmqpURI)
	if err != nil {
		t.Fatalf("Dial: %s", err)
	}
	defer conn.Close()

	channel, err := conn.Channel()
	if err != nil {
		t.Fatalf("Channel: %s", err)
	}

	_, err = channel.QueueDelete(testQueueName, false, false, false)
	if err != nil {
		t.Fatalf("QueueDelete: %s", err)
	}
}

func getTestQueueLength(t *testing.T) int {
	conn, err := amqp.Dial(testAmqpURI)
	if err != nil {
		t.Fatalf("Dial: %s", err)
	}
	defer conn.Close()

	channel, err := conn.Channel()
	if err != nil {
		t.Fatalf("Channel: %s", err)
	}

	queue, err := channel.QueueInspect(testQueueName)
	if err != nil {
		t.Fatalf("QueueInspect: %s", err)
	}

	return queue.Messages
}

func run(t *testing.T, commandLine string) string {
	queueLengthBeforeDump := getTestQueueLength(t)
	args := strings.Split(commandLine, " ")
	output, err := exec.Command("./rabbitmq-dump-queue", args...).CombinedOutput()
	if err != nil {
		t.Fatalf("run: %s: %s", err, string(output))
	}
	queueLengthAfterDump := getTestQueueLength(t)
	if queueLengthAfterDump != queueLengthBeforeDump {
		t.Errorf("Queue length changed after rabbitmq-dump-queue: expected %d but got %d", queueLengthBeforeDump, queueLengthAfterDump)
	}
	return string(output)
}

func verifyOutput(t *testing.T) {
	output := run(t, "-uri="+testAmqpURI+" -queue="+testQueueName+" -max-messages=3 -output-dir=tmp-test -full")

	expectedOutput := "tmp-test/msg-0000\n" +
		"tmp-test/msg-0000-headers+properties.json\n" +
		"tmp-test/msg-0001\n" +
		"tmp-test/msg-0001-headers+properties.json\n" +
		"tmp-test/msg-0002\n" +
		"tmp-test/msg-0002-headers+properties.json\n"

	if output != expectedOutput {
		t.Errorf("Wrong output: expected '%s' but got '%s'", expectedOutput, output)
	}
}

func verifyFileContent(t *testing.T, filename, expectedContent string) {
	content, err := ioutil.ReadFile(filename)
	if err != nil {
		t.Fatalf("Error reading %s: %s", filename, err)
	}
	if expectedContent != string(content) {
		t.Errorf("Wrong content for %s: expected '%s', got '%s'", filename, expectedContent, string(content))
	}
}

func verifyAndGetDefaultMetadata(t *testing.T) (map[string]interface{}, map[string]interface{}) {
	headers, properties := getMetadataFromFile(t, "tmp-test/msg-0000-headers+properties.json")

	if properties["priority"] != 4.0 || // JSON numbers are floats
		properties["content_type"] != "text/plain" ||
		properties["message_id"] != "msgid-0" {
		t.Errorf("Wrong property value: properties = %#v", properties)
	}

	if headers["my-header"] != "my-value-0" {
		t.Errorf("Wrong header value: header = %#v", headers)
	}

	return headers, properties
}

func TestAcknowledge(t *testing.T) {
	os.MkdirAll("tmp-test", 0775)
	defer os.RemoveAll("tmp-test")
	populateTestQueue(t, 10)
	defer deleteTestQueue(t)
	output, err := exec.Command("./rabbitmq-dump-queue", "-uri="+testAmqpURI, "-queue="+testQueueName, "-max-messages=3", "-output-dir=tmp-test", "-ack=true").CombinedOutput()
	if err != nil {
		t.Fatalf("run: %s: %s", err, string(output))
	}
	expectedOutput := "tmp-test/msg-0000\n" +
		"tmp-test/msg-0001\n" +
		"tmp-test/msg-0002\n"
	if string(output) != expectedOutput {
		t.Errorf("Wrong output: expected '%s' but got '%s'", expectedOutput, output)
	}
	output2, err2 := exec.Command("./rabbitmq-dump-queue", "-uri="+testAmqpURI, "-queue="+testQueueName, "-max-messages=10", "-output-dir=tmp-test", "-ack=true").CombinedOutput()
	if err2 != nil {
		t.Fatalf("run: %s: %s", err, string(output))
	}
	expectedOutput2 := "tmp-test/msg-0000\n" +
		"tmp-test/msg-0001\n" +
		"tmp-test/msg-0002\n" +
		"tmp-test/msg-0003\n" +
		"tmp-test/msg-0004\n" +
		"tmp-test/msg-0005\n" +
		"tmp-test/msg-0006\n"
	if string(output2) != expectedOutput2 {
		t.Errorf("Wrong output: expected '%s' but got '%s'", expectedOutput2, output2)
	}
}

func TestNormal(t *testing.T) {
	os.MkdirAll("tmp-test", 0775)
	defer os.RemoveAll("tmp-test")
	populateTestQueue(t, 10)
	defer deleteTestQueue(t)
	output := run(t, "-uri="+testAmqpURI+" -queue="+testQueueName+" -max-messages=3 -output-dir=tmp-test")
	expectedOutput := "tmp-test/msg-0000\n" +
		"tmp-test/msg-0001\n" +
		"tmp-test/msg-0002\n"
	if output != expectedOutput {
		t.Errorf("Wrong output: expected '%s' but got '%s'", expectedOutput, output)
	}
	verifyFileContent(t, "tmp-test/msg-0000", "message-0-body")
	verifyFileContent(t, "tmp-test/msg-0001", "message-1-body")
	verifyFileContent(t, "tmp-test/msg-0002", "message-2-body")
	_, err := os.Stat("tmp-test/msg-0003")
	if !os.IsNotExist(err) {
		t.Errorf("Expected msg-0003 to not exist: %v", err)
	}
}

func TestEmptyQueue(t *testing.T) {
	os.MkdirAll("tmp-test", 0775)
	defer os.RemoveAll("tmp-test")
	populateTestQueue(t, 0)
	defer deleteTestQueue(t)
	output := run(t, "-uri="+testAmqpURI+" -queue="+testQueueName+" -max-messages=3 -output-dir=tmp-test")
	expectedOutput := ""
	if output != expectedOutput {
		t.Errorf("Wrong output: expected '%s' but got '%s'", expectedOutput, output)
	}
}

func TestMaxMessagesLargerThanQueueLength(t *testing.T) {
	os.MkdirAll("tmp-test", 0775)
	defer os.RemoveAll("tmp-test")
	populateTestQueue(t, 3)
	defer deleteTestQueue(t)
	output := run(t, "-uri="+testAmqpURI+" -queue="+testQueueName+" -max-messages=9 -output-dir=tmp-test")
	expectedOutput := "tmp-test/msg-0000\n" +
		"tmp-test/msg-0001\n" +
		"tmp-test/msg-0002\n"
	if output != expectedOutput {
		t.Errorf("Wrong output: expected '%s' but got '%s'", expectedOutput, output)
	}
}

func TestFull(t *testing.T) {
	os.MkdirAll("tmp-test", 0775)
	defer os.RemoveAll("tmp-test")
	populateTestQueue(t, 10)
	defer deleteTestQueue(t)

	verifyOutput(t)
	verifyFileContent(t, "tmp-test/msg-0000", "message-0-body")

	verifyAndGetDefaultMetadata(t)
}

func TestFullRouted(t *testing.T) {
	os.MkdirAll("tmp-test", 0775)
	defer os.RemoveAll("tmp-test")
	populateTestQueue(t, 10, testExchangeName)
	defer deleteTestQueue(t)

	verifyOutput(t)
	verifyFileContent(t, "tmp-test/msg-0000", "message-0-body")

	_, properties := verifyAndGetDefaultMetadata(t)

	//Extended properties, only available when published through exchange
	if properties["exchange"] != testExchangeName ||
		properties["routing_key"] != testRoutingKey {
		t.Errorf("Wrong property value: properties = %#v", properties)
	}
}
<|endoftext|>


<commit_before>package integration_test

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/cloudfoundry/libbuildpack"
	"github.com/cloudfoundry/libbuildpack/cutlass"

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

	"testing"
)

var bpDir string
var buildpackVersion string
var packagedBuildpack cutlass.VersionedBuildpackPackage

var _ = func() bool {
	testing.Init()
	return true
}()

func init() {
	flag.StringVar(&buildpackVersion, "version", "", "version to use (builds if empty)")
	flag.BoolVar(&cutlass.Cached, "cached", true, "cached buildpack")
	flag.StringVar(&cutlass.DefaultMemory, "memory", "256M", "default memory for pushed apps")
	flag.StringVar(&cutlass.DefaultDisk, "disk", "384M", "default disk for pushed apps")
	flag.Parse()
}

var _ = SynchronizedBeforeSuite(func() []byte {
	// Run once
	if buildpackVersion == "" {
		packagedBuildpack, err := cutlass.PackageUniquelyVersionedBuildpack(os.Getenv("CF_STACK"), ApiHasStackAssociation())
		Expect(err).NotTo(HaveOccurred())

		data, err := json.Marshal(packagedBuildpack)
		Expect(err).NotTo(HaveOccurred())
		return data
	}

	return []byte{}
}, func(data []byte) {
	// Run on all nodes
	var err error
	if len(data) > 0 {
		err = json.Unmarshal(data, &packagedBuildpack)
		Expect(err).NotTo(HaveOccurred())
		buildpackVersion = packagedBuildpack.Version
	}

	bpDir, err = cutlass.FindRoot()
	Expect(err).NotTo(HaveOccurred())

	Expect(cutlass.CopyCfHome()).To(Succeed())

	cutlass.SeedRandom()
	cutlass.DefaultStdoutStderr = GinkgoWriter

	SetDefaultEventuallyTimeout(10 * time.Second)
})

var _ = SynchronizedAfterSuite(func() {
	// Run on all nodes
}, func() {
	// Run once
	cutlass.RemovePackagedBuildpack(packagedBuildpack)
	Expect(cutlass.DeleteOrphanedRoutes()).To(Succeed())
})

func TestIntegration(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Integration Suite")
}

func PushAppAndConfirm(app *cutlass.App) {
	Expect(app.Push()).To(Succeed(), func() string {
		buffer := bytes.NewBuffer(nil)
		cmd := exec.Command("cf", "logs", app.Name, "--recent")
		cmd.Stdout = buffer
		cmd.Stderr = buffer
		cmd.Run()

		return buffer.String()
	})
	Eventually(app.InstanceStates, 20*time.Second).Should(Equal([]string{"RUNNING"}))
	Expect(app.ConfirmBuildpack(buildpackVersion)).To(Succeed())
}

func Restart(app *cutlass.App) {
	Expect(app.Restart()).To(Succeed())
	Eventually(app.InstanceStates, 20*time.Second).Should(Equal([]string{"RUNNING"}))
}

func ApiHasTask() bool {
	supported, err := cutlass.ApiGreaterThan("2.75.0")
	Expect(err).NotTo(HaveOccurred())
	return supported
}

func ApiHasMultiBuildpack() bool {
	supported, err := cutlass.ApiGreaterThan("2.90.0")
	Expect(err).NotTo(HaveOccurred())
	return supported
}

func ApiHasStackAssociation() bool {
	supported, err := cutlass.ApiGreaterThan("2.113.0")
	Expect(err).NotTo(HaveOccurred())
	return supported
}

func SkipUnlessUncached() {
	if cutlass.Cached {
		Skip("Running cached tests")
	}
}

func SkipUnlessCached() {
	if !cutlass.Cached {
		Skip("Running uncached tests")
	}
}

func Fixtures(names ...string) string {
	root, err := cutlass.FindRoot()
	Expect(err).NotTo(HaveOccurred())

	names = append([]string{root, "fixtures"}, names...)
	return filepath.Join(names...)
}

func DestroyApp(app *cutlass.App) *cutlass.App {
	if app != nil {
		app.Destroy()
	}
	return nil
}

func DefaultVersion(name string) string {
	m := &libbuildpack.Manifest{}
	err := (&libbuildpack.YAML{}).Load(filepath.Join(bpDir, "manifest.yml"), m)
	Expect(err).ToNot(HaveOccurred())
	dep, err := m.DefaultVersion(name)
	Expect(err).ToNot(HaveOccurred())
	Expect(dep.Version).ToNot(Equal(""))
	return dep.Version
}

func AssertUsesProxyDuringStagingIfPresent(fixtureName string) {
	Context("with an uncached buildpack", func() {
		BeforeEach(SkipUnlessUncached)

		It("uses a proxy during staging if present", func() {
			proxy, err := cutlass.NewProxy()
			Expect(err).To(BeNil())
			defer proxy.Close()

			bpFile := filepath.Join(bpDir, buildpackVersion+"tmp")
			cmd := exec.Command("cp", packagedBuildpack.File, bpFile)
			err = cmd.Run()
			Expect(err).To(BeNil())
			defer os.Remove(bpFile)

			traffic, _, _, err := cutlass.InternetTraffic(
				Fixtures(fixtureName),
				bpFile,
				[]string{"HTTP_PROXY=" + proxy.URL, "HTTPS_PROXY=" + proxy.URL},
			)
			Expect(err).To(BeNil())
			// Expect(built).To(BeTrue())

			destUrl, err := url.Parse(proxy.URL)
			Expect(err).To(BeNil())

			Expect(cutlass.UniqueDestination(
				traffic, fmt.Sprintf("%s.%s", destUrl.Hostname(), destUrl.Port()),
			)).To(BeNil())
		})
	})
}

func AssertNoInternetTraffic(fixtureName string) {
	It("has no traffic", func() {
		SkipUnlessCached()

		bpFile := filepath.Join(bpDir, buildpackVersion+"tmp")
		cmd := exec.Command("cp", packagedBuildpack.File, bpFile)
		err := cmd.Run()
		Expect(err).To(BeNil())
		defer os.Remove(bpFile)

		traffic, _, _, err := cutlass.InternetTraffic(
			Fixtures(fixtureName),
			bpFile,
			[]string{},
		)
		Expect(err).To(BeNil())
		// Expect(built).To(BeTrue())
		Expect(traffic).To(BeEmpty())
	})
}
<commit_msg>Adds github-token for integration suite<commit_after>package integration_test

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/cloudfoundry/libbuildpack"
	"github.com/cloudfoundry/libbuildpack/cutlass"

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

	"testing"
)

var bpDir string
var buildpackVersion string
var packagedBuildpack cutlass.VersionedBuildpackPackage
var token string

var _ = func() bool {
	testing.Init()
	return true
}()

func init() {
	flag.StringVar(&buildpackVersion, "version", "", "version to use (builds if empty)")
	flag.BoolVar(&cutlass.Cached, "cached", true, "cached buildpack")
	flag.StringVar(&cutlass.DefaultMemory, "memory", "256M", "default memory for pushed apps")
	flag.StringVar(&cutlass.DefaultDisk, "disk", "384M", "default disk for pushed apps")
	flag.StringVar(&token, "github-token", "", "use the token to make GitHub API requests")
	flag.Parse()
}

var _ = SynchronizedBeforeSuite(func() []byte {
	// Run once
	if buildpackVersion == "" {
		packagedBuildpack, err := cutlass.PackageUniquelyVersionedBuildpack(os.Getenv("CF_STACK"), ApiHasStackAssociation())
		Expect(err).NotTo(HaveOccurred())

		data, err := json.Marshal(packagedBuildpack)
		Expect(err).NotTo(HaveOccurred())
		return data
	}

	return []byte{}
}, func(data []byte) {
	// Run on all nodes
	var err error
	if len(data) > 0 {
		err = json.Unmarshal(data, &packagedBuildpack)
		Expect(err).NotTo(HaveOccurred())
		buildpackVersion = packagedBuildpack.Version
	}

	bpDir, err = cutlass.FindRoot()
	Expect(err).NotTo(HaveOccurred())

	Expect(cutlass.CopyCfHome()).To(Succeed())

	cutlass.SeedRandom()
	cutlass.DefaultStdoutStderr = GinkgoWriter

	SetDefaultEventuallyTimeout(10 * time.Second)
})

var _ = SynchronizedAfterSuite(func() {
	// Run on all nodes
}, func() {
	// Run once
	cutlass.RemovePackagedBuildpack(packagedBuildpack)
	Expect(cutlass.DeleteOrphanedRoutes()).To(Succeed())
})

func TestIntegration(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Integration Suite")
}

func PushAppAndConfirm(app *cutlass.App) {
	Expect(app.Push()).To(Succeed(), func() string {
		buffer := bytes.NewBuffer(nil)
		cmd := exec.Command("cf", "logs", app.Name, "--recent")
		cmd.Stdout = buffer
		cmd.Stderr = buffer
		cmd.Run()

		return buffer.String()
	})
	Eventually(app.InstanceStates, 20*time.Second).Should(Equal([]string{"RUNNING"}))
	Expect(app.ConfirmBuildpack(buildpackVersion)).To(Succeed())
}

func Restart(app *cutlass.App) {
	Expect(app.Restart()).To(Succeed())
	Eventually(app.InstanceStates, 20*time.Second).Should(Equal([]string{"RUNNING"}))
}

func ApiHasTask() bool {
	supported, err := cutlass.ApiGreaterThan("2.75.0")
	Expect(err).NotTo(HaveOccurred())
	return supported
}

func ApiHasMultiBuildpack() bool {
	supported, err := cutlass.ApiGreaterThan("2.90.0")
	Expect(err).NotTo(HaveOccurred())
	return supported
}

func ApiHasStackAssociation() bool {
	supported, err := cutlass.ApiGreaterThan("2.113.0")
	Expect(err).NotTo(HaveOccurred())
	return supported
}

func SkipUnlessUncached() {
	if cutlass.Cached {
		Skip("Running cached tests")
	}
}

func SkipUnlessCached() {
	if !cutlass.Cached {
		Skip("Running uncached tests")
	}
}

func Fixtures(names ...string) string {
	root, err := cutlass.FindRoot()
	Expect(err).NotTo(HaveOccurred())

	names = append([]string{root, "fixtures"}, names...)
	return filepath.Join(names...)
}

func DestroyApp(app *cutlass.App) *cutlass.App {
	if app != nil {
		app.Destroy()
	}
	return nil
}

func DefaultVersion(name string) string {
	m := &libbuildpack.Manifest{}
	err := (&libbuildpack.YAML{}).Load(filepath.Join(bpDir, "manifest.yml"), m)
	Expect(err).ToNot(HaveOccurred())
	dep, err := m.DefaultVersion(name)
	Expect(err).ToNot(HaveOccurred())
	Expect(dep.Version).ToNot(Equal(""))
	return dep.Version
}

func AssertUsesProxyDuringStagingIfPresent(fixtureName string) {
	Context("with an uncached buildpack", func() {
		BeforeEach(SkipUnlessUncached)

		It("uses a proxy during staging if present", func() {
			proxy, err := cutlass.NewProxy()
			Expect(err).To(BeNil())
			defer proxy.Close()

			bpFile := filepath.Join(bpDir, buildpackVersion+"tmp")
			cmd := exec.Command("cp", packagedBuildpack.File, bpFile)
			err = cmd.Run()
			Expect(err).To(BeNil())
			defer os.Remove(bpFile)

			traffic, _, _, err := cutlass.InternetTraffic(
				Fixtures(fixtureName),
				bpFile,
				[]string{"HTTP_PROXY=" + proxy.URL, "HTTPS_PROXY=" + proxy.URL},
			)
			Expect(err).To(BeNil())
			// Expect(built).To(BeTrue())

			destUrl, err := url.Parse(proxy.URL)
			Expect(err).To(BeNil())

			Expect(cutlass.UniqueDestination(
				traffic, fmt.Sprintf("%s.%s", destUrl.Hostname(), destUrl.Port()),
			)).To(BeNil())
		})
	})
}

func AssertNoInternetTraffic(fixtureName string) {
	It("has no traffic", func() {
		SkipUnlessCached()

		bpFile := filepath.Join(bpDir, buildpackVersion+"tmp")
		cmd := exec.Command("cp", packagedBuildpack.File, bpFile)
		err := cmd.Run()
		Expect(err).To(BeNil())
		defer os.Remove(bpFile)

		traffic, _, _, err := cutlass.InternetTraffic(
			Fixtures(fixtureName),
			bpFile,
			[]string{},
		)
		Expect(err).To(BeNil())
		// Expect(built).To(BeTrue())
		Expect(traffic).To(BeEmpty())
	})
}
<|endoftext|>


<commit_before>package server

import (
	"strings"
	"testing"

	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/pkg/require"
)

func TestPutGet(t *testing.T) {
	c := getPachClient(t)
	object, err := c.PutObject(strings.NewReader("foo"))
	require.NoError(t, err)
	value, err := c.GetObject(object.Hash)
	require.NoError(t, err)
	require.Equal(t, []byte("foo"), value)
	objectInfo, err := c.InspectObject(object.Hash)
	require.NoError(t, err)
	require.Equal(t, uint64(3), objectInfo.BlockRef.Range.Upper-objectInfo.BlockRef.Range.Lower)
}

func TestTags(t *testing.T) {
	c := getPachClient(t)
	_, err := c.PutObject(strings.NewReader("foo"), "bar", "buzz")
	require.NoError(t, err)
	value, err := c.GetTag("bar")
	require.NoError(t, err)
	require.Equal(t, []byte("foo"), value)
	value, err = c.GetTag("buzz")
	require.NoError(t, err)
	require.Equal(t, []byte("foo"), value)
}

func TestManyObjects(t *testing.T) {
	c := getPachClient(t)
	var objects []string
	for i := 0; i < 100; i++ {
		object, err := c.PutObject(strings.NewReader(string(i)), string(i))
		require.NoError(t, err)
		objects = append(objects, object.Hash)
	}
	require.NoError(t, c.Compact())
	for i, hash := range objects {
		value, err := c.GetObject(hash)
		require.NoError(t, err)
		require.Equal(t, []byte(string(i)), value)
		value, err = c.GetTag(string(i))
		require.NoError(t, err)
		require.Equal(t, []byte(string(i)), value)
	}
}

func getPachClient(t testing.TB) *client.APIClient {
	client, err := client.NewFromAddress("0.0.0.0:30650")
	require.NoError(t, err)
	return client
}
<commit_msg>Make TestManyObjects a bit easier.<commit_after>package server

import (
	"strings"
	"testing"

	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/pkg/require"
)

func TestPutGet(t *testing.T) {
	c := getPachClient(t)
	object, err := c.PutObject(strings.NewReader("foo"))
	require.NoError(t, err)
	value, err := c.GetObject(object.Hash)
	require.NoError(t, err)
	require.Equal(t, []byte("foo"), value)
	objectInfo, err := c.InspectObject(object.Hash)
	require.NoError(t, err)
	require.Equal(t, uint64(3), objectInfo.BlockRef.Range.Upper-objectInfo.BlockRef.Range.Lower)
}

func TestTags(t *testing.T) {
	c := getPachClient(t)
	_, err := c.PutObject(strings.NewReader("foo"), "bar", "buzz")
	require.NoError(t, err)
	value, err := c.GetTag("bar")
	require.NoError(t, err)
	require.Equal(t, []byte("foo"), value)
	value, err = c.GetTag("buzz")
	require.NoError(t, err)
	require.Equal(t, []byte("foo"), value)
}

func TestManyObjects(t *testing.T) {
	c := getPachClient(t)
	var objects []string
	for i := 0; i < 25; i++ {
		object, err := c.PutObject(strings.NewReader(string(i)), string(i))
		require.NoError(t, err)
		objects = append(objects, object.Hash)
	}
	require.NoError(t, c.Compact())
	for i, hash := range objects {
		value, err := c.GetObject(hash)
		require.NoError(t, err)
		require.Equal(t, []byte(string(i)), value)
		value, err = c.GetTag(string(i))
		require.NoError(t, err)
		require.Equal(t, []byte(string(i)), value)
	}
}

func getPachClient(t testing.TB) *client.APIClient {
	client, err := client.NewFromAddress("0.0.0.0:30650")
	require.NoError(t, err)
	return client
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"github.com/nais/naisd/api/app"
	v1 "k8s.io/api/core/v1"
	k8sapps "k8s.io/api/apps/v1"
	k8smeta "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/client-go/kubernetes"
	"strconv"
)

const (
	defaultRedisPort          = 6379
	defaultRedisExporterPort  = 9121
	defaultRedisExporterImage = "oliver006/redis_exporter:v1.0.4-alpine"
	defaultRedisImage         = "redis:5-alpine"
)

type Redis struct {
	Enabled  bool
	Image    string
	Limits   ResourceList
	Requests ResourceList
}

func updateDefaultRedisValues(redis Redis) Redis {
	if redis.Image == "" {
		redis.Image = defaultRedisImage
	}
	if len(redis.Limits.Cpu) == 0 {
		redis.Limits.Cpu = "100m"
	}
	if len(redis.Limits.Memory) == 0 {
		redis.Limits.Memory = "128Mi"
	}
	if len(redis.Requests.Cpu) == 0 {
		redis.Requests.Cpu = "100m"
	}
	if len(redis.Requests.Memory) == 0 {
		redis.Requests.Memory = "128Mi"
	}
	return redis
}

func createRedisPodSpec(redis Redis) v1.PodSpec {
	return v1.PodSpec{
		Containers: []v1.Container{
			{
				Name:  "redis",
				Image: redis.Image,
				Resources: createResourceLimits(redis.Requests.Cpu, redis.Requests.Memory,
					redis.Limits.Cpu, redis.Limits.Memory),
				ImagePullPolicy: v1.PullIfNotPresent,
				Ports: []v1.ContainerPort{
					{
						ContainerPort: int32(defaultRedisPort),
						Name:          DefaultPortName,
						Protocol:      v1.ProtocolTCP,
					},
				},
			},
			{
				Name:  "exporter",
				Image: defaultRedisExporterImage,
				Resources: createResourceLimits("100m", "100Mi",
					"100m", "100Mi"),
				ImagePullPolicy: v1.PullIfNotPresent,
				Ports: []v1.ContainerPort{
					{
						ContainerPort: int32(defaultRedisExporterPort),
						Name:          DefaultPortName,
						Protocol:      v1.ProtocolTCP,
					},
				},
			},
		},
	}
}

func createRedisDeploymentSpec(resourceName string, spec app.Spec, redis Redis) k8sapps.DeploymentSpec {
	objectMeta := generateObjectMeta(spec)
	objectMeta.Name = resourceName
	objectMeta.Annotations = map[string]string{
		"prometheus.io/scrape": "true",
		"prometheus.io/port":   strconv.Itoa(defaultRedisExporterPort),
		"prometheus.io/path":   "/metrics",
	}

	return k8sapps.DeploymentSpec{
		Replicas: int32p(1),
		Selector: &k8smeta.LabelSelector{
			MatchLabels: createPodSelector(spec),
		},
		Strategy: k8sapps.DeploymentStrategy{
			Type: k8sapps.RecreateDeploymentStrategyType,
		},
		ProgressDeadlineSeconds: int32p(300),
		RevisionHistoryLimit:    int32p(10),
		Template: v1.PodTemplateSpec{
			ObjectMeta: objectMeta,
			Spec:       createRedisPodSpec(redis),
		},
	}
}

func createRedisDeploymentDef(resourceName string, spec app.Spec, redis Redis, existingDeployment *k8sapps.Deployment) *k8sapps.Deployment {
	deploymentSpec := createRedisDeploymentSpec(resourceName, spec, redis)
	if existingDeployment != nil {
		existingDeployment.ObjectMeta = addLabelsToObjectMeta(existingDeployment.ObjectMeta, spec)
		existingDeployment.Spec = deploymentSpec
		return existingDeployment
	} else {
		return &k8sapps.Deployment{
			TypeMeta: k8smeta.TypeMeta{
				Kind:       "Deployment",
				APIVersion: "apps/v1beta1",
			},
			ObjectMeta: generateObjectMeta(spec),
			Spec:       deploymentSpec,
		}
	}
}

func createOrUpdateRedisInstance(spec app.Spec, redis Redis, k8sClient kubernetes.Interface) (*k8sapps.Deployment, error) {
	redisName := fmt.Sprintf("%s-redis", spec.ResourceName())
	existingDeployment, err := getExistingDeployment(redisName, spec.Namespace, k8sClient)

	if err != nil {
		return nil, fmt.Errorf("unable to get existing deployment: %s", err)
	}

	deploymentDef := createRedisDeploymentDef(redisName, spec, redis, existingDeployment)
	deploymentDef.Name = fmt.Sprintf("%s-redis", spec.ResourceName())

	return createOrUpdateDeploymentResource(deploymentDef, spec.Namespace, k8sClient)
}

func createRedisServiceDef(spec app.Spec) *v1.Service {
	return &v1.Service{
		TypeMeta: k8smeta.TypeMeta{
			Kind:       "Service",
			APIVersion: "v1",
		},
		ObjectMeta: generateObjectMeta(spec),
		Spec: v1.ServiceSpec{
			Type:     v1.ServiceTypeClusterIP,
			Selector: createPodSelector(spec),
			Ports: []v1.ServicePort{
				{
					Name:     DefaultPortName,
					Protocol: v1.ProtocolTCP,
					Port:     6379,
					TargetPort: intstr.IntOrString{
						Type:   intstr.String,
						StrVal: DefaultPortName,
					},
				},
			},
		},
	}
}

func createOrUpdateRedisService(spec app.Spec, k8sClient kubernetes.Interface) (*v1.Service, error) {
	redisName := fmt.Sprintf("%s-redis", spec.ResourceName())
	service, err := getExistingService(redisName, spec.Namespace, k8sClient)

	if err != nil {
		return nil, fmt.Errorf("unable to get existing service: %s", err)
	} else if service == nil {
		service = createRedisServiceDef(spec)
		service.Name = redisName
	}

	service.ObjectMeta = addLabelsToObjectMeta(service.ObjectMeta, spec)
	return createOrUpdateServiceResource(service, spec.Namespace, k8sClient)
}
<commit_msg>Feil selector, må treffe appnavn-redis<commit_after>package api

import (
	"fmt"
	"github.com/nais/naisd/api/app"
	v1 "k8s.io/api/core/v1"
	k8sapps "k8s.io/api/apps/v1"
	k8smeta "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/client-go/kubernetes"
	"strconv"
)

const (
	defaultRedisPort          = 6379
	defaultRedisExporterPort  = 9121
	defaultRedisExporterImage = "oliver006/redis_exporter:v1.0.4-alpine"
	defaultRedisImage         = "redis:5-alpine"
)

type Redis struct {
	Enabled  bool
	Image    string
	Limits   ResourceList
	Requests ResourceList
}

func updateDefaultRedisValues(redis Redis) Redis {
	if redis.Image == "" {
		redis.Image = defaultRedisImage
	}
	if len(redis.Limits.Cpu) == 0 {
		redis.Limits.Cpu = "100m"
	}
	if len(redis.Limits.Memory) == 0 {
		redis.Limits.Memory = "128Mi"
	}
	if len(redis.Requests.Cpu) == 0 {
		redis.Requests.Cpu = "100m"
	}
	if len(redis.Requests.Memory) == 0 {
		redis.Requests.Memory = "128Mi"
	}
	return redis
}

func createRedisPodSpec(redis Redis) v1.PodSpec {
	return v1.PodSpec{
		Containers: []v1.Container{
			{
				Name:  "redis",
				Image: redis.Image,
				Resources: createResourceLimits(redis.Requests.Cpu, redis.Requests.Memory,
					redis.Limits.Cpu, redis.Limits.Memory),
				ImagePullPolicy: v1.PullIfNotPresent,
				Ports: []v1.ContainerPort{
					{
						ContainerPort: int32(defaultRedisPort),
						Name:          DefaultPortName,
						Protocol:      v1.ProtocolTCP,
					},
				},
			},
			{
				Name:  "exporter",
				Image: defaultRedisExporterImage,
				Resources: createResourceLimits("100m", "100Mi",
					"100m", "100Mi"),
				ImagePullPolicy: v1.PullIfNotPresent,
				Ports: []v1.ContainerPort{
					{
						ContainerPort: int32(defaultRedisExporterPort),
						Name:          DefaultPortName,
						Protocol:      v1.ProtocolTCP,
					},
				},
			},
		},
	}
}

func createRedisDeploymentSpec(resourceName string, spec app.Spec, redis Redis) k8sapps.DeploymentSpec {
	objectMeta := generateObjectMeta(spec)
	objectMeta.Name = resourceName
	objectMeta.Annotations = map[string]string{
		"prometheus.io/scrape": "true",
		"prometheus.io/port":   strconv.Itoa(defaultRedisExporterPort),
		"prometheus.io/path":   "/metrics",
	}

	return k8sapps.DeploymentSpec{
		Replicas: int32p(1),
		Selector: &k8smeta.LabelSelector{
			MatchLabels: createPodSelector(spec),
		},
		Strategy: k8sapps.DeploymentStrategy{
			Type: k8sapps.RecreateDeploymentStrategyType,
		},
		ProgressDeadlineSeconds: int32p(300),
		RevisionHistoryLimit:    int32p(10),
		Template: v1.PodTemplateSpec{
			ObjectMeta: objectMeta,
			Spec:       createRedisPodSpec(redis),
		},
	}
}

func createRedisDeploymentDef(resourceName string, spec app.Spec, redis Redis, existingDeployment *k8sapps.Deployment) *k8sapps.Deployment {
	deploymentSpec := createRedisDeploymentSpec(resourceName, spec, redis)
	if existingDeployment != nil {
		existingDeployment.ObjectMeta = addLabelsToObjectMeta(existingDeployment.ObjectMeta, spec)
		existingDeployment.Spec = deploymentSpec
		return existingDeployment
	} else {
		return &k8sapps.Deployment{
			TypeMeta: k8smeta.TypeMeta{
				Kind:       "Deployment",
				APIVersion: "apps/v1beta1",
			},
			ObjectMeta: generateObjectMeta(spec),
			Spec:       deploymentSpec,
		}
	}
}

func createOrUpdateRedisInstance(spec app.Spec, redis Redis, k8sClient kubernetes.Interface) (*k8sapps.Deployment, error) {
	redisName := fmt.Sprintf("%s-redis", spec.ResourceName())
	existingDeployment, err := getExistingDeployment(redisName, spec.Namespace, k8sClient)

	if err != nil {
		return nil, fmt.Errorf("unable to get existing deployment: %s", err)
	}

	deploymentDef := createRedisDeploymentDef(redisName, spec, redis, existingDeployment)
	deploymentDef.Name = fmt.Sprintf("%s-redis", spec.ResourceName())

	return createOrUpdateDeploymentResource(deploymentDef, spec.Namespace, k8sClient)
}

func createRedisServiceDef(spec app.Spec) *v1.Service {
	return &v1.Service{
		TypeMeta: k8smeta.TypeMeta{
			Kind:       "Service",
			APIVersion: "v1",
		},
		ObjectMeta: generateObjectMeta(spec),
		Spec: v1.ServiceSpec{
			Type:     v1.ServiceTypeClusterIP,
			Selector: map[string]string{
		                "app": fmt.Sprintf("%s-redis", spec.ResourceName()),
                        },
			Ports: []v1.ServicePort{
				{
					Name:     DefaultPortName,
					Protocol: v1.ProtocolTCP,
					Port:     6379,
					TargetPort: intstr.IntOrString{
						Type:   intstr.String,
						StrVal: DefaultPortName,
					},
				},
			},
		},
	}
}

func createOrUpdateRedisService(spec app.Spec, k8sClient kubernetes.Interface) (*v1.Service, error) {
	redisName := fmt.Sprintf("%s-redis", spec.ResourceName())
	service, err := getExistingService(redisName, spec.Namespace, k8sClient)

	if err != nil {
		return nil, fmt.Errorf("unable to get existing service: %s", err)
	} else if service == nil {
		service = createRedisServiceDef(spec)
		service.Name = redisName
	}

	service.ObjectMeta = addLabelsToObjectMeta(service.ObjectMeta, spec)
	return createOrUpdateServiceResource(service, spec.Namespace, k8sClient)
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
)

const (
	originalUrl    = "http://farm%v.staticflickr.com/%v/%v_%v_o.%v"
	thumbnailUrl   = "http://farm%v.staticflickr.com/%v/%v_%v_%v.jpg"
	thumbnailSizeL = "q"
	thumbnailSizeS = "s"
)

/*
{
    "stat"      : "fail",
    "code"      : "97",
    "message"   : "Missing signature"
}
*/
type FlickrResult struct {
	Status string `json:"stat"`
	Error  string `json:"message"`
}

/*
PhotoSearchResult is the result of a flickr.photos.search API call.

{
    "photos":
    {
        "page":1,
        "pages":583,
        "perpage":1,
        "total":"583",
        "photo":
        [
            {
                "id":"14691360159",
                "owner":"48475357@N00",
                "secret":"5ff0a05549",
                "server":"3916",
                "farm":4,
                "title":"",
                "ispublic":1,
                "isfriend":0,
                "isfamily":0,
                "originalsecret":"d09d2d858b",
                "originalformat":"jpg"
            }
        ]
    },
    "stat":"ok"
}
*/
type PhotoSearchResult struct {
	FlickrResult
	Photos PhotoSearchPage
}
type PhotoSearchPage struct {
	// Current page number
	Page int
	// Total number of pages
	Pages  int
	Photos []PhotoSummary `json:"photo"`
}

type PhotoSummary struct {
	PhotoCommon
	Owner     string
	JsonTitle string `json:"title"`
	IsPublic  int    `json:"ispublic"`
	IsFriend  int    `json:"isfriend"`
	IsFamily  int    `json:"isfamily"`
}

type PhotoCommon struct {
	Id             string
	Secret         string
	Server         string
	Farm           int
	OriginalSecret string `json:"originalsecret"`
	OriginalFormat string `json:"originalformat"`
}

/*
PhotoInfoResult is the result of a flickr.photos.getInfo API call.

{
    "photo":
    {
        "id":"14691360159",
        "secret":"5ff0a05549",
        "server":"3916",
        "farm":4,
        "dateuploaded":"1407690835",
        "isfavorite":0,
        "license":"0",
        "safety_level":"0",
        "rotation":0,
        "originalsecret":"d09d2d858b",
        "originalformat":"jpg",
        "owner":
        {
            "nsid":"48475357@N00",
            "username":"pete-t",
            "realname":"Peter Thompson",
            "location":"Vienna, Austria",
            "iconserver":"23",
            "iconfarm":1,
            "path_alias":"petert"
        },
        "title":
        {
            "_content":""
        },
        "description":
        {
            "_content":"Olympus digital camera"
        },
        "visibility":{"ispublic":1,"isfriend":0,"isfamily":0},
        "dates":{"posted":"1407690835","taken":"2013-08-15 16:07:38","takengranularity":"0","takenunknown":0,"lastupdate":"1407701318"},
        "views":"95",
        "editability":{"cancomment":0,"canaddmeta":0},
        "publiceditability":{"cancomment":1,"canaddmeta":0},
        "usage":{"candownload":1,"canblog":0,"canprint":0,"canshare":1},
        "comments":{"_content":"1"},
        "notes":{"note":[]},
        "people":{"haspeople":0},
        "tags":{"tag":[]},
        "urls":{"url":[{"type":"photopage","_content":"https:\/\/www.flickr.com\/photos\/petert\/14691360159\/"}]},
        "media":"photo"
    },
    "stat":"ok"
}
*/
type PhotoInfoResult struct {
	FlickrResult
	Photo PhotoInfo `json:"photo"`
}

type PhotoInfo struct {
	PhotoCommon
	JsonTitle       contentString `json:"title"`
	JsonDescription contentString `json:"description"`
}

type contentString struct {
	Content string `json:"_content"`
}

type Photoer interface {
	// Url to large format thumbnail (150 x 150)
	LargeThumbnailUrl() string
	// Url to small format thumbnail (75 x 75)
	SmallThumbnailUrl() string
	// URL of original-size photo
	OriginalUrl() string
	// The photo's title
	Title() string
}

func (p PhotoCommon) LargeThumbnailUrl() string {
	// "http://farm"+photo.farm+".staticflickr.com/"+photo.server+"/"+photo.id+"_"+photo.secret+"_q.jpg"
	return fmt.Sprintf(thumbnailUrl, p.Farm, p.Server, p.Id, p.Secret, thumbnailSizeL)
}

func (p PhotoCommon) SmallThumbnailUrl() string {
	// "http://farm"+photo.farm+".staticflickr.com/"+photo.server+"/"+photo.id+"_"+photo.secret+"_q.jpg"
	return fmt.Sprintf(thumbnailUrl, p.Farm, p.Server, p.Id, p.Secret, thumbnailSizeS)
}

func (p PhotoCommon) OriginalUrl() string {
	// 'http://farm'+photo.farm+'.staticflickr.com/'+photo.server+'/'+photo.id+'_'+photo.originalSecret+'_o.'+photo.originalFormat
	return fmt.Sprintf(originalUrl, p.Farm, p.Server, p.Id, p.OriginalSecret, p.OriginalFormat)
}

func (p PhotoSummary) Title() string {
	return p.Title()
}

func (p PhotoInfo) Title() string {
	return p.JsonTitle.Content
}
<commit_msg>Add Id() to Photoer interface<commit_after>package api

import (
	"fmt"
)

const (
	originalUrl    = "http://farm%v.staticflickr.com/%v/%v_%v_o.%v"
	thumbnailUrl   = "http://farm%v.staticflickr.com/%v/%v_%v_%v.jpg"
	thumbnailSizeL = "q"
	thumbnailSizeS = "s"
)

/*
{
    "stat"      : "fail",
    "code"      : "97",
    "message"   : "Missing signature"
}
*/
type FlickrResult struct {
	Status string `json:"stat"`
	Error  string `json:"message"`
}

/*
PhotoSearchResult is the result of a flickr.photos.search API call.

{
    "photos":
    {
        "page":1,
        "pages":583,
        "perpage":1,
        "total":"583",
        "photo":
        [
            {
                "id":"14691360159",
                "owner":"48475357@N00",
                "secret":"5ff0a05549",
                "server":"3916",
                "farm":4,
                "title":"",
                "ispublic":1,
                "isfriend":0,
                "isfamily":0,
                "originalsecret":"d09d2d858b",
                "originalformat":"jpg"
            }
        ]
    },
    "stat":"ok"
}
*/
type PhotoSearchResult struct {
	FlickrResult
	Photos PhotoSearchPage
}
type PhotoSearchPage struct {
	// Current page number
	Page int
	// Total number of pages
	Pages  int
	Photos []PhotoSummary `json:"photo"`
}

type PhotoSummary struct {
	PhotoCommon
	Owner     string
	JsonTitle string `json:"title"`
	IsPublic  int    `json:"ispublic"`
	IsFriend  int    `json:"isfriend"`
	IsFamily  int    `json:"isfamily"`
}

type PhotoCommon struct {
	JsonId         string `json:"id"`
	Secret         string
	Server         string
	Farm           int
	OriginalSecret string `json:"originalsecret"`
	OriginalFormat string `json:"originalformat"`
}

/*
PhotoInfoResult is the result of a flickr.photos.getInfo API call.

{
    "photo":
    {
        "id":"14691360159",
        "secret":"5ff0a05549",
        "server":"3916",
        "farm":4,
        "dateuploaded":"1407690835",
        "isfavorite":0,
        "license":"0",
        "safety_level":"0",
        "rotation":0,
        "originalsecret":"d09d2d858b",
        "originalformat":"jpg",
        "owner":
        {
            "nsid":"48475357@N00",
            "username":"pete-t",
            "realname":"Peter Thompson",
            "location":"Vienna, Austria",
            "iconserver":"23",
            "iconfarm":1,
            "path_alias":"petert"
        },
        "title":
        {
            "_content":""
        },
        "description":
        {
            "_content":"Olympus digital camera"
        },
        "visibility":{"ispublic":1,"isfriend":0,"isfamily":0},
        "dates":{"posted":"1407690835","taken":"2013-08-15 16:07:38","takengranularity":"0","takenunknown":0,"lastupdate":"1407701318"},
        "views":"95",
        "editability":{"cancomment":0,"canaddmeta":0},
        "publiceditability":{"cancomment":1,"canaddmeta":0},
        "usage":{"candownload":1,"canblog":0,"canprint":0,"canshare":1},
        "comments":{"_content":"1"},
        "notes":{"note":[]},
        "people":{"haspeople":0},
        "tags":{"tag":[]},
        "urls":{"url":[{"type":"photopage","_content":"https:\/\/www.flickr.com\/photos\/petert\/14691360159\/"}]},
        "media":"photo"
    },
    "stat":"ok"
}
*/
type PhotoInfoResult struct {
	FlickrResult
	Photo PhotoInfo `json:"photo"`
}

type PhotoInfo struct {
	PhotoCommon
	JsonTitle       contentString `json:"title"`
	JsonDescription contentString `json:"description"`
}

type contentString struct {
	Content string `json:"_content"`
}

type Photoer interface {
	Id() string
	// Url to large format thumbnail (150 x 150)
	LargeThumbnailUrl() string
	// Url to small format thumbnail (75 x 75)
	SmallThumbnailUrl() string
	// URL of original-size photo
	OriginalUrl() string
	// The photo's title
	Title() string
}

type FullPhotoer interface {
	Photoer
	Description() string
}

func (p PhotoCommon) Id() string {
	return p.JsonId
}

func (p PhotoCommon) LargeThumbnailUrl() string {
	// "http://farm"+photo.farm+".staticflickr.com/"+photo.server+"/"+photo.id+"_"+photo.secret+"_q.jpg"
	return fmt.Sprintf(thumbnailUrl, p.Farm, p.Server, p.JsonId, p.Secret, thumbnailSizeL)
}

func (p PhotoCommon) SmallThumbnailUrl() string {
	// "http://farm"+photo.farm+".staticflickr.com/"+photo.server+"/"+photo.id+"_"+photo.secret+"_q.jpg"
	return fmt.Sprintf(thumbnailUrl, p.Farm, p.Server, p.JsonId, p.Secret, thumbnailSizeS)
}

func (p PhotoCommon) OriginalUrl() string {
	// 'http://farm'+photo.farm+'.staticflickr.com/'+photo.server+'/'+photo.id+'_'+photo.originalSecret+'_o.'+photo.originalFormat
	return fmt.Sprintf(originalUrl, p.Farm, p.Server, p.JsonId, p.OriginalSecret, p.OriginalFormat)
}

func (p PhotoSummary) Title() string {
	return p.Title()
}

func (p PhotoInfo) Title() string {
	return p.JsonTitle.Content
}

func (p PhotoInfo) Description() string {
	return p.JsonDescription.Content
}
<|endoftext|>