<commit_before>package giterminism_test

import (
	"fmt"

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

	"github.com/werf/werf/integration/pkg/utils"
)

var _ = Describe("config go template", func() {
	BeforeEach(CommonBeforeEach)

	{
		type entryBase struct {
			allowUncommittedFilesGlob string
			addFiles                  []string
			commitFiles               []string
			changeFilesAfterCommit    []string
			expectedErrSubstring      string
		}

		bodyFuncBase := func(e entryBase) {
			if e.allowUncommittedFilesGlob != "" {
				contentToAppend := fmt.Sprintf(`
config:
  goTemplateRendering:
    allowUncommittedFiles: [%s]`, e.allowUncommittedFilesGlob)
				fileCreateOrAppend("werf-giterminism.yaml", contentToAppend)
				gitAddAndCommit("werf-giterminism.yaml")
			}

			for _, relPath := range e.addFiles {
				fileCreateOrAppend(relPath, fmt.Sprintf("# %s", relPath))
			}

			for _, relPath := range e.commitFiles {
				gitAddAndCommit(relPath)
			}

			for _, relPath := range e.changeFilesAfterCommit {
				fileCreateOrAppend(relPath, "\n")
			}

			output, err := utils.RunCommand(
				SuiteData.TestDirPath,
				SuiteData.WerfBinPath,
				"config", "render",
			)

			if e.expectedErrSubstring != "" {
				Ω(err).Should(HaveOccurred())
				Ω(string(output)).Should(ContainSubstring(e.expectedErrSubstring))
			} else {
				Ω(err).ShouldNot(HaveOccurred())

				for _, relPath := range e.addFiles {
					Ω(string(output)).Should(ContainSubstring(fmt.Sprintf("# %s", relPath)))
				}
			}
		}

		Context(".Files.Get", func() {
			DescribeTable("config.goTemplateRendering.allowUncommittedFiles",
				func(e entryBase) {
					fileCreateOrAppend("werf.yaml", fmt.Sprintf(`{{ .Files.Get "%s" }}`, ".werf/file"))
					gitAddAndCommit("werf.yaml")

					bodyFuncBase(e)
				},
				Entry("the file not found", entryBase{
					expectedErrSubstring: "error calling Get: {{ .Files.Get '.werf/file' }}: the file '.werf/file' not found in the project git repository",
				}),
				Entry("the file not committed", entryBase{
					addFiles:             []string{".werf/file"},
					expectedErrSubstring: "error calling Get: {{ .Files.Get '.werf/file' }}: the uncommitted configuration found in the project directory: the file '.werf/file' must be committed",
				}),
				Entry("the file committed", entryBase{
					addFiles:    []string{".werf/file"},
					commitFiles: []string{".werf/file"},
				}),
				Entry("config.goTemplateRendering.allowUncommittedFiles covers the not committed file", entryBase{
					allowUncommittedFilesGlob: ".werf/file",
					addFiles:                  []string{".werf/file"},
				}),
				Entry("config.goTemplateRendering.allowUncommittedFiles covers the committed file", entryBase{
					allowUncommittedFilesGlob: ".werf/file",
					addFiles:                  []string{".werf/file"},
					commitFiles:               []string{".werf/file"},
				}),
				Entry("the file committed, the file has uncommitted changes", entryBase{
					addFiles:               []string{".werf/file"},
					commitFiles:            []string{".werf/file"},
					changeFilesAfterCommit: []string{".werf/file"},
					expectedErrSubstring:   "error calling Get: {{ .Files.Get '.werf/file' }}: the uncommitted configuration found in the project directory: the file '.werf/file' changes must be committed",
				}),
			)
		})

		Context(".Files.Glob", func() {
			type entry struct {
				filesGlob string
				entryBase
			}

			DescribeTable("config.goTemplateRendering.allowUncommittedFiles",
				func(e entry) {
					fileCreateOrAppend("werf.yaml", fmt.Sprintf(`
{{ range $path, $content := .Files.Glob "%s" }}
{{ $content }}
{{ end }}
`, e.filesGlob))
					gitAddAndCommit("werf.yaml")

					bodyFuncBase(e.entryBase)
				},
				Entry("nothing found", entry{}),
				Entry("the file1 not committed", entry{
					filesGlob: ".werf/file1",
					entryBase: entryBase{
						addFiles:             []string{".werf/file1"},
						expectedErrSubstring: "error calling Glob: {{ .Files.Glob '.werf/file1' }}: the uncommitted configuration found in the project directory: the file '.werf/file1' must be committed",
					},
				}),
				Entry("the files not committed", entry{
					filesGlob: ".werf/*",
					entryBase: entryBase{
						addFiles: []string{".werf/file1", ".werf/file2", ".werf/file3"},
						expectedErrSubstring: `error calling Glob: {{ .Files.Glob '.werf/*' }}: the uncommitted configuration found in the project directory: the following files must be committed:

 - .werf/file1
 - .werf/file2
 - .werf/file3

`,
					},
				}),
				Entry("the file1 committed", entry{
					filesGlob: ".werf/file1",
					entryBase: entryBase{
						addFiles:    []string{".werf/file1"},
						commitFiles: []string{".werf/file1"},
					},
				}),
				Entry("the file1 committed, the file1 has uncommitted changes", entry{
					filesGlob: ".werf/file1",
					entryBase: entryBase{
						addFiles:               []string{".werf/file1"},
						commitFiles:            []string{".werf/file1"},
						changeFilesAfterCommit: []string{".werf/file1"},
						expectedErrSubstring:   "error calling Glob: {{ .Files.Glob '.werf/file1' }}: the uncommitted configuration found in the project directory: the file '.werf/file1' changes must be committed",
					},
				}),
				Entry("config.goTemplateRendering.allowUncommittedFiles (.werf/file1) covers the not committed file", entry{
					filesGlob: ".werf/file1",
					entryBase: entryBase{
						allowUncommittedFilesGlob: ".werf/file1",
						addFiles:                  []string{".werf/file1"},
					},
				}),
				Entry("config.goTemplateRendering.allowUncommittedFiles (.werf/file1) covers the committed file", entry{
					filesGlob: ".werf/file1",
					entryBase: entryBase{
						allowUncommittedFilesGlob: ".werf/file1",
						addFiles:                  []string{".werf/file1"},
						changeFilesAfterCommit:    []string{".werf/file1"},
					},
				}),
				Entry("config.goTemplateRendering.allowUncommittedFiles (/**/*/) covers the not committed files", entry{
					filesGlob: ".werf/*",
					entryBase: entryBase{
						allowUncommittedFilesGlob: `/**/*/`,
						addFiles:                  []string{".werf/file1", ".werf/file2", ".werf/file3"},
					},
				}),
			)
		})
	}
})
<commit_msg>[tests] Giterminism Suite: go template env tests<commit_after>package giterminism_test

import (
	"fmt"

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

	"github.com/werf/werf/integration/pkg/utils"
)

var _ = Describe("config go template", func() {
	BeforeEach(CommonBeforeEach)

	{
		type entryBase struct {
			allowUncommittedFilesGlob string
			addFiles                  []string
			commitFiles               []string
			changeFilesAfterCommit    []string
			expectedErrSubstring      string
		}

		bodyFuncBase := func(e entryBase) {
			if e.allowUncommittedFilesGlob != "" {
				contentToAppend := fmt.Sprintf(`
config:
  goTemplateRendering:
    allowUncommittedFiles: [%s]`, e.allowUncommittedFilesGlob)
				fileCreateOrAppend("werf-giterminism.yaml", contentToAppend)
				gitAddAndCommit("werf-giterminism.yaml")
			}

			for _, relPath := range e.addFiles {
				fileCreateOrAppend(relPath, fmt.Sprintf("# %s", relPath))
			}

			for _, relPath := range e.commitFiles {
				gitAddAndCommit(relPath)
			}

			for _, relPath := range e.changeFilesAfterCommit {
				fileCreateOrAppend(relPath, "\n")
			}

			output, err := utils.RunCommand(
				SuiteData.TestDirPath,
				SuiteData.WerfBinPath,
				"config", "render",
			)

			if e.expectedErrSubstring != "" {
				Ω(err).Should(HaveOccurred())
				Ω(string(output)).Should(ContainSubstring(e.expectedErrSubstring))
			} else {
				Ω(err).ShouldNot(HaveOccurred())

				for _, relPath := range e.addFiles {
					Ω(string(output)).Should(ContainSubstring(fmt.Sprintf("# %s", relPath)))
				}
			}
		}

		Context(".Files.Get", func() {
			DescribeTable("config.goTemplateRendering.allowUncommittedFiles",
				func(e entryBase) {
					fileCreateOrAppend("werf.yaml", fmt.Sprintf(`{{ .Files.Get "%s" }}`, ".werf/file"))
					gitAddAndCommit("werf.yaml")

					bodyFuncBase(e)
				},
				Entry("the file not found", entryBase{
					expectedErrSubstring: "error calling Get: {{ .Files.Get '.werf/file' }}: the file '.werf/file' not found in the project git repository",
				}),
				Entry("the file not committed", entryBase{
					addFiles:             []string{".werf/file"},
					expectedErrSubstring: "error calling Get: {{ .Files.Get '.werf/file' }}: the uncommitted configuration found in the project directory: the file '.werf/file' must be committed",
				}),
				Entry("the file committed", entryBase{
					addFiles:    []string{".werf/file"},
					commitFiles: []string{".werf/file"},
				}),
				Entry("config.goTemplateRendering.allowUncommittedFiles covers the not committed file", entryBase{
					allowUncommittedFilesGlob: ".werf/file",
					addFiles:                  []string{".werf/file"},
				}),
				Entry("config.goTemplateRendering.allowUncommittedFiles covers the committed file", entryBase{
					allowUncommittedFilesGlob: ".werf/file",
					addFiles:                  []string{".werf/file"},
					commitFiles:               []string{".werf/file"},
				}),
				Entry("the file committed, the file has uncommitted changes", entryBase{
					addFiles:               []string{".werf/file"},
					commitFiles:            []string{".werf/file"},
					changeFilesAfterCommit: []string{".werf/file"},
					expectedErrSubstring:   "error calling Get: {{ .Files.Get '.werf/file' }}: the uncommitted configuration found in the project directory: the file '.werf/file' changes must be committed",
				}),
			)
		})

		Context(".Files.Glob", func() {
			type entry struct {
				filesGlob string
				entryBase
			}

			DescribeTable("config.goTemplateRendering.allowUncommittedFiles",
				func(e entry) {
					fileCreateOrAppend("werf.yaml", fmt.Sprintf(`
{{ range $path, $content := .Files.Glob "%s" }}
{{ $content }}
{{ end }}
`, e.filesGlob))
					gitAddAndCommit("werf.yaml")

					bodyFuncBase(e.entryBase)
				},
				Entry("nothing found", entry{}),
				Entry("the file1 not committed", entry{
					filesGlob: ".werf/file1",
					entryBase: entryBase{
						addFiles:             []string{".werf/file1"},
						expectedErrSubstring: "error calling Glob: {{ .Files.Glob '.werf/file1' }}: the uncommitted configuration found in the project directory: the file '.werf/file1' must be committed",
					},
				}),
				Entry("the files not committed", entry{
					filesGlob: ".werf/*",
					entryBase: entryBase{
						addFiles: []string{".werf/file1", ".werf/file2", ".werf/file3"},
						expectedErrSubstring: `error calling Glob: {{ .Files.Glob '.werf/*' }}: the uncommitted configuration found in the project directory: the following files must be committed:

 - .werf/file1
 - .werf/file2
 - .werf/file3

`,
					},
				}),
				Entry("the file1 committed", entry{
					filesGlob: ".werf/file1",
					entryBase: entryBase{
						addFiles:    []string{".werf/file1"},
						commitFiles: []string{".werf/file1"},
					},
				}),
				Entry("the file1 committed, the file1 has uncommitted changes", entry{
					filesGlob: ".werf/file1",
					entryBase: entryBase{
						addFiles:               []string{".werf/file1"},
						commitFiles:            []string{".werf/file1"},
						changeFilesAfterCommit: []string{".werf/file1"},
						expectedErrSubstring:   "error calling Glob: {{ .Files.Glob '.werf/file1' }}: the uncommitted configuration found in the project directory: the file '.werf/file1' changes must be committed",
					},
				}),
				Entry("config.goTemplateRendering.allowUncommittedFiles (.werf/file1) covers the not committed file", entry{
					filesGlob: ".werf/file1",
					entryBase: entryBase{
						allowUncommittedFilesGlob: ".werf/file1",
						addFiles:                  []string{".werf/file1"},
					},
				}),
				Entry("config.goTemplateRendering.allowUncommittedFiles (.werf/file1) covers the committed file", entry{
					filesGlob: ".werf/file1",
					entryBase: entryBase{
						allowUncommittedFilesGlob: ".werf/file1",
						addFiles:                  []string{".werf/file1"},
						changeFilesAfterCommit:    []string{".werf/file1"},
					},
				}),
				Entry("config.goTemplateRendering.allowUncommittedFiles (/**/*/) covers the not committed files", entry{
					filesGlob: ".werf/*",
					entryBase: entryBase{
						allowUncommittedFilesGlob: `/**/*/`,
						addFiles:                  []string{".werf/file1", ".werf/file2", ".werf/file3"},
					},
				}),
			)
		})
	}

	Context("env", func() {
		type entry struct {
			allowEnvVariablesRegexp string
			addEnvName              string
			expectedErrSubstring    string
		}

		DescribeTable("config.goTemplateRendering.allowEnvVariables",
			func(e entry) {
				fileCreateOrAppend("werf.yaml", fmt.Sprintf(`{{ env "%s" }}`, e.addEnvName))
				gitAddAndCommit("werf.yaml")

				if e.allowEnvVariablesRegexp != "" {
					contentToAppend := fmt.Sprintf(`
config:
  goTemplateRendering:
    allowEnvVariables: [%s]`, e.allowEnvVariablesRegexp)
					fileCreateOrAppend("werf-giterminism.yaml", contentToAppend)
					gitAddAndCommit("werf-giterminism.yaml")
				}

				SuiteData.Stubs.SetEnv(e.addEnvName, fmt.Sprintf("# %s", e.addEnvName))
				output, err := utils.RunCommand(
					SuiteData.TestDirPath,
					SuiteData.WerfBinPath,
					"config", "render",
				)

				if e.expectedErrSubstring != "" {
					Ω(err).Should(HaveOccurred())
					Ω(string(output)).Should(ContainSubstring(e.expectedErrSubstring))
				} else {
					Ω(err).ShouldNot(HaveOccurred())
					Ω(string(output)).Should(ContainSubstring(fmt.Sprintf("# %s", e.addEnvName)))
				}
			},
			Entry("the env name not allowed", entry{
				addEnvName:           "NAME",
				expectedErrSubstring: "error calling env: the configuration with external dependency found in the werf config: env name 'NAME' not allowed",
			}),
			Entry("config.goTemplateRendering.allowEnvVariables (NAME) covers the env name", entry{
				allowEnvVariablesRegexp: "NAME",
				addEnvName:              "NAME",
			}),
			Entry("config.goTemplateRendering.allowEnvVariables (NA*) does not cover the env name", entry{
				allowEnvVariablesRegexp: "NA*",
				addEnvName:              "NAME",
				expectedErrSubstring:    "error calling env: the configuration with external dependency found in the werf config: env name 'NAME' not allowed",
			}),
			Entry("config.goTemplateRendering.allowEnvVariables (/NA*/) does not cover the env name", entry{
				allowEnvVariablesRegexp: "/NA*/",
				addEnvName:              "NAME",
				expectedErrSubstring:    "error calling env: the configuration with external dependency found in the werf config: env name 'NAME' not allowed",
			}),
		)
	})
})
<|endoftext|>


