<commit_before>package integration_test

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

	"code.cloudfoundry.org/lager"

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

	. "github.com/concourse/atc/cessna"
	"github.com/concourse/baggageclaim"

	bclient "github.com/concourse/baggageclaim/client"

	"testing"

	"archive/tar"
	"bytes"
	"io"

	"code.cloudfoundry.org/garden/gardenfakes"
	"code.cloudfoundry.org/lager/lagertest"
	"github.com/concourse/atc/cessna/cessnafakes"
	"github.com/concourse/baggageclaim/baggageclaimfakes"
)

var (
	testBaseResource Resource
	worker           Worker
	baseResourceType BaseResourceType
	workerIp         string
	tarPath          string
	tarURL           string

	found bool

	logger lager.Logger

	fakeWorker             *cessnafakes.FakeWorker
	fakeGardenClient       *gardenfakes.FakeClient
	fakeBaggageClaimClient *baggageclaimfakes.FakeClient
)

var _ = BeforeSuite(func() {
	_, found = os.LookupEnv("RUN_CESSNA_TESTS")
	if !found {
		Skip("Must set RUN_CESSNA_TESTS")
	}
}, 10)

var _ = SynchronizedAfterSuite(func() {}, func() {
	worker = NewWorker(fmt.Sprintf("%s:7777", workerIp), fmt.Sprintf("http://%s:7788", workerIp))

	containers, err := worker.GardenClient().Containers(nil)
	Expect(err).NotTo(HaveOccurred())

	for _, container := range containers {
		err = worker.GardenClient().Destroy(container.Handle())
		Expect(err).NotTo(HaveOccurred())
	}

	volumes, err := worker.BaggageClaimClient().ListVolumes(logger, nil)
	Expect(err).NotTo(HaveOccurred())

	for _, volume := range volumes {
		err = volume.Destroy()
		Expect(err).NotTo(HaveOccurred())
	}
}, 10)

var _ = BeforeEach(func() {
	fakeWorker = new(cessnafakes.FakeWorker)
	fakeGardenClient = new(gardenfakes.FakeClient)
	fakeBaggageClaimClient = new(baggageclaimfakes.FakeClient)

	fakeWorker.BaggageClaimClientReturns(fakeBaggageClaimClient)
	fakeWorker.GardenClientReturns(fakeGardenClient)

	workerIp, found = os.LookupEnv("WORKER_IP")
	Expect(found).To(BeTrue(), "Must set WORKER_IP")

	tarPath, found = os.LookupEnv("ROOTFS_TAR_PATH")
	Expect(found).To(BeTrue(), "Must set ROOTFS_TAR_PATH")

	tarURL, found = os.LookupEnv("ROOTFS_TAR_URL")
	Expect(found).To(BeTrue(), "Must set ROOTFS_TAR_URL")

	worker = NewWorker(fmt.Sprintf("%s:7777", workerIp), fmt.Sprintf("http://%s:7788", workerIp))
	logger = lagertest.NewTestLogger("logger-test")
})

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

func createBaseResourceVolume(r io.Reader) (string, error) {
	baggageclaimClient := bclient.New(fmt.Sprintf("http://%s:7788", workerIp), http.DefaultTransport)

	volumeSpec := baggageclaim.VolumeSpec{
		Strategy:   baggageclaim.EmptyStrategy{},
		Privileged: true,
	}

	volume, err := baggageclaimClient.CreateVolume(
		lager.NewLogger("create-volume-for-base-resource"),
		volumeSpec)

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

	err = volume.StreamIn("/", r)
	if err != nil {
		return "", err
	}

	return volume.Path(), nil
}

func NewResourceContainer(check string, in string, out string) ResourceContainer {
	return ResourceContainer{
		Check:         check,
		In:            in,
		Out:           out,
		RootFSTarPath: tarPath,
	}
}

type ResourceContainer struct {
	Check         string
	In            string
	Out           string
	RootFSTarPath string
}

func (r ResourceContainer) RootFSify() (io.Reader, error) {
	f, err := os.Open(r.RootFSTarPath)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	buffer := new(bytes.Buffer)

	t := tar.NewWriter(buffer)
	rootFS := tar.NewReader(f)

	err = t.WriteHeader(&tar.Header{
		Name: "./opt/resource/check",
		Mode: 0755,
		Size: int64(len(r.Check)),
	})
	if err != nil {
		return nil, err
	}
	_, err = t.Write([]byte(r.Check))
	if err != nil {
		return nil, err
	}

	err = t.WriteHeader(&tar.Header{
		Name: "./opt/resource/in",
		Mode: 0755,
		Size: int64(len(r.In)),
	})
	if err != nil {
		return nil, err
	}
	_, err = t.Write([]byte(r.In))
	if err != nil {
		return nil, err
	}

	err = t.WriteHeader(&tar.Header{
		Name: "./opt/resource/out",
		Mode: 0755,
		Size: int64(len(r.Out)),
	})
	if err != nil {
		return nil, err
	}
	_, err = t.Write([]byte(r.Out))
	if err != nil {
		return nil, err
	}

	for {
		header, err := rootFS.Next()
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, err
		}

		err = t.WriteHeader(header)
		if err != nil {
			return nil, err
		}

		_, err = io.Copy(t, rootFS)
		if err != nil {
			return nil, err
		}
	}

	err = t.Close()
	if err != nil {
		return nil, err
	}

	return bytes.NewBuffer(buffer.Bytes()), nil
}
<commit_msg>cessna: succeed and just skip everything<commit_after>package integration_test

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

	"code.cloudfoundry.org/lager"

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

	. "github.com/concourse/atc/cessna"
	"github.com/concourse/baggageclaim"

	bclient "github.com/concourse/baggageclaim/client"

	"testing"

	"archive/tar"
	"bytes"
	"io"

	"code.cloudfoundry.org/garden/gardenfakes"
	"code.cloudfoundry.org/lager/lagertest"
	"github.com/concourse/atc/cessna/cessnafakes"
	"github.com/concourse/baggageclaim/baggageclaimfakes"
)

var (
	testBaseResource Resource
	worker           Worker
	baseResourceType BaseResourceType
	workerIp         string
	tarPath          string
	tarURL           string

	shouldRun bool

	logger lager.Logger

	fakeWorker             *cessnafakes.FakeWorker
	fakeGardenClient       *gardenfakes.FakeClient
	fakeBaggageClaimClient *baggageclaimfakes.FakeClient
)

var _ = BeforeSuite(func() {
	_, shouldRun = os.LookupEnv("RUN_CESSNA_TESTS")
}, 10)

var _ = SynchronizedAfterSuite(func() {}, func() {
	if !shouldRun {
		return
	}

	worker = NewWorker(fmt.Sprintf("%s:7777", workerIp), fmt.Sprintf("http://%s:7788", workerIp))

	containers, err := worker.GardenClient().Containers(nil)
	Expect(err).NotTo(HaveOccurred())

	for _, container := range containers {
		err = worker.GardenClient().Destroy(container.Handle())
		Expect(err).NotTo(HaveOccurred())
	}

	volumes, err := worker.BaggageClaimClient().ListVolumes(logger, nil)
	Expect(err).NotTo(HaveOccurred())

	for _, volume := range volumes {
		err = volume.Destroy()
		Expect(err).NotTo(HaveOccurred())
	}
}, 10)

var _ = BeforeEach(func() {
	if !shouldRun {
		Skip("$RUN_CESSNA_TESTS not configured; skipping")
	}

	fakeWorker = new(cessnafakes.FakeWorker)
	fakeGardenClient = new(gardenfakes.FakeClient)
	fakeBaggageClaimClient = new(baggageclaimfakes.FakeClient)

	fakeWorker.BaggageClaimClientReturns(fakeBaggageClaimClient)
	fakeWorker.GardenClientReturns(fakeGardenClient)

	workerIp, found := os.LookupEnv("WORKER_IP")
	Expect(found).To(BeTrue(), "Must set WORKER_IP")

	tarPath, found = os.LookupEnv("ROOTFS_TAR_PATH")
	Expect(found).To(BeTrue(), "Must set ROOTFS_TAR_PATH")

	tarURL, found = os.LookupEnv("ROOTFS_TAR_URL")
	Expect(found).To(BeTrue(), "Must set ROOTFS_TAR_URL")

	worker = NewWorker(fmt.Sprintf("%s:7777", workerIp), fmt.Sprintf("http://%s:7788", workerIp))
	logger = lagertest.NewTestLogger("logger-test")
})

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

func createBaseResourceVolume(r io.Reader) (string, error) {
	baggageclaimClient := bclient.New(fmt.Sprintf("http://%s:7788", workerIp), http.DefaultTransport)

	volumeSpec := baggageclaim.VolumeSpec{
		Strategy:   baggageclaim.EmptyStrategy{},
		Privileged: true,
	}

	volume, err := baggageclaimClient.CreateVolume(
		lager.NewLogger("create-volume-for-base-resource"),
		volumeSpec)

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

	err = volume.StreamIn("/", r)
	if err != nil {
		return "", err
	}

	return volume.Path(), nil
}

func NewResourceContainer(check string, in string, out string) ResourceContainer {
	return ResourceContainer{
		Check:         check,
		In:            in,
		Out:           out,
		RootFSTarPath: tarPath,
	}
}

type ResourceContainer struct {
	Check         string
	In            string
	Out           string
	RootFSTarPath string
}

func (r ResourceContainer) RootFSify() (io.Reader, error) {
	f, err := os.Open(r.RootFSTarPath)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	buffer := new(bytes.Buffer)

	t := tar.NewWriter(buffer)
	rootFS := tar.NewReader(f)

	err = t.WriteHeader(&tar.Header{
		Name: "./opt/resource/check",
		Mode: 0755,
		Size: int64(len(r.Check)),
	})
	if err != nil {
		return nil, err
	}
	_, err = t.Write([]byte(r.Check))
	if err != nil {
		return nil, err
	}

	err = t.WriteHeader(&tar.Header{
		Name: "./opt/resource/in",
		Mode: 0755,
		Size: int64(len(r.In)),
	})
	if err != nil {
		return nil, err
	}
	_, err = t.Write([]byte(r.In))
	if err != nil {
		return nil, err
	}

	err = t.WriteHeader(&tar.Header{
		Name: "./opt/resource/out",
		Mode: 0755,
		Size: int64(len(r.Out)),
	})
	if err != nil {
		return nil, err
	}
	_, err = t.Write([]byte(r.Out))
	if err != nil {
		return nil, err
	}

	for {
		header, err := rootFS.Next()
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, err
		}

		err = t.WriteHeader(header)
		if err != nil {
			return nil, err
		}

		_, err = io.Copy(t, rootFS)
		if err != nil {
			return nil, err
		}
	}

	err = t.Close()
	if err != nil {
		return nil, err
	}

	return bytes.NewBuffer(buffer.Bytes()), nil
}
<|endoftext|>


<commit_before><commit_msg>Amended comments on exported functions to start with the function name to clear lint messages<commit_after><|endoftext|>


<commit_before><commit_msg>remove strconv dependency from page.go<commit_after><|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"os"
	"text/template"
)