<commit_before>package html

import (
	"errors"
	"net/http"

	"github.com/chrisolsen/ae/auth"
)

type page map[string]interface{}

// NewPage creates a new page
func NewPage(r *http.Request) page {
	p := page(make(map[string]interface{}))
	token := auth.NewCSRFToken(r)
	p["CSRFToken"] = token
	return p
}

// SetError sets any error that needs to be shown
func (p page) SetError(err interface{}) {
	switch err.(type) {
	case string:
		p["Error"] = errors.New(err.(string))
	case error:
		p["Error"] = err.(error)
	default:
		p["Error"] = nil
	}
}

// SetUser sets the current user
func (p page) SetUser(user interface{}) {
	p["CurrentUser"] = user
}

// Set sets the key and value
func (p page) Set(key string, val interface{}) {
	p[key] = val
}
<commit_msg>allow pages to be created with CSRF tokens<commit_after>package html

import (
	"errors"
	"net/http"

	"github.com/chrisolsen/ae/auth"
)

type page map[string]interface{}

// NewPage creates a new page
func NewPage() page {
	return page(make(map[string]interface{}))
}

// NewPageWithCSRFToken create a page with an initialized CSRF token
func NewPageWithCSRFToken(r *http.Request) page {
	p := NewPage()
	token := auth.NewCSRFToken(r)
	p["CSRFToken"] = token
	return p
}

// SetError sets any error that needs to be shown
func (p page) SetError(err interface{}) {
	switch err.(type) {
	case string:
		p["Error"] = errors.New(err.(string))
	case error:
		p["Error"] = err.(error)
	default:
		p["Error"] = nil
	}
}

// SetUser sets the current user
func (p page) SetUser(user interface{}) {
	p["CurrentUser"] = user
}

// Set sets the key and value
func (p page) Set(key string, val interface{}) {
	p[key] = val
}
<|endoftext|>


<commit_before>package libxml2

import "testing"

func TestParseHTML(t *testing.T) {
	doc, err := ParseHTMLString(`<html><body><h1>Hello, World!</h1><p>Lorem Ipsum</p></body></html>`)
	if err != nil {
		t.Errorf("Failed to parse: %s", err)
		return
	}
	defer doc.Free()

	root := doc.DocumentElement()
	if ! root.IsSameNode(root) {
		t.Errorf("IsSameNode fails...")
		return
	}

	t.Logf(root.ToString(1, false))

	nodes, err := doc.FindNodes("/html/body/h1")
	if err != nil {
		t.Errorf("Failed to evaluate xpath: %s", err)
		return
	}
	if len(nodes) != 1 {
		t.Errorf("Could not find matching nodes")
		return
	}

	if nodes[0].TextContent() != "Hello, World!" {
		t.Errorf("h1 content is not 'Hello, World!', got %s", nodes[0].TextContent())
		return
	}
}
<commit_msg>oops, stray debug<commit_after>package libxml2

import "testing"

func TestParseHTML(t *testing.T) {
	doc, err := ParseHTMLString(`<html><body><h1>Hello, World!</h1><p>Lorem Ipsum</p></body></html>`)
	if err != nil {
		t.Errorf("Failed to parse: %s", err)
		return
	}
	defer doc.Free()

	root := doc.DocumentElement()
	if ! root.IsSameNode(root) {
		t.Errorf("IsSameNode fails...")
		return
	}

	nodes, err := doc.FindNodes("/html/body/h1")
	if err != nil {
		t.Errorf("Failed to evaluate xpath: %s", err)
		return
	}
	if len(nodes) != 1 {
		t.Errorf("Could not find matching nodes")
		return
	}

	if nodes[0].TextContent() != "Hello, World!" {
		t.Errorf("h1 content is not 'Hello, World!', got %s", nodes[0].TextContent())
		return
	}
}
<|endoftext|>


<commit_before>package http

import (
	"net/http"
	"webup/push"
	"webup/push/apns/sideshow"

	"github.com/labstack/echo"
)

type SendResource struct {
	Pusher push.Pusher
}

func (s *SendResource) Send() echo.HandlerFunc {
	return func(c echo.Context) error {
		s.Pusher = new(sideshow.Pusher)
		// s.Pusher.Setup()

		tokens := []push.Token{
			push.Token{
				UUID:     "tartanpion",
				Value:    "8ddea0121cfb9c770d5dd593c5332368a82cef5ab8aa71b9ce2a8218ce541166",
				Platform: push.IOS,
				Language: "fr",
			},
		}
		s.Pusher.Send(tokens)

		return c.String(http.StatusOK, "Tokens sent successfully")
	}
}
<commit_msg>Fix setup<commit_after>package http

import (
	"net/http"
	"webup/push"
	"webup/push/apns/sideshow"

	"github.com/labstack/echo"
)

type SendResource struct {
	Pusher push.Pusher
}

func (s *SendResource) Send() echo.HandlerFunc {
	return func(c echo.Context) error {
		s.Pusher = new(sideshow.Pusher)
		s.Pusher.Setup()

		tokens := []push.Token{
			push.Token{
				UUID:     "tartanpion",
				Value:    "8ddea0121cfb9c770d5dd593c5332368a82cef5ab8aa71b9ce2a8218ce541166",
				Platform: push.IOS,
				Language: "fr",
			},
		}
		s.Pusher.Send(tokens)

		return c.String(http.StatusOK, "Tokens sent successfully")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"net/http/httptest"
	"sync/atomic"
	"testing"
)

var getRequestConfig = &Config{
	method: "GET",
	url:    "http://localhost/",
}
var postRequestConfig = &Config{
	method: "POST",
	url:    "http://localhost/",
}

func init() {
	loadFile(postRequestConfig, "testdata/postfile.txt")
}

func TestHttpWorker(t *testing.T) {

	requests := 1
	var received int64 = 0

	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("hello"))
		atomic.AddInt64(&received, 1)
	}))
	defer ts.Close()

	config := &Config{
		concurrency: 1,
		requests:    requests,
		method:      "GET",
		url:         ts.URL,
	}

	context := NewContext(config)
	context.SetInt(CONTENT_SIZE, 5)

	jobs := make(chan *http.Request, 1)
	collector := make(chan *Record, 1)

	worker := NewHttpWorker(context, jobs, collector)

	go worker.Run()
	go func() {
		counter := 0
		for record := range collector {
			counter += 1
			if counter == requests || record.Error != nil {
				break
			}
		}
		close(context.stop)
	}()

	request, err := NewHttpRequest(config)
	if err != nil {
		t.Fatalf("new http request failed: %s", err)
	}

	jobs <- request
	<-context.stop

	if actualReceived := atomic.LoadInt64(&received); int64(requests) != actualReceived {
		t.Fatalf("expected to send %d requests and receive %d responses, but get %d responses", requests, requests, actualReceived)
	}
}

func BenchmarkNewHttpRequest_Get(b *testing.B) {
	for i := 0; i < b.N; i++ {
		NewHttpRequest(getRequestConfig)
	}
	b.ReportAllocs()
}

func BenchmarkNewHttpRequest_Post(b *testing.B) {
	for i := 0; i < b.N; i++ {
		NewHttpRequest(postRequestConfig)
	}
	b.ReportAllocs()
}

func BenchmarkCopyHttpRequest_Get(b *testing.B) {
	base, _ := NewHttpRequest(getRequestConfig)
	for i := 0; i < b.N; i++ {
		CopyHttpRequest(getRequestConfig, base)
	}
	b.ReportAllocs()
}

func BenchmarkCopyHttpRequest_Post(b *testing.B) {
	base, _ := NewHttpRequest(postRequestConfig)
	for i := 0; i < b.N; i++ {
		CopyHttpRequest(postRequestConfig, base)
	}
	b.ReportAllocs()
}
<commit_msg>compatible with go 1.0<commit_after>package main

import (
	"net/http"
	"net/http/httptest"
	"sync/atomic"
	"testing"
)

var getRequestConfig = &Config{
	method: "GET",
	url:    "http://localhost/",
}
var postRequestConfig = &Config{
	method: "POST",
	url:    "http://localhost/",
}

func init() {
	loadFile(postRequestConfig, "testdata/postfile.txt")
}

func TestHttpWorker(t *testing.T) {

	requests := 1
	var received int64 = 0

	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("hello"))
		atomic.AddInt64(&received, 1)
	}))
	defer ts.Close()

	config := &Config{
		concurrency: 1,
		requests:    requests,
		method:      "GET",
		url:         ts.URL,
	}

	context := NewContext(config)
	context.SetInt(CONTENT_SIZE, 5)

	jobs := make(chan *http.Request, 1)
	collector := make(chan *Record, 1)

	worker := NewHttpWorker(context, jobs, collector)

	go worker.Run()
	go func() {
		counter := 0
		for record := range collector {
			counter += 1
			if counter == requests || record.Error != nil {
				break
			}
		}
		close(context.stop)
	}()

	request, err := NewHttpRequest(config)
	if err != nil {
		t.Fatalf("new http request failed: %s", err)
	}

	jobs <- request
	<-context.stop

	if actualReceived := atomic.LoadInt64(&received); int64(requests) != actualReceived {
		t.Fatalf("expected to send %d requests and receive %d responses, but get %d responses", requests, requests, actualReceived)
	}
}

func BenchmarkNewHttpRequest_Get(b *testing.B) {
	for i := 0; i < b.N; i++ {
		NewHttpRequest(getRequestConfig)
	}
	//b.ReportAllocs()
}

func BenchmarkNewHttpRequest_Post(b *testing.B) {
	for i := 0; i < b.N; i++ {
		NewHttpRequest(postRequestConfig)
	}
	//b.ReportAllocs()
}

func BenchmarkCopyHttpRequest_Get(b *testing.B) {
	base, _ := NewHttpRequest(getRequestConfig)
	for i := 0; i < b.N; i++ {
		CopyHttpRequest(getRequestConfig, base)
	}
	//b.ReportAllocs()
}

func BenchmarkCopyHttpRequest_Post(b *testing.B) {
	base, _ := NewHttpRequest(postRequestConfig)
	for i := 0; i < b.N; i++ {
		CopyHttpRequest(postRequestConfig, base)
	}
	//b.ReportAllocs()
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/urfave/cli"
	"github.com/sergio-zerg/httpcheck/engine"
	"os"
	"sort"
	"time"
)

const version = "1.0.0"

func flags() []cli.Flag {
	flags := []cli.Flag{
		cli.StringFlag{
			Name:   "log-level, l",
			Value:  "warning",
			Usage:  "Set logging level: info, warning, error, fatal, debug, panic",
			EnvVar: "HTTPCHECK_LOG_LEVEL",
		},
		cli.StringFlag{
			Name:   "env, e",
			Value:  "dev",
			Usage:  "dev: input - set with flag -c (default checks.yaml), output - stdout. prod: input - consul, output - sensu",
			EnvVar: "HTTPCHECK_ENV",
		},
		cli.StringFlag{
			Name: "ip, i",
			//Value:  "127.0.0.1",
			Usage:  "ip-address for checks",
			EnvVar: "HTTPCHECK_IP",
		},
		cli.StringFlag{
			Name:   "config, f",
			Value:  "checks.yaml",
			Usage:  "ip-address for checks",
			EnvVar: "HTTPCHECK_CONFIG",
		},
		cli.StringFlag{
			Name:   "consul-api, c",
			Value:  "http://consul-api:8500",
			Usage:  "Consul API host",
			EnvVar: "HTTPCHECK_CONSUL_API",
		},
		cli.StringFlag{
			Name:   "sensu-api, s",
			Value:  "http://sensu-api:4567",
			Usage:  "Sensu API host",
			EnvVar: "HTTPCHECK_SENSU_API",
		},
		//@todo write validator for this flag?
		cli.StringFlag{
			Name:  "validate, d",
			Usage: "validate config",
		},
	}
	return flags
}

func main() {
	app := cli.NewApp()
	app.Compiled = time.Now()
	app.Name = "httpcheck"
	app.Usage = "make check web projects"
	app.Author = "sergio_zerg"
	app.Email = "zerg123@gmail.com"
	app.Copyright = "MIT License"
	app.HelpName = "httpcheck"
	app.Version = version
	app.Flags = flags()
	app.Action = func(c *cli.Context) {
		core := engine.NewEngine(c)
		core.Run()
	}
	sort.Sort(cli.FlagsByName(app.Flags))
	sort.Sort(cli.CommandsByName(app.Commands))
	app.Run(os.Args)
}
<commit_msg>fix glide config<commit_after>package main

import (
	"github.com/sergio-zerg/httpcheck/engine"
	"github.com/urfave/cli"
	"os"
	"sort"
	"time"
)

const version = "1.0.0"

func flags() []cli.Flag {
	flags := []cli.Flag{
		cli.StringFlag{
			Name:   "log-level, l",
			Value:  "warning",
			Usage:  "Set logging level: info, warning, error, fatal, debug, panic",
			EnvVar: "HTTPCHECK_LOG_LEVEL",
		},
		cli.StringFlag{
			Name:   "env, e",
			Value:  "dev",
			Usage:  "dev: input - set with flag -c (default checks.yaml), output - stdout. prod: input - consul, output - sensu",
			EnvVar: "HTTPCHECK_ENV",
		},
		cli.StringFlag{
			Name: "ip, i",
			//Value:  "127.0.0.1",
			Usage:  "ip-address for checks",
			EnvVar: "HTTPCHECK_IP",
		},
		cli.StringFlag{
			Name:   "config, f",
			Value:  "checks.yaml",
			Usage:  "ip-address for checks",
			EnvVar: "HTTPCHECK_CONFIG",
		},
		cli.StringFlag{
			Name:   "consul-api, c",
			Value:  "http://consul-api:8500",
			Usage:  "Consul API host",
			EnvVar: "HTTPCHECK_CONSUL_API",
		},
		cli.StringFlag{
			Name:   "sensu-api, s",
			Value:  "http://sensu-api:4567",
			Usage:  "Sensu API host",
			EnvVar: "HTTPCHECK_SENSU_API",
		},
		//@todo write validator for this flag?
		cli.StringFlag{
			Name:  "validate, d",
			Usage: "validate config",
		},
	}
	return flags
}