const (
	HTML_TEMPLATE = `<!DOCTYPE html>

<title>Changelog</title>
<meta charset="utf-8">
{{ range $stylesheet := .Stylesheets }}

{{ end }}

Changelog

{{ range $release := .Changelog }}

Release {{ .Version }} ({{ .Date }})

{{ .Summary }}

{{ if .Added }}

Added

{{ range $entry := .Added }}

{{ . }}

{{ end }}

{{ end }}
{{ if .Changed }}

Changed

{{ range $entry := .Changed }}

{{ . }}

{{ end }}

{{ end }}
{{ if .Deprecated }}

Deprecated

{{ range $entry := .Deprecated }}

{{ . }}

{{ end }}

{{ end }}
{{ if .Removed }}

Removed

{{ range $entry := .Removed }}

{{ . }}

{{ end }}

{{ end }}
{{ if .Fixed }}

Fixed

{{ range $entry := .Fixed }}

{{ . }}

{{ end }}

{{ end }}
{{ if .Security }}

Security

{{ range $entry := .Security }}

{{ . }}

{{ end }}

{{ end }}
{{ if .Rejected }}

Rejected

{{ range $entry := .Rejected }}

{{ . }}

{{ end }}

{{ end }}
{{ if .Notes }}

Notes

{{ range $entry := .Notes }}

{{ . }}

{{ end }}

{{ end }}
{{ end }}

`
	STYLESHEET = `
body {
  font-family: Helvetica, arial, sans-serif;
  font-size: 16px;
  line-height: 1.4;
  padding-top: 10px;
  padding-bottom: 10px;
  background-color: white;
  padding: 30px;
  color: #333;
}

body > *:first-child {
  margin-top: 0 !important;
}

body > *:last-child {
  margin-bottom: 0 !important;
}

a {
  color: #4183C4;
  text-decoration: none;
}

a.absent {
  color: #cc0000;
}

a.anchor {
  display: block;
  padding-left: 30px;
  margin-left: -30px;
  cursor: pointer;
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
}

h1, h2, h3, h4, h5, h6 {
  margin: 20px 0 10px;
  padding: 0;
  font-weight: bold;
  -webkit-font-smoothing: antialiased;
  cursor: text;
  position: relative;
}

h2:first-child, h1:first-child, h1:first-child + h2, h3:first-child, h4:first-child, h5:first-child, h6:first-child {
  margin-top: 0;
  padding-top: 0;
}

h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor {
  text-decoration: none;
}

h1 tt, h1 code {
  font-size: inherit;
}

h2 tt, h2 code {
  font-size: inherit;
}

h3 tt, h3 code {
  font-size: inherit;
}

h4 tt, h4 code {
  font-size: inherit;
}

h5 tt, h5 code {
  font-size: inherit;
}

h6 tt, h6 code {
  font-size: inherit;
}

h1 {
  font-size: 32px;
  color: black;
}

h2 {
  font-size: 28px;
  border-bottom: 1px solid #cccccc;
  color: black;
}

h3 {
  font-size: 20px;
}

h4 {
  font-size: 16px;
}

h5 {
  font-size: 14px;
}

h6 {
  color: #777777;
  font-size: 14px;
}

p, blockquote, ul, ol, dl, table, pre {
  margin: 15px 0;
}

hr {
  background: transparent url("http://tinyurl.com/bq5kskr") repeat-x 0 0;
  border: 0 none;
  color: #cccccc;
  height: 4px;
  padding: 0;
}

body > h2:first-child {
  margin-top: 0;
  padding-top: 0;
}

body > h1:first-child {
  margin-top: 0;
  padding-top: 0;
}

body > h1:first-child + h2 {
  margin-top: 0;
  padding-top: 0;
}

body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child {
  margin-top: 0;
  padding-top: 0;
}

a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
  margin-top: 0;
  padding-top: 0;
}

h1 p, h2 p, h3 p, h4 p, h5 p, h6 p {
  margin-top: 0;
}

li p.first {
  display: inline-block;
}

ul, ol {
  padding-left: 30px;
}

ul :first-child, ol :first-child {
  margin-top: 0;
}

ul :last-child, ol :last-child {
  margin-bottom: 0;
}

dl {
  padding: 0;
}

dl dt {
  font-size: 14px;
  font-weight: bold;
  font-style: italic;
  padding: 0;
  margin: 15px 0 5px;
}

dl dt:first-child {
  padding: 0;
}

dl dt > :first-child {
  margin-top: 0;
}

dl dt > :last-child {
  margin-bottom: 0;
}

dl dd {
  margin: 0 0 15px;
  padding: 0 15px;
}

dl dd > :first-child {
  margin-top: 0;
}

dl dd > :last-child {
  margin-bottom: 0;
}

blockquote {
  border-left: 4px solid #dddddd;
  padding: 0 15px;
  color: #777777;
}

blockquote > :first-child {
  margin-top: 0;
}

blockquote > :last-child {
  margin-bottom: 0;
}

table {
  padding: 0;
  border-spacing: 0;
  border-collapse: collapse;
}

table tr {
  border-top: 1px solid #cccccc;
  background-color: white;
  margin: 0;
  padding: 0;
}

table tr:nth-child(2n) {
  background-color: #f7f7f7;
}

table th {
  border: 1px solid #cccccc;
  font-weight: bold;
  text-align: left;
  margin: 0;
  padding: 5px 10px;
  background-color: #f0f0f0;
}

table td {
  border: 1px solid #cccccc;
  text-align: left;
  margin: 0;
  padding: 5px 10px;
}

table tr th :first-child, table tr td :first-child {
  margin-top: 0;
}

table tr th :last-child, table tr td :last-child {
  margin-bottom: 0;
}

img {
  max-width: 100%;
}

span.frame {
  display: block;
  overflow: hidden;
}

span.frame > span {
  border: 1px solid #dddddd;
  display: block;
  float: left;
  overflow: hidden;
  margin: 13px 0 0;
  padding: 7px;
  width: auto;
}

span.frame span img {
  display: block;
  float: left;
}

span.frame span span {
  clear: both;
  color: #333333;
  display: block;
  padding: 5px 0 0;
}

span.align-center {
  display: block;
  overflow: hidden;
  clear: both;
}

span.align-center > span {
  display: block;
  overflow: hidden;
  margin: 13px auto 0;
  text-align: center;
}

span.align-center span img {
  margin: 0 auto;
  text-align: center;
}

span.align-right {
  display: block;
  overflow: hidden;
  clear: both;
}

span.align-right > span {
  display: block;
  overflow: hidden;
  margin: 13px 0 0;
  text-align: right;
}

span.align-right span img {
  margin: 0;
  text-align: right;
}

span.float-left {
  display: block;
  margin-right: 13px;
  overflow: hidden;
  float: left;
}

span.float-left span {
  margin: 13px 0 0;
}

span.float-right {
  display: block;
  margin-left: 13px;
  overflow: hidden;
  float: right;
}

span.float-right > span {
  display: block;
  overflow: hidden;
  margin: 13px auto 0;
  text-align: right;
}

code, tt {
  margin: 0 2px;
  padding: 0 5px;
  white-space: nowrap;
  border: 1px solid #eaeaea;
  background-color: #f8f8f8;
  border-radius: 3px;
}

pre code {
  margin: 0;
  padding: 0;
  white-space: pre;
  border: none;
  background: transparent;
}

.highlight pre {
  background-color: #f8f8f8;
  border: 1px solid #cccccc;
  font-size: 13px;
  line-height: 19px;
  overflow: auto;
  padding: 6px 10px;
  border-radius: 3px;
}

pre {
  background-color: #f8f8f8;
  border: 1px solid #cccccc;
  font-size: 13px;
  line-height: 19px;
  overflow: auto;
  padding: 6px 10px;
  border-radius: 3px;
}

pre code, pre tt {
  background-color: transparent;
  border: none;
}`

	MD_TEMPLATE = `# Changelog

{{ range $release := .Changelog }}## Release {{ .Version }} ({{ .Date }})

{{ if .Summary }}{{ .Summary }}{{ end }}

{{ if .Added }}### Added

{{ range $entry := .Added }}- {{ . }}
{{ end }}{{ end }}{{ if .Changed }}
### Changed

{{ range $entry := .Changed }}- {{ . }}
{{ end }}{{ end }}{{ if .Deprecated }}
### Deprecated

{{ range $entry := .Deprecated }}- {{ . }}
{{ end }}{{ end }}{{ if .Removed }}
### Removed

{{ range $entry := .Removed }}- {{ . }}
{{ end }}{{ end }}{{ if .Fixed }}
### Fixed

{{ range $entry := .Fixed }}- {{ . }}
{{ end }}{{ end }}{{ if .Security }}
### Security

{{ range $entry := .Security }}- {{ . }}
{{ end }}{{ end }}{{ if .Rejected }}
### Rejected

{{ range $entry := .Rejected }}- {{ . }}
{{ end }}{{ end }}{{ if .Notes }}
### Notes

{{ range $entry := .Notes }}- {{ . }}
{{ end }}{{ end }}
{{ end }}`

	MD_TEMPLATE_RELEASE = `# Release {{ .Version }} ({{ .Date }})

{{ if .Summary }}{{ .Summary }}{{ end }}

{{ if .Added }}## Added

{{ range $entry := .Added }}- {{ . }}
{{ end }}{{ end }}{{ if .Changed }}
## Changed

{{ range $entry := .Changed }}- {{ . }}
{{ end }}{{ end }}{{ if .Deprecated }}
## Deprecated

{{ range $entry := .Deprecated }}- {{ . }}
{{ end }}{{ end }}{{ if .Removed }}
## Removed

{{ range $entry := .Removed }}- {{ . }}
{{ end }}{{ end }}{{ if .Fixed }}
## Fixed

{{ range $entry := .Fixed }}- {{ . }}
{{ end }}{{ end }}{{ if .Security }}
## Security

{{ range $entry := .Security }}- {{ . }}
{{ end }}{{ end }}{{ if .Rejected }}
## Rejected

{{ range $entry := .Rejected }}- {{ . }}
{{ end }}{{ end }}{{ if .Notes }}
## Notes

{{ range $entry := .Notes }}- {{ . }}
{{ end }}{{ end }}`
)

type TemplateDataChangelog struct {
	Changelog   Changelog
	Stylesheets []string
}

func toHtml(changelog Changelog, args []string) {
	stylesheets := make([]string, 0)
	for _, file := range args {
		var stylesheet []byte
		var err error
		if file == "style" {
			stylesheet = []byte(STYLESHEET)
		} else {
			stylesheet, err = ioutil.ReadFile(file)
			if err != nil {
				Errorf(ERROR_TRANSFORM, "Error loading stylesheet %s: %s", file, err.Error())
			}
		}
		stylesheets = append(stylesheets, string(stylesheet))
	}
	data := TemplateDataChangelog{
		Stylesheets: stylesheets,
		Changelog:   changelog,
	}
	t := template.Must(template.New("changelog").Parse(HTML_TEMPLATE))
	err := t.Execute(os.Stdout, data)
	if err != nil {
		Errorf(ERROR_TRANSFORM, "Error processing template: %s", err)
	}
}

func toMarkdown(changelog Changelog) {
	data := TemplateDataChangelog{
		Stylesheets: nil,
		Changelog:   changelog,
	}
	t := template.Must(template.New("changelog").Parse(MD_TEMPLATE))
	err := t.Execute(os.Stdout, data)
	if err != nil {
		Errorf(ERROR_TRANSFORM, "Error processing template: %s", err)
	}
}

func releaseToMarkdown(release Release) {
	t := template.Must(template.New("release").Parse(MD_TEMPLATE_RELEASE))
	err := t.Execute(os.Stdout, release)
	if err != nil {
		Errorf(ERROR_TRANSFORM, "Error processing template: %s", err)
	}
}

func transform(changelog Changelog, args []string) {
	checkChangelog(changelog)
	if len(args) < 1 {
		Error(ERROR_TRANSFORM, "You must pass format to transform to")
	}
	format := args[0]
	if format == "html" {
		toHtml(changelog, args[1:])
	} else if format == "markdown" {
		toMarkdown(changelog)
	} else {
		Errorf(ERROR_TRANSFORM, "Unknown format %s", args[0])
	}
}
<commit_msg>Fixed release text<commit_after>package main

import (
	"io/ioutil"
	"os"
	"text/template"
)

const (
	HTML_TEMPLATE = `<!DOCTYPE html>

<title>Changelog</title>
<meta charset="utf-8">
{{ range $stylesheet := .Stylesheets }}

{{ end }}

Changelog

{{ range $release := .Changelog }}

Release {{ .Version }} ({{ .Date }})

{{ .Summary }}

{{ if .Added }}

Added

{{ range $entry := .Added }}

{{ . }}

{{ end }}

{{ end }}
{{ if .Changed }}

Changed

{{ range $entry := .Changed }}

{{ . }}

{{ end }}

{{ end }}
{{ if .Deprecated }}

Deprecated

{{ range $entry := .Deprecated }}

{{ . }}

{{ end }}

{{ end }}
{{ if .Removed }}

Removed

{{ range $entry := .Removed }}

{{ . }}

{{ end }}

{{ end }}
{{ if .Fixed }}

Fixed

{{ range $entry := .Fixed }}

{{ . }}

{{ end }}

{{ end }}
{{ if .Security }}

Security

{{ range $entry := .Security }}

{{ . }}

{{ end }}

{{ end }}
{{ if .Rejected }}

Rejected

{{ range $entry := .Rejected }}

{{ . }}

{{ end }}

{{ end }}
{{ if .Notes }}

Notes

{{ range $entry := .Notes }}

{{ . }}

{{ end }}

{{ end }}
{{ end }}

`
	STYLESHEET = `
body {
  font-family: Helvetica, arial, sans-serif;
  font-size: 16px;
  line-height: 1.4;
  padding-top: 10px;
  padding-bottom: 10px;
  background-color: white;
  padding: 30px;
  color: #333;
}

body > *:first-child {
  margin-top: 0 !important;
}

body > *:last-child {
  margin-bottom: 0 !important;
}

a {
  color: #4183C4;
  text-decoration: none;
}

a.absent {
  color: #cc0000;
}

a.anchor {
  display: block;
  padding-left: 30px;
  margin-left: -30px;
  cursor: pointer;
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
}

h1, h2, h3, h4, h5, h6 {
  margin: 20px 0 10px;
  padding: 0;
  font-weight: bold;
  -webkit-font-smoothing: antialiased;
  cursor: text;
  position: relative;
}

h2:first-child, h1:first-child, h1:first-child + h2, h3:first-child, h4:first-child, h5:first-child, h6:first-child {
  margin-top: 0;
  padding-top: 0;
}

h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor {
  text-decoration: none;
}

h1 tt, h1 code {
  font-size: inherit;
}

h2 tt, h2 code {
  font-size: inherit;
}

h3 tt, h3 code {
  font-size: inherit;
}

h4 tt, h4 code {
  font-size: inherit;
}

h5 tt, h5 code {
  font-size: inherit;
}

h6 tt, h6 code {
  font-size: inherit;
}

h1 {
  font-size: 32px;
  color: black;
}

h2 {
  font-size: 28px;
  border-bottom: 1px solid #cccccc;
  color: black;
}

h3 {
  font-size: 20px;
}

h4 {
  font-size: 16px;
}

h5 {
  font-size: 14px;
}

h6 {
  color: #777777;
  font-size: 14px;
}

p, blockquote, ul, ol, dl, table, pre {
  margin: 15px 0;
}

hr {
  background: transparent url("http://tinyurl.com/bq5kskr") repeat-x 0 0;
  border: 0 none;
  color: #cccccc;
  height: 4px;
  padding: 0;
}

body > h2:first-child {
  margin-top: 0;
  padding-top: 0;
}

body > h1:first-child {
  margin-top: 0;
  padding-top: 0;
}

body > h1:first-child + h2 {
  margin-top: 0;
  padding-top: 0;
}

body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child {
  margin-top: 0;
  padding-top: 0;
}

a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
  margin-top: 0;
  padding-top: 0;
}

h1 p, h2 p, h3 p, h4 p, h5 p, h6 p {
  margin-top: 0;
}

li p.first {
  display: inline-block;
}

ul, ol {
  padding-left: 30px;
}

ul :first-child, ol :first-child {
  margin-top: 0;
}

ul :last-child, ol :last-child {
  margin-bottom: 0;
}

dl {
  padding: 0;
}

dl dt {
  font-size: 14px;
  font-weight: bold;
  font-style: italic;
  padding: 0;
  margin: 15px 0 5px;
}

dl dt:first-child {
  padding: 0;
}

dl dt > :first-child {
  margin-top: 0;
}

dl dt > :last-child {
  margin-bottom: 0;
}

dl dd {
  margin: 0 0 15px;
  padding: 0 15px;
}

dl dd > :first-child {
  margin-top: 0;
}

dl dd > :last-child {
  margin-bottom: 0;
}

blockquote {
  border-left: 4px solid #dddddd;
  padding: 0 15px;
  color: #777777;
}

blockquote > :first-child {
  margin-top: 0;
}

blockquote > :last-child {
  margin-bottom: 0;
}

table {
  padding: 0;
  border-spacing: 0;
  border-collapse: collapse;
}

table tr {
  border-top: 1px solid #cccccc;
  background-color: white;
  margin: 0;
  padding: 0;
}

table tr:nth-child(2n) {
  background-color: #f7f7f7;
}

table th {
  border: 1px solid #cccccc;
  font-weight: bold;
  text-align: left;
  margin: 0;
  padding: 5px 10px;
  background-color: #f0f0f0;
}

table td {
  border: 1px solid #cccccc;
  text-align: left;
  margin: 0;
  padding: 5px 10px;
}

table tr th :first-child, table tr td :first-child {
  margin-top: 0;
}

table tr th :last-child, table tr td :last-child {
  margin-bottom: 0;
}

img {
  max-width: 100%;
}

span.frame {
  display: block;
  overflow: hidden;
}

span.frame > span {
  border: 1px solid #dddddd;
  display: block;
  float: left;
  overflow: hidden;
  margin: 13px 0 0;
  padding: 7px;
  width: auto;
}

span.frame span img {
  display: block;
  float: left;
}

span.frame span span {
  clear: both;
  color: #333333;
  display: block;
  padding: 5px 0 0;
}

span.align-center {
  display: block;
  overflow: hidden;
  clear: both;
}

span.align-center > span {
  display: block;
  overflow: hidden;
  margin: 13px auto 0;
  text-align: center;
}

span.align-center span img {
  margin: 0 auto;
  text-align: center;
}

span.align-right {
  display: block;
  overflow: hidden;
  clear: both;
}

span.align-right > span {
  display: block;
  overflow: hidden;
  margin: 13px 0 0;
  text-align: right;
}

span.align-right span img {
  margin: 0;
  text-align: right;
}

span.float-left {
  display: block;
  margin-right: 13px;
  overflow: hidden;
  float: left;
}

span.float-left span {
  margin: 13px 0 0;
}

span.float-right {
  display: block;
  margin-left: 13px;
  overflow: hidden;
  float: right;
}

span.float-right > span {
  display: block;
  overflow: hidden;
  margin: 13px auto 0;
  text-align: right;
}

code, tt {
  margin: 0 2px;
  padding: 0 5px;
  white-space: nowrap;
  border: 1px solid #eaeaea;
  background-color: #f8f8f8;
  border-radius: 3px;
}

pre code {
  margin: 0;
  padding: 0;
  white-space: pre;
  border: none;
  background: transparent;
}

.highlight pre {
  background-color: #f8f8f8;
  border: 1px solid #cccccc;
  font-size: 13px;
  line-height: 19px;
  overflow: auto;
  padding: 6px 10px;
  border-radius: 3px;
}

pre {
  background-color: #f8f8f8;
  border: 1px solid #cccccc;
  font-size: 13px;
  line-height: 19px;
  overflow: auto;
  padding: 6px 10px;
  border-radius: 3px;
}

pre code, pre tt {
  background-color: transparent;
  border: none;
}`

	MD_TEMPLATE = `# Changelog

{{ range $release := .Changelog }}## Release {{ .Version }} ({{ .Date }})

{{ if .Summary }}{{ .Summary }}{{ end }}

{{ if .Added }}### Added

{{ range $entry := .Added }}- {{ . }}
{{ end }}{{ end }}{{ if .Changed }}
### Changed

{{ range $entry := .Changed }}- {{ . }}
{{ end }}{{ end }}{{ if .Deprecated }}
### Deprecated

{{ range $entry := .Deprecated }}- {{ . }}
{{ end }}{{ end }}{{ if .Removed }}
### Removed

{{ range $entry := .Removed }}- {{ . }}
{{ end }}{{ end }}{{ if .Fixed }}
### Fixed

{{ range $entry := .Fixed }}- {{ . }}
{{ end }}{{ end }}{{ if .Security }}
### Security

{{ range $entry := .Security }}- {{ . }}
{{ end }}{{ end }}{{ if .Rejected }}
### Rejected

{{ range $entry := .Rejected }}- {{ . }}
{{ end }}{{ end }}{{ if .Notes }}
### Notes

{{ range $entry := .Notes }}- {{ . }}
{{ end }}{{ end }}
{{ end }}`

	MD_TEMPLATE_RELEASE = `{{ if .Summary }}{{ .Summary }}{{ end }}

{{ if .Added }}# Added

{{ range $entry := .Added }}- {{ . }}
{{ end }}{{ end }}{{ if .Changed }}
# Changed

{{ range $entry := .Changed }}- {{ . }}
{{ end }}{{ end }}{{ if .Deprecated }}
# Deprecated

{{ range $entry := .Deprecated }}- {{ . }}
{{ end }}{{ end }}{{ if .Removed }}
# Removed

{{ range $entry := .Removed }}- {{ . }}
{{ end }}{{ end }}{{ if .Fixed }}
# Fixed

{{ range $entry := .Fixed }}- {{ . }}
{{ end }}{{ end }}{{ if .Security }}
# Security

{{ range $entry := .Security }}- {{ . }}
{{ end }}{{ end }}{{ if .Rejected }}
# Rejected

{{ range $entry := .Rejected }}- {{ . }}
{{ end }}{{ end }}{{ if .Notes }}
# Notes

{{ range $entry := .Notes }}- {{ . }}
{{ end }}{{ end }}`
)

type TemplateDataChangelog struct {
	Changelog   Changelog
	Stylesheets []string
}

func toHtml(changelog Changelog, args []string) {
	stylesheets := make([]string, 0)
	for _, file := range args {
		var stylesheet []byte
		var err error
		if file == "style" {
			stylesheet = []byte(STYLESHEET)
		} else {
			stylesheet, err = ioutil.ReadFile(file)
			if err != nil {
				Errorf(ERROR_TRANSFORM, "Error loading stylesheet %s: %s", file, err.Error())
			}
		}
		stylesheets = append(stylesheets, string(stylesheet))
	}
	data := TemplateDataChangelog{
		Stylesheets: stylesheets,
		Changelog:   changelog,
	}
	t := template.Must(template.New("changelog").Parse(HTML_TEMPLATE))
	err := t.Execute(os.Stdout, data)
	if err != nil {
		Errorf(ERROR_TRANSFORM, "Error processing template: %s", err)
	}
}

func toMarkdown(changelog Changelog) {
	data := TemplateDataChangelog{
		Stylesheets: nil,
		Changelog:   changelog,
	}
	t := template.Must(template.New("changelog").Parse(MD_TEMPLATE))
	err := t.Execute(os.Stdout, data)
	if err != nil {
		Errorf(ERROR_TRANSFORM, "Error processing template: %s", err)
	}
}

func releaseToMarkdown(release Release) {
	t := template.Must(template.New("release").Parse(MD_TEMPLATE_RELEASE))
	err := t.Execute(os.Stdout, release)
	if err != nil {
		Errorf(ERROR_TRANSFORM, "Error processing template: %s", err)
	}
}

func transform(changelog Changelog, args []string) {
	checkChangelog(changelog)
	if len(args) < 1 {
		Error(ERROR_TRANSFORM, "You must pass format to transform to")
	}
	format := args[0]
	if format == "html" {
		toHtml(changelog, args[1:])
	} else if format == "markdown" {
		toMarkdown(changelog)
	} else {
		Errorf(ERROR_TRANSFORM, "Unknown format %s", args[0])
	}
}
<|endoftext|>


<commit_before>package stripe

import (
	"testing"

	. "github.com/smartystreets/goconvey/convey"

	stripe "github.com/stripe/stripe-go"
	stripeSub "github.com/stripe/stripe-go/sub"
)

func TestSubscribe(t *testing.T) {
	Convey("Given nonexistent customer, plan", t, func() {
		token, accId, email := generateFakeUserInfo()
		err := Subscribe(token, accId, email, "hobbyist_month")

		So(err, ShouldBeNil)

		customerModel, err := FindCustomerByOldId(accId)
		id := customerModel.ProviderCustomerId

		So(err, ShouldBeNil)
		So(customerModel, ShouldNotBeNil)

		Convey("Then it should save customer", func() {
			So(checkCustomerIsSaved(accId), ShouldBeTrue)
		})

		Convey("Then it should create an customer in Stripe", func() {
			So(checkCustomerExistsInStripe(id), ShouldBeTrue)
		})

		Convey("Then it should subscribe user to plan", func() {
			customer, err := GetCustomerFromStripe(id)
			So(err, ShouldBeNil)

			So(customer.Subs.Count, ShouldEqual, 1)
		})

		Convey("Then customer can't subscribe to same plan again", func() {
			err = Subscribe(token, accId, email, "hobbyist_month")
			So(err, ShouldEqual, ErrCustomerAlreadySubscribedToPlan)
		})
	})

	Convey("Given existent customer, plan", t, func() {
		token, accId, email := generateFakeUserInfo()

		_, err := CreateCustomer(token, accId, email)
		So(err, ShouldBeNil)

		err = Subscribe(token, accId, email, "hobbyist_month")
		So(err, ShouldBeNil)

		customerModel, err := FindCustomerByOldId(accId)
		id := customerModel.ProviderCustomerId

		Convey("Then it should subscribe user to plan", func() {
			customer, err := GetCustomerFromStripe(id)
			So(err, ShouldBeNil)

			So(customer.Subs.Count, ShouldEqual, 1)
		})
	})

	Convey("Given customer already subscribed to a plan", t, func() {
		token, accId, email := generateFakeUserInfo()
		planId := "hobbyist_month"

		_, err := CreateCustomer(token, accId, email)
		So(err, ShouldBeNil)

		err = Subscribe(token, accId, email, planId)
		So(err, ShouldBeNil)

		customer, err := FindCustomerByOldId(accId)
		So(err, ShouldBeNil)

		customerId := customer.ProviderCustomerId

		subs, err := FindCustomerActiveSubscriptions(customer)
		So(err, ShouldBeNil)

		So(len(subs), ShouldEqual, 1)

		currentSub := subs[0]
		subId := currentSub.ProviderSubscriptionId

		Convey("Then customer can't subscribe to same plan again", func() {
			err = Subscribe(token, accId, email, planId)
			So(err, ShouldEqual, ErrCustomerAlreadySubscribedToPlan)
		})

		Convey("When customer upgrades to higher plan", func() {
			newPlanId := "developer_month"

			err = Subscribe(token, accId, email, newPlanId)
			So(err, ShouldBeNil)

			Convey("Then subscription is updated on stripe", func() {
				subParams := &stripe.SubParams{Customer: customerId}
				sub, err := stripeSub.Get(subId, subParams)

				So(err, ShouldBeNil)

				So(sub.Plan.Id, ShouldEqual, newPlanId)
			})

			Convey("Then subscription is saved", func() {
				subs, err := FindCustomerActiveSubscriptions(customer)
				So(err, ShouldBeNil)

				So(len(subs), ShouldEqual, 1)

				currentSub := subs[0]
				newPlan, err := FindPlanByTitle(newPlanId)

				So(err, ShouldBeNil)
				So(currentSub.PlanId, ShouldEqual, newPlan.Id)
			})
		})
	})

	Convey("Given customer already subscribed to a plan", t, func() {
		token, accId, email := generateFakeUserInfo()
		planId := "professional_month"

		_, err := CreateCustomer(token, accId, email)
		So(err, ShouldBeNil)

		err = Subscribe(token, accId, email, planId)
		So(err, ShouldBeNil)

		customer, err := FindCustomerByOldId(accId)
		So(err, ShouldBeNil)

		customerId := customer.ProviderCustomerId

		subs, err := FindCustomerActiveSubscriptions(customer)
		So(err, ShouldBeNil)

		So(len(subs), ShouldEqual, 1)

		currentSub := subs[0]
		subId := currentSub.ProviderSubscriptionId

		Convey("When customer downgrades to lower plan", func() {
			newPlanId := "hobbyist_month"

			err = Subscribe(token, accId, email, newPlanId)
			So(err, ShouldBeNil)

			Convey("Then subscription is updated on stripe", func() {
				subParams := &stripe.SubParams{Customer: customerId}
				sub, err := stripeSub.Get(subId, subParams)

				So(err, ShouldBeNil)

				So(sub.Plan.Id, ShouldEqual, newPlanId)
			})

			Convey("Then subscription is saved", func() {
				subs, err := FindCustomerActiveSubscriptions(customer)
				So(err, ShouldBeNil)

				So(len(subs), ShouldEqual, 1)

				currentSub := subs[0]
				newPlan, err := FindPlanByTitle(newPlanId)

				So(err, ShouldBeNil)
				So(currentSub.PlanId, ShouldEqual, newPlan.Id)
			})
		})
	})
}
<commit_msg>payment: add test for when plan doesn't exist<commit_after>package stripe

import (
	"testing"

	. "github.com/smartystreets/goconvey/convey"

	stripe "github.com/stripe/stripe-go"
	stripeSub "github.com/stripe/stripe-go/sub"
)