func main() {
	app := cli.NewApp()
	app.Compiled = time.Now()
	app.Name = "httpcheck"
	app.Usage = "make check web projects"
	app.Author = "sergio_zerg"
	app.Email = "zerg123@gmail.com"
	app.Copyright = "MIT License"
	app.HelpName = "httpcheck"
	app.Version = version
	app.Flags = flags()
	app.Action = func(c *cli.Context) {
		core := engine.NewEngine(c)
		core.Run()
	}
	sort.Sort(cli.FlagsByName(app.Flags))
	//sort.Sort(cli.CommandsByName(app.Commands))
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package albionstate

type AlbionState struct {
	locationId int
}
<commit_msg>Exposed LocationId.<commit_after>package albionstate

type AlbionState struct {
	LocationId int
}
<|endoftext|>


<commit_before>package obj

import (
	"fmt"
	"reflect"

	"github.com/polydawn/refmt/obj/atlas"
	. "github.com/polydawn/refmt/tok"
)

/*
	A lovely mechanism to stash marshalMachine objects pre-allocated and avoid mallocs.
	Works together with the Atlas: the Atlas says what kind of machinery is needed;
	the marshalSlab "allocates" it and returns it upon your request.
*/
type marshalSlab struct {
	atlas atlas.Atlas
	rows  []marshalSlabRow
}

type marshalSlabRow struct {
	ptrDerefDelegateMarshalMachine
	marshalMachinePrimitive
	marshalMachineWildcard
	marshalMachineMapWildcard
	marshalMachineSliceWildcard
	marshalMachineStructAtlas
	marshalMachineTransform

	errThunkMarshalMachine
}

// A thunk value that can be used to trigger `isNil` paths.
// (Substituting an 'invalid' kind reflect.Value with this is an easy way
// to emit a null without needing any additional special cases or error handling.)
var nil_rv reflect.Value = reflect.Zero(reflect.PtrTo(reflect.TypeOf(0)))

/*
	Return a reference to a machine from the slab.
	*You must release() when done.*

	Errors -- including "no info in Atlas for this type" -- are expressed by
	returning a machine that is a constantly-erroring thunk.
*/
func (slab *marshalSlab) requisitionMachine(rt reflect.Type) MarshalMachine {
	// Acquire a row.
	off := len(slab.rows)
	slab.grow()
	row := &slab.rows[off]

	// Indirect pointers as necessary.
	//  Keep count of how many times we do this; we'll use this again at the end.
	peelCount := 0
	for rt.Kind() == reflect.Ptr {
		rt = rt.Elem()
		peelCount++
	}

	// Figure out what machinery to use at heart.
	mach := _yieldMarshalMachinePtr(row, slab.atlas, rt)
	// If nil answer, we had no match: yield an error thunk.
	if mach == nil {
		mach := &row.errThunkMarshalMachine
		mach.err = fmt.Errorf("no machine found")
		return mach
	}

	// If no indirection steps, return;
	//  otherwise wrap it in the ptrDeref machine and return that.
	if peelCount == 0 {
		return mach
	}
	row.ptrDerefDelegateMarshalMachine.MarshalMachine = mach
	row.ptrDerefDelegateMarshalMachine.peelCount = peelCount
	row.ptrDerefDelegateMarshalMachine.isNil = false
	return &row.ptrDerefDelegateMarshalMachine
}

func _yieldMarshalMachinePtr(row *marshalSlabRow, atl atlas.Atlas, rt reflect.Type) MarshalMachine {
	rtid := reflect.ValueOf(rt).Pointer()

	// Check primitives first; cheapest (and unoverridable).
	switch rtid {
	case rtid_bool,
		rtid_string,
		rtid_int, rtid_int8, rtid_int16, rtid_int32, rtid_int64,
		rtid_uint, rtid_uint8, rtid_uint16, rtid_uint32, rtid_uint64, rtid_uintptr,
		rtid_float32, rtid_float64,
		rtid_bytes:
		row.marshalMachinePrimitive.kind = rt.Kind()
		return &row.marshalMachinePrimitive
	}

	// Consult atlas second.
	if entry, ok := atl.Get(rtid); ok {
		// Switch across which of the union of configurations is applicable.
		switch {
		case entry.MarshalTransformFunc != nil:
			// Return a machine that calls the func(s), then later a real machine.
			// The entry.MarshalTransformTargetType is used to do a recursive lookup.
			// We can't just call the func here because we're still working off typeinfo
			// and don't have a real value to transform until later.
			row.marshalMachineTransform.trFunc = entry.MarshalTransformFunc
			// Pick delegate without growing stack.  (This currently means recursive transform won't fly.)
			row.marshalMachineTransform.delegate = _yieldMarshalMachinePtr(row, atl, entry.MarshalTransformTargetType)
			// If tags are in play: have the transformer machine glue that on.
			if entry.Tagged {
				row.marshalMachineTransform.tagged = true
				row.marshalMachineTransform.tag = entry.Tag
			}
			return &row.marshalMachineTransform
		case entry.StructMap != nil:
			row.marshalMachineStructAtlas.cfg = entry
			return &row.marshalMachineStructAtlas
		default:
			panic("invalid atlas entry")
		}
	}

	// If no specific behavior found, use default behavior based on kind.
	switch rt.Kind() {
	case reflect.Bool,
		reflect.String,
		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
		reflect.Float32, reflect.Float64:
		row.marshalMachinePrimitive.kind = rt.Kind()
		return &row.marshalMachinePrimitive
	case reflect.Slice:
		// un-typedef'd byte slices were handled already, but a typedef'd one still gets gets treated like a special kind:
		if rt.Elem().Kind() == reflect.Uint8 {
			row.marshalMachinePrimitive.kind = rt.Kind()
			return &row.marshalMachinePrimitive
		}
		return &row.marshalMachineSliceWildcard
	case reflect.Array:
		return &row.marshalMachineSliceWildcard.marshalMachineArrayWildcard
	case reflect.Map:
		return &row.marshalMachineMapWildcard
	case reflect.Struct:
		panic("todo") // this is where we auto-detect, if you've enabled that; otherwise, raise error for missing configuration / unexpected type.
	case reflect.Interface:
		return &row.marshalMachineWildcard
	case reflect.Func:
		panic(fmt.Errorf("functions cannot be marshalled!"))
	case reflect.Ptr:
		panic(fmt.Errorf("unreachable: ptrs must already be resolved"))
	default:
		panic(fmt.Errorf("excursion %s", rt.Kind()))
	}
}

func (s *marshalSlab) grow() {
	s.rows = append(s.rows, marshalSlabRow{})
}

func (s *marshalSlab) release() {
	s.rows = s.rows[0 : len(s.rows)-1]
}

type errThunkMarshalMachine struct {
	err error
}

func (m *errThunkMarshalMachine) Reset(_ *marshalSlab, _ reflect.Value, _ reflect.Type) error {
	return m.err
}
func (m *errThunkMarshalMachine) Step(d *Marshaller, s *marshalSlab, tok *Token) (done bool, err error) {
	return true, m.err
}
<commit_msg>obj: better-behaved error message for missing struct atlas.<commit_after>package obj

import (
	"fmt"
	"reflect"

	"github.com/polydawn/refmt/obj/atlas"
	. "github.com/polydawn/refmt/tok"
)

/*
	A lovely mechanism to stash marshalMachine objects pre-allocated and avoid mallocs.
	Works together with the Atlas: the Atlas says what kind of machinery is needed;
	the marshalSlab "allocates" it and returns it upon your request.
*/
type marshalSlab struct {
	atlas atlas.Atlas
	rows  []marshalSlabRow
}

type marshalSlabRow struct {
	ptrDerefDelegateMarshalMachine
	marshalMachinePrimitive
	marshalMachineWildcard
	marshalMachineMapWildcard
	marshalMachineSliceWildcard
	marshalMachineStructAtlas
	marshalMachineTransform

	errThunkMarshalMachine
}

// A thunk value that can be used to trigger `isNil` paths.
// (Substituting an 'invalid' kind reflect.Value with this is an easy way
// to emit a null without needing any additional special cases or error handling.)
var nil_rv reflect.Value = reflect.Zero(reflect.PtrTo(reflect.TypeOf(0)))

/*
	Return a reference to a machine from the slab.
	*You must release() when done.*

	Errors -- including "no info in Atlas for this type" -- are expressed by
	returning a machine that is a constantly-erroring thunk.
*/
func (slab *marshalSlab) requisitionMachine(rt reflect.Type) MarshalMachine {
	// Acquire a row.
	off := len(slab.rows)
	slab.grow()
	row := &slab.rows[off]

	// Indirect pointers as necessary.
	//  Keep count of how many times we do this; we'll use this again at the end.
	peelCount := 0
	for rt.Kind() == reflect.Ptr {
		rt = rt.Elem()
		peelCount++
	}

	// Figure out what machinery to use at heart.
	mach := _yieldMarshalMachinePtr(row, slab.atlas, rt)
	// If nil answer, we had no match: yield an error thunk.
	if mach == nil {
		mach := &row.errThunkMarshalMachine
		mach.err = fmt.Errorf("no machine found")
		return mach
	}

	// If no indirection steps, return;
	//  otherwise wrap it in the ptrDeref machine and return that.
	if peelCount == 0 {
		return mach
	}
	row.ptrDerefDelegateMarshalMachine.MarshalMachine = mach
	row.ptrDerefDelegateMarshalMachine.peelCount = peelCount
	row.ptrDerefDelegateMarshalMachine.isNil = false
	return &row.ptrDerefDelegateMarshalMachine
}

func _yieldMarshalMachinePtr(row *marshalSlabRow, atl atlas.Atlas, rt reflect.Type) MarshalMachine {
	rtid := reflect.ValueOf(rt).Pointer()

	// Check primitives first; cheapest (and unoverridable).
	switch rtid {
	case rtid_bool,
		rtid_string,
		rtid_int, rtid_int8, rtid_int16, rtid_int32, rtid_int64,
		rtid_uint, rtid_uint8, rtid_uint16, rtid_uint32, rtid_uint64, rtid_uintptr,
		rtid_float32, rtid_float64,
		rtid_bytes:
		row.marshalMachinePrimitive.kind = rt.Kind()
		return &row.marshalMachinePrimitive
	}

	// Consult atlas second.
	if entry, ok := atl.Get(rtid); ok {
		// Switch across which of the union of configurations is applicable.
		switch {
		case entry.MarshalTransformFunc != nil:
			// Return a machine that calls the func(s), then later a real machine.
			// The entry.MarshalTransformTargetType is used to do a recursive lookup.
			// We can't just call the func here because we're still working off typeinfo
			// and don't have a real value to transform until later.
			row.marshalMachineTransform.trFunc = entry.MarshalTransformFunc
			// Pick delegate without growing stack.  (This currently means recursive transform won't fly.)
			row.marshalMachineTransform.delegate = _yieldMarshalMachinePtr(row, atl, entry.MarshalTransformTargetType)
			// If tags are in play: have the transformer machine glue that on.
			if entry.Tagged {
				row.marshalMachineTransform.tagged = true
				row.marshalMachineTransform.tag = entry.Tag
			}
			return &row.marshalMachineTransform
		case entry.StructMap != nil:
			row.marshalMachineStructAtlas.cfg = entry
			return &row.marshalMachineStructAtlas
		default:
			panic("invalid atlas entry")
		}
	}

	// If no specific behavior found, use default behavior based on kind.
	switch rt.Kind() {
	case reflect.Bool,
		reflect.String,
		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
		reflect.Float32, reflect.Float64:
		row.marshalMachinePrimitive.kind = rt.Kind()
		return &row.marshalMachinePrimitive
	case reflect.Slice:
		// un-typedef'd byte slices were handled already, but a typedef'd one still gets gets treated like a special kind:
		if rt.Elem().Kind() == reflect.Uint8 {
			row.marshalMachinePrimitive.kind = rt.Kind()
			return &row.marshalMachinePrimitive
		}
		return &row.marshalMachineSliceWildcard
	case reflect.Array:
		return &row.marshalMachineSliceWildcard.marshalMachineArrayWildcard
	case reflect.Map:
		return &row.marshalMachineMapWildcard
	case reflect.Struct:
		// TODO here we could also invoke automatic atlas autogen, if configured to be permitted
		mach := &row.errThunkMarshalMachine
		mach.err = fmt.Errorf("no atlas found for struct type %q", rt.String())
		return mach
	case reflect.Interface:
		return &row.marshalMachineWildcard
	case reflect.Func:
		panic(fmt.Errorf("functions cannot be marshalled!"))
	case reflect.Ptr:
		panic(fmt.Errorf("unreachable: ptrs must already be resolved"))
	default:
		panic(fmt.Errorf("excursion %s", rt.Kind()))
	}
}

func (s *marshalSlab) grow() {
	s.rows = append(s.rows, marshalSlabRow{})
}

func (s *marshalSlab) release() {
	s.rows = s.rows[0 : len(s.rows)-1]
}

type errThunkMarshalMachine struct {
	err error
}

func (m *errThunkMarshalMachine) Reset(_ *marshalSlab, _ reflect.Value, _ reflect.Type) error {
	return m.err
}
func (m *errThunkMarshalMachine) Step(d *Marshaller, s *marshalSlab, tok *Token) (done bool, err error) {
	return true, m.err
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All Rights Reserved.
//
// 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.

// ctclient is a command-line utility for interacting with CT logs.
package main

import (
	"context"
	"crypto/sha256"
	"encoding/pem"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"

	ct "github.com/google/certificate-transparency-go"
	"github.com/google/certificate-transparency-go/client"
	"github.com/google/certificate-transparency-go/jsonclient"
	"github.com/google/certificate-transparency-go/merkletree"
	"github.com/google/certificate-transparency-go/tls"
	"github.com/google/certificate-transparency-go/x509"
	"github.com/google/certificate-transparency-go/x509util"
)

var logURI = flag.String("log_uri", "http://ct.googleapis.com/aviator", "CT log base URI")
var pubKey = flag.String("pub_key", "", "Name of file containing log's public key")
var certChain = flag.String("cert_chain", "", "Name of file containing certificate chain as concatenated PEM files")
var textOut = flag.Bool("text", true, "Display certificates as text")
var getFirst = flag.Int64("first", -1, "First entry to get")
var getLast = flag.Int64("last", -1, "Last entry to get")

func ctTimestampToTime(ts uint64) time.Time {
	secs := int64(ts / 1000)
	msecs := int64(ts % 1000)
	return time.Unix(secs, msecs*1000000)
}

func signatureToString(signed *ct.DigitallySigned) string {
	return fmt.Sprintf("Signature: Hash=%v Sign=%v Value=%x", signed.Algorithm.Hash, signed.Algorithm.Signature, signed.Signature)
}

func getSTH(ctx context.Context, logClient *client.LogClient) {
	sth, err := logClient.GetSTH(ctx)
	if err != nil {
		log.Fatal(err)
	}
	// Display the STH
	when := ctTimestampToTime(sth.Timestamp)
	fmt.Printf("%v: Got STH for %v log (size=%d) at %v, hash %x\n", when, sth.Version, sth.TreeSize, *logURI, sth.SHA256RootHash)
	fmt.Printf("%v\n", signatureToString(&sth.TreeHeadSignature))
}

func addChain(ctx context.Context, logClient *client.LogClient) {
	if *certChain == "" {
		log.Fatalf("No certificate chain file specified with -cert_chain")
	}
	rest, err := ioutil.ReadFile(*certChain)
	if err != nil {
		log.Fatalf("Failed to read certificate file: %v", err)
	}
	var chain []ct.ASN1Cert
	for {
		var block *pem.Block
		block, rest = pem.Decode(rest)
		if block == nil {
			break
		}
		if block.Type == "CERTIFICATE" {
			chain = append(chain, ct.ASN1Cert{Data: block.Bytes})
		}
	}
	if len(chain) == 0 {
		log.Fatalf("No certificates found in %s", *certChain)
	}

	// Examine the leaf to see if it looks like a pre-certificate.
	isPrecert := false
	leaf, err := x509.ParseCertificate(chain[0].Data)
	if err == nil {
		count, _ := x509util.OidInExtensions(x509.OIDExtensionCTPoison, leaf.Extensions)
		if count > 0 {
			isPrecert = true
			fmt.Print("Uploading pre-certificate to log\n")
		}
	}

	var sct *ct.SignedCertificateTimestamp
	if isPrecert {
		sct, err = logClient.AddPreChain(ctx, chain)
	} else {
		sct, err = logClient.AddChain(ctx, chain)
	}
	if err != nil {
		log.Fatal(err)
	}
	// Calculate the leaf hash
	leafEntry := ct.CreateX509MerkleTreeLeaf(chain[0], sct.Timestamp)
	leafData, err := tls.Marshal(*leafEntry)
	if err != nil {
		log.Fatalf("Failed to tls.Marshal leaf: %v", err)
	}
	leafHash := sha256.Sum256(append([]byte{merkletree.LeafPrefix}, leafData...))

	// Display the SCT
	when := ctTimestampToTime(sct.Timestamp)
	fmt.Printf("%v: Uploaded chain of %d certs to %v log at %v\n", when, len(chain), sct.SCTVersion, *logURI)
	fmt.Printf("LeafHash: %x\n", leafHash)
	fmt.Printf("Signature: %v\n", signatureToString(&sct.Signature))
}

func getRoots(ctx context.Context, logClient *client.LogClient) {
	roots, err := logClient.GetAcceptedRoots(ctx)
	if err != nil {
		log.Fatal(err)
	}
	for _, root := range roots {
		showRawCert(root)
	}
}

func getEntries(ctx context.Context, logClient *client.LogClient) {
	if *getFirst == -1 {
		log.Fatal("No -first option supplied")
	}
	if *getLast == -1 {
		log.Fatal("No -last option supplied")
	}
	entries, err := logClient.GetEntries(ctx, *getFirst, *getLast)
	if err != nil {
		log.Fatal(err)
	}
	for _, entry := range entries {
		ts := entry.Leaf.TimestampedEntry
		when := ctTimestampToTime(ts.Timestamp)
		fmt.Printf("Index=%d Timestamp=%v ", entry.Index, when)
		switch ts.EntryType {
		case ct.X509LogEntryType:
			fmt.Printf("X.509 certificate:\n")
			showParsedCert(entry.X509Cert)
		case ct.PrecertLogEntryType:
			fmt.Printf("pre-certificate from issuer with keyhash %x:\n", entry.Precert.IssuerKeyHash)
			showRawCert(entry.Precert.Submitted)
		default:
			log.Fatalf("Unhandled log entry type %d", entry.Leaf.TimestampedEntry.EntryType)
		}
	}
}

func showRawCert(cert ct.ASN1Cert) {
	if *textOut {
		c, err := x509.ParseCertificate(cert.Data)
		if err != nil {
			log.Printf("Error parsing certificate: %q", err.Error())
			return
		}
		showParsedCert(c)
	} else {
		showPEMData(cert.Data)
	}
}

func showParsedCert(cert *x509.Certificate) {
	if *textOut {
		fmt.Printf("%s\n", x509util.CertificateToString(cert))
	} else {
		showPEMData(cert.Raw)
	}
}

func showPEMData(data []byte) {
	if err := pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: data}); err != nil {
		log.Printf("Failed to PEM encode cert: %q", err.Error())
	}
}

func dieWithUsage(msg string) {
	fmt.Fprintln(os.Stderr, msg)
	fmt.Fprintf(os.Stderr, "Usage: ctclient [options] <cmd>\n"+
		"where cmd is one of:\n"+
		"   sth         retrieve signed tree head\n"+
		"   upload      upload cert chain and show SCT (needs -cert_chain)\n"+
		"   getroots    show accepted roots\n"+
		"   getentries  get log entries (needs -first and -last)\n")
	os.Exit(1)
}

func main() {
	flag.Parse()
	httpClient := &http.Client{
		Timeout: 10 * time.Second,
		Transport: &http.Transport{
			TLSHandshakeTimeout:   30 * time.Second,
			ResponseHeaderTimeout: 30 * time.Second,
			MaxIdleConnsPerHost:   10,
			DisableKeepAlives:     false,
			MaxIdleConns:          100,
			IdleConnTimeout:       90 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
		},
	}
	var opts jsonclient.Options
	if *pubKey != "" {
		pubkey, err := ioutil.ReadFile(*pubKey)
		if err != nil {
			log.Fatal(err)
		}
		opts.PublicKey = string(pubkey)
	}
	logClient, err := client.New(*logURI, httpClient, opts)
	if err != nil {
		log.Fatal(err)
	}
	args := flag.Args()
	if len(args) != 1 {
		dieWithUsage("Need command argument")
	}
	ctx := context.Background()
	cmd := args[0]
	switch cmd {
	case "sth":
		getSTH(ctx, logClient)
	case "upload":
		addChain(ctx, logClient)
	case "getroots", "get_roots", "get-roots":
		getRoots(ctx, logClient)
	case "getentries", "get_entries", "get-entries":
		getEntries(ctx, logClient)
	default:
		dieWithUsage(fmt.Sprintf("Unknown command '%s'", cmd))
	}
}
<commit_msg>ctclient: add audit subcommand<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
//
// 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.

// ctclient is a command-line utility for interacting with CT logs.
package main

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"encoding/pem"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"

	ct "github.com/google/certificate-transparency-go"
	"github.com/google/certificate-transparency-go/client"
	"github.com/google/certificate-transparency-go/jsonclient"
	"github.com/google/certificate-transparency-go/merkletree"
	"github.com/google/certificate-transparency-go/tls"
	"github.com/google/certificate-transparency-go/x509"
	"github.com/google/certificate-transparency-go/x509util"
)

var (
	logURI    = flag.String("log_uri", "http://ct.googleapis.com/aviator", "CT log base URI")
	pubKey    = flag.String("pub_key", "", "Name of file containing log's public key")
	certChain = flag.String("cert_chain", "", "Name of file containing certificate chain as concatenated PEM files")
	textOut   = flag.Bool("text", true, "Display certificates as text")
	getFirst  = flag.Int64("first", -1, "First entry to get")
	getLast   = flag.Int64("last", -1, "Last entry to get")
	treeSize  = flag.Int64("size", -1, "Tree size to query at")
	leafHash  = flag.String("leaf_hash", "", "Leaf hash to retrieve (as hex string)")
)

func ctTimestampToTime(ts uint64) time.Time {
	secs := int64(ts / 1000)
	msecs := int64(ts % 1000)
	return time.Unix(secs, msecs*1000000)
}

func signatureToString(signed *ct.DigitallySigned) string {
	return fmt.Sprintf("Signature: Hash=%v Sign=%v Value=%x", signed.Algorithm.Hash, signed.Algorithm.Signature, signed.Signature)
}

func getSTH(ctx context.Context, logClient *client.LogClient) {
	sth, err := logClient.GetSTH(ctx)
	if err != nil {
		log.Fatal(err)
	}
	// Display the STH
	when := ctTimestampToTime(sth.Timestamp)
	fmt.Printf("%v: Got STH for %v log (size=%d) at %v, hash %x\n", when, sth.Version, sth.TreeSize, *logURI, sth.SHA256RootHash)
	fmt.Printf("%v\n", signatureToString(&sth.TreeHeadSignature))
}

func addChain(ctx context.Context, logClient *client.LogClient) {
	if *certChain == "" {
		log.Fatalf("No certificate chain file specified with -cert_chain")
	}
	rest, err := ioutil.ReadFile(*certChain)
	if err != nil {
		log.Fatalf("Failed to read certificate file: %v", err)
	}
	var chain []ct.ASN1Cert
	for {
		var block *pem.Block
		block, rest = pem.Decode(rest)
		if block == nil {
			break
		}
		if block.Type == "CERTIFICATE" {
			chain = append(chain, ct.ASN1Cert{Data: block.Bytes})
		}
	}
	if len(chain) == 0 {
		log.Fatalf("No certificates found in %s", *certChain)
	}

	// Examine the leaf to see if it looks like a pre-certificate.
	isPrecert := false
	leaf, err := x509.ParseCertificate(chain[0].Data)
	if err == nil {
		count, _ := x509util.OidInExtensions(x509.OIDExtensionCTPoison, leaf.Extensions)
		if count > 0 {
			isPrecert = true
			fmt.Print("Uploading pre-certificate to log\n")
		}
	}

	var sct *ct.SignedCertificateTimestamp
	if isPrecert {
		sct, err = logClient.AddPreChain(ctx, chain)
	} else {
		sct, err = logClient.AddChain(ctx, chain)
	}
	if err != nil {
		log.Fatal(err)
	}
	// Calculate the leaf hash
	leafEntry := ct.CreateX509MerkleTreeLeaf(chain[0], sct.Timestamp)
	leafData, err := tls.Marshal(*leafEntry)
	if err != nil {
		log.Fatalf("Failed to tls.Marshal leaf: %v", err)
	}
	leafHash := sha256.Sum256(append([]byte{merkletree.LeafPrefix}, leafData...))

	// Display the SCT
	when := ctTimestampToTime(sct.Timestamp)
	fmt.Printf("%v: Uploaded chain of %d certs to %v log at %v\n", when, len(chain), sct.SCTVersion, *logURI)
	fmt.Printf("LeafHash: %x\n", leafHash)
	fmt.Printf("Signature: %v\n", signatureToString(&sct.Signature))
}

func getRoots(ctx context.Context, logClient *client.LogClient) {
	roots, err := logClient.GetAcceptedRoots(ctx)
	if err != nil {
		log.Fatal(err)
	}
	for _, root := range roots {
		showRawCert(root)
	}
}

func getEntries(ctx context.Context, logClient *client.LogClient) {
	if *getFirst == -1 {
		log.Fatal("No -first option supplied")
	}
	if *getLast == -1 {
		log.Fatal("No -last option supplied")
	}
	entries, err := logClient.GetEntries(ctx, *getFirst, *getLast)
	if err != nil {
		log.Fatal(err)
	}
	for _, entry := range entries {
		ts := entry.Leaf.TimestampedEntry
		when := ctTimestampToTime(ts.Timestamp)
		fmt.Printf("Index=%d Timestamp=%v ", entry.Index, when)
		switch ts.EntryType {
		case ct.X509LogEntryType:
			fmt.Printf("X.509 certificate:\n")
			showParsedCert(entry.X509Cert)
		case ct.PrecertLogEntryType:
			fmt.Printf("pre-certificate from issuer with keyhash %x:\n", entry.Precert.IssuerKeyHash)
			showRawCert(entry.Precert.Submitted)
		default:
			log.Fatalf("Unhandled log entry type %d", entry.Leaf.TimestampedEntry.EntryType)
		}
	}
}

func getInclusionProof(ctx context.Context, logClient *client.LogClient) {
	var sth *ct.SignedTreeHead
	size := *treeSize
	if size <= 0 {
		var err error
		sth, err = logClient.GetSTH(ctx)
		if err != nil {
			log.Fatalf("Failed to get current STH: %v", err)
		}
		size = int64(sth.TreeSize)
	}
	// Display the inclusion proof.
	hash, err := hex.DecodeString(*leafHash)
	if err != nil || len(hash) != 32 {
		log.Fatal("No valid --leaf_hash supplied in hex")
	}
	rsp, err := logClient.GetProofByHash(ctx, hash, uint64(size))
	if err != nil {
		log.Fatalf("Failed to get-proof-by-hash: %v", err)
	}
	fmt.Printf("Inclusion proof for index %d in tree of size %d:\n", rsp.LeafIndex, size)
	for _, e := range rsp.AuditPath {
		fmt.Printf("  %x\n", e)
	}
	if sth != nil {
		// If we retrieved an STH we can verify the proof.
		verifier := merkletree.NewMerkleVerifier(func(data []byte) []byte {
			hash := sha256.Sum256(data)
			return hash[:]
		})
		if err := verifier.VerifyInclusionProofByHash(rsp.LeafIndex, int64(sth.TreeSize), rsp.AuditPath, sth.SHA256RootHash[:], hash); err != nil {
			log.Fatalf("Failed to VerifyInclusionProofByHash(%d, %d)=%v", rsp.LeafIndex, sth.TreeSize, err)
		}
		fmt.Printf("Verified that hash %x + proof = root hash %x\n", hash, sth.SHA256RootHash)
	}
}

func showRawCert(cert ct.ASN1Cert) {
	if *textOut {
		c, err := x509.ParseCertificate(cert.Data)
		if err != nil {
			log.Printf("Error parsing certificate: %q", err.Error())
			return
		}
		showParsedCert(c)
	} else {
		showPEMData(cert.Data)
	}
}

func showParsedCert(cert *x509.Certificate) {
	if *textOut {
		fmt.Printf("%s\n", x509util.CertificateToString(cert))
	} else {
		showPEMData(cert.Raw)
	}
}

func showPEMData(data []byte) {
	if err := pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: data}); err != nil {
		log.Printf("Failed to PEM encode cert: %q", err.Error())
	}
}

func dieWithUsage(msg string) {
	fmt.Fprintln(os.Stderr, msg)
	fmt.Fprintf(os.Stderr, "Usage: ctclient [options] <cmd>\n"+
		"where cmd is one of:\n"+
		"   sth         retrieve signed tree head\n"+
		"   upload      upload cert chain and show SCT (needs -cert_chain)\n"+
		"   getroots    show accepted roots\n"+
		"   getentries  get log entries (needs -first and -last)\n"+
		"   inclusion   get inclusion proof (needs -leaf_hash and optionally -size)\n")
	os.Exit(1)
}

func main() {
	flag.Parse()
	httpClient := &http.Client{
		Timeout: 10 * time.Second,
		Transport: &http.Transport{
			TLSHandshakeTimeout:   30 * time.Second,
			ResponseHeaderTimeout: 30 * time.Second,
			MaxIdleConnsPerHost:   10,
			DisableKeepAlives:     false,
			MaxIdleConns:          100,
			IdleConnTimeout:       90 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
		},
	}
	var opts jsonclient.Options
	if *pubKey != "" {
		pubkey, err := ioutil.ReadFile(*pubKey)
		if err != nil {
			log.Fatal(err)
		}
		opts.PublicKey = string(pubkey)
	}
	logClient, err := client.New(*logURI, httpClient, opts)
	if err != nil {
		log.Fatal(err)
	}
	args := flag.Args()
	if len(args) != 1 {
		dieWithUsage("Need command argument")
	}
	ctx := context.Background()
	cmd := args[0]
	switch cmd {
	case "sth":
		getSTH(ctx, logClient)
	case "upload":
		addChain(ctx, logClient)
	case "getroots", "get_roots", "get-roots":
		getRoots(ctx, logClient)
	case "getentries", "get_entries", "get-entries":
		getEntries(ctx, logClient)
	case "inclusion", "inclusion-proof":
		getInclusionProof(ctx, logClient)
	default:
		dieWithUsage(fmt.Sprintf("Unknown command '%s'", cmd))
	}
}
<|endoftext|>


<commit_before>package leaktest

import (
	"fmt"
	"testing"
	"time"
)

type testReporter struct {
	failed bool
	msg    string
}

func (tr *testReporter) Errorf(format string, args ...interface{}) {
	tr.failed = true
	tr.msg = fmt.Sprintf(format, args)
}

func TestCheck(t *testing.T) {
	checker := &testReporter{}

	snapshot := Check(checker)
	go func() {
		for {
			time.Sleep(time.Second)
		}
	}()

	snapshot()
	if !checker.failed {
		t.Errorf("didn't catch sleeping goroutine")
	}
}
<commit_msg>add several tests cases from golang/go#12989<commit_after>package leaktest

import (
	"fmt"
	"sync"
	"testing"
	"time"
)

type testReporter struct {
	failed bool
	msg    string
}

func (tr *testReporter) Errorf(format string, args ...interface{}) {
	tr.failed = true
	tr.msg = fmt.Sprintf(format, args)
}

var leakyFuncs = []func(){
	// Infinite for loop
	func() {
		for {
			time.Sleep(time.Second)
		}
	},
	// Select on a channel not referenced by other goroutines.
	func() {
		c := make(chan struct{}, 0)
		select {
		case <-c:
		}
	},
	// Blocked select on channels not referenced by other goroutines.
	func() {
		c := make(chan struct{}, 0)
		c2 := make(chan struct{}, 0)
		select {
		case <-c:
		case c2 <- struct{}{}:
		}
	},
	// Blocking wait on sync.Mutex that isn't referenced by other goroutines.
	func() {
		var mu sync.Mutex
		mu.Lock()
		mu.Lock()
	},
	// Blocking wait on sync.RWMutex that isn't referenced by other goroutines.
	func() {
		var mu sync.RWMutex
		mu.RLock()
		mu.Lock()
	},
	func() {
		var mu sync.Mutex
		mu.Lock()
		c := sync.NewCond(&mu)
		c.Wait()
	},
}

func TestCheck(t *testing.T) {

	// this works because the running goroutine is left running at the
	// start of the next test case - so the previous leaks don't affect the
	// check for the next one
	for i, fn := range leakyFuncs {
		checker := &testReporter{}
		snapshot := Check(checker)
		go fn()

		snapshot()
		if !checker.failed {
			t.Errorf("didn't catch sleeping goroutine, test #%d", i)
		}
	}
}
<|endoftext|>


<commit_before>package couchbase

import (
	"errors"
	"time"

	"github.com/dustin/gomemcached/client"
)

var TimeoutError = errors.New("timeout waiting to build connection")

type connectionPool struct {
	host        string
	mkConn      func(host string, ah AuthHandler) (*memcached.Client, error)
	auth        AuthHandler
	connections chan *memcached.Client
	createsem   chan bool
}

func newConnectionPool(host string, ah AuthHandler, poolSize int) *connectionPool {
	return &connectionPool{
		host:        host,
		connections: make(chan *memcached.Client, poolSize),
		createsem:   make(chan bool, 2*poolSize),
		mkConn:      defaultMkConn,
		auth:        ah,
	}
}

func defaultMkConn(host string, ah AuthHandler) (*memcached.Client, error) {
	conn, err := memcached.Connect("tcp", host)
	if err != nil {
		return nil, err
	}
	name, pass := ah.GetCredentials()
	if name != "default" {
		conn.Auth(name, pass) // error checking?
	}
	return conn, nil
}

func (cp *connectionPool) Close() (err error) {
	defer func() { err, _ = recover().(error) }()
	close(cp.connections)
	for c := range cp.connections {
		c.Close()
	}
	return
}

func (cp *connectionPool) GetWithTimeout(d time.Duration) (*memcached.Client, error) {
	if cp == nil {
		return nil, errors.New("no pool")
	}

	select {
	case rv := <-cp.connections:
		return rv, nil
	case <-time.After(time.Millisecond):
		select {
		case rv := <-cp.connections:
			return rv, nil
		case cp.createsem <- true:
			// Build a connection if we can't get a real one.
			// This can potentially be an overflow connection, or
			// a pooled connection.
			return cp.mkConn(cp.host, cp.auth)
		case <-time.After(d):
			return nil, TimeoutError
		}
	}
}

func (cp *connectionPool) Get() (*memcached.Client, error) {
	return cp.GetWithTimeout(time.Hour * 24 * 30)
}

func (cp *connectionPool) Return(c *memcached.Client) {
	if cp == nil || c == nil {
		return
	}

	if c.IsHealthy() {
		select {
		case cp.connections <- c:
		default:
			// Overflow connection.
			<-cp.createsem
			c.Close()
		}
	} else {
		<-cp.createsem
		c.Close()
	}
}