func TestSubscribe(t *testing.T) {
	Convey("Given nonexistent plan", t, func() {
		token, accId, email := generateFakeUserInfo()
		err := Subscribe(token, accId, email, "random_plans")

		So(err, ShouldEqual, ErrPlanNotFound)
	})

	Convey("Given nonexistent customer, plan", t, func() {
		token, accId, email := generateFakeUserInfo()
		err := Subscribe(token, accId, email, "hobbyist_month")

		So(err, ShouldBeNil)

		customerModel, err := FindCustomerByOldId(accId)
		id := customerModel.ProviderCustomerId

		So(err, ShouldBeNil)
		So(customerModel, ShouldNotBeNil)

		Convey("Then it should save customer", func() {
			So(checkCustomerIsSaved(accId), ShouldBeTrue)
		})

		Convey("Then it should create an customer in Stripe", func() {
			So(checkCustomerExistsInStripe(id), ShouldBeTrue)
		})

		Convey("Then it should subscribe user to plan", func() {
			customer, err := GetCustomerFromStripe(id)
			So(err, ShouldBeNil)

			So(customer.Subs.Count, ShouldEqual, 1)
		})

		Convey("Then customer can't subscribe to same plan again", func() {
			err = Subscribe(token, accId, email, "hobbyist_month")
			So(err, ShouldEqual, ErrCustomerAlreadySubscribedToPlan)
		})
	})

	Convey("Given existent customer, plan", t, func() {
		token, accId, email := generateFakeUserInfo()

		_, err := CreateCustomer(token, accId, email)
		So(err, ShouldBeNil)

		err = Subscribe(token, accId, email, "hobbyist_month")
		So(err, ShouldBeNil)

		customerModel, err := FindCustomerByOldId(accId)
		id := customerModel.ProviderCustomerId

		Convey("Then it should subscribe user to plan", func() {
			customer, err := GetCustomerFromStripe(id)
			So(err, ShouldBeNil)

			So(customer.Subs.Count, ShouldEqual, 1)
		})
	})

	Convey("Given customer already subscribed to a plan", t, func() {
		token, accId, email := generateFakeUserInfo()
		planId := "hobbyist_month"

		_, err := CreateCustomer(token, accId, email)
		So(err, ShouldBeNil)

		err = Subscribe(token, accId, email, planId)
		So(err, ShouldBeNil)

		customer, err := FindCustomerByOldId(accId)
		So(err, ShouldBeNil)

		customerId := customer.ProviderCustomerId

		subs, err := FindCustomerActiveSubscriptions(customer)
		So(err, ShouldBeNil)

		So(len(subs), ShouldEqual, 1)

		currentSub := subs[0]
		subId := currentSub.ProviderSubscriptionId

		Convey("Then customer can't subscribe to same plan again", func() {
			err = Subscribe(token, accId, email, planId)
			So(err, ShouldEqual, ErrCustomerAlreadySubscribedToPlan)
		})

		Convey("When customer upgrades to higher plan", func() {
			newPlanId := "developer_month"

			err = Subscribe(token, accId, email, newPlanId)
			So(err, ShouldBeNil)

			Convey("Then subscription is updated on stripe", func() {
				subParams := &stripe.SubParams{Customer: customerId}
				sub, err := stripeSub.Get(subId, subParams)

				So(err, ShouldBeNil)

				So(sub.Plan.Id, ShouldEqual, newPlanId)
			})

			Convey("Then subscription is saved", func() {
				subs, err := FindCustomerActiveSubscriptions(customer)
				So(err, ShouldBeNil)

				So(len(subs), ShouldEqual, 1)

				currentSub := subs[0]
				newPlan, err := FindPlanByTitle(newPlanId)

				So(err, ShouldBeNil)
				So(currentSub.PlanId, ShouldEqual, newPlan.Id)
			})
		})
	})

	Convey("Given customer already subscribed to a plan", t, func() {
		token, accId, email := generateFakeUserInfo()
		planId := "professional_month"

		_, err := CreateCustomer(token, accId, email)
		So(err, ShouldBeNil)

		err = Subscribe(token, accId, email, planId)
		So(err, ShouldBeNil)

		customer, err := FindCustomerByOldId(accId)
		So(err, ShouldBeNil)

		customerId := customer.ProviderCustomerId

		subs, err := FindCustomerActiveSubscriptions(customer)
		So(err, ShouldBeNil)

		So(len(subs), ShouldEqual, 1)

		currentSub := subs[0]
		subId := currentSub.ProviderSubscriptionId

		Convey("When customer downgrades to lower plan", func() {
			newPlanId := "hobbyist_month"

			err = Subscribe(token, accId, email, newPlanId)
			So(err, ShouldBeNil)

			Convey("Then subscription is updated on stripe", func() {
				subParams := &stripe.SubParams{Customer: customerId}
				sub, err := stripeSub.Get(subId, subParams)

				So(err, ShouldBeNil)

				So(sub.Plan.Id, ShouldEqual, newPlanId)
			})

			Convey("Then subscription is saved", func() {
				subs, err := FindCustomerActiveSubscriptions(customer)
				So(err, ShouldBeNil)

				So(len(subs), ShouldEqual, 1)

				currentSub := subs[0]
				newPlan, err := FindPlanByTitle(newPlanId)

				So(err, ShouldBeNil)
				So(currentSub.PlanId, ShouldEqual, newPlan.Id)
			})
		})
	})
}
<|endoftext|>


<commit_before>// archive is package that helps create archives in a format that
// Atlas expects with its various upload endpoints.
package archive

import (
	"archive/tar"
	"bufio"
	"compress/gzip"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
)

// Archive is the resulting archive. The archive data is generally streamed
// so the io.ReadCloser can be used to backpressure the archive progress
// and avoid memory pressure.
type Archive struct {
	io.ReadCloser

	Size     int64
	Metadata map[string]string
}

// ArchiveOpts are the options for defining how the archive will be built.
type ArchiveOpts struct {
	// Exclude and Include are filters of files to include/exclude in
	// the archive when creating it from a directory. These filters should
	// be relative to the packaging directory and should be basic glob
	// patterns.
	Exclude []string
	Include []string

	// Extra is a mapping of extra files to include within the archive. The
	// key should be the path within the archive and the value should be
	// an absolute path to the file to put into the archive. These extra
	// files will override any other files in the archive.
	Extra map[string]string

	// VCS, if true, will detect and use a VCS system to determine what
	// files to include the archive.
	VCS bool
}

// IsSet says whether any options were set.
func (o *ArchiveOpts) IsSet() bool {
	return len(o.Exclude) > 0 || len(o.Include) > 0 || o.VCS
}

// Constants related to setting special values for Extra in ArchiveOpts.
const (
	// ExtraEntryDir just creates the Extra key as a directory entry.
	ExtraEntryDir = ""
)

// CreateArchive takes the given path and ArchiveOpts and archives it.
//
// The archive will be fully completed and put into a temporary file.
// This must be done to retrieve the content length of the archive which
// is needed for almost all operations involving archives with Atlas. Because
// of this, sufficient disk space will be required to buffer the archive.
func CreateArchive(path string, opts *ArchiveOpts) (*Archive, error) {
	log.Printf("[INFO] creating archive from %s", path)

	// Dereference any symlinks and determine the real path and info
	fi, err := os.Lstat(path)
	if err != nil {
		return nil, err
	}
	if fi.Mode()&os.ModeSymlink != 0 {
		path, fi, err = readLinkFull(path, fi)
		if err != nil {
			return nil, err
		}
	}

	// Windows
	path = filepath.ToSlash(path)

	// Direct file paths cannot have archive options
	if !fi.IsDir() && opts.IsSet() {
		return nil, fmt.Errorf(
			"options such as exclude, include, and VCS can't be set when " +
				"the path is a file.")
	}

	if fi.IsDir() {
		return archiveDir(path, opts)
	} else {
		return archiveFile(path)
	}
}

func archiveFile(path string) (*Archive, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}

	if _, err := gzip.NewReader(f); err == nil {
		// Reset the read offset for future reading
		if _, err := f.Seek(0, 0); err != nil {
			f.Close()
			return nil, err
		}

		// Get the file info for the size
		fi, err := f.Stat()
		if err != nil {
			f.Close()
			return nil, err
		}

		// This is a gzip file, let it through.
		return &Archive{ReadCloser: f, Size: fi.Size()}, nil
	}

	// Close the file, no use for it anymore
	f.Close()

	// We have a single file that is not gzipped. Compress it.
	path, err = filepath.Abs(path)
	if err != nil {
		return nil, err
	}

	// Act like we're compressing a directory, but only include this one
	// file.
	return archiveDir(filepath.Dir(path), &ArchiveOpts{
		Include: []string{filepath.Base(path)},
	})
}

func archiveDir(root string, opts *ArchiveOpts) (*Archive, error) {

	var vcsInclude []string
	var metadata map[string]string
	if opts.VCS {
		var err error

		if err = vcsPreflight(root); err != nil {
			return nil, err
		}

		vcsInclude, err = vcsFiles(root)
		if err != nil {
			return nil, err
		}

		metadata, err = vcsMetadata(root)
		if err != nil {
			return nil, err
		}
	}

	// Make sure the root path is absolute
	root, err := filepath.Abs(root)
	if err != nil {
		return nil, err
	}

	// Create the temporary file that we'll send the archive data to.
	archiveF, err := ioutil.TempFile("", "atlas-archive")
	if err != nil {
		return nil, err
	}

	// Create the wrapper for the result which will automatically
	// remove the temporary file on close.
	archiveWrapper := &readCloseRemover{F: archiveF}

	// Buffer the writer so that we can push as much data to disk at
	// a time as possible. 4M should be good.
	bufW := bufio.NewWriterSize(archiveF, 4096*1024)

	// Gzip compress all the output data
	gzipW := gzip.NewWriter(bufW)

	// Tar the file contents
	tarW := tar.NewWriter(gzipW)

	// First, walk the path and do the normal files
	werr := filepath.Walk(root, copyDirWalkFn(
		tarW, root, "", opts, vcsInclude))
	if werr == nil {
		// If that succeeded, handle the extra files
		werr = copyExtras(tarW, opts.Extra)
	}

	// Attempt to close all the things. If we get an error on the way
	// and we haven't had an error yet, then record that as the critical
	// error. But we still try to close everything.

	// Close the tar writer
	if err := tarW.Close(); err != nil && werr == nil {
		werr = err
	}

	// Close the gzip writer
	if err := gzipW.Close(); err != nil && werr == nil {
		werr = err
	}

	// Flush the buffer
	if err := bufW.Flush(); err != nil && werr == nil {
		werr = err
	}

	// If we had an error, then close the file (removing it) and
	// return the error.
	if werr != nil {
		archiveWrapper.Close()
		return nil, werr
	}

	// Seek to the beginning
	if _, err := archiveWrapper.F.Seek(0, 0); err != nil {
		archiveWrapper.Close()
		return nil, err
	}

	// Get the file information so we can get the size
	fi, err := archiveWrapper.F.Stat()
	if err != nil {
		archiveWrapper.Close()
		return nil, err
	}

	return &Archive{
		ReadCloser: archiveWrapper,
		Size:       fi.Size(),
		Metadata:   metadata,
	}, nil
}

func copyDirWalkFn(
	tarW *tar.Writer, root string, prefix string,
	opts *ArchiveOpts, vcsInclude []string) filepath.WalkFunc {

	errFunc := func(err error) filepath.WalkFunc {
		return func(string, os.FileInfo, error) error {
			return err
		}
	}

	// Windows
	root = filepath.ToSlash(root)

	var includeMap map[string]struct{}

	// If we have an include/exclude pattern set, then setup the lookup
	// table to determine what we want to include.
	if opts != nil && len(opts.Include) > 0 {
		includeMap = make(map[string]struct{})
		for _, pattern := range opts.Include {
			matches, err := filepath.Glob(filepath.Join(root, pattern))
			if err != nil {
				return errFunc(fmt.Errorf(
					"error checking include glob '%s': %s",
					pattern, err))
			}

			for _, path := range matches {
				// Windows
				path = filepath.ToSlash(path)
				subpath, err := filepath.Rel(root, path)
				subpath = filepath.ToSlash(subpath)

				if err != nil {
					return errFunc(err)
				}

				for {
					includeMap[subpath] = struct{}{}
					subpath = filepath.Dir(subpath)
					if subpath == "." {
						break
					}
				}
			}
		}
	}

	return func(path string, info os.FileInfo, err error) error {
		path = filepath.ToSlash(path)

		if err != nil {
			return err
		}

		// Get the relative path from the path since it contains the root
		// plus the path.
		subpath, err := filepath.Rel(root, path)
		if err != nil {
			return err
		}
		if subpath == "." {
			return nil
		}
		if prefix != "" {
			subpath = filepath.Join(prefix, subpath)
		}
		// Windows
		subpath = filepath.ToSlash(subpath)

		// If we have a list of VCS files, check that first
		skip := false
		if len(vcsInclude) > 0 {
			skip = true
			for _, f := range vcsInclude {
				if f == subpath {
					skip = false
					break
				}

				if info.IsDir() && strings.HasPrefix(f, subpath+"/") {
					skip = false
					break
				}
			}
		}

		// If include is present, we only include what is listed
		if len(includeMap) > 0 {
			if _, ok := includeMap[subpath]; !ok {
				skip = true
			}
		}

		// If exclude, it is one last gate to excluding files
		if opts != nil {
			for _, exclude := range opts.Exclude {
				match, err := filepath.Match(exclude, subpath)
				if err != nil {
					return err
				}
				if match {
					skip = true
					break
				}
			}
		}

		// If we have to skip this file, then skip it, properly skipping
		// children if we're a directory.
		if skip {
			if info.IsDir() {
				return filepath.SkipDir
			}

			return nil
		}

		// If this is a symlink, then we need to get the symlink target
		// rather than the symlink itself.
		if info.Mode()&os.ModeSymlink != 0 {
			target, info, err := readLinkFull(path, info)
			if err != nil {
				return err
			}

			// Copy the concrete entry for this path. This will either
			// be the file itself or just a directory entry.
			if err := copyConcreteEntry(tarW, subpath, target, info); err != nil {
				return err
			}

			if info.IsDir() {
				return filepath.Walk(target, copyDirWalkFn(
					tarW, target, subpath, opts, vcsInclude))
			}
			// return now so that we don't try to copy twice
			return nil
		}

		return copyConcreteEntry(tarW, subpath, path, info)
	}
}