func (cp *connectionPool) StartTapFeed(args *memcached.TapArguments) (*memcached.TapFeed, error) {
	if cp == nil {
		return nil, errors.New("no pool")
	}
	mc, err := cp.Get()
	if err != nil {
		return nil, err
	}
	return mc.StartTapFeed(*args)
}
<commit_msg>Fix potential pool leak on conn error<commit_after>package couchbase

import (
	"errors"
	"time"

	"github.com/dustin/gomemcached/client"
)

var TimeoutError = errors.New("timeout waiting to build connection")

type connectionPool struct {
	host        string
	mkConn      func(host string, ah AuthHandler) (*memcached.Client, error)
	auth        AuthHandler
	connections chan *memcached.Client
	createsem   chan bool
}

func newConnectionPool(host string, ah AuthHandler, poolSize int) *connectionPool {
	return &connectionPool{
		host:        host,
		connections: make(chan *memcached.Client, poolSize),
		createsem:   make(chan bool, 2*poolSize),
		mkConn:      defaultMkConn,
		auth:        ah,
	}
}

func defaultMkConn(host string, ah AuthHandler) (*memcached.Client, error) {
	conn, err := memcached.Connect("tcp", host)
	if err != nil {
		return nil, err
	}
	name, pass := ah.GetCredentials()
	if name != "default" {
		conn.Auth(name, pass) // error checking?
	}
	return conn, nil
}

func (cp *connectionPool) Close() (err error) {
	defer func() { err, _ = recover().(error) }()
	close(cp.connections)
	for c := range cp.connections {
		c.Close()
	}
	return
}

func (cp *connectionPool) GetWithTimeout(d time.Duration) (*memcached.Client, error) {
	if cp == nil {
		return nil, errors.New("no pool")
	}

	select {
	case rv := <-cp.connections:
		return rv, nil
	case <-time.After(time.Millisecond):
		select {
		case rv := <-cp.connections:
			return rv, nil
		case cp.createsem <- true:
			// Build a connection if we can't get a real one.
			// This can potentially be an overflow connection, or
			// a pooled connection.
			rv, err := cp.mkConn(cp.host, cp.auth)
			if err != nil {
				// On error, release our create hold
				<-cp.createsem
			}
			return rv, err
		case <-time.After(d):
			return nil, TimeoutError
		}
	}
}

func (cp *connectionPool) Get() (*memcached.Client, error) {
	return cp.GetWithTimeout(time.Hour * 24 * 30)
}

func (cp *connectionPool) Return(c *memcached.Client) {
	if cp == nil || c == nil {
		return
	}

	if c.IsHealthy() {
		select {
		case cp.connections <- c:
		default:
			// Overflow connection.
			<-cp.createsem
			c.Close()
		}
	} else {
		<-cp.createsem
		c.Close()
	}
}

func (cp *connectionPool) StartTapFeed(args *memcached.TapArguments) (*memcached.TapFeed, error) {
	if cp == nil {
		return nil, errors.New("no pool")
	}
	mc, err := cp.Get()
	if err != nil {
		return nil, err
	}
	return mc.StartTapFeed(*args)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/fsouza/go-dockerclient"
	"github.com/pwaller/barrier"
)

type Container struct {
	Name      string
	ImageName string
	Args, Env []string
	Volumes   []string
	StatusURI string

	client    *docker.Client
	container *docker.Container

	Failed, Superceded, Obtained, Ready, Closing barrier.Barrier

	wg *sync.WaitGroup

	Errors  <-chan error
	errorsW chan<- error
}

type SourceType int

const (
	// Run build with current directory with context
	BuildCwd            SourceType = iota
	BuildTarballContent            // Build with specified io.Reader as context
	BuildTarballURL                // Build with specified remote URL as context
	DockerPull                     // Run a docker pull to obtain the image
	GithubRepository               // build a github repository by making a local mirror
)

type ContainerSource struct {
	Type                SourceType
	buildTarballContent io.Reader
	buildDirectory      string
	buildTarballURL     string
	dockerImageName     string
	githubURL           string
	githubRef           string
}

// Construct a *Container. When the `wg` WaitGroup is zero, there is nothing
// outstanding (such as firewall rules which need garbage collecting).
func NewContainer(client *docker.Client, name string, wg *sync.WaitGroup) *Container {

	errors := make(chan error)

	c := &Container{
		Name:    name,
		client:  client,
		wg:      wg,
		Errors:  errors,
		errorsW: errors,
	}

	// If the container fails we should assume it should be torn down.
	c.Failed.Forward(&c.Closing)

	return c
}

func makeVolumeSet(in []string) map[string]struct{} {
	volumes := map[string]struct{}{}
	for _, v := range in {
		if strings.Contains(v, ":") {
			continue
		}
		volumes[v] = struct{}{}
	}
	return volumes
}

func makeBinds(in []string) []string {
	binds := []string{}
	for _, v := range in {
		if !strings.Contains(v, ":") {
			continue
		}
		binds = append(binds, v)
	}
	return binds
}

// `docker create` the container.
func (c *Container) Create(imageName string) error {
	opts := docker.CreateContainerOptions{
		Name: c.Name,
		Config: &docker.Config{
			Hostname:     c.Name,
			AttachStdout: true,
			AttachStderr: true,
			Env:          c.Env,
			Cmd:          c.Args,
			Image:        imageName,
			Volumes:      makeVolumeSet(c.Volumes),
		},
	}

	var err error
	c.container, err = c.client.CreateContainer(opts)

	return err
}

// CopyOutput copies the output of the container to `w` and blocks until
// completion
func (c *Container) CopyOutput() error {

	// TODO(pwaller): at some point move this on to 'c' for configurability?
	w := os.Stderr
	// Blocks until stream closed
	return c.client.AttachToContainer(docker.AttachToContainerOptions{
		Container:    c.container.ID,
		OutputStream: w,
		ErrorStream:  w,
		Logs:         true,
		Stdout:       true,
		Stderr:       true,
		Stream:       true,
	})
}

// :todo(drj): May want to return errors for truly broken containers (timeout).
// Poll for the program inside the container being ready to accept connections
// Returns `true` for success and `false` for failure.
func (c *Container) AwaitListening() bool {

	const (
		DefaultTimeout = 5 * time.Minute
		PollFrequency  = 10 // times per second (via integer division of ns)
	)

	startDeadline := time.Now().Add(DefaultTimeout)

	for _, port := range c.container.NetworkSettings.PortMappingAPI() {
		url := fmt.Sprint("http://", port.IP, ":", port.PublicPort, c.StatusURI)
		for {
			response, err := http.Get(url)
			if response != nil && response.Body != nil {
				response.Body.Close()
			}
			if err == nil {
				switch response.StatusCode {
				case http.StatusOK:
					return true
				case http.StatusNotFound:
				default:
					log.Printf("Got non-200 status code: %v, giving up",
						response.StatusCode)
					c.Failed.Fall()
					return false
				}
			}

			if time.Now().After(startDeadline) {
				log.Printf("Took longer than %v to start, giving up", DefaultTimeout)
				return false
			}

			time.Sleep(time.Second / PollFrequency)

			select {
			case <-c.Closing.Barrier():
				// If the container has closed, cease waiting
				return false
			default:
			}
		}
	}

	return false
}

// Given an internal port, return the port mapped by docker, if there is one.
func (c *Container) MappedPort(internal int) (int, bool) {
	for _, m := range c.container.NetworkSettings.PortMappingAPI() {
		if int(m.PrivatePort) == internal {
			return int(m.PublicPort), true
		}
	}
	return -1, false
}

// Start the container (and notify it if c.Closing falls)
func (c *Container) Start() error {
	hc := &docker.HostConfig{
		PublishAllPorts: true,
		Binds:           makeBinds(c.Volumes),
	}
	err := c.client.StartContainer(c.container.ID, hc)
	if err != nil {
		return err
	}

	// Load container.NetworkSettings
	c.container, err = c.client.InspectContainer(c.container.ID)
	if err != nil {
		return err
	}

	// Listen on the Closing barrier and send a kill to the container if it
	// falls.
	c.wg.Add(1)
	go func() {
		defer c.wg.Done()

		<-c.Closing.Barrier()
		// If the container is signaled to close, send a kill signal
		err := c.client.KillContainer(docker.KillContainerOptions{
			ID: c.container.ID,
		})
		if err == nil {
			return
		}
		switch err := err.(type) {
		case *docker.NoSuchContainer:
			// The container already went away, who cares.
			return
		default:
			log.Println("Killing container failed:", c.container.ID, err)
		}
	}()
	return nil
}

// Wait until container exits
func (c *Container) Wait() (int, error) {
	return c.client.WaitContainer(c.container.ID)
}

// Internal function for raising an error.
func (c *Container) err(err error) {
	c.errorsW <- err
	c.Closing.Fall()
}

// Manage the whole lifecycle of the container in response to a request to
// start it.
func (c *Container) Run(imageSource ImageSource, payload []byte) (int, error) {

	defer c.Closing.Fall()
	defer close(c.errorsW)

	go func() {
		for err := range c.Errors {
			log.Println("BUG: Async container error:", err)
			// TODO(pwaller): If this case is hit we might not want to
			// tear the container down really.
			c.Failed.Fall()
		}
	}()

	imageName, err := imageSource.Obtain(c.client, payload)
	c.Obtained.Fall()
	if err != nil {
		c.Failed.Fall()
		return -2, err
	}

	err = c.Create(imageName)
	if err != nil {
		c.Failed.Fall()
		return -1, err
	}
	defer c.Delete()

	err = c.Start()
	if err != nil {
		c.Failed.Fall()
		return -1, err
	}

	// Must come after container start has succeeded, otherwise we end up
	// perpetually attached if it fails to succeed, which blocks program exit.
	// Program exit must be blocked ordinarily until this completes so that
	// if we are quitting we see all of the messages sent by the container
	// until it quit.
	c.wg.Add(1)
	go func() {
		defer c.wg.Done()

		err := c.CopyOutput()
		if err != nil {
			c.err(err)
		}
	}()

	go func() {
		if !c.AwaitListening() {
			c.Failed.Fall()
			return
		}
		c.Ready.Fall()
	}()

	return c.Wait()
}

func (c *Container) Delete() {
	err := c.client.RemoveContainer(docker.RemoveContainerOptions{
		ID:            c.container.ID,
		RemoveVolumes: true,
		Force:         true,
	})
	if err != nil {
		log.Println("Warn: failed to delete container:", err)
	}
}
<commit_msg>Force-delete duplicate containers<commit_after>package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/fsouza/go-dockerclient"
	"github.com/pwaller/barrier"
)

type Container struct {
	Name      string
	ImageName string
	Args, Env []string
	Volumes   []string
	StatusURI string

	client    *docker.Client
	container *docker.Container

	Failed, Superceded, Obtained, Ready, Closing barrier.Barrier

	wg *sync.WaitGroup

	Errors  <-chan error
	errorsW chan<- error
}

type SourceType int

const (
	// Run build with current directory with context
	BuildCwd            SourceType = iota
	BuildTarballContent            // Build with specified io.Reader as context
	BuildTarballURL                // Build with specified remote URL as context
	DockerPull                     // Run a docker pull to obtain the image
	GithubRepository               // build a github repository by making a local mirror
)

type ContainerSource struct {
	Type                SourceType
	buildTarballContent io.Reader
	buildDirectory      string
	buildTarballURL     string
	dockerImageName     string
	githubURL           string
	githubRef           string
}

// Construct a *Container. When the `wg` WaitGroup is zero, there is nothing
// outstanding (such as firewall rules which need garbage collecting).
func NewContainer(client *docker.Client, name string, wg *sync.WaitGroup) *Container {

	errors := make(chan error)

	c := &Container{
		Name:    name,
		client:  client,
		wg:      wg,
		Errors:  errors,
		errorsW: errors,
	}

	// If the container fails we should assume it should be torn down.
	c.Failed.Forward(&c.Closing)

	return c
}

func makeVolumeSet(in []string) map[string]struct{} {
	volumes := map[string]struct{}{}
	for _, v := range in {
		if strings.Contains(v, ":") {
			continue
		}
		volumes[v] = struct{}{}
	}
	return volumes
}

func makeBinds(in []string) []string {
	binds := []string{}
	for _, v := range in {
		if !strings.Contains(v, ":") {
			continue
		}
		binds = append(binds, v)
	}
	return binds
}

// `docker create` the container.
func (c *Container) Create(imageName string) error {
	opts := docker.CreateContainerOptions{
		Name: c.Name,
		Config: &docker.Config{
			Hostname:     c.Name,
			AttachStdout: true,
			AttachStderr: true,
			Env:          c.Env,
			Cmd:          c.Args,
			Image:        imageName,
			Volumes:      makeVolumeSet(c.Volumes),
		},
	}

	var err error

	c.container, err = c.client.CreateContainer(opts)

	switch err := err.(type) {
	case *docker.Error:
		if err.Status == http.StatusConflict {

			log.Printf("Container already exists, removing old one: %q", c.Name)
			// Remove the old one and try again
			err := c.client.RemoveContainer(docker.RemoveContainerOptions{
				Force: true, ID: c.Name,
			})

			if err != nil {
				return fmt.Errorf("duplicate container, failed to remove other: %v", err)
			}

			// Try again
			c.container, err = c.client.CreateContainer(opts)

			return err
		}
	default:
	}

	return err
}