func copyConcreteEntry(
	tarW *tar.Writer, entry string,
	path string, info os.FileInfo) error {
	// Windows
	path = filepath.ToSlash(path)

	// Build the file header for the tar entry
	header, err := tar.FileInfoHeader(info, path)
	if err != nil {
		return fmt.Errorf(
			"failed creating archive header: %s", path)
	}

	// Modify the header to properly be the full entry name
	header.Name = entry
	if info.IsDir() {
		header.Name += "/"
	}

	// Write the header first to the archive.
	if err := tarW.WriteHeader(header); err != nil {
		return fmt.Errorf(
			"failed writing archive header: %s", path)
	}

	// If it is a directory, then we're done (no body to write)
	if info.IsDir() {
		return nil
	}

	// Open the real file to write the data
	f, err := os.Open(path)
	if err != nil {
		return fmt.Errorf(
			"failed opening file '%s' to write compressed archive.", path)
	}
	defer f.Close()

	if _, err = io.Copy(tarW, f); err != nil {
		return fmt.Errorf(
			"failed copying file to archive: %s", path)
	}

	return nil
}

func copyExtras(w *tar.Writer, extra map[string]string) error {
	var tmpDir string
	defer func() {
		if tmpDir != "" {
			os.RemoveAll(tmpDir)
		}
	}()

	for entry, path := range extra {
		// If the path is empty, then we set it to a generic empty directory
		if path == "" {
			// If tmpDir is still empty, then we create an empty dir
			if tmpDir == "" {
				td, err := ioutil.TempDir("", "archive")
				if err != nil {
					return err
				}

				tmpDir = td
			}

			path = tmpDir
		}

		info, err := os.Stat(path)
		if err != nil {
			return err
		}

		// No matter what, write the entry. If this is a directory,
		// it'll just write the directory header.
		if err := copyConcreteEntry(w, entry, path, info); err != nil {
			return err
		}

		// If this is a directory, then we walk the internal contents
		// and copy those as well.
		if info.IsDir() {
			err := filepath.Walk(path, copyDirWalkFn(
				w, path, entry, nil, nil))
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func readLinkFull(path string, info os.FileInfo) (string, os.FileInfo, error) {
	// Read the symlink continously until we reach a concrete file.
	target := path
	tries := 0
	for info.Mode()&os.ModeSymlink != 0 {
		var err error
		target, err = os.Readlink(target)
		if err != nil {
			return "", nil, err
		}
		if !filepath.IsAbs(target) {
			target, err = filepath.Abs(target)
			if err != nil {
				return "", nil, err
			}
		}
		info, err = os.Lstat(target)
		if err != nil {
			return "", nil, err
		}

		tries++
		if tries > 100 {
			return "", nil, fmt.Errorf(
				"Symlink for %s is too deep, over 100 levels deep",
				path)
		}
	}

	return target, info, nil
}

// readCloseRemover is an io.ReadCloser implementation that will remove
// the file on Close(). We use this to clean up our temporary file for
// the archive.
type readCloseRemover struct {
	F *os.File
}

func (r *readCloseRemover) Read(p []byte) (int, error) {
	return r.F.Read(p)
}

func (r *readCloseRemover) Close() error {
	// First close the file
	err := r.F.Close()

	// Next make sure to remove it, or at least try, regardless of error
	// above.
	os.Remove(r.F.Name())

	return err
}
<commit_msg>Actually show the error message when we fail to add a file to the archive<commit_after>// archive is package that helps create archives in a format that
// Atlas expects with its various upload endpoints.
package archive

import (
	"archive/tar"
	"bufio"
	"compress/gzip"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
)

// Archive is the resulting archive. The archive data is generally streamed
// so the io.ReadCloser can be used to backpressure the archive progress
// and avoid memory pressure.
type Archive struct {
	io.ReadCloser

	Size     int64
	Metadata map[string]string
}

// ArchiveOpts are the options for defining how the archive will be built.
type ArchiveOpts struct {
	// Exclude and Include are filters of files to include/exclude in
	// the archive when creating it from a directory. These filters should
	// be relative to the packaging directory and should be basic glob
	// patterns.
	Exclude []string
	Include []string

	// Extra is a mapping of extra files to include within the archive. The
	// key should be the path within the archive and the value should be
	// an absolute path to the file to put into the archive. These extra
	// files will override any other files in the archive.
	Extra map[string]string

	// VCS, if true, will detect and use a VCS system to determine what
	// files to include the archive.
	VCS bool
}

// IsSet says whether any options were set.
func (o *ArchiveOpts) IsSet() bool {
	return len(o.Exclude) > 0 || len(o.Include) > 0 || o.VCS
}

// Constants related to setting special values for Extra in ArchiveOpts.
const (
	// ExtraEntryDir just creates the Extra key as a directory entry.
	ExtraEntryDir = ""
)

// CreateArchive takes the given path and ArchiveOpts and archives it.
//
// The archive will be fully completed and put into a temporary file.
// This must be done to retrieve the content length of the archive which
// is needed for almost all operations involving archives with Atlas. Because
// of this, sufficient disk space will be required to buffer the archive.
func CreateArchive(path string, opts *ArchiveOpts) (*Archive, error) {
	log.Printf("[INFO] creating archive from %s", path)

	// Dereference any symlinks and determine the real path and info
	fi, err := os.Lstat(path)
	if err != nil {
		return nil, err
	}
	if fi.Mode()&os.ModeSymlink != 0 {
		path, fi, err = readLinkFull(path, fi)
		if err != nil {
			return nil, err
		}
	}

	// Windows
	path = filepath.ToSlash(path)

	// Direct file paths cannot have archive options
	if !fi.IsDir() && opts.IsSet() {
		return nil, fmt.Errorf(
			"options such as exclude, include, and VCS can't be set when " +
				"the path is a file.")
	}

	if fi.IsDir() {
		return archiveDir(path, opts)
	} else {
		return archiveFile(path)
	}
}

func archiveFile(path string) (*Archive, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}

	if _, err := gzip.NewReader(f); err == nil {
		// Reset the read offset for future reading
		if _, err := f.Seek(0, 0); err != nil {
			f.Close()
			return nil, err
		}

		// Get the file info for the size
		fi, err := f.Stat()
		if err != nil {
			f.Close()
			return nil, err
		}

		// This is a gzip file, let it through.
		return &Archive{ReadCloser: f, Size: fi.Size()}, nil
	}

	// Close the file, no use for it anymore
	f.Close()

	// We have a single file that is not gzipped. Compress it.
	path, err = filepath.Abs(path)
	if err != nil {
		return nil, err
	}

	// Act like we're compressing a directory, but only include this one
	// file.
	return archiveDir(filepath.Dir(path), &ArchiveOpts{
		Include: []string{filepath.Base(path)},
	})
}

func archiveDir(root string, opts *ArchiveOpts) (*Archive, error) {

	var vcsInclude []string
	var metadata map[string]string
	if opts.VCS {
		var err error

		if err = vcsPreflight(root); err != nil {
			return nil, err
		}

		vcsInclude, err = vcsFiles(root)
		if err != nil {
			return nil, err
		}

		metadata, err = vcsMetadata(root)
		if err != nil {
			return nil, err
		}
	}

	// Make sure the root path is absolute
	root, err := filepath.Abs(root)
	if err != nil {
		return nil, err
	}

	// Create the temporary file that we'll send the archive data to.
	archiveF, err := ioutil.TempFile("", "atlas-archive")
	if err != nil {
		return nil, err
	}

	// Create the wrapper for the result which will automatically
	// remove the temporary file on close.
	archiveWrapper := &readCloseRemover{F: archiveF}

	// Buffer the writer so that we can push as much data to disk at
	// a time as possible. 4M should be good.
	bufW := bufio.NewWriterSize(archiveF, 4096*1024)

	// Gzip compress all the output data
	gzipW := gzip.NewWriter(bufW)

	// Tar the file contents
	tarW := tar.NewWriter(gzipW)

	// First, walk the path and do the normal files
	werr := filepath.Walk(root, copyDirWalkFn(
		tarW, root, "", opts, vcsInclude))
	if werr == nil {
		// If that succeeded, handle the extra files
		werr = copyExtras(tarW, opts.Extra)
	}

	// Attempt to close all the things. If we get an error on the way
	// and we haven't had an error yet, then record that as the critical
	// error. But we still try to close everything.

	// Close the tar writer
	if err := tarW.Close(); err != nil && werr == nil {
		werr = err
	}

	// Close the gzip writer
	if err := gzipW.Close(); err != nil && werr == nil {
		werr = err
	}

	// Flush the buffer
	if err := bufW.Flush(); err != nil && werr == nil {
		werr = err
	}

	// If we had an error, then close the file (removing it) and
	// return the error.
	if werr != nil {
		archiveWrapper.Close()
		return nil, werr
	}

	// Seek to the beginning
	if _, err := archiveWrapper.F.Seek(0, 0); err != nil {
		archiveWrapper.Close()
		return nil, err
	}

	// Get the file information so we can get the size
	fi, err := archiveWrapper.F.Stat()
	if err != nil {
		archiveWrapper.Close()
		return nil, err
	}

	return &Archive{
		ReadCloser: archiveWrapper,
		Size:       fi.Size(),
		Metadata:   metadata,
	}, nil
}

func copyDirWalkFn(
	tarW *tar.Writer, root string, prefix string,
	opts *ArchiveOpts, vcsInclude []string) filepath.WalkFunc {

	errFunc := func(err error) filepath.WalkFunc {
		return func(string, os.FileInfo, error) error {
			return err
		}
	}

	// Windows
	root = filepath.ToSlash(root)

	var includeMap map[string]struct{}

	// If we have an include/exclude pattern set, then setup the lookup
	// table to determine what we want to include.
	if opts != nil && len(opts.Include) > 0 {
		includeMap = make(map[string]struct{})
		for _, pattern := range opts.Include {
			matches, err := filepath.Glob(filepath.Join(root, pattern))
			if err != nil {
				return errFunc(fmt.Errorf(
					"error checking include glob '%s': %s",
					pattern, err))
			}

			for _, path := range matches {
				// Windows
				path = filepath.ToSlash(path)
				subpath, err := filepath.Rel(root, path)
				subpath = filepath.ToSlash(subpath)

				if err != nil {
					return errFunc(err)
				}

				for {
					includeMap[subpath] = struct{}{}
					subpath = filepath.Dir(subpath)
					if subpath == "." {
						break
					}
				}
			}
		}
	}

	return func(path string, info os.FileInfo, err error) error {
		path = filepath.ToSlash(path)

		if err != nil {
			return err
		}

		// Get the relative path from the path since it contains the root
		// plus the path.
		subpath, err := filepath.Rel(root, path)
		if err != nil {
			return err
		}
		if subpath == "." {
			return nil
		}
		if prefix != "" {
			subpath = filepath.Join(prefix, subpath)
		}
		// Windows
		subpath = filepath.ToSlash(subpath)

		// If we have a list of VCS files, check that first
		skip := false
		if len(vcsInclude) > 0 {
			skip = true
			for _, f := range vcsInclude {
				if f == subpath {
					skip = false
					break
				}

				if info.IsDir() && strings.HasPrefix(f, subpath+"/") {
					skip = false
					break
				}
			}
		}

		// If include is present, we only include what is listed
		if len(includeMap) > 0 {
			if _, ok := includeMap[subpath]; !ok {
				skip = true
			}
		}

		// If exclude, it is one last gate to excluding files
		if opts != nil {
			for _, exclude := range opts.Exclude {
				match, err := filepath.Match(exclude, subpath)
				if err != nil {
					return err
				}
				if match {
					skip = true
					break
				}
			}
		}

		// If we have to skip this file, then skip it, properly skipping
		// children if we're a directory.
		if skip {
			if info.IsDir() {
				return filepath.SkipDir
			}

			return nil
		}

		// If this is a symlink, then we need to get the symlink target
		// rather than the symlink itself.
		if info.Mode()&os.ModeSymlink != 0 {
			target, info, err := readLinkFull(path, info)
			if err != nil {
				return err
			}

			// Copy the concrete entry for this path. This will either
			// be the file itself or just a directory entry.
			if err := copyConcreteEntry(tarW, subpath, target, info); err != nil {
				return err
			}

			if info.IsDir() {
				return filepath.Walk(target, copyDirWalkFn(
					tarW, target, subpath, opts, vcsInclude))
			}
			// return now so that we don't try to copy twice
			return nil
		}

		return copyConcreteEntry(tarW, subpath, path, info)
	}
}

func copyConcreteEntry(
	tarW *tar.Writer, entry string,
	path string, info os.FileInfo) error {
	// Windows
	path = filepath.ToSlash(path)

	// Build the file header for the tar entry
	header, err := tar.FileInfoHeader(info, path)
	if err != nil {
		return fmt.Errorf(
			"failed creating archive header: %s", path)
	}

	// Modify the header to properly be the full entry name
	header.Name = entry
	if info.IsDir() {
		header.Name += "/"
	}

	// Write the header first to the archive.
	if err := tarW.WriteHeader(header); err != nil {
		return fmt.Errorf(
			"failed writing archive header: %s", path)
	}

	// If it is a directory, then we're done (no body to write)
	if info.IsDir() {
		return nil
	}

	// Open the real file to write the data
	f, err := os.Open(path)
	if err != nil {
		return fmt.Errorf(
			"failed opening file '%s' to write compressed archive.", path)
	}
	defer f.Close()

	if _, err = io.Copy(tarW, f); err != nil {
		return fmt.Errorf(
			"failed copying file to archive: %s, %s", path, err)
	}

	return nil
}

func copyExtras(w *tar.Writer, extra map[string]string) error {
	var tmpDir string
	defer func() {
		if tmpDir != "" {
			os.RemoveAll(tmpDir)
		}
	}()

	for entry, path := range extra {
		// If the path is empty, then we set it to a generic empty directory
		if path == "" {
			// If tmpDir is still empty, then we create an empty dir
			if tmpDir == "" {
				td, err := ioutil.TempDir("", "archive")
				if err != nil {
					return err
				}

				tmpDir = td
			}

			path = tmpDir
		}

		info, err := os.Stat(path)
		if err != nil {
			return err
		}

		// No matter what, write the entry. If this is a directory,
		// it'll just write the directory header.
		if err := copyConcreteEntry(w, entry, path, info); err != nil {
			return err
		}

		// If this is a directory, then we walk the internal contents
		// and copy those as well.
		if info.IsDir() {
			err := filepath.Walk(path, copyDirWalkFn(
				w, path, entry, nil, nil))
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func readLinkFull(path string, info os.FileInfo) (string, os.FileInfo, error) {
	// Read the symlink continously until we reach a concrete file.
	target := path
	tries := 0
	for info.Mode()&os.ModeSymlink != 0 {
		var err error
		target, err = os.Readlink(target)
		if err != nil {
			return "", nil, err
		}
		if !filepath.IsAbs(target) {
			target, err = filepath.Abs(target)
			if err != nil {
				return "", nil, err
			}
		}
		info, err = os.Lstat(target)
		if err != nil {
			return "", nil, err
		}

		tries++
		if tries > 100 {
			return "", nil, fmt.Errorf(
				"Symlink for %s is too deep, over 100 levels deep",
				path)
		}
	}

	return target, info, nil
}

// readCloseRemover is an io.ReadCloser implementation that will remove
// the file on Close(). We use this to clean up our temporary file for
// the archive.
type readCloseRemover struct {
	F *os.File
}

func (r *readCloseRemover) Read(p []byte) (int, error) {
	return r.F.Read(p)
}

func (r *readCloseRemover) Close() error {
	// First close the file
	err := r.F.Close()

	// Next make sure to remove it, or at least try, regardless of error
	// above.
	os.Remove(r.F.Name())

	return err
}
<|endoftext|>


<commit_before>package quic

import (
	"net"
)

// TODO: should be encrypt at proper timing
type Transport struct {
	//Conn     *tls.Conn
	UDPConn    *net.UDPConn
	RemoteAddr *net.UDPAddr
	CertPath   string
	KeyPath    string
}

func NewTransport(certPath, keyPath string) (*Transport, error) {
	/*
		var config *tls.Config = nil
		if certPath != "" && keyPath != "" {
			cert, err := tls.LoadX509KeyPair(certPath, keyPath)
			if err != nil {
				return nil, err
			}
			// TODO: others?
			config = &tls.Config{
				Certificates: []tls.Certificate{cert},
			}
		}
		conn, err := tls.Dial("udp4", string(rAddr.IP)+":"+strconv.Itoa(rAddr.Port), config)
		if err != nil {
			return nil, err
		}
		return &Transport{conn, certPath, keyPath}, nil
	*/
	//conn, err := net.ListenUDP("udp", rAddr)
	return &Transport{
		CertPath: certPath,
		KeyPath:  keyPath,
	}, nil
}

func (self *Transport) Listen(rAddr *net.UDPAddr) error {
	conn, err := net.ListenUDP("udp", rAddr)
	if err != nil {
		return err
	}
	self.UDPConn = conn
	return nil
}

func (self *Transport) Dial(rAddr *net.UDPAddr) error {
	conn, err := net.DialUDP("udp", nil, rAddr)
	if err != nil {
		return err
	}
	self.UDPConn = conn
	return nil
}

func (self *Transport) Close() error {
	return self.UDPConn.Close()
}

func (self *Transport) Send(p Packet) (err error) {
	wire, err := p.GetWire()
	if err != nil {
		return err
	}
	_, err = self.UDPConn.Write(wire)
	return err
}

func (self *Transport) Recv() (Packet, int, *net.UDPAddr, error) {
	wire := make([]byte, MTU) // TODO: need to check
	len, sourceAddr, err := self.UDPConn.ReadFromUDP(wire)
	if err != nil {
		return nil, len, nil, err
	}
	ph, len, err := ParsePacketHeader(wire, false) //
	if err != nil {
		return nil, len, nil, err
	}
	f, ok := PacketParserMap[ph.Type]
	if !ok {
		return nil, len, nil, QUIC_INVALID_PACKET_HEADER
	}
	p, nxt := f(ph, wire[len:])

	return p, len + nxt, sourceAddr, nil
}
<commit_msg>remove RemoteAddr, use SendTo to specify peer<commit_after>package quic

import (
	"net"
)

// TODO: should be encrypt at proper timing
type Transport struct {
	//Conn     *tls.Conn
	UDPConn  *net.UDPConn
	CertPath string
	KeyPath  string
}

func NewTransport(certPath, keyPath string) (*Transport, error) {
	/*
		var config *tls.Config = nil
		if certPath != "" && keyPath != "" {
			cert, err := tls.LoadX509KeyPair(certPath, keyPath)
			if err != nil {
				return nil, err
			}
			// TODO: others?
			config = &tls.Config{
				Certificates: []tls.Certificate{cert},
			}
		}
		conn, err := tls.Dial("udp4", string(rAddr.IP)+":"+strconv.Itoa(rAddr.Port), config)
		if err != nil {
			return nil, err
		}
		return &Transport{conn, certPath, keyPath}, nil
	*/
	//conn, err := net.ListenUDP("udp", rAddr)
	return &Transport{
		CertPath: certPath,
		KeyPath:  keyPath,
	}, nil
}

func (self *Transport) Listen(lAddr *net.UDPAddr) error {
	conn, err := net.ListenUDP("udp", lAddr)
	if err != nil {
		return err
	}
	self.UDPConn = conn
	return nil
}

func (self *Transport) Dial(rAddr *net.UDPAddr) error {
	conn, err := net.DialUDP("udp", nil, rAddr)
	if err != nil {
		return err
	}
	self.UDPConn = conn
	return nil
}

func (self *Transport) Close() error {
	return self.UDPConn.Close()
}

func (self *Transport) SendTo(p Packet, rAddr *net.UDPAddr) (err error) {
	wire, err := p.GetWire()
	if err != nil {
		return err
	}
	_, err = self.UDPConn.WriteToUDP(wire, rAddr)
	return err
}

func (self *Transport) Send(p Packet) (err error) {
	wire, err := p.GetWire()
	if err != nil {
		return err
	}
	_, err = self.UDPConn.Write(wire)
	return err
}

func (self *Transport) Recv() (Packet, int, *net.UDPAddr, error) {
	wire := make([]byte, MTU) // TODO: need to check

	len, sourceAddr, err := self.UDPConn.ReadFromUDP(wire)
	if err != nil {
		return nil, len, nil, err
	}
	ph, len, err := ParsePacketHeader(wire, false) //
	if err != nil {
		return nil, len, nil, err
	}
	f, ok := PacketParserMap[ph.Type]
	if !ok {
		return nil, len, nil, QUIC_INVALID_PACKET_HEADER
	}
	p, nxt := f(ph, wire[len:])

	return p, len + nxt, sourceAddr, nil
}
<|endoftext|>


<commit_before>// Go trie data structure

package trie

/*
 * Tests for the Rune and ASCII trie data structure implementations.
 */

import (
	"math/rand"
	"testing"

	"github.com/stretchr/testify/assert"
)

const alphabet = "abcdefghijklmnopqrstuvwxyz"

func TestStringTrie(t *testing.T) {
	tr := NewASCIITrie()
	for i := 0; i < 10000; i++ {
		l := rand.Int() % 100
		for l == 0 {
			l = rand.Int() % 100
		}
		b := make([]byte, 0, l)
		for j := 0; j < l; j++ {
			ch := alphabet[rand.Int()%26]
			b = append(b, ch)
		}
		w := string(b)
		tr.Add(w)
		if !tr.Contains(w) {
			if !assert.True(t, tr.Contains(w), "The trie shoud contain %v", w) {
				return
			}
		}
	}
}

func TestStringReduxTrie(t *testing.T) {
	tr := NewASCIIReduxTrie()
	for i := 0; i < 10000; i++ {
		l := rand.Int() % 100
		for l == 0 {
			l = rand.Int() % 100
		}
		b := make([]byte, 0, l)
		for j := 0; j < l; j++ {
			ch := alphabet[rand.Int()%26]
			b = append(b, ch)
		}
		w := string(b)
		tr.Add(w)
		if !tr.Contains(w) {
			if !assert.True(t, tr.Contains(w), "The trie shoud contain %v", w) {
				return
			}
		}
	}
}

func TestRuneTrie(t *testing.T) {
	runes := [...]string{"日本語", "essaiTest", "こんにちは", "分からない", "パソコン"}
	tr := New(RuneGetter)
	for _, w := range runes {
		tr.Add(w)
		if !tr.Contains(w) {
			if !assert.True(t, tr.Contains(w), "The trie shoud contain %v", w) {
				return
			}
		}
	}
}

func TestLinkedRuneTrie(t *testing.T) {
	runes := [...]string{"日本語", "essaiTest", "こんにちは", "分からない", "パソコン"}
	tr := NewLinked(RuneGetter)
	for _, w := range runes {
		tr.Add(w)
		if !tr.Contains(w) {
			if !assert.True(t, tr.Contains(w), "The trie shoud contain %v", w) {
				return
			}
		}
	}
}
<commit_msg>Remove unused statements in test<commit_after>// Go trie data structure

package trie

/*
 * Tests for the Rune and ASCII trie data structure implementations.
 */

import (
	"math/rand"
	"testing"

	"github.com/stretchr/testify/assert"
)

const alphabet = "abcdefghijklmnopqrstuvwxyz"

func TestStringTrie(t *testing.T) {
	tr := NewASCIITrie()
	for i := 0; i < 10000; i++ {
		l := rand.Int() % 100
		for l == 0 {
			l = rand.Int() % 100
		}
		b := make([]byte, 0, l)
		for j := 0; j < l; j++ {
			ch := alphabet[rand.Int()%26]
			b = append(b, ch)
		}
		w := string(b)
		tr.Add(w)
		if !assert.True(t, tr.Contains(w), "The trie shoud contain %v", w) {
			return
		}
	}
}

func TestStringReduxTrie(t *testing.T) {
	tr := NewASCIIReduxTrie()
	for i := 0; i < 10000; i++ {
		l := rand.Int() % 100
		for l == 0 {
			l = rand.Int() % 100
		}
		b := make([]byte, 0, l)
		for j := 0; j < l; j++ {
			ch := alphabet[rand.Int()%26]
			b = append(b, ch)
		}
		w := string(b)
		tr.Add(w)
		if !assert.True(t, tr.Contains(w), "The trie shoud contain %v", w) {
			return
		}
	}
}

func TestRuneTrie(t *testing.T) {
	runes := [...]string{"日本語", "essaiTest", "こんにちは", "分からない", "パソコン"}
	tr := New(RuneGetter)
	for _, w := range runes {
		tr.Add(w)
		if !assert.True(t, tr.Contains(w), "The trie shoud contain %v", w) {
			return
		}
	}
}

func TestLinkedRuneTrie(t *testing.T) {
	runes := [...]string{"日本語", "essaiTest", "こんにちは", "分からない", "パソコン"}
	tr := NewLinked(RuneGetter)
	for _, w := range runes {
		tr.Add(w)
		if !assert.True(t, tr.Contains(w), "The trie shoud contain %v", w) {
			return
		}
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 RightScale, Inc. - see LICENSE

package tunnel

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	_ "net/http/pprof"
	"time"

	"github.com/gorilla/websocket"
	"gopkg.in/inconshreveable/log15.v2"
)

var _ fmt.Formatter

func httpError(log log15.Logger, w http.ResponseWriter, token, err string, code int) {
	log.Info("ERR", "token", token, "status", code, "err", err)
	http.Error(w, err, code)
}

const (
	WS_read_close  = iota
	WS_read_error  = iota
	WS_write_error = iota
)

func wsp(ws *websocket.Conn) string { return fmt.Sprintf("%p", ws) }

// Handler for websockets tunnel establishment requests
func wsHandler(t *WSTunnelServer, w http.ResponseWriter, r *http.Request) {
	addr := r.Header.Get("X-Forwarded-For")
	if addr == "" {
		addr = r.RemoteAddr
	}
	// Verify that an origin header with a token is provided
	tok := r.Header.Get("Origin")
	if tok == "" {
		httpError(t.Log, w, addr, "Origin header with rendez-vous token required", 400)
		return
	}
	if len(tok) < MIN_TOKEN_LEN {
		httpError(t.Log, w, addr,
			fmt.Sprintf("Rendez-vous token (%s) is too short (must be %d chars)",
				tok, MIN_TOKEN_LEN), 400)
		return
	}
	logTok := cutToken(token(tok))
	// Upgrade to web sockets
	ws, err := websocket.Upgrade(w, r, nil, 100*1024, 100*1024)
	t.Log.Info("WS new tunnel connection", "token", logTok, "addr", addr, "ws", wsp(ws))
	if _, ok := err.(websocket.HandshakeError); ok {
		httpError(t.Log, w, logTok, "Not a websocket handshake", 400)
		return
	} else if err != nil {
		httpError(t.Log, w, logTok, err.Error(), 400)
		return
	}
	// Get/Create RemoteServer
	rs := t.getRemoteServer(token(tok), true)
	rs.remoteAddr = addr
	rs.lastActivity = time.Now()
	// do reverse DNS lookup asynchronously
	go func() {
		rs.remoteName, rs.remoteWhois = ipAddrLookup(t.Log, rs.remoteAddr)
	}()
	// Set safety limits
	ws.SetReadLimit(100 * 1024 * 1024)
	// Start timout handling
	wsSetPingHandler(t, ws, rs)
	// Create synchronization channel
	ch := make(chan int, 2)
	// Spawn goroutine to read responses
	go wsReader(rs, ws, t.WSTimeout, ch)
	// Send requests
	wsWriter(rs, ws, ch)
}

func wsSetPingHandler(t *WSTunnelServer, ws *websocket.Conn, rs *remoteServer) {
	// timeout handler sends a close message, waits a few seconds, then kills the socket
	timeout := func() {
		ws.WriteControl(websocket.CloseMessage, nil, time.Now().Add(1*time.Second))
		time.Sleep(5 * time.Second)
		rs.log.Info("WS closing due to ping timeout", "ws", wsp(ws))
		ws.Close()
	}
	// timeout timer
	timer := time.AfterFunc(t.WSTimeout, timeout)
	// ping handler resets last ping time
	ph := func(message string) error {
		timer.Reset(t.WSTimeout)
		ws.WriteControl(websocket.PongMessage, []byte(message), time.Now().Add(t.WSTimeout/3))
		// update lastActivity
		rs.lastActivity = time.Now()
		return nil
	}
	ws.SetPingHandler(ph)
}

// Pick requests off the RemoteServer queue and send them into the tunnel
func wsWriter(rs *remoteServer, ws *websocket.Conn, ch chan int) {
	var req *remoteRequest
	var err error
	for {
		// fetch a request
		select {
		case req = <-rs.requestQueue:
			// awesome...
		case _ = <-ch:
			// time to close shop
			rs.log.Info("WS closing on signal", "ws", wsp(ws))
			ws.Close()
			return
		}
		//log.Printf("WS->%s#%d start %s", req.token, req.id, req.info)
		// See whether the request has already expired
		if req.deadline.Before(time.Now()) {
			req.replyChan <- responseBuffer{
				err: errors.New("Timeout before forwarding the request"),
			}
			req.log.Info("WS   SND timeout before sending", "ago",
				time.Now().Sub(req.deadline).Seconds())
			continue
		}
		// write the request into the tunnel
		ws.SetWriteDeadline(time.Now().Add(time.Minute))
		var w io.WriteCloser
		w, err = ws.NextWriter(websocket.BinaryMessage)
		// got an error, reply with a "hey, retry" to the request handler
		if err != nil {
			break
		}
		// write the request Id
		_, err = fmt.Fprintf(w, "%04x", req.id)
		if err != nil {
			break
		}
		// write the request itself
		_, err = req.buffer.WriteTo(w)
		if err != nil {
			break
		}
		// done
		err = w.Close()
		if err != nil {
			break
		}
		req.log.Info("WS   SND", "info", req.info)
	}
	// tell the sender to retry the request
	req.replyChan <- responseBuffer{err: RetryError}
	req.log.Info("WS error causes retry")
	// close up shop
	ws.WriteControl(websocket.CloseMessage, nil, time.Now().Add(5*time.Second))
	time.Sleep(2 * time.Second)
	ws.Close()
}

// Read responses from the tunnel and fulfill pending requests
func wsReader(rs *remoteServer, ws *websocket.Conn, wsTimeout time.Duration, ch chan int) {
	var err error
	log_token := cutToken(rs.token)
	// continue reading until we get an error
	for {
		ws.SetReadDeadline(time.Time{}) // no timeout, there's the ping-pong for that
		// read a message from the tunnel
		t, r, err := ws.NextReader()
		if err != nil {
			break
		}
		if t != websocket.BinaryMessage {
			err = fmt.Errorf("non-binary message received, type=%d", t)
			break
		}
		// give the sender a fixed time to get us the data
		ws.SetReadDeadline(time.Now().Add(wsTimeout))
		// get request id
		var id int16
		_, err = fmt.Fscanf(io.LimitReader(r, 4), "%04x", &id)
		if err != nil {
			break
		}
		// read request itself
		buf, err := ioutil.ReadAll(r)
		if err != nil {
			break
		}
		rs.log.Info("WS   RCV", "id", id, "ws", wsp(ws))
		// try to match request
		rs.requestSetMutex.Lock()
		req := rs.requestSet[id]
		rs.lastActivity = time.Now()
		rs.requestSetMutex.Unlock()
		// let's see...
		if req != nil {
			rb := responseBuffer{response: bytes.NewBuffer(buf)}
			// try to enqueue response
			select {
			case req.replyChan <- rb:
				// great!
			default:
				rs.log.Info("WS   RCV can't enqueue response", "id", id, "ws", wsp(ws))
			}
		} else {
			rs.log.Info("%s #%d: WS   RCV orphan response", "id", id, "ws", wsp(ws))
		}
	}
	// print error message
	if err != nil {
		rs.log.Info("WS   closing", "token", log_token, "err", err.Error(), "ws", wsp(ws))
	}
	// close up shop
	ch <- 0 // notify sender
	time.Sleep(2 * time.Second)
	ws.Close()
}
<commit_msg>fix error propagation in wsReader<commit_after>// Copyright (c) 2014 RightScale, Inc. - see LICENSE

package tunnel

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	_ "net/http/pprof"
	"time"

	"github.com/gorilla/websocket"
	"gopkg.in/inconshreveable/log15.v2"
)

var _ fmt.Formatter

func httpError(log log15.Logger, w http.ResponseWriter, token, err string, code int) {
	log.Info("ERR", "token", token, "status", code, "err", err)
	http.Error(w, err, code)
}

const (
	WS_read_close  = iota
	WS_read_error  = iota
	WS_write_error = iota
)

func wsp(ws *websocket.Conn) string { return fmt.Sprintf("%p", ws) }

// Handler for websockets tunnel establishment requests
func wsHandler(t *WSTunnelServer, w http.ResponseWriter, r *http.Request) {
	addr := r.Header.Get("X-Forwarded-For")
	if addr == "" {
		addr = r.RemoteAddr
	}
	// Verify that an origin header with a token is provided
	tok := r.Header.Get("Origin")
	if tok == "" {
		httpError(t.Log, w, addr, "Origin header with rendez-vous token required", 400)
		return
	}
	if len(tok) < MIN_TOKEN_LEN {
		httpError(t.Log, w, addr,
			fmt.Sprintf("Rendez-vous token (%s) is too short (must be %d chars)",
				tok, MIN_TOKEN_LEN), 400)
		return
	}
	logTok := cutToken(token(tok))
	// Upgrade to web sockets
	ws, err := websocket.Upgrade(w, r, nil, 100*1024, 100*1024)
	t.Log.Info("WS new tunnel connection", "token", logTok, "addr", addr, "ws", wsp(ws))
	if _, ok := err.(websocket.HandshakeError); ok {
		httpError(t.Log, w, logTok, "Not a websocket handshake", 400)
		return
	} else if err != nil {
		httpError(t.Log, w, logTok, err.Error(), 400)
		return
	}
	// Get/Create RemoteServer
	rs := t.getRemoteServer(token(tok), true)
	rs.remoteAddr = addr
	rs.lastActivity = time.Now()
	// do reverse DNS lookup asynchronously
	go func() {
		rs.remoteName, rs.remoteWhois = ipAddrLookup(t.Log, rs.remoteAddr)
	}()
	// Set safety limits
	ws.SetReadLimit(100 * 1024 * 1024)
	// Start timeout handling
	wsSetPingHandler(t, ws, rs)
	// Create synchronization channel
	ch := make(chan int, 2)
	// Spawn goroutine to read responses
	go wsReader(rs, ws, t.WSTimeout, ch)
	// Send requests
	wsWriter(rs, ws, ch)
}

func wsSetPingHandler(t *WSTunnelServer, ws *websocket.Conn, rs *remoteServer) {
	// timeout handler sends a close message, waits a few seconds, then kills the socket
	timeout := func() {
		ws.WriteControl(websocket.CloseMessage, nil, time.Now().Add(1*time.Second))
		time.Sleep(5 * time.Second)
		rs.log.Info("WS closing due to ping timeout", "ws", wsp(ws))
		ws.Close()
	}
	// timeout timer
	timer := time.AfterFunc(t.WSTimeout, timeout)
	// ping handler resets last ping time
	ph := func(message string) error {
		timer.Reset(t.WSTimeout)
		ws.WriteControl(websocket.PongMessage, []byte(message), time.Now().Add(t.WSTimeout/3))
		// update lastActivity
		rs.lastActivity = time.Now()
		return nil
	}
	ws.SetPingHandler(ph)
}

// Pick requests off the RemoteServer queue and send them into the tunnel
func wsWriter(rs *remoteServer, ws *websocket.Conn, ch chan int) {
	var req *remoteRequest
	var err error
	for {
		// fetch a request
		select {
		case req = <-rs.requestQueue:
			// awesome...
		case _ = <-ch:
			// time to close shop
			rs.log.Info("WS closing on signal", "ws", wsp(ws))
			ws.Close()
			return
		}
		//log.Printf("WS->%s#%d start %s", req.token, req.id, req.info)
		// See whether the request has already expired
		if req.deadline.Before(time.Now()) {
			req.replyChan <- responseBuffer{
				err: errors.New("Timeout before forwarding the request"),
			}
			req.log.Info("WS   SND timeout before sending", "ago",
				time.Now().Sub(req.deadline).Seconds())
			continue
		}
		// write the request into the tunnel
		ws.SetWriteDeadline(time.Now().Add(time.Minute))
		var w io.WriteCloser
		w, err = ws.NextWriter(websocket.BinaryMessage)
		// got an error, reply with a "hey, retry" to the request handler
		if err != nil {
			break
		}
		// write the request Id
		_, err = fmt.Fprintf(w, "%04x", req.id)
		if err != nil {
			break
		}
		// write the request itself
		_, err = req.buffer.WriteTo(w)
		if err != nil {
			break
		}
		// done
		err = w.Close()
		if err != nil {
			break
		}
		req.log.Info("WS   SND", "info", req.info)
	}
	// tell the sender to retry the request
	req.replyChan <- responseBuffer{err: RetryError}
	req.log.Info("WS error causes retry")
	// close up shop
	ws.WriteControl(websocket.CloseMessage, nil, time.Now().Add(5*time.Second))
	time.Sleep(2 * time.Second)
	ws.Close()
}

// Read responses from the tunnel and fulfill pending requests
func wsReader(rs *remoteServer, ws *websocket.Conn, wsTimeout time.Duration, ch chan int) {
	var err error
	log_token := cutToken(rs.token)
	// continue reading until we get an error
	for {
		ws.SetReadDeadline(time.Time{}) // no timeout, there's the ping-pong for that
		// read a message from the tunnel
		var t int
		var r io.Reader
		t, r, err = ws.NextReader()
		if err != nil {
			break
		}
		if t != websocket.BinaryMessage {
			err = fmt.Errorf("non-binary message received, type=%d", t)
			break
		}
		// give the sender a fixed time to get us the data
		ws.SetReadDeadline(time.Now().Add(wsTimeout))
		// get request id
		var id int16
		_, err = fmt.Fscanf(io.LimitReader(r, 4), "%04x", &id)
		if err != nil {
			break
		}
		// read request itself
		var buf []byte
		buf, err = ioutil.ReadAll(r)
		if err != nil {
			break
		}
		rs.log.Info("WS   RCV", "id", id, "ws", wsp(ws))
		// try to match request
		rs.requestSetMutex.Lock()
		req := rs.requestSet[id]
		rs.lastActivity = time.Now()
		rs.requestSetMutex.Unlock()
		// let's see...
		if req != nil {
			rb := responseBuffer{response: bytes.NewBuffer(buf)}
			// try to enqueue response
			select {
			case req.replyChan <- rb:
				// great!
			default:
				rs.log.Info("WS   RCV can't enqueue response", "id", id, "ws", wsp(ws))
			}
		} else {
			rs.log.Info("%s #%d: WS   RCV orphan response", "id", id, "ws", wsp(ws))
		}
	}
	// print error message
	if err != nil {
		rs.log.Info("WS   closing", "token", log_token, "err", err.Error(), "ws", wsp(ws))
	}
	// close up shop
	ch <- 0 // notify sender
	time.Sleep(2 * time.Second)
	ws.Close()
}
<|endoftext|>


<commit_before>// errchk $G $D/$F.go

// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

func main() {
	if {  // ERROR "missing condition"
	}

	if x(); {  // ERROR "missing condition"
	}
}
<commit_msg>test: avoid undefined error in syntax/if.go.<commit_after>// errchk $G $D/$F.go

// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

func x() {
}

func main() {
	if {  // ERROR "missing condition"
	}

	if x(); {  // ERROR "missing condition"
	}
}
<|endoftext|>


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

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"time"

	"github.com/uber/kraken/agent/agentserver"
	"github.com/uber/kraken/build-index/tagclient"
	"github.com/uber/kraken/core"
	"github.com/uber/kraken/lib/dockerdaemon"
	"github.com/uber/kraken/lib/dockerregistry/transfer"
	"github.com/uber/kraken/lib/store"
	"github.com/uber/kraken/lib/torrent/networkevent"
	"github.com/uber/kraken/lib/torrent/scheduler"
	"github.com/uber/kraken/metrics"
	"github.com/uber/kraken/nginx"
	"github.com/uber/kraken/utils/configutil"
	"github.com/uber/kraken/utils/log"
	"github.com/uber/kraken/utils/netutil"

	"github.com/uber-go/tally"
	"go.uber.org/zap"
)

// Flags defines agent CLI flags.
type Flags struct {
	PeerIP            string
	PeerPort          int
	AgentServerPort   int
	AgentRegistryPort int
	ConfigFile        string
	Zone              string
	KrakenCluster     string
	SecretsFile       string
}

// ParseFlags parses agent CLI flags.
func ParseFlags() *Flags {
	var flags Flags
	flag.StringVar(
		&flags.PeerIP, "peer-ip", "", "ip which peer will announce itself as")
	flag.IntVar(
		&flags.PeerPort, "peer-port", 0, "port which peer will announce itself as")
	flag.IntVar(
		&flags.AgentServerPort, "agent-server-port", 0, "port which agent server listens on")
	flag.IntVar(
		&flags.AgentRegistryPort, "agent-registry-port", 0, "port which agent registry listens on")
	flag.StringVar(
		&flags.ConfigFile, "config", "", "configuration file path")
	flag.StringVar(
		&flags.Zone, "zone", "", "zone/datacenter name")
	flag.StringVar(
		&flags.KrakenCluster, "cluster", "", "cluster name (e.g. prod01-zone1)")
	flag.StringVar(
		&flags.SecretsFile, "secrets", "", "path to a secrets YAML file to load into configuration")
	flag.Parse()
	return &flags
}

type options struct {
	config  *Config
	metrics tally.Scope
	logger  *zap.Logger
}

// Option defines an optional Run parameter.
type Option func(*options)

// WithConfig ignores config/secrets flags and directly uses the provided config
// struct.
func WithConfig(c Config) Option {
	return func(o *options) { o.config = &c }
}

// WithMetrics ignores metrics config and directly uses the provided tally scope.
func WithMetrics(s tally.Scope) Option {
	return func(o *options) { o.metrics = s }
}

// WithLogger ignores logging config and directly uses the provided logger.
func WithLogger(l *zap.Logger) Option {
	return func(o *options) { o.logger = l }
}

// Run runs the agent.
func Run(flags *Flags, opts ...Option) {
	if flags.PeerPort == 0 {
		panic("must specify non-zero peer port")
	}
	if flags.AgentServerPort == 0 {
		panic("must specify non-zero agent server port")
	}
	if flags.AgentRegistryPort == 0 {
		panic("must specify non-zero agent registry port")
	}

	var overrides options
	for _, o := range opts {
		o(&overrides)
	}

	var config Config
	if overrides.config != nil {
		config = *overrides.config
	} else {
		if err := configutil.Load(flags.ConfigFile, &config); err != nil {
			panic(err)
		}
		if flags.SecretsFile != "" {
			if err := configutil.Load(flags.SecretsFile, &config); err != nil {
				panic(err)
			}
		}
	}

	if overrides.logger != nil {
		log.SetGlobalLogger(overrides.logger.Sugar())
	} else {
		zlog := log.ConfigureLogger(config.ZapLogging)
		defer zlog.Sync()
	}

	stats := overrides.metrics
	if stats == nil {
		s, closer, err := metrics.New(config.Metrics, flags.KrakenCluster)
		if err != nil {
			log.Fatalf("Failed to init metrics: %s", err)
		}
		stats = s
		defer closer.Close()
	}

	go metrics.EmitVersion(stats)

	if flags.PeerIP == "" {
		localIP, err := netutil.GetLocalIP()
		if err != nil {
			log.Fatalf("Error getting local ip: %s", err)
		}
		flags.PeerIP = localIP
	}

	pctx, err := core.NewPeerContext(
		config.PeerIDFactory, flags.Zone, flags.KrakenCluster, flags.PeerIP, flags.PeerPort, false)
	if err != nil {
		log.Fatalf("Failed to create peer context: %s", err)
	}

	cads, err := store.NewCADownloadStore(config.CADownloadStore, stats)
	if err != nil {
		log.Fatalf("Failed to create local store: %s", err)
	}

	netevents, err := networkevent.NewProducer(config.NetworkEvent)
	if err != nil {
		log.Fatalf("Failed to create network event producer: %s", err)
	}

	trackers, err := config.Tracker.Build()
	if err != nil {
		log.Fatalf("Error building tracker upstream: %s", err)
	}
	go trackers.Monitor(nil)

	tls, err := config.TLS.BuildClient()
	if err != nil {
		log.Fatalf("Error building client tls config: %s", err)
	}

	sched, err := scheduler.NewAgentScheduler(
		config.Scheduler, stats, pctx, cads, netevents, trackers, tls)
	if err != nil {
		log.Fatalf("Error creating scheduler: %s", err)
	}

	buildIndexes, err := config.BuildIndex.Build()
	if err != nil {
		log.Fatalf("Error building build-index upstream: %s", err)
	}

	tagClient := tagclient.NewClusterClient(buildIndexes, tls)

	transferer := transfer.NewReadOnlyTransferer(stats, cads, tagClient, sched)

	registry, err := config.Registry.Build(config.Registry.ReadOnlyParameters(transferer, cads, stats))
	if err != nil {
		log.Fatalf("Failed to init registry: %s", err)
	}

	registryAddr := fmt.Sprintf("127.0.0.1:%d", flags.AgentRegistryPort)
	dockerCli, err := dockerdaemon.NewDockerClient(config.DockerDaemon, registryAddr)
	if err != nil {
		log.Fatalf("failed to init docker client for preload: %s", err)
	}

	agentServer := agentserver.New(
		config.AgentServer, stats, cads, sched, tagClient, dockerCli)
	addr := fmt.Sprintf(":%d", flags.AgentServerPort)
	log.Infof("Starting agent server on %s", addr)
	go func() {
		log.Fatal(http.ListenAndServe(addr, agentServer.Handler()))
	}()

	log.Info("Starting registry...")
	go func() {
		log.Fatal(registry.ListenAndServe())
	}()

	go heartbeat(stats)

	// Wipe log files created by the old nginx process which ran as root.
	// TODO(codyg): Swap these with the v2 log files once they are deleted.
	for _, name := range []string{
		"/var/log/kraken/kraken-agent/nginx-access.log",
		"/var/log/kraken/kraken-agent/nginx-error.log",
	} {
		if err := os.Remove(name); err != nil && !os.IsNotExist(err) {
			log.Warnf("Could not remove old root-owned nginx log: %s", err)
		}
	}

	log.Fatal(nginx.Run(config.Nginx, map[string]interface{}{
		"allowed_cidrs": config.AllowedCidrs,
		"port":          flags.AgentRegistryPort,
		"registry_server": nginx.GetServer(
			config.Registry.Docker.HTTP.Net, config.Registry.Docker.HTTP.Addr),
		"registry_backup": config.RegistryBackup},
		nginx.WithTLS(config.TLS)))
}

// heartbeat periodically emits a counter metric which allows us to monitor the
// number of active agents.
func heartbeat(stats tally.Scope) {
	for {
		stats.Counter("heartbeat").Inc(1)
		time.Sleep(10 * time.Second)
	}
}
<commit_msg>Delete outdated nginx log deletion (#281)<commit_after>// Copyright (c) 2016-2019 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd

import (
	"flag"
	"fmt"
	"net/http"
	"time"

	"github.com/uber/kraken/agent/agentserver"
	"github.com/uber/kraken/build-index/tagclient"
	"github.com/uber/kraken/core"
	"github.com/uber/kraken/lib/dockerdaemon"
	"github.com/uber/kraken/lib/dockerregistry/transfer"
	"github.com/uber/kraken/lib/store"
	"github.com/uber/kraken/lib/torrent/networkevent"
	"github.com/uber/kraken/lib/torrent/scheduler"
	"github.com/uber/kraken/metrics"
	"github.com/uber/kraken/nginx"
	"github.com/uber/kraken/utils/configutil"
	"github.com/uber/kraken/utils/log"
	"github.com/uber/kraken/utils/netutil"

	"github.com/uber-go/tally"
	"go.uber.org/zap"
)

// Flags defines agent CLI flags.
type Flags struct {
	PeerIP            string
	PeerPort          int
	AgentServerPort   int
	AgentRegistryPort int
	ConfigFile        string
	Zone              string
	KrakenCluster     string
	SecretsFile       string
}

// ParseFlags parses agent CLI flags.
func ParseFlags() *Flags {
	var flags Flags
	flag.StringVar(
		&flags.PeerIP, "peer-ip", "", "ip which peer will announce itself as")
	flag.IntVar(
		&flags.PeerPort, "peer-port", 0, "port which peer will announce itself as")
	flag.IntVar(
		&flags.AgentServerPort, "agent-server-port", 0, "port which agent server listens on")
	flag.IntVar(
		&flags.AgentRegistryPort, "agent-registry-port", 0, "port which agent registry listens on")
	flag.StringVar(
		&flags.ConfigFile, "config", "", "configuration file path")
	flag.StringVar(
		&flags.Zone, "zone", "", "zone/datacenter name")
	flag.StringVar(
		&flags.KrakenCluster, "cluster", "", "cluster name (e.g. prod01-zone1)")
	flag.StringVar(
		&flags.SecretsFile, "secrets", "", "path to a secrets YAML file to load into configuration")
	flag.Parse()
	return &flags
}

type options struct {
	config  *Config
	metrics tally.Scope
	logger  *zap.Logger
}

// Option defines an optional Run parameter.
type Option func(*options)

// WithConfig ignores config/secrets flags and directly uses the provided config
// struct.
func WithConfig(c Config) Option {
	return func(o *options) { o.config = &c }
}

// WithMetrics ignores metrics config and directly uses the provided tally scope.
func WithMetrics(s tally.Scope) Option {
	return func(o *options) { o.metrics = s }
}

// WithLogger ignores logging config and directly uses the provided logger.
func WithLogger(l *zap.Logger) Option {
	return func(o *options) { o.logger = l }
}

// Run runs the agent.
func Run(flags *Flags, opts ...Option) {
	if flags.PeerPort == 0 {
		panic("must specify non-zero peer port")
	}
	if flags.AgentServerPort == 0 {
		panic("must specify non-zero agent server port")
	}
	if flags.AgentRegistryPort == 0 {
		panic("must specify non-zero agent registry port")
	}

	var overrides options
	for _, o := range opts {
		o(&overrides)
	}

	var config Config
	if overrides.config != nil {
		config = *overrides.config
	} else {
		if err := configutil.Load(flags.ConfigFile, &config); err != nil {
			panic(err)
		}
		if flags.SecretsFile != "" {
			if err := configutil.Load(flags.SecretsFile, &config); err != nil {
				panic(err)
			}
		}
	}

	if overrides.logger != nil {
		log.SetGlobalLogger(overrides.logger.Sugar())
	} else {
		zlog := log.ConfigureLogger(config.ZapLogging)
		defer zlog.Sync()
	}

	stats := overrides.metrics
	if stats == nil {
		s, closer, err := metrics.New(config.Metrics, flags.KrakenCluster)
		if err != nil {
			log.Fatalf("Failed to init metrics: %s", err)
		}
		stats = s
		defer closer.Close()
	}

	go metrics.EmitVersion(stats)

	if flags.PeerIP == "" {
		localIP, err := netutil.GetLocalIP()
		if err != nil {
			log.Fatalf("Error getting local ip: %s", err)
		}
		flags.PeerIP = localIP
	}

	pctx, err := core.NewPeerContext(
		config.PeerIDFactory, flags.Zone, flags.KrakenCluster, flags.PeerIP, flags.PeerPort, false)
	if err != nil {
		log.Fatalf("Failed to create peer context: %s", err)
	}

	cads, err := store.NewCADownloadStore(config.CADownloadStore, stats)
	if err != nil {
		log.Fatalf("Failed to create local store: %s", err)
	}

	netevents, err := networkevent.NewProducer(config.NetworkEvent)
	if err != nil {
		log.Fatalf("Failed to create network event producer: %s", err)
	}

	trackers, err := config.Tracker.Build()
	if err != nil {
		log.Fatalf("Error building tracker upstream: %s", err)
	}
	go trackers.Monitor(nil)

	tls, err := config.TLS.BuildClient()
	if err != nil {
		log.Fatalf("Error building client tls config: %s", err)
	}

	sched, err := scheduler.NewAgentScheduler(
		config.Scheduler, stats, pctx, cads, netevents, trackers, tls)
	if err != nil {
		log.Fatalf("Error creating scheduler: %s", err)
	}

	buildIndexes, err := config.BuildIndex.Build()
	if err != nil {
		log.Fatalf("Error building build-index upstream: %s", err)
	}

	tagClient := tagclient.NewClusterClient(buildIndexes, tls)

	transferer := transfer.NewReadOnlyTransferer(stats, cads, tagClient, sched)

	registry, err := config.Registry.Build(config.Registry.ReadOnlyParameters(transferer, cads, stats))
	if err != nil {
		log.Fatalf("Failed to init registry: %s", err)
	}

	registryAddr := fmt.Sprintf("127.0.0.1:%d", flags.AgentRegistryPort)
	dockerCli, err := dockerdaemon.NewDockerClient(config.DockerDaemon, registryAddr)
	if err != nil {
		log.Fatalf("failed to init docker client for preload: %s", err)
	}

	agentServer := agentserver.New(
		config.AgentServer, stats, cads, sched, tagClient, dockerCli)
	addr := fmt.Sprintf(":%d", flags.AgentServerPort)
	log.Infof("Starting agent server on %s", addr)
	go func() {
		log.Fatal(http.ListenAndServe(addr, agentServer.Handler()))
	}()

	log.Info("Starting registry...")
	go func() {
		log.Fatal(registry.ListenAndServe())
	}()

	go heartbeat(stats)

	log.Fatal(nginx.Run(config.Nginx, map[string]interface{}{
		"allowed_cidrs": config.AllowedCidrs,
		"port":          flags.AgentRegistryPort,
		"registry_server": nginx.GetServer(
			config.Registry.Docker.HTTP.Net, config.Registry.Docker.HTTP.Addr),
		"registry_backup": config.RegistryBackup},
		nginx.WithTLS(config.TLS)))
}

// heartbeat periodically emits a counter metric which allows us to monitor the
// number of active agents.
func heartbeat(stats tally.Scope) {
	for {
		stats.Counter("heartbeat").Inc(1)
		time.Sleep(10 * time.Second)
	}
}
<|endoftext|>