// CopyOutput copies the output of the container to `w` and blocks until
// completion
func (c *Container) CopyOutput() error {

	// TODO(pwaller): at some point move this on to 'c' for configurability?
	w := os.Stderr
	// Blocks until stream closed
	return c.client.AttachToContainer(docker.AttachToContainerOptions{
		Container:    c.container.ID,
		OutputStream: w,
		ErrorStream:  w,
		Logs:         true,
		Stdout:       true,
		Stderr:       true,
		Stream:       true,
	})
}

// :todo(drj): May want to return errors for truly broken containers (timeout).
// Poll for the program inside the container being ready to accept connections
// Returns `true` for success and `false` for failure.
func (c *Container) AwaitListening() bool {

	const (
		DefaultTimeout = 5 * time.Minute
		PollFrequency  = 10 // times per second (via integer division of ns)
	)

	startDeadline := time.Now().Add(DefaultTimeout)

	for _, port := range c.container.NetworkSettings.PortMappingAPI() {
		url := fmt.Sprint("http://", port.IP, ":", port.PublicPort, c.StatusURI)
		for {
			response, err := http.Get(url)
			if response != nil && response.Body != nil {
				response.Body.Close()
			}
			if err == nil {
				switch response.StatusCode {
				case http.StatusOK:
					return true
				case http.StatusNotFound:
				default:
					log.Printf("Got non-200 status code: %v, giving up",
						response.StatusCode)
					c.Failed.Fall()
					return false
				}
			}

			if time.Now().After(startDeadline) {
				log.Printf("Took longer than %v to start, giving up", DefaultTimeout)
				return false
			}

			time.Sleep(time.Second / PollFrequency)

			select {
			case <-c.Closing.Barrier():
				// If the container has closed, cease waiting
				return false
			default:
			}
		}
	}

	return false
}

// Given an internal port, return the port mapped by docker, if there is one.
func (c *Container) MappedPort(internal int) (int, bool) {
	for _, m := range c.container.NetworkSettings.PortMappingAPI() {
		if int(m.PrivatePort) == internal {
			return int(m.PublicPort), true
		}
	}
	return -1, false
}

// Start the container (and notify it if c.Closing falls)
func (c *Container) Start() error {
	hc := &docker.HostConfig{
		PublishAllPorts: true,
		Binds:           makeBinds(c.Volumes),
	}
	err := c.client.StartContainer(c.container.ID, hc)
	if err != nil {
		return err
	}

	// Load container.NetworkSettings
	c.container, err = c.client.InspectContainer(c.container.ID)
	if err != nil {
		return err
	}

	// Listen on the Closing barrier and send a kill to the container if it
	// falls.
	c.wg.Add(1)
	go func() {
		defer c.wg.Done()

		<-c.Closing.Barrier()
		// If the container is signaled to close, send a kill signal
		err := c.client.KillContainer(docker.KillContainerOptions{
			ID: c.container.ID,
		})
		if err == nil {
			return
		}
		switch err := err.(type) {
		case *docker.NoSuchContainer:
			// The container already went away, who cares.
			return
		default:
			log.Println("Killing container failed:", c.container.ID, err)
		}
	}()
	return nil
}

// Wait until container exits
func (c *Container) Wait() (int, error) {
	return c.client.WaitContainer(c.container.ID)
}

// Internal function for raising an error.
func (c *Container) err(err error) {
	c.errorsW <- err
	c.Closing.Fall()
}

// Manage the whole lifecycle of the container in response to a request to
// start it.
func (c *Container) Run(imageSource ImageSource, payload []byte) (int, error) {

	defer c.Closing.Fall()
	defer close(c.errorsW)

	go func() {
		for err := range c.Errors {
			log.Println("BUG: Async container error:", err)
			// TODO(pwaller): If this case is hit we might not want to
			// tear the container down really.
			c.Failed.Fall()
		}
	}()

	imageName, err := imageSource.Obtain(c.client, payload)
	c.Obtained.Fall()
	if err != nil {
		c.Failed.Fall()
		return -2, err
	}

	err = c.Create(imageName)
	if err != nil {
		c.Failed.Fall()
		return -1, err
	}
	defer c.Delete()

	err = c.Start()
	if err != nil {
		c.Failed.Fall()
		return -1, err
	}

	// Must come after container start has succeeded, otherwise we end up
	// perpetually attached if it fails to succeed, which blocks program exit.
	// Program exit must be blocked ordinarily until this completes so that
	// if we are quitting we see all of the messages sent by the container
	// until it quit.
	c.wg.Add(1)
	go func() {
		defer c.wg.Done()

		err := c.CopyOutput()
		if err != nil {
			c.err(err)
		}
	}()

	go func() {
		if !c.AwaitListening() {
			c.Failed.Fall()
			return
		}
		c.Ready.Fall()
	}()

	return c.Wait()
}

func (c *Container) Delete() {
	err := c.client.RemoveContainer(docker.RemoveContainerOptions{
		ID:            c.container.ID,
		RemoveVolumes: true,
		Force:         true,
	})
	if err != nil {
		log.Println("Warn: failed to delete container:", err)
	}
}
<|endoftext|>


<commit_before>// Package `di` implements basic dependency injection (DI) container.
package di

import "fmt"
import "reflect"
import "sync"

// `Lifetime` represents lifetime of instance resolved by container.
type Lifetime int

const (
	// `Transient` lifetime specifies that instances should
	// be created each time container resolves dependency.
	Transient Lifetime = iota

	// `Scoped` lifetime specifies that the instance should be reused
	// by the same container.
	Scoped

	// `Singleton` lifetime specifies that the instance should be
	// reused for process lifetime.
	Singleton
)

// `Constructor` is generic function type to create instance.
//
// `Container` does not support constructor injection.
// Instead, you can use supplied `Container` to
// wire up your instance's dependency.
type Constructor func(c Container) (interface{}, error)

// `Destructor` is cleanup function for created instance.
type Destructor func(i interface{}) error

// `Container` is type registry and dependency resolver.
//
// You can use `RegisterType`, `RegisterValue` or `RegisterFactory` to
// register types to the container, and then use `Resolve` to obtain
// an registered instance.
//
// `Container` instance is obtained by `Registry.NewScope()`.
type Container interface {
	// `NewScope` creates new scoped container from parent container.
	//
	// Created scope inherits type registrations from it's parent.
	NewScope() Container

	// `RegisterType` registers type (`reflect.TypeOf(v)`) for
	// it's type name (`reflect.TypeOf(v).String`).
	//
	// When resolved, new zero/nil instance is created.
	// If type is pointer, type is resolved as an pointer points to
	// newly created element instead of nil pointer.
	//
	// If created instance is struct, it's field with
	// tag `di:"<tag>"` will be injected. see `Inject`.
	RegisterType(v interface{}, lifetime Lifetime)

	// `RegisterValue` registers supplied value `v` for
	// it's type name.
	RegisterValue(v interface{})

	// `RegisterFactory` registers factory method for type name of `v`.
	//
	// You can optionally use destructor. If supplied, `dtor` is called
	// for each resolved instance when asociated container is `Close`ed.
	//
	// TODO: inspect type name from constructor return value
	RegisterFactory(v interface{}, ctor Constructor, dtor Destructor, lifetime Lifetime)

	// `Use` sets an alias for other tag or type name.
	Use(tag string, tagOrTypeName string)

	// `UseType` registers type for specified tag.
	//
	// See `RegisterType`.
	UseType(tag string, v interface{}, lifetime Lifetime)

	// `UseValue` registers value for specified tag.
	//
	// See `RegisterValue`
	UseValue(tag string, v interface{})

	// `UseFactory` registers factory method for specified tag.
	//
	// See `RegisterFactory`.
	UseFactory(tag string, ctor Constructor, dtor Destructor, lifetime Lifetime)

	// `Resolve` returns instance for specified tag.
	//
	// If multiple type is registered for tag,
	// `Resolve` returns instance of last registered type.
	//
	// If no type is registered for tag, returns `ErrNoMatchingTag`.
	Resolve(tag string) (interface{}, error)

	// `ResolveAll` returns slice of instances for specified tag.
	ResolveAll(tag string) ([]interface{}, error)

	// `Inject` fills struct `v`'s fields with resolved instances
	// if field with tagged as `di:"<tag>"`
	Inject(v interface{}) error

	// `Close` invokes destructors for all instances resolved by
	// the container.
	Close() error
}

// `Registry` is global registry of types being resolved by containers.
var Registry Container = newContainer(nil)

var ErrNoMatchingTag error = fmt.Errorf("no matching tag found")

type container struct {
	parent      *container
	aliases     []*alias
	factories   []*factory
	cache       map[*factory]interface{}
	destructors []func() error
	lock        sync.RWMutex
}

type alias struct {
	Tag     string
	Aliased string
}

type factory struct {
	Tag         string
	Lifetime    Lifetime
	Constructor Constructor
	Destructor  Destructor
}

func newContainer(parent *container) *container {
	return &container{
		parent,
		make([]*alias, 0),
		make([]*factory, 0),
		make(map[*factory]interface{}),
		make([]func() error, 0),
		sync.RWMutex{},
	}
}

func (this *container) NewScope() Container {
	return newContainer(this)
}

func (this *container) RegisterType(v interface{}, lifetime Lifetime) {
	t := reflect.TypeOf(v)
	this.UseType(t.String(), v, lifetime)
}

func (this *container) RegisterValue(v interface{}) {
	t := reflect.TypeOf(v)
	this.UseValue(t.String(), v)
}

func (this *container) RegisterFactory(v interface{}, ctor Constructor, dtor Destructor, lifetime Lifetime) {
	t := reflect.TypeOf(v)
	this.UseFactory(t.String(), ctor, dtor, lifetime)
}

func (this *container) UseType(tag string, v interface{}, lifetime Lifetime) {
	this.lock.Lock()
	defer this.lock.Unlock()

	typ := reflect.TypeOf(v)
	this.factories = append(this.factories, &factory{
		Tag:      tag,
		Lifetime: lifetime,
		Constructor: func(c Container) (interface{}, error) {
			var instanceType reflect.Type
			if typ.Kind() == reflect.Ptr {
				instanceType = typ.Elem()
			} else {
				instanceType = typ
			}

			ptrInstance := reflect.New(instanceType)
			if instanceType.Kind() == reflect.Struct {
				c.Inject(ptrInstance.Interface())
			}

			if typ.Kind() == reflect.Ptr {
				return ptrInstance.Interface(), nil
			} else {
				return reflect.Indirect(ptrInstance).Interface(), nil
			}
		},
		Destructor: nil,
	})
}

func (this *container) UseValue(tag string, v interface{}) {
	this.lock.Lock()
	defer this.lock.Unlock()

	this.factories = append(this.factories, &factory{
		Tag:      tag,
		Lifetime: Transient,
		Constructor: func(c Container) (interface{}, error) {
			return v, nil
		},
		Destructor: nil,
	})
}

func (this *container) UseFactory(tag string, ctor Constructor, dtor Destructor, lifetime Lifetime) {
	this.lock.Lock()
	defer this.lock.Unlock()

	this.factories = append(this.factories, &factory{
		Tag:         tag,
		Lifetime:    lifetime,
		Constructor: ctor,
		Destructor:  dtor,
	})
}

func (this *container) Use(tag string, tagOrTypeName string) {
	this.lock.Lock()
	defer this.lock.Unlock()

	this.aliases = append(this.aliases, &alias{
		Tag:     tag,
		Aliased: tagOrTypeName,
	})
}

type tags []string

func (this tags) Contains(tag string) bool {
	for _, x := range this {
		if x == tag {
			return true
		}
	}
	return false
}

func (this *container) resolveAliases(tag string) tags {
	tags := []string{tag}

	for i := 0; i < len(tags); i++ {
		for c := this; c != nil; c = c.parent {
			for _, alias := range c.aliases {
				if tags[i] != alias.Tag {
					continue
				}

				found := false
				for j := 0; j < len(tags); j++ {
					if alias.Aliased == tags[j] {
						found = true
						break
					}
				}
				if found {
					continue
				}

				tags = append(tags, alias.Aliased)
			}
		}
	}

	return tags
}

func (this *container) createInstance(f *factory) (interface{}, error) {
	root := func(c *container) *container {
		for ; c.parent != nil; c = c.parent {
		}
		return c
	}(this)

	switch f.Lifetime {
	case Scoped:
		this.lock.RLock()
		cached, ok := this.cache[f]
		this.lock.RUnlock()
		if ok {
			return cached, nil
		}
	case Singleton:
		root.lock.RLock()
		cached, ok := root.cache[f]
		root.lock.RUnlock()
		if ok {
			return cached, nil
		}
	}

	instance, err := f.Constructor(this)
	if err != nil {
		return instance, err
	}

	switch f.Lifetime {
	case Scoped:
		this.lock.Lock()
		this.cache[f] = instance
		this.lock.Unlock()
	case Singleton:
		root.lock.Lock()
		root.cache[f] = instance
		root.lock.Unlock()
	}

	if f.Destructor != nil {
		switch f.Lifetime {
		case Transient:
			fallthrough
		case Scoped:
			this.lock.Lock()
			this.destructors = append(this.destructors, func() error {
				return f.Destructor(instance)
			})
			this.lock.Unlock()
		case Singleton:
			root.lock.Lock()
			root.destructors = append(root.destructors, func() error {
				return f.Destructor(instance)
			})
			root.lock.Unlock()
		}
	}

	return instance, nil
}

func (this *container) Resolve(tag string) (interface{}, error) {
	factory, found := func() (*factory, bool) {
		this.lock.RLock()
		defer this.lock.RUnlock()

		tags := this.resolveAliases(tag)

		for c := this; c != nil; c = c.parent {
			for i := len(c.factories) - 1; 0 <= i; i-- {
				if tags.Contains(c.factories[i].Tag) {
					return c.factories[i], true
				}
			}
		}

		return nil, false
	}()

	if found {
		return this.createInstance(factory)
	} else {
		return nil, ErrNoMatchingTag
	}
}

func (this *container) ResolveAll(tag string) ([]interface{}, error) {
	factories := make([]*factory, 0, 10)
	func() {
		this.lock.RLock()
		defer this.lock.RUnlock()

		tags := this.resolveAliases(tag)

		for c := this; c != nil; c = c.parent {
			for i := len(c.factories) - 1; 0 <= i; i-- {
				if tags.Contains(c.factories[i].Tag) {
					factories = append(factories, c.factories[i])
				}
			}
		}
	}()

	var instances []interface{}
	for i := len(factories) - 1; 0 <= i; i-- {
		v, err := this.createInstance(factories[i])
		if err != nil {
			return nil, err
		}
		instances = append(instances, v)
	}

	return instances, nil
}

func (this *container) Inject(v interface{}) error {
	val := reflect.ValueOf(v)
	if val.Type().Kind() == reflect.Ptr {
		val = reflect.Indirect(val)
	}
	if val.Type().Kind() != reflect.Struct {
		panic("struct or pointer to struct required")
	}
	t := val.Type()

	for i := 0; i < t.NumField(); i++ {
		f := t.Field(i)
		if tag := f.Tag.Get("di"); tag != "" {
			fi, err := this.Resolve(tag)
			if err != nil {
				return err
			}
			val.Field(i).Set(reflect.ValueOf(fi))
		}
	}

	return nil
}

func (this *container) Close() error {
	this.lock.Lock()
	dtors := make([]func() error, len(this.destructors))
	copy(dtors, this.destructors)
	this.destructors = this.destructors[:0]
	this.lock.Unlock()

	for i := len(dtors) - 1; 0 <= i; i-- {
		err := dtors[i]()
		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>scoped instanceが親のキャッシュを調べるよう変更<commit_after>// Package `di` implements basic dependency injection (DI) container.
package di

import "fmt"
import "reflect"
import "sync"

// `Lifetime` represents lifetime of instance resolved by container.
type Lifetime int

const (
	// `Transient` lifetime specifies that instances should
	// be created each time container resolves dependency.
	Transient Lifetime = iota

	// `Scoped` lifetime specifies that the instance should be reused
	// by the same container.
	Scoped

	// `Singleton` lifetime specifies that the instance should be
	// reused for process lifetime.
	Singleton
)

// `Constructor` is generic function type to create instance.
//
// `Container` does not support constructor injection.
// Instead, you can use supplied `Container` to
// wire up your instance's dependency.
type Constructor func(c Container) (interface{}, error)

// `Destructor` is cleanup function for created instance.
type Destructor func(i interface{}) error

// `Container` is type registry and dependency resolver.
//
// You can use `RegisterType`, `RegisterValue` or `RegisterFactory` to
// register types to the container, and then use `Resolve` to obtain
// an registered instance.
//
// `Container` instance is obtained by `Registry.NewScope()`.
type Container interface {
	// `NewScope` creates new scoped container from parent container.
	//
	// Created scope inherits type registrations from it's parent.
	NewScope() Container

	// `RegisterType` registers type (`reflect.TypeOf(v)`) for
	// it's type name (`reflect.TypeOf(v).String`).
	//
	// When resolved, new zero/nil instance is created.
	// If type is pointer, type is resolved as an pointer points to
	// newly created element instead of nil pointer.
	//
	// If created instance is struct, it's field with
	// tag `di:"<tag>"` will be injected. see `Inject`.
	RegisterType(v interface{}, lifetime Lifetime)

	// `RegisterValue` registers supplied value `v` for
	// it's type name.
	RegisterValue(v interface{})

	// `RegisterFactory` registers factory method for type name of `v`.
	//
	// You can optionally use destructor. If supplied, `dtor` is called
	// for each resolved instance when asociated container is `Close`ed.
	//
	// TODO: inspect type name from constructor return value
	RegisterFactory(v interface{}, ctor Constructor, dtor Destructor, lifetime Lifetime)

	// `Use` sets an alias for other tag or type name.
	Use(tag string, tagOrTypeName string)

	// `UseType` registers type for specified tag.
	//
	// See `RegisterType`.
	UseType(tag string, v interface{}, lifetime Lifetime)

	// `UseValue` registers value for specified tag.
	//
	// See `RegisterValue`
	UseValue(tag string, v interface{})

	// `UseFactory` registers factory method for specified tag.
	//
	// See `RegisterFactory`.
	UseFactory(tag string, ctor Constructor, dtor Destructor, lifetime Lifetime)

	// `Resolve` returns instance for specified tag.
	//
	// If multiple type is registered for tag,
	// `Resolve` returns instance of last registered type.
	//
	// If no type is registered for tag, returns `ErrNoMatchingTag`.
	Resolve(tag string) (interface{}, error)

	// `ResolveAll` returns slice of instances for specified tag.
	ResolveAll(tag string) ([]interface{}, error)

	// `Inject` fills struct `v`'s fields with resolved instances
	// if field is tagged with `di:"<tag>"`
	Inject(v interface{}) error

	// `Close` invokes destructors for all instances resolved by
	// the container.
	Close() error
}

// `Registry` is global registry of types being resolved by containers.
var Registry Container = newContainer(nil)

var ErrNoMatchingTag error = fmt.Errorf("no matching tag found")

type container struct {
	parent      *container
	aliases     []*alias
	factories   []*factory
	cache       map[*factory]interface{}
	destructors []func() error
	lock        sync.RWMutex
}

type alias struct {
	Tag     string
	Aliased string
}

type factory struct {
	Tag         string
	Lifetime    Lifetime
	Constructor Constructor
	Destructor  Destructor
}

func newContainer(parent *container) *container {
	return &container{
		parent,
		make([]*alias, 0),
		make([]*factory, 0),
		make(map[*factory]interface{}),
		make([]func() error, 0),
		sync.RWMutex{},
	}
}

func (this *container) NewScope() Container {
	return newContainer(this)
}

func (this *container) RegisterType(v interface{}, lifetime Lifetime) {
	t := reflect.TypeOf(v)
	this.UseType(t.String(), v, lifetime)
}

func (this *container) RegisterValue(v interface{}) {
	t := reflect.TypeOf(v)
	this.UseValue(t.String(), v)
}

func (this *container) RegisterFactory(v interface{}, ctor Constructor, dtor Destructor, lifetime Lifetime) {
	t := reflect.TypeOf(v)
	this.UseFactory(t.String(), ctor, dtor, lifetime)
}

func (this *container) UseType(tag string, v interface{}, lifetime Lifetime) {
	this.lock.Lock()
	defer this.lock.Unlock()

	typ := reflect.TypeOf(v)
	this.factories = append(this.factories, &factory{
		Tag:      tag,
		Lifetime: lifetime,
		Constructor: func(c Container) (interface{}, error) {
			var instanceType reflect.Type
			if typ.Kind() == reflect.Ptr {
				instanceType = typ.Elem()
			} else {
				instanceType = typ
			}

			ptrInstance := reflect.New(instanceType)
			if instanceType.Kind() == reflect.Struct {
				c.Inject(ptrInstance.Interface())
			}

			if typ.Kind() == reflect.Ptr {
				return ptrInstance.Interface(), nil
			} else {
				return reflect.Indirect(ptrInstance).Interface(), nil
			}
		},
		Destructor: nil,
	})
}

func (this *container) UseValue(tag string, v interface{}) {
	this.lock.Lock()
	defer this.lock.Unlock()

	this.factories = append(this.factories, &factory{
		Tag:      tag,
		Lifetime: Transient,
		Constructor: func(c Container) (interface{}, error) {
			return v, nil
		},
		Destructor: nil,
	})
}

func (this *container) UseFactory(tag string, ctor Constructor, dtor Destructor, lifetime Lifetime) {
	this.lock.Lock()
	defer this.lock.Unlock()

	this.factories = append(this.factories, &factory{
		Tag:         tag,
		Lifetime:    lifetime,
		Constructor: ctor,
		Destructor:  dtor,
	})
}

func (this *container) Use(tag string, tagOrTypeName string) {
	this.lock.Lock()
	defer this.lock.Unlock()

	this.aliases = append(this.aliases, &alias{
		Tag:     tag,
		Aliased: tagOrTypeName,
	})
}

type tags []string

func (this tags) Contains(tag string) bool {
	for _, x := range this {
		if x == tag {
			return true
		}
	}
	return false
}

func (this *container) resolveAliases(tag string) tags {
	tags := []string{tag}

	for i := 0; i < len(tags); i++ {
		for c := this; c != nil; c = c.parent {
			for _, alias := range c.aliases {
				if tags[i] != alias.Tag {
					continue
				}

				found := false
				for j := 0; j < len(tags); j++ {
					if alias.Aliased == tags[j] {
						found = true
						break
					}
				}
				if found {
					continue
				}

				tags = append(tags, alias.Aliased)
			}
		}
	}

	return tags
}

func (this *container) createInstance(f *factory) (interface{}, error) {
	root := func(c *container) *container {
		for ; c.parent != nil; c = c.parent {
		}
		return c
	}(this)

	switch f.Lifetime {
	case Scoped:
		for c := this; c != nil; c = c.parent {
			c.lock.RLock()
			cached, ok := c.cache[f]
			c.lock.RUnlock()
			if ok {
				return cached, nil
			}
		}
	case Singleton:
		root.lock.RLock()
		cached, ok := root.cache[f]
		root.lock.RUnlock()
		if ok {
			return cached, nil
		}
	}

	instance, err := f.Constructor(this)
	if err != nil {
		return instance, err
	}

	switch f.Lifetime {
	case Scoped:
		this.lock.Lock()
		this.cache[f] = instance
		this.lock.Unlock()
	case Singleton:
		root.lock.Lock()
		root.cache[f] = instance
		root.lock.Unlock()
	}

	if f.Destructor != nil {
		switch f.Lifetime {
		case Transient:
			fallthrough
		case Scoped:
			this.lock.Lock()
			this.destructors = append(this.destructors, func() error {
				return f.Destructor(instance)
			})
			this.lock.Unlock()
		case Singleton:
			root.lock.Lock()
			root.destructors = append(root.destructors, func() error {
				return f.Destructor(instance)
			})
			root.lock.Unlock()
		}
	}

	return instance, nil
}

func (this *container) Resolve(tag string) (interface{}, error) {
	factory, found := func() (*factory, bool) {
		this.lock.RLock()
		defer this.lock.RUnlock()

		tags := this.resolveAliases(tag)

		for c := this; c != nil; c = c.parent {
			for i := len(c.factories) - 1; 0 <= i; i-- {
				if tags.Contains(c.factories[i].Tag) {
					return c.factories[i], true
				}
			}
		}

		return nil, false
	}()

	if found {
		return this.createInstance(factory)
	} else {
		return nil, ErrNoMatchingTag
	}
}

func (this *container) ResolveAll(tag string) ([]interface{}, error) {
	factories := make([]*factory, 0, 10)
	func() {
		this.lock.RLock()
		defer this.lock.RUnlock()

		tags := this.resolveAliases(tag)

		for c := this; c != nil; c = c.parent {
			for i := len(c.factories) - 1; 0 <= i; i-- {
				if tags.Contains(c.factories[i].Tag) {
					factories = append(factories, c.factories[i])
				}
			}
		}
	}()

	var instances []interface{}
	for i := len(factories) - 1; 0 <= i; i-- {
		v, err := this.createInstance(factories[i])
		if err != nil {
			return nil, err
		}
		instances = append(instances, v)
	}

	return instances, nil
}

func (this *container) Inject(v interface{}) error {
	val := reflect.ValueOf(v)
	if val.Type().Kind() == reflect.Ptr {
		val = reflect.Indirect(val)
	}
	if val.Type().Kind() != reflect.Struct {
		panic("struct or pointer to struct required")
	}
	t := val.Type()

	for i := 0; i < t.NumField(); i++ {
		f := t.Field(i)
		if tag := f.Tag.Get("di"); tag != "" {
			fi, err := this.Resolve(tag)
			if err != nil {
				return err
			}
			val.Field(i).Set(reflect.ValueOf(fi))
		}
	}

	return nil
}

func (this *container) Close() error {
	this.lock.Lock()
	dtors := make([]func() error, len(this.destructors))
	copy(dtors, this.destructors)
	this.destructors = this.destructors[:0]
	this.lock.Unlock()

	for i := len(dtors) - 1; 0 <= i; i-- {
		err := dtors[i]()
		if err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>