<commit_before>package log

import (
	"errors"
	"fmt"
	"io"
	golog "log"
	"os"
	"path/filepath"
	"strings"
)

type WOFLog interface {
	Fatal(format string, v ...interface{})
	Error(format string, v ...interface{})
	Warning(format string, v ...interface{})
	Status(format string, v ...interface{})
	Info(format string, v ...interface{})
	Debug(format string, v ...interface{})
}

type MockLogger struct{}

func (m *MockLogger) Fatal(format string, v ...interface{})   {}
func (m *MockLogger) Error(format string, v ...interface{})   {}
func (m *MockLogger) Warning(format string, v ...interface{}) {}
func (m *MockLogger) Status(format string, v ...interface{})  {}
func (m *MockLogger) Info(format string, v ...interface{})    {}
func (m *MockLogger) Debug(format string, v ...interface{})   {}

type WOFLogger struct {
	Loggers map[string]*golog.Logger
	levels  map[string]int
	Prefix  string
}

func Prefix(args ...string) string {

	whoami := os.Args[0]
	prefix := filepath.Base(whoami)

	if len(args) > 0 {
		prefix = args[0]
	}

	return fmt.Sprintf("[%s] ", prefix)
}

func SimpleWOFLogger(args ...string) *WOFLogger {

	prefix := Prefix(args...)

	logger := NewWOFLogger(prefix)

	stdout := io.Writer(os.Stdout)
	stderr := io.Writer(os.Stderr)

	logger.AddLogger(stdout, "status")
	logger.AddLogger(stderr, "error")

	return logger
}

func NewWOFLogger(args ...string) *WOFLogger {

	prefix := Prefix(args...)

	loggers := make(map[string]*golog.Logger)
	levels := make(map[string]int)

	levels["fatal"] = 0
	levels["error"] = 10
	levels["warning"] = 20
	levels["status"] = 25
	levels["info"] = 30
	levels["debug"] = 40

	l := WOFLogger{Loggers: loggers, Prefix: prefix, levels: levels}
	return &l
}

func (l WOFLogger) AddLogger(out io.Writer, minlevel string) (bool, error) {

	_, ok := l.Loggers[minlevel]

	if ok {
		return false, errors.New("log level already defined")
	}

	prefix := l.Prefix

	if !strings.HasSuffix(prefix, " ") {
		prefix += " "
	}

	logger := golog.New(out, prefix, golog.Lmicroseconds)
	l.Loggers[minlevel] = logger

	return true, nil
}

func (l WOFLogger) Debug(format string, v ...interface{}) {
	l.dispatch("debug", format, v...)
}

func (l WOFLogger) Info(format string, v ...interface{}) {
	l.dispatch("info", format, v...)
}

func (l WOFLogger) Status(format string, v ...interface{}) {
	l.dispatch("status", format, v...)
}

func (l WOFLogger) Warning(format string, v ...interface{}) {
	l.dispatch("warning", format, v...)
}

func (l WOFLogger) Error(format string, v ...interface{}) {
	l.dispatch("error", format, v...)
}

func (l WOFLogger) Fatal(format string, v ...interface{}) {
	l.dispatch("fatal", format, v...)
	os.Exit(1)
}

func (l WOFLogger) dispatch(level string, format string, v ...interface{}) {

	for minlevel, logger := range l.Loggers {

		if l.emit(level, minlevel) {

			msg := fmt.Sprintf(format, v...)
			out := fmt.Sprintf("[%s] %s", level, msg)
			logger.Println(out)
		}
	}
}

func (l WOFLogger) emit(level string, minlevel string) bool {

	this_level, ok := l.levels[level]

	if !ok {
		return false
	}

	min_level, ok := l.levels[minlevel]

	if !ok {
		return false
	}

	if this_level > min_level {
		return false
	}

	return true
}
<commit_msg>more better formatting<commit_after>package log

import (
	"errors"
	"fmt"
	"io"
	golog "log"
	"os"
	"path/filepath"
	_ "strings"
)

type WOFLog interface {
	Fatal(format string, v ...interface{})
	Error(format string, v ...interface{})
	Warning(format string, v ...interface{})
	Status(format string, v ...interface{})
	Info(format string, v ...interface{})
	Debug(format string, v ...interface{})
}

type MockLogger struct{}

func (m *MockLogger) Fatal(format string, v ...interface{})   {}
func (m *MockLogger) Error(format string, v ...interface{})   {}
func (m *MockLogger) Warning(format string, v ...interface{}) {}
func (m *MockLogger) Status(format string, v ...interface{})  {}
func (m *MockLogger) Info(format string, v ...interface{})    {}
func (m *MockLogger) Debug(format string, v ...interface{})   {}

type WOFLogger struct {
	Loggers map[string]*golog.Logger
	levels  map[string]int
	Prefix  string
}

func Prefix(args ...string) string {

	whoami := os.Args[0]
	whoami = filepath.Base(whoami)

	prefix := fmt.Sprintf("[%s]", whoami)

	for _, s := range args {
		prefix = fmt.Sprintf("%s[%s]", prefix, s)
	}

	return prefix
}

func SimpleWOFLogger(args ...string) *WOFLogger {

	logger := NewWOFLogger(args...)

	stdout := io.Writer(os.Stdout)
	stderr := io.Writer(os.Stderr)

	logger.AddLogger(stdout, "status")
	logger.AddLogger(stderr, "error")

	return logger
}

func NewWOFLogger(args ...string) *WOFLogger {

	prefix := Prefix(args...)

	loggers := make(map[string]*golog.Logger)
	levels := make(map[string]int)

	levels["fatal"] = 0
	levels["error"] = 10
	levels["warning"] = 20
	levels["status"] = 25
	levels["info"] = 30
	levels["debug"] = 40

	l := WOFLogger{
		Loggers: loggers,
		Prefix:  prefix,
		levels:  levels,
	}

	return &l
}

func (l WOFLogger) AddLogger(out io.Writer, minlevel string) (bool, error) {

	_, ok := l.Loggers[minlevel]

	if ok {
		return false, errors.New("log level already defined")
	}

	logger := golog.New(out, "", golog.Lmicroseconds)
	l.Loggers[minlevel] = logger

	return true, nil
}

func (l WOFLogger) Debug(format string, v ...interface{}) {
	l.dispatch("debug", format, v...)
}

func (l WOFLogger) Info(format string, v ...interface{}) {
	l.dispatch("info", format, v...)
}

func (l WOFLogger) Status(format string, v ...interface{}) {
	l.dispatch("status", format, v...)
}

func (l WOFLogger) Warning(format string, v ...interface{}) {
	l.dispatch("warning", format, v...)
}

func (l WOFLogger) Error(format string, v ...interface{}) {
	l.dispatch("error", format, v...)
}

func (l WOFLogger) Fatal(format string, v ...interface{}) {
	l.dispatch("fatal", format, v...)
	os.Exit(1)
}

func (l WOFLogger) dispatch(level string, format string, v ...interface{}) {

	for minlevel, logger := range l.Loggers {

		if l.emit(level, minlevel) {

			msg := fmt.Sprintf(format, v...)

			out := fmt.Sprintf("%s[%s] %s", l.Prefix, level, msg)
			logger.Println(out)
		}
	}
}

func (l WOFLogger) emit(level string, minlevel string) bool {

	this_level, ok := l.levels[level]

	if !ok {
		return false
	}

	min_level, ok := l.levels[minlevel]

	if !ok {
		return false
	}

	if this_level > min_level {
		return false
	}

	return true
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package framework_test

import (
	"errors"
	"regexp"
	"sort"
	"strings"
	"testing"

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

	"k8s.io/kubernetes/test/e2e/framework"
)

// The line number of the following code is checked in TestFailureOutput below.
// Be careful when moving it around or changing the import statements above.
// Here are some intentionally blank lines that can be removed to compensate
// for future additional import statements.
//
//
//
//
//
//

func runTests(t *testing.T, reporter ginkgo.Reporter) {
	// This source code line will be part of the stack dump comparison.
	ginkgo.RunSpecsWithDefaultAndCustomReporters(t, "Logging Suite", []ginkgo.Reporter{reporter})
}

var _ = ginkgo.Describe("log", func() {
	ginkgo.BeforeEach(func() {
		framework.Logf("before")
	})
	ginkgo.It("fails", func() {
		func() {
			framework.Failf("I'm failing.")
		}()
	})
	ginkgo.It("asserts", func() {
		framework.ExpectEqual(false, true, "false is never true")
	})
	ginkgo.It("error", func() {
		err := errors.New("an error with a long, useless description")
		framework.ExpectNoError(err, "hard-coded error")
	})
	ginkgo.It("equal", func() {
		framework.ExpectEqual(0, 1, "of course it's not equal...")
	})
	ginkgo.AfterEach(func() {
		framework.Logf("after")
		framework.ExpectEqual(true, false, "true is never false either")
	})
})

func TestFailureOutput(t *testing.T) {
	// Run the Ginkgo suite with output collected by a custom
	// reporter in adddition to the default one. To see what the full
	// Ginkgo report looks like, run this test with "go test -v".
	config.DefaultReporterConfig.FullTrace = true
	gomega.RegisterFailHandler(framework.Fail)
	fakeT := &testing.T{}
	reporter := reporters.NewFakeReporter()
	runTests(fakeT, reporter)

	// Now check the output.
	actual := normalizeReport(*reporter)

	// output from AfterEach
	commonOutput := "\n\nINFO: after\nFAIL: true is never false either\nExpected\n    <bool>: true\nto equal\n    <bool>: false\n\nFull Stack Trace\nk8s.io/kubernetes/test/e2e/framework_test.glob..func1.6()\n\tlog_test.go:71\nk8s.io/kubernetes/test/e2e/framework_test.runTests()\n\tlog_test.go:47\n\n"

	// Sorted by name!
	expected := suiteResults{
		testResult{
			name:    "[Top Level] log asserts",
			output:  "INFO: before\nFAIL: false is never true\nExpected\n    <bool>: false\nto equal\n    <bool>: true\n\nFull Stack Trace\nk8s.io/kubernetes/test/e2e/framework_test.glob..func1.3()\n\tlog_test.go:60\nk8s.io/kubernetes/test/e2e/framework_test.runTests()\n\tlog_test.go:47" + commonOutput,
			failure: "false is never true\nExpected\n    <bool>: false\nto equal\n    <bool>: true",
			stack:   "k8s.io/kubernetes/test/e2e/framework_test.glob..func1.3()\n\tlog_test.go:60\nk8s.io/kubernetes/test/e2e/framework_test.runTests()\n\tlog_test.go:47\n",
		},
		testResult{
			name:    "[Top Level] log equal",
			output:  "INFO: before\nFAIL: of course it's not equal...\nExpected\n    <int>: 0\nto equal\n    <int>: 1\n\nFull Stack Trace\nk8s.io/kubernetes/test/e2e/framework_test.glob..func1.5()\n\tlog_test.go:67\nk8s.io/kubernetes/test/e2e/framework_test.runTests()\n\tlog_test.go:47" + commonOutput,
			failure: "of course it's not equal...\nExpected\n    <int>: 0\nto equal\n    <int>: 1",
			stack:   "k8s.io/kubernetes/test/e2e/framework_test.glob..func1.5()\n\tlog_test.go:67\nk8s.io/kubernetes/test/e2e/framework_test.runTests()\n\tlog_test.go:47\n",
		},
		testResult{
			name:    "[Top Level] log error",
			output:  "INFO: before\nFAIL: hard-coded error\nUnexpected error:\n    <*errors.errorString>: {\n        s: \"an error with a long, useless description\",\n    }\n    an error with a long, useless description\noccurred\n\nFull Stack Trace\nk8s.io/kubernetes/test/e2e/framework_test.glob..func1.4()\n\tlog_test.go:64\nk8s.io/kubernetes/test/e2e/framework_test.runTests()\n\tlog_test.go:47" + commonOutput,
			failure: "hard-coded error\nUnexpected error:\n    <*errors.errorString>: {\n        s: \"an error with a long, useless description\",\n    }\n    an error with a long, useless description\noccurred",
			stack:   "k8s.io/kubernetes/test/e2e/framework_test.glob..func1.4()\n\tlog_test.go:64\nk8s.io/kubernetes/test/e2e/framework_test.runTests()\n\tlog_test.go:47\n",
		},
		testResult{
			name:    "[Top Level] log fails",
			output:  "INFO: before\nFAIL: I'm failing.\n\nFull Stack Trace\nk8s.io/kubernetes/test/e2e/framework_test.glob..func1.2()\n\tlog_test.go:57\nk8s.io/kubernetes/test/e2e/framework_test.runTests()\n\tlog_test.go:47" + commonOutput,
			failure: "I'm failing.",
			stack:   "k8s.io/kubernetes/test/e2e/framework_test.glob..func1.2()\n\tlog_test.go:57\nk8s.io/kubernetes/test/e2e/framework_test.runTests()\n\tlog_test.go:47\n",
		},
	}
	// Compare individual fields. Comparing the slices leads to unreadable error output when there is any mismatch.
	framework.ExpectEqual(len(actual), len(expected), "%d entries in %v", len(expected), actual)
	for i, a := range actual {
		b := expected[i]
		framework.ExpectEqual(a.name, b.name, "name in %d", i)
		framework.ExpectEqual(a.output, b.output, "output in %d", i)
		framework.ExpectEqual(a.failure, b.failure, "failure in %d", i)
		// There may be additional stack entries from the "testing" package at the
		// end. We ignore those in the comparison because the line number in them
		// varies.
		framework.ExpectEqual(a.stack, b.stack, "stack in %d: %s", i, a.stack)
	}
}

type testResult struct {
	name string
	// output written to GinkgoWriter during test.
	output string
	// failure is SpecSummary.Failure.Message with varying parts stripped.
	failure string
	// stack is a normalized version (just file names, function parametes stripped) of
	// Ginkgo's FullStackTrace of a failure. Empty if no failure.
	stack string
}

type suiteResults []testResult

func normalizeReport(report reporters.FakeReporter) suiteResults {
	var results suiteResults
	for _, spec := range report.SpecSummaries {
		results = append(results, testResult{
			name:    strings.Join(spec.ComponentTexts, " "),
			output:  normalizeLocation(stripAddresses(stripTimes(spec.CapturedOutput))),
			failure: stripAddresses(stripTimes(spec.Failure.Message)),
			stack:   normalizeLocation(spec.Failure.Location.FullStackTrace),
		})
	}
	sort.Slice(results, func(i, j int) bool {
		return strings.Compare(results[i].name, results[j].name) < 0
	})
	return results
}

// timePrefix matches "Jul 17 08:08:25.950: " at the beginning of each line.
var timePrefix = regexp.MustCompile(`(?m)^[[:alpha:]]{3} +[[:digit:]]{1,2} +[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}.[[:digit:]]{3}: `)

func stripTimes(in string) string {
	return timePrefix.ReplaceAllString(in, "")
}

// instanceAddr matches " | 0xc0003dec60>"
var instanceAddr = regexp.MustCompile(` \| 0x[0-9a-fA-F]+>`)

func stripAddresses(in string) string {
	return instanceAddr.ReplaceAllString(in, ">")
}

// stackLocation matches "<some path>/<file>.go:75 +0x1f1" after a slash (built
// locally) or one of a few relative paths (built in the Kubernetes CI).
var stackLocation = regexp.MustCompile(`(?:/|vendor/|test/|GOROOT/).*/([[:^space:]]+.go:[[:digit:]]+)( \+0x[0-9a-fA-F]+)?`)

// functionArgs matches "<function name>(...)".
var functionArgs = regexp.MustCompile(`([[:alpha:]]+)\(.*\)`)

// testFailureOutput matches TestFailureOutput() and its source followed by additional stack entries:
//
// k8s.io/kubernetes/test/e2e/framework_test.TestFailureOutput(0xc000558800)
//	/nvme/gopath/src/k8s.io/kubernetes/test/e2e/framework/log/log_test.go:73 +0x1c9
// testing.tRunner(0xc000558800, 0x1af2848)
// 	/nvme/gopath/go/src/testing/testing.go:865 +0xc0
// created by testing.(*T).Run
//	/nvme/gopath/go/src/testing/testing.go:916 +0x35a
var testFailureOutput = regexp.MustCompile(`(?m)^k8s.io/kubernetes/test/e2e/framework_test\.TestFailureOutput\(.*\n\t.*(\n.*\n\t.*)*`)

// normalizeLocation removes path prefix and function parameters and certain stack entries
// that we don't care about.
func normalizeLocation(in string) string {
	out := in
	out = stackLocation.ReplaceAllString(out, "$1")
	out = functionArgs.ReplaceAllString(out, "$1()")
	out = testFailureOutput.ReplaceAllString(out, "")
	return out
}
<commit_msg>e2e: fix logs unit test<commit_after>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package framework_test

import (
	"errors"
	"regexp"
	"sort"
	"strings"
	"testing"

	"github.com/onsi/ginkgo"
	"github.com/onsi/ginkgo/config"
	"github.com/onsi/ginkgo/reporters"
	"github.com/onsi/gomega"
	"github.com/stretchr/testify/assert"

	"k8s.io/kubernetes/test/e2e/framework"
)

// The line number of the following code is checked in TestFailureOutput below.
// Be careful when moving it around or changing the import statements above.
// Here are some intentionally blank lines that can be removed to compensate
// for future additional import statements.
//
//
//
//
//

func runTests(t *testing.T, reporter ginkgo.Reporter) {
	// This source code line will be part of the stack dump comparison.
	ginkgo.RunSpecsWithDefaultAndCustomReporters(t, "Logging Suite", []ginkgo.Reporter{reporter})
}

var _ = ginkgo.Describe("log", func() {
	ginkgo.BeforeEach(func() {
		framework.Logf("before")
	})
	ginkgo.It("fails", func() {
		func() {
			framework.Failf("I'm failing.")
		}()
	})
	ginkgo.It("asserts", func() {
		framework.ExpectEqual(false, true, "false is never true")
	})
	ginkgo.It("error", func() {
		err := errors.New("an error with a long, useless description")
		framework.ExpectNoError(err, "hard-coded error")
	})
	ginkgo.It("equal", func() {
		framework.ExpectEqual(0, 1, "of course it's not equal...")
	})
	ginkgo.AfterEach(func() {
		framework.Logf("after")
		framework.ExpectEqual(true, false, "true is never false either")
	})
})

func TestFailureOutput(t *testing.T) {
	// Run the Ginkgo suite with output collected by a custom
	// reporter in adddition to the default one. To see what the full
	// Ginkgo report looks like, run this test with "go test -v".
	config.DefaultReporterConfig.FullTrace = true
	gomega.RegisterFailHandler(framework.Fail)
	fakeT := &testing.T{}
	reporter := reporters.NewFakeReporter()
	runTests(fakeT, reporter)

	// Now check the output.
	actual := normalizeReport(*reporter)

	// output from AfterEach
	commonOutput := "\n\nINFO: after\nFAIL: true is never false either\nExpected\n    <bool>: true\nto equal\n    <bool>: false\n\nFull Stack Trace\nk8s.io/kubernetes/test/e2e/framework_test.glob..func1.6()\n\tlog_test.go:71\nk8s.io/kubernetes/test/e2e/framework_test.runTests()\n\tlog_test.go:47\n\n"

	// Sorted by name!
	expected := suiteResults{
		testResult{
			name:    "[Top Level] log asserts",
			output:  "INFO: before\nFAIL: false is never true\nExpected\n    <bool>: false\nto equal\n    <bool>: true\n\nFull Stack Trace\nk8s.io/kubernetes/test/e2e/framework_test.glob..func1.3()\n\tlog_test.go:60\nk8s.io/kubernetes/test/e2e/framework_test.runTests()\n\tlog_test.go:47" + commonOutput,
			failure: "false is never true\nExpected\n    <bool>: false\nto equal\n    <bool>: true",
			stack:   "k8s.io/kubernetes/test/e2e/framework_test.glob..func1.3()\n\tlog_test.go:60\nk8s.io/kubernetes/test/e2e/framework_test.runTests()\n\tlog_test.go:47\n",
		},
		testResult{
			name:    "[Top Level] log equal",
			output:  "INFO: before\nFAIL: of course it's not equal...\nExpected\n    <int>: 0\nto equal\n    <int>: 1\n\nFull Stack Trace\nk8s.io/kubernetes/test/e2e/framework_test.glob..func1.5()\n\tlog_test.go:67\nk8s.io/kubernetes/test/e2e/framework_test.runTests()\n\tlog_test.go:47" + commonOutput,
			failure: "of course it's not equal...\nExpected\n    <int>: 0\nto equal\n    <int>: 1",
			stack:   "k8s.io/kubernetes/test/e2e/framework_test.glob..func1.5()\n\tlog_test.go:67\nk8s.io/kubernetes/test/e2e/framework_test.runTests()\n\tlog_test.go:47\n",
		},
		testResult{
			name:    "[Top Level] log error",
			output:  "INFO: before\nFAIL: hard-coded error\nUnexpected error:\n    <*errors.errorString>: {\n        s: \"an error with a long, useless description\",\n    }\n    an error with a long, useless description\noccurred\n\nFull Stack Trace\nk8s.io/kubernetes/test/e2e/framework_test.glob..func1.4()\n\tlog_test.go:64\nk8s.io/kubernetes/test/e2e/framework_test.runTests()\n\tlog_test.go:47" + commonOutput,
			failure: "hard-coded error\nUnexpected error:\n    <*errors.errorString>: {\n        s: \"an error with a long, useless description\",\n    }\n    an error with a long, useless description\noccurred",
			stack:   "k8s.io/kubernetes/test/e2e/framework_test.glob..func1.4()\n\tlog_test.go:64\nk8s.io/kubernetes/test/e2e/framework_test.runTests()\n\tlog_test.go:47\n",
		},
		testResult{
			name:    "[Top Level] log fails",
			output:  "INFO: before\nFAIL: I'm failing.\n\nFull Stack Trace\nk8s.io/kubernetes/test/e2e/framework_test.glob..func1.2()\n\tlog_test.go:57\nk8s.io/kubernetes/test/e2e/framework_test.runTests()\n\tlog_test.go:47" + commonOutput,
			failure: "I'm failing.",
			stack:   "k8s.io/kubernetes/test/e2e/framework_test.glob..func1.2()\n\tlog_test.go:57\nk8s.io/kubernetes/test/e2e/framework_test.runTests()\n\tlog_test.go:47\n",
		},
	}
	// assert.Equal prints a useful diff if the slices are not
	// equal. However, the diff does not show changes inside the
	// strings. Therefore we also compare the individual fields.
	if !assert.Equal(t, expected, actual) {
		for i := 0; i < len(expected) && i < len(actual); i++ {
			assert.Equal(t, expected[i].output, actual[i].output, "output from test #%d: %s", i, expected[i].name)
			assert.Equal(t, expected[i].stack, actual[i].stack, "stack from test #%d: %s", i, expected[i].name)
		}
	}
}

type testResult struct {
	name string
	// output written to GinkgoWriter during test.
	output string
	// failure is SpecSummary.Failure.Message with varying parts stripped.
	failure string
	// stack is a normalized version (just file names, function parametes stripped) of
	// Ginkgo's FullStackTrace of a failure. Empty if no failure.
	stack string
}

type suiteResults []testResult

func normalizeReport(report reporters.FakeReporter) suiteResults {
	var results suiteResults
	for _, spec := range report.SpecSummaries {
		results = append(results, testResult{
			name:    strings.Join(spec.ComponentTexts, " "),
			output:  normalizeLocation(stripAddresses(stripTimes(spec.CapturedOutput))),
			failure: stripAddresses(stripTimes(spec.Failure.Message)),
			stack:   normalizeLocation(spec.Failure.Location.FullStackTrace),
		})
	}
	sort.Slice(results, func(i, j int) bool {
		return strings.Compare(results[i].name, results[j].name) < 0
	})
	return results
}

// timePrefix matches "Jul 17 08:08:25.950: " at the beginning of each line.
var timePrefix = regexp.MustCompile(`(?m)^[[:alpha:]]{3} +[[:digit:]]{1,2} +[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}.[[:digit:]]{3}: `)

func stripTimes(in string) string {
	return timePrefix.ReplaceAllString(in, "")
}

// instanceAddr matches " | 0xc0003dec60>"
var instanceAddr = regexp.MustCompile(` \| 0x[0-9a-fA-F]+>`)

func stripAddresses(in string) string {
	return instanceAddr.ReplaceAllString(in, ">")
}

// stackLocation matches "<some path>/<file>.go:75 +0x1f1" after a slash (built
// locally) or one of a few relative paths (built in the Kubernetes CI).
var stackLocation = regexp.MustCompile(`(?:/|vendor/|test/|GOROOT/).*/([[:^space:]]+.go:[[:digit:]]+)( \+0x[0-9a-fA-F]+)?`)

// functionArgs matches "<function name>(...)".
var functionArgs = regexp.MustCompile(`([[:alpha:]]+)\(.*\)`)

// testFailureOutput matches TestFailureOutput() and its source followed by additional stack entries:
//
// k8s.io/kubernetes/test/e2e/framework_test.TestFailureOutput(0xc000558800)
//	/nvme/gopath/src/k8s.io/kubernetes/test/e2e/framework/log/log_test.go:73 +0x1c9
// testing.tRunner(0xc000558800, 0x1af2848)
// 	/nvme/gopath/go/src/testing/testing.go:865 +0xc0
// created by testing.(*T).Run
//	/nvme/gopath/go/src/testing/testing.go:916 +0x35a
var testFailureOutput = regexp.MustCompile(`(?m)^k8s.io/kubernetes/test/e2e/framework_test\.TestFailureOutput\(.*\n\t.*(\n.*\n\t.*)*`)

// normalizeLocation removes path prefix and function parameters and certain stack entries
// that we don't care about.
func normalizeLocation(in string) string {
	out := in
	out = stackLocation.ReplaceAllString(out, "$1")
	out = functionArgs.ReplaceAllString(out, "$1()")
	out = testFailureOutput.ReplaceAllString(out, "")
	return out
}
<|endoftext|>


<commit_before>// Copyright (C) 2015-2016 Thomas de Zeeuw.
//
// Licensed under the MIT license that can be found in the LICENSE file.

package logger

import (
	"bytes"
	"fmt"
	"runtime"
	"sync"
	"time"

	"github.com/Thomasdezeeuw/logger/internal/util"
)

const (
	defaultStackSize        = 4 * 1024
	defaultEventChannelSize = 1024
	maxNWriteErrors         = 5
)

// EventWriter is the backend of the logger package. It takes events and writes
// them to a storage, this can for example be a file, a database or the
// terminal.
type EventWriter interface {
	// Write is called every time a Log operation is called. This call should
	// block until the event is written. Advised is to buffer the events. All
	// calls to Write are synchronous.
	//
	// If an error is returned the event is expected to NOT have been written. The
	// event will written again after the error handler is called. If the
	// EventWriter returns 5 errors in a row the EventWriter is consided to be bad
	// and will be removed from the list of event writers. HandlerError will be
	// called with ErrBadEventWriter if this happens.
	Write(Event) error

	// HandleError is called every time Write returns an error. A special case is
	// ErrBadEventWriter, if this error gets passed it means the EventWriter is
	// considered bad and will no longer receive events.
	HandleError(error)

	// Close is called on the EventWriter once Close() (on the package) is called.
	Close() error
}

var (
	eventChannel       = make(chan Event, defaultEventChannelSize)
	eventChannelClosed = make(chan struct{}, 1) // Can't block.
	eventWriters       []EventWriter
	started            bool
)

// Start starts the logger package and enables writing to the given
// EventWriters.
func Start(ews ...EventWriter) {
	if started {
		panic("logger: can only Start once")
	} else if len(ews) < 1 {
		panic("logger: need atleast a single EventWriter to write to")
	}

	started = true
	eventWriters = ews

	go writeEvents()
}

// ErrBadEventWriter gets passed to the error handler of an EventWriter after it
// returned too many write errors in a row. After the error handler of the
// EventWriter is called with this error the writer is considered faulty and
// will no longer recive any Events.
var ErrBadEventWriter = fmt.Errorf("EventWriter is bad, %d faulty writes, EventWriter will be dropped", maxNWriteErrors)

// Needs to be run in it's own goroutine, it blocks until eventChannel is
// closed. After eventChannel is closed it sends a signal to eventChannelClosed.
func writeEvents() {
	var wg sync.WaitGroup
	wg.Add(len(eventWriters))

	// Create event sub channels for each EventWriter and start each EventWriter.
	var eventSubChannels = make([]chan Event, len(eventWriters))
	for i, ew := range eventWriters {
		eventSubChannels[i] = make(chan Event, defaultEventChannelSize)
		go startEventWriter(ew, eventSubChannels[i], &wg)
	}

	// Fan out the events to all the sub channels.
	for event := range eventChannel {
		for _, eventSubChannel := range eventSubChannels {
			eventSubChannel <- event
		}
	}

	// Close each sub channel.
	for _, eventSubChannel := range eventSubChannels {
		close(eventSubChannel)
	}

	wg.Wait()
	eventChannelClosed <- struct{}{}
}

// StartEventWriter blocks until the events channel is closed.
func startEventWriter(ew EventWriter, events <-chan Event, wg *sync.WaitGroup) {
	for event := range events {
		err := writeEvent(ew, event)
		if err == nil {
			continue
		}

		// At this point the EventWriter is bad and we won't write to it anymore.
		ew.HandleError(err)

		// todo: improve this, don't send to the channel anymore if the writer is
		// bad.
		drain(events)
		break
	}

	wg.Done()
}

// Drain an events channel. It returns once the event channel is closed.
func drain(events <-chan Event) {
	for range events {
	}
}

// WriteEvent tries to write the event to the given EventWriter, it tries it up
// to maxNWriteErrors times. If EventWriter.Write returns an error it gets
// passed to the error handler of the EventWriter.
//
// This function either returns ErrBadEventWriter or nil as an error.
func writeEvent(ew EventWriter, event Event) error {
	for n := 1; n <= maxNWriteErrors; n++ {
		err := ew.Write(event)
		if err == nil {
			return nil
		}

		// Handle the error and try again.
		ew.HandleError(err)
	}

	return ErrBadEventWriter
}

// Close stops all the Log Operations from being usable, and they will panic if
// used after Close is called. It also closes all EventWriters and returns the
// first returned error. The EventWriters are closed in the order they are
// passed to Start.
func Close() error {
	close(eventChannel)
	<-eventChannelClosed

	var err error
	for _, eventWriter := range eventWriters {
		er := eventWriter.Close()
		if er != nil && err == nil {
			err = er
		}
	}
	return err
}

// Subbed for testing.
var now = time.Now

// Debug logs a debug message.
func Debug(tags Tags, msg string) {
	eventChannel <- Event{DebugEvent, now(), tags, msg, nil}
}

// Debugf is a formatted function of Debug.
func Debugf(tags Tags, format string, v ...interface{}) {
	Debug(tags, fmt.Sprintf(format, v...))
}

// Info logs an informational message.
func Info(tags Tags, msg string) {
	eventChannel <- Event{InfoEvent, now(), tags, msg, nil}
}

// Infof is a formatted function of Info.
func Infof(tags Tags, format string, v ...interface{}) {
	Info(tags, fmt.Sprintf(format, v...))
}

// Warn logs a warning message.
func Warn(tags Tags, msg string) {
	eventChannel <- Event{WarnEvent, now(), tags, msg, nil}
}

// Warnf is a formatted function of Warn.
func Warnf(tags Tags, format string, v ...interface{}) {
	Warn(tags, fmt.Sprintf(format, v...))
}

// Error logs an error message.
func Error(tags Tags, err error) {
	eventChannel <- Event{ErrorEvent, now(), tags, err.Error(), nil}
}

// Errorf is a formatted function of Error.
func Errorf(tags Tags, format string, v ...interface{}) {
	Error(tags, fmt.Errorf(format, v...))
}

// Fatal logs a recovered error which could have killed the application. Fatal
// adds a stack trace (type []byte) as Event.Data.
func Fatal(tags Tags, recv interface{}) {
	stackTrace := getStackTrace()
	msg := util.InterfaceToString(recv)
	eventChannel <- Event{FatalEvent, now(), tags, msg, stackTrace}
}

// Create a stack trace and remove the caller's function from the trace.
func getStackTrace() []byte {
	stackTrace := make([]byte, defaultStackSize)
	for {
		n := runtime.Stack(stackTrace, false)
		if n < len(stackTrace) {
			return removeFnsFromStack(stackTrace[:n])
		}

		stackTrace = make([]byte, 2*len(stackTrace))
	}
}

const newLine byte = '\n'

// Threat as constant.
var newLineBytes = []byte{newLine}

// Remove the first two functions from the stack trace. Given the following
// stack trace:
//
//	1. goroutine 17 [running]:
//	2. github.com/Thomasdezeeuw/logger.getStackTrace(0x0, 0x0, 0x0)
//	3. 	/Users/thomas/go/src/github.com/Thomasdezeeuw/logger/log.go:215 +0x83
//	4. github.com/Thomasdezeeuw/logger.Fatal(0xc82000cb40, 0x2, 0x2, 0x14dac0, 0xc82000b3e0)
//	5. 	/Users/thomas/go/src/github.com/Thomasdezeeuw/logger/log.go:206 +0x24
//	6. github.com/Thomasdezeeuw/logger.TestLog.func1(0xc82000cb40, 0x2, 0x2, 0x7, 0xecd77abac, 0x0, 0x2a6ee0, 0xc82000cb40, 0x2, 0x2, ...)
//	7. 	/Users/thomas/go/src/github.com/Thomasdezeeuw/logger/log_test.go:87 +0x9f
//	8. github.com/Thomasdezeeuw/logger.TestLog(0xc8200a0e10)
//	9. 	/Users/thomas/go/src/github.com/Thomasdezeeuw/logger/log_test.go:147 +0x9de
//
// The following stacktrace will be returned.
//
//	1. goroutine 17 [running]:
//	2. github.com/Thomasdezeeuw/logger.TestLog.func1(0xc82000cb40, 0x2, 0x2, 0x7, 0xecd77abac, 0x0, 0x2a6ee0, 0xc82000cb40, 0x2, 0x2, ...)
//	3. 	/Users/thomas/go/src/github.com/Thomasdezeeuw/logger/log_test.go:87 +0x9f
//	4. github.com/Thomasdezeeuw/logger.TestLog(0xc8200a0e10)
//	5. 	/Users/thomas/go/src/github.com/Thomasdezeeuw/logger/log_test.go:147 +0x9de
func removeFnsFromStack(stackTrace []byte) []byte {
	newStackTrace := make([]byte, 0, len(stackTrace))

	lines := bytes.Split(stackTrace, newLineBytes)
	newStackTrace = append(newStackTrace, lines[0]...)
	newStackTrace = append(newStackTrace, newLine)

	// Ignore the second though fifth lines, the first two functions.
	for _, line := range lines[4:] {
		newStackTrace = append(newStackTrace, line...)
		newStackTrace = append(newStackTrace, newLine)
	}

	return newStackTrace
}

// Thumbstone indicates a function is still used in production. When developing
// software it's possible to introduce dead code with updates and new features.
// If a function is being suspected of being dead (not used) in production, add
// a call to Thumbstone and check the production logs to see if you're right.
//
// The caller of the (possibly) dead function will be put in the message, using
// the following format:
//	Function functionName called by callerFunctionName, from file /path/to/file on line lineNumber
// For example:
//	Function myFunction called by main.main, from file /main.go on line 20
func Thumbstone(tags Tags, functionName string) {
	var msg string
	if pc, file, line, ok := runtime.Caller(2); ok {
		fn := runtime.FuncForPC(pc)
		msg = fmt.Sprintf("Function %s called by %s, from file %s on line %d",
			functionName, fn.Name(), file, line)
	} else {
		msg = "Function " + functionName + " called from unkown location"
	}

	eventChannel <- Event{ThumbEvent, now(), tags, msg, nil}
}

// Log logs a custom created event.
//
// Note: the timestamp doesn't need to be set, because it will be set by Log.
func Log(event Event) {
	event.Timestamp = now()
	eventChannel <- event
}
<commit_msg>Improve removeFnsFromStack performance<commit_after>// Copyright (C) 2015-2016 Thomas de Zeeuw.
//
// Licensed under the MIT license that can be found in the LICENSE file.

package logger

import (
	"bytes"
	"fmt"
	"runtime"
	"sync"
	"time"

	"github.com/Thomasdezeeuw/logger/internal/util"
)

const (
	defaultStackSize        = 4 * 1024
	defaultEventChannelSize = 1024
	maxNWriteErrors         = 5
)

// EventWriter is the backend of the logger package. It takes events and writes
// them to a storage, this can for example be a file, a database or the
// terminal.
type EventWriter interface {
	// Write is called every time a Log operation is called. This call should
	// block until the event is written. Advised is to buffer the events. All
	// calls to Write are synchronous.
	//
	// If an error is returned the event is expected to NOT have been written. The
	// event will written again after the error handler is called. If the
	// EventWriter returns 5 errors in a row the EventWriter is consided to be bad
	// and will be removed from the list of event writers. HandlerError will be
	// called with ErrBadEventWriter if this happens.
	Write(Event) error

	// HandleError is called every time Write returns an error. A special case is
	// ErrBadEventWriter, if this error gets passed it means the EventWriter is
	// considered bad and will no longer receive events.
	HandleError(error)

	// Close is called on the EventWriter once Close() (on the package) is called.
	Close() error
}

var (
	eventChannel       = make(chan Event, defaultEventChannelSize)
	eventChannelClosed = make(chan struct{}, 1) // Can't block.
	eventWriters       []EventWriter
	started            bool
)

// Start starts the logger package and enables writing to the given
// EventWriters.
func Start(ews ...EventWriter) {
	if started {
		panic("logger: can only Start once")
	} else if len(ews) < 1 {
		panic("logger: need atleast a single EventWriter to write to")
	}

	started = true
	eventWriters = ews

	go writeEvents()
}

// ErrBadEventWriter gets passed to the error handler of an EventWriter after it
// returned too many write errors in a row. After the error handler of the
// EventWriter is called with this error the writer is considered faulty and
// will no longer recive any Events.
var ErrBadEventWriter = fmt.Errorf("EventWriter is bad, %d faulty writes, EventWriter will be dropped", maxNWriteErrors)

// Needs to be run in it's own goroutine, it blocks until eventChannel is
// closed. After eventChannel is closed it sends a signal to eventChannelClosed.
func writeEvents() {
	var wg sync.WaitGroup
	wg.Add(len(eventWriters))

	// Create event sub channels for each EventWriter and start each EventWriter.
	var eventSubChannels = make([]chan Event, len(eventWriters))
	for i, ew := range eventWriters {
		eventSubChannels[i] = make(chan Event, defaultEventChannelSize)
		go startEventWriter(ew, eventSubChannels[i], &wg)
	}

	// Fan out the events to all the sub channels.
	for event := range eventChannel {
		for _, eventSubChannel := range eventSubChannels {
			eventSubChannel <- event
		}
	}

	// Close each sub channel.
	for _, eventSubChannel := range eventSubChannels {
		close(eventSubChannel)
	}

	wg.Wait()
	eventChannelClosed <- struct{}{}
}

// StartEventWriter blocks until the events channel is closed.
func startEventWriter(ew EventWriter, events <-chan Event, wg *sync.WaitGroup) {
	for event := range events {
		err := writeEvent(ew, event)
		if err == nil {
			continue
		}

		// At this point the EventWriter is bad and we won't write to it anymore.
		ew.HandleError(err)

		// todo: improve this, don't send to the channel anymore if the writer is
		// bad.
		drain(events)
		break
	}

	wg.Done()
}

// Drain an events channel. It returns once the event channel is closed.
func drain(events <-chan Event) {
	for range events {
	}
}

// WriteEvent tries to write the event to the given EventWriter, it tries it up
// to maxNWriteErrors times. If EventWriter.Write returns an error it gets
// passed to the error handler of the EventWriter.
//
// This function either returns ErrBadEventWriter or nil as an error.
func writeEvent(ew EventWriter, event Event) error {
	for n := 1; n <= maxNWriteErrors; n++ {
		err := ew.Write(event)
		if err == nil {
			return nil
		}

		// Handle the error and try again.
		ew.HandleError(err)
	}

	return ErrBadEventWriter
}

// Close stops all the Log Operations from being usable, and they will panic if
// used after Close is called. It also closes all EventWriters and returns the
// first returned error. The EventWriters are closed in the order they are
// passed to Start.
func Close() error {
	close(eventChannel)
	<-eventChannelClosed

	var err error
	for _, eventWriter := range eventWriters {
		er := eventWriter.Close()
		if er != nil && err == nil {
			err = er
		}
	}
	return err
}

// Subbed for testing.
var now = time.Now

// Debug logs a debug message.
func Debug(tags Tags, msg string) {
	eventChannel <- Event{DebugEvent, now(), tags, msg, nil}
}

// Debugf is a formatted function of Debug.
func Debugf(tags Tags, format string, v ...interface{}) {
	Debug(tags, fmt.Sprintf(format, v...))
}

// Info logs an informational message.
func Info(tags Tags, msg string) {
	eventChannel <- Event{InfoEvent, now(), tags, msg, nil}
}

// Infof is a formatted function of Info.
func Infof(tags Tags, format string, v ...interface{}) {
	Info(tags, fmt.Sprintf(format, v...))
}

// Warn logs a warning message.
func Warn(tags Tags, msg string) {
	eventChannel <- Event{WarnEvent, now(), tags, msg, nil}
}

// Warnf is a formatted function of Warn.
func Warnf(tags Tags, format string, v ...interface{}) {
	Warn(tags, fmt.Sprintf(format, v...))
}

// Error logs an error message.
func Error(tags Tags, err error) {
	eventChannel <- Event{ErrorEvent, now(), tags, err.Error(), nil}
}

// Errorf is a formatted function of Error.
func Errorf(tags Tags, format string, v ...interface{}) {
	Error(tags, fmt.Errorf(format, v...))
}

// Fatal logs a recovered error which could have killed the application. Fatal
// adds a stack trace (type []byte) as Event.Data.
func Fatal(tags Tags, recv interface{}) {
	stackTrace := getStackTrace()
	msg := util.InterfaceToString(recv)
	eventChannel <- Event{FatalEvent, now(), tags, msg, stackTrace}
}

// Create a stack trace and remove the caller's function from the trace.
func getStackTrace() []byte {
	stackTrace := make([]byte, defaultStackSize)
	for {
		n := runtime.Stack(stackTrace, false)
		if n < len(stackTrace) {
			return removeFnsFromStack(stackTrace[:n])
		}

		stackTrace = make([]byte, 2*len(stackTrace))
	}
}

const newLine byte = '\n'

// Threat as constant.
var newLineBytes = []byte{newLine}

// Remove the first two functions from the stack trace. Given the following
// stack trace:
//
//	1. goroutine 17 [running]:
//	2. github.com/Thomasdezeeuw/logger.getStackTrace(0x0, 0x0, 0x0)
//	3. 	/Users/thomas/go/src/github.com/Thomasdezeeuw/logger/log.go:215 +0x83
//	4. github.com/Thomasdezeeuw/logger.Fatal(0xc82000cb40, 0x2, 0x2, 0x14dac0, 0xc82000b3e0)
//	5. 	/Users/thomas/go/src/github.com/Thomasdezeeuw/logger/log.go:206 +0x24
//	6. github.com/Thomasdezeeuw/logger.TestLog.func1(0xc82000cb40, 0x2, 0x2, 0x7, 0xecd77abac, 0x0, 0x2a6ee0, 0xc82000cb40, 0x2, 0x2, ...)
//	7. 	/Users/thomas/go/src/github.com/Thomasdezeeuw/logger/log_test.go:87 +0x9f
//	8. github.com/Thomasdezeeuw/logger.TestLog(0xc8200a0e10)
//	9. 	/Users/thomas/go/src/github.com/Thomasdezeeuw/logger/log_test.go:147 +0x9de
//
// The following stacktrace will be returned.
//
//	1. goroutine 17 [running]:
//	2. github.com/Thomasdezeeuw/logger.TestLog.func1(0xc82000cb40, 0x2, 0x2, 0x7, 0xecd77abac, 0x0, 0x2a6ee0, 0xc82000cb40, 0x2, 0x2, ...)
//	3. 	/Users/thomas/go/src/github.com/Thomasdezeeuw/logger/log_test.go:87 +0x9f
//	4. github.com/Thomasdezeeuw/logger.TestLog(0xc8200a0e10)
//	5. 	/Users/thomas/go/src/github.com/Thomasdezeeuw/logger/log_test.go:147 +0x9de
//
// Note: this modifies the given stackTrace.
func removeFnsFromStack(stackTrace []byte) []byte {
	endFirstLine := bytes.IndexByte(stackTrace, newLine)
	startFithLine := endFirstLine

	// Ignore the second though fifth lines, the first two functions.
	for i := 0; i <= 3; i++ {
		n := bytes.IndexByte(stackTrace[startFithLine+1:], newLine)
		if n != -1 {
			startFithLine += n + 1
		} else {
			break
		}
	}

	return append(stackTrace[:endFirstLine], stackTrace[startFithLine:]...)
}

// Thumbstone indicates a function is still used in production. When developing
// software it's possible to introduce dead code with updates and new features.
// If a function is being suspected of being dead (not used) in production, add
// a call to Thumbstone and check the production logs to see if you're right.
//
// The caller of the (possibly) dead function will be put in the message, using
// the following format:
//	Function functionName called by callerFunctionName, from file /path/to/file on line lineNumber
// For example:
//	Function myFunction called by main.main, from file /main.go on line 20
func Thumbstone(tags Tags, functionName string) {
	var msg string
	if pc, file, line, ok := runtime.Caller(2); ok {
		fn := runtime.FuncForPC(pc)
		msg = fmt.Sprintf("Function %s called by %s, from file %s on line %d",
			functionName, fn.Name(), file, line)
	} else {
		msg = "Function " + functionName + " called from unkown location"
	}

	eventChannel <- Event{ThumbEvent, now(), tags, msg, nil}
}

// Log logs a custom created event.
//
// Note: the timestamp doesn't need to be set, because it will be set by Log.
func Log(event Event) {
	event.Timestamp = now()
	eventChannel <- event
}
<|endoftext|>


<commit_before>//  ---------------------------------------------------------------------------
//
//  log.go
//
//  Copyright (c) 2015, Jared Chavez.
//  All rights reserved.
//
//  Use of this source code is governed by a BSD-style
//  license that can be found in the LICENSE file.
//
//  -----------

// Package log provides interfaces, basic types and formatting helpers
// for common application logging tasks.
package log

import (
	"bytes"
	"container/ring"
	"encoding/json"
	"fmt"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"
)

var timeFormat = "2006/01/02 15:04:05.0000"

type DebugLogger interface {
	Debug(format string, v ...interface{})
}

type ErrorLogger interface {
	Error(format string, v ...interface{})
}

type InfoLogger interface {
	Info(format string, v ...interface{})
}

type LogMsg struct {
	Timestamp time.Time
	Name      string
	File      string
	Line      int
	Message   string
}

func (this *LogMsg) String() string {
	var buffer bytes.Buffer

	if this.File != "" {
		buffer.WriteString(" <")
		buffer.WriteString(this.File)
		if this.Line != 0 {
			buffer.WriteString(fmt.Sprintf(":%d", this.Line))
		}
		buffer.WriteString("> ")
	}

	if this.Message[len(this.Message)-1] == '\n' {
		return fmt.Sprintf(
			"%s [%s]%s %s",
			this.Timestamp.Format(timeFormat),
			strings.ToUpper(this.Name),
			buffer.String(),
			this.Message,
		)
	}

	return fmt.Sprintf(
		"%s [%s]%s %s\n",
		this.Timestamp.Format(timeFormat),
		strings.ToUpper(this.Name),
		buffer.String(),
		this.Message,
	)
}

// LogCloser represents the interface for a closable log object, such
// as those provided by file-backed logging.
type LogCloser interface {
	Close()
}

// Log represents the interface for a generalized log object.
type LogNotify interface {
	Print(msg *LogMsg)
}

// LogToggler represents the interface needed to temporarily enabled/disable
// a given logger.
type LogToggler interface {
	SetEnabled(bool)
}

// LogBuffer holds a specified number of logs in memory for rendering
// by the http logs Uri.
type LogBuffer struct {
	changed bool
	enabled bool
	lock    sync.RWMutex
	logs    *ring.Ring
}

// NewLogBuffer returns a pointer to a new LogBuffer instance.
func NewLogBuffer(maxSize int) *LogBuffer {
	logBuffer := &LogBuffer{
		enabled: true,
		logs:    ring.New(maxSize),
	}

	return logBuffer
}

// HasChanged reports whether or not the buffer has been modified
// since it was last read.
func (this *LogBuffer) HasChanged() bool {
	this.lock.RLock()
	defer this.lock.RUnlock()

	return this.changed
}

// Print formats and adds a new message to the log buffer. If the new
// message causes the log buffer to grow larger than its maxSize, it
// truncates the end oldest entry in the buffer. Once the message is
// stored, the changed flag is set to true.
func (this *LogBuffer) Print(msg *LogMsg) {
	this.lock.Lock()
	defer this.lock.Unlock()

	if !this.enabled {
		return
	}

	this.logs.Value = msg
	this.logs.Next()

	this.changed = true
}

// ReadAll returns a list of all log messages currently in the buffer.
func (this *LogBuffer) ReadAll() []*LogMsg {
	this.lock.RLock()
	defer this.lock.RUnlock()

	results := make([]*LogMsg, 0, this.logs.Len())
	this.logs.Do(func(item interface{}) {
		if item == nil {
			return
		}

		results = append(results, item.(*LogMsg))
	})

	this.changed = false

	return results
}

// MarshalJSON implements the standard go json marshaler for the
// LogBuffer type.
func (this *LogBuffer) MarshalJSON() ([]byte, error) {
	this.lock.RLock()
	defer this.lock.RUnlock()

	results := make([]*LogMsg, 0, this.logs.Len())
	this.logs.Do(func(item interface{}) {
		if item == nil {
			return
		}

		results = append(results, item.(*LogMsg))
	})

	return json.Marshal(&results)
}

// SetEnabled temporarily enables/disables the logger.
func (this *LogBuffer) SetEnabled(enabled bool) {
	this.lock.Lock()
	defer this.lock.Unlock()

	this.enabled = enabled
}

// NewLogMsg generates a log object given the standard logging format
// yyyy/mm/dd MM:HH:SS.ssssss [NAME] <file:line> msg
func NewLogMsg(name, format string, callDepth int, v ...interface{}) *LogMsg {
	var msg string
	if len(v) < 1 {
		msg = format
	} else {
		msg = fmt.Sprintf(format, v...)
	}

	newLog := &LogMsg{
		Timestamp: time.Now(),
		Name:      name,
		Message:   msg,
	}

	_, file, line, ok := runtime.Caller(callDepth)
	if ok {
		newLog.File = filepath.Base(file)
		newLog.Line = line
	}

	return newLog
}
<commit_msg>log buffer print fix<commit_after>//  ---------------------------------------------------------------------------
//
//  log.go
//
//  Copyright (c) 2015, Jared Chavez.
//  All rights reserved.
//
//  Use of this source code is governed by a BSD-style
//  license that can be found in the LICENSE file.
//
//  -----------

// Package log provides interfaces, basic types and formatting helpers
// for common application logging tasks.
package log

import (
	"bytes"
	"container/ring"
	"encoding/json"
	"fmt"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"
)

var timeFormat = "2006/01/02 15:04:05.0000"

type DebugLogger interface {
	Debug(format string, v ...interface{})
}

type ErrorLogger interface {
	Error(format string, v ...interface{})
}

type InfoLogger interface {
	Info(format string, v ...interface{})
}

type LogMsg struct {
	Timestamp time.Time
	Name      string
	File      string
	Line      int
	Message   string
}

func (this *LogMsg) String() string {
	var buffer bytes.Buffer

	if this.File != "" {
		buffer.WriteString(" <")
		buffer.WriteString(this.File)
		if this.Line != 0 {
			buffer.WriteString(fmt.Sprintf(":%d", this.Line))
		}
		buffer.WriteString("> ")
	}

	if this.Message[len(this.Message)-1] == '\n' {
		return fmt.Sprintf(
			"%s [%s]%s %s",
			this.Timestamp.Format(timeFormat),
			strings.ToUpper(this.Name),
			buffer.String(),
			this.Message,
		)
	}

	return fmt.Sprintf(
		"%s [%s]%s %s\n",
		this.Timestamp.Format(timeFormat),
		strings.ToUpper(this.Name),
		buffer.String(),
		this.Message,
	)
}

// LogCloser represents the interface for a closable log object, such
// as those provided by file-backed logging.
type LogCloser interface {
	Close()
}

// Log represents the interface for a generalized log object.
type LogNotify interface {
	Print(msg *LogMsg)
}

// LogToggler represents the interface needed to temporarily enabled/disable
// a given logger.
type LogToggler interface {
	SetEnabled(bool)
}

// LogBuffer holds a specified number of logs in memory for rendering
// by the http logs Uri.
type LogBuffer struct {
	changed bool
	enabled bool
	lock    sync.RWMutex
	logs    *ring.Ring
}

// NewLogBuffer returns a pointer to a new LogBuffer instance.
func NewLogBuffer(maxSize int) *LogBuffer {
	logBuffer := &LogBuffer{
		enabled: true,
		logs:    ring.New(maxSize),
	}

	return logBuffer
}

// HasChanged reports whether or not the buffer has been modified
// since it was last read.
func (this *LogBuffer) HasChanged() bool {
	this.lock.RLock()
	defer this.lock.RUnlock()

	return this.changed
}

// Print formats and adds a new message to the log buffer. If the new
// message causes the log buffer to grow larger than its maxSize, it
// truncates the end oldest entry in the buffer. Once the message is
// stored, the changed flag is set to true.
func (this *LogBuffer) Print(msg *LogMsg) {
	this.lock.Lock()
	defer this.lock.Unlock()

	if !this.enabled {
		return
	}

	this.logs.Value = msg
	this.logs = this.logs.Next()

	this.changed = true
}

// ReadAll returns a list of all log messages currently in the buffer.
func (this *LogBuffer) ReadAll() []*LogMsg {
	this.lock.RLock()
	defer this.lock.RUnlock()

	results := make([]*LogMsg, 0, this.logs.Len())
	this.logs.Do(func(item interface{}) {
		if item == nil {
			return
		}

		results = append(results, item.(*LogMsg))
	})

	this.changed = false

	return results
}

// MarshalJSON implements the standard go json marshaler for the
// LogBuffer type.
func (this *LogBuffer) MarshalJSON() ([]byte, error) {
	this.lock.RLock()
	defer this.lock.RUnlock()

	results := make([]*LogMsg, 0, this.logs.Len())
	this.logs.Do(func(item interface{}) {
		if item == nil {
			return
		}

		results = append(results, item.(*LogMsg))
	})

	return json.Marshal(&results)
}

// SetEnabled temporarily enables/disables the logger.
func (this *LogBuffer) SetEnabled(enabled bool) {
	this.lock.Lock()
	defer this.lock.Unlock()

	this.enabled = enabled
}

// NewLogMsg generates a log object given the standard logging format
// yyyy/mm/dd MM:HH:SS.ssssss [NAME] <file:line> msg
func NewLogMsg(name, format string, callDepth int, v ...interface{}) *LogMsg {
	var msg string
	if len(v) < 1 {
		msg = format
	} else {
		msg = fmt.Sprintf(format, v...)
	}

	newLog := &LogMsg{
		Timestamp: time.Now(),
		Name:      name,
		Message:   msg,
	}

	_, file, line, ok := runtime.Caller(callDepth)
	if ok {
		newLog.File = filepath.Base(file)
		newLog.Line = line
	}

	return newLog
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"html/template"
	"net/http"

	log "github.com/Sirupsen/logrus"
	"github.com/xyproto/permissions2"
	"github.com/yuin/gopher-lua"
)

// Retrieve all the arguments given to a lua function
// and gather the strings in a buffer.
func arguments2buffer(L *lua.LState) bytes.Buffer {
	var buf bytes.Buffer
	top := L.GetTop()
	// Add all the string arguments to the buffer
	for i := 1; i <= top; i++ {
		buf.WriteString(L.Get(i).String())
		if i != top {
			buf.WriteString(" ")
		}
	}
	buf.WriteString("\n")
	return buf
}

// Convert a string slice to a lua table
func strings2table(L *lua.LState, sl []string) *lua.LTable {
	table := L.NewTable()
	for _, element := range sl {
		table.Append(lua.LString(element))
	}
	return table
}

// Convert a map[string]string to a lua table
func map2table(L *lua.LState, m map[string]string) *lua.LTable {
	table := L.NewTable()
	for key, value := range m {
		L.RawSet(table, lua.LString(key), lua.LString(value))
	}
	return table
}

// Return a *lua.LState object that contains several exposed functions
func exportCommonFunctions(w http.ResponseWriter, req *http.Request, filename string, perm *permissions.Permissions, L *lua.LState, luapool *lStatePool) {

	// Retrieve the userstate
	userstate := perm.UserState()

	// Make basic functions, like print, available to the Lua script.
	// Only exports functions that can relate to HTTP responses or requests.
	exportBasicWeb(w, req, L, filename)

	// Functions for serving files in the same directory as a script
	exportServeFile(w, req, L, filename, perm, luapool)

	// Make other basic functions available
	exportBasicSystemFunctions(L)

	// Functions for rendering markdown or amber
	exportRenderFunctions(w, req, L)

	// Make the functions related to userstate available to the Lua script
	exportUserstate(w, req, L, userstate)

	// Simpleredis data structures
	exportList(L, userstate)
	exportSet(L, userstate)
	exportHash(L, userstate)
	exportKeyValue(L, userstate)
}

// Run a Lua file as a HTTP handler. Also has access to the userstate and permissions.
// Returns an error if there was a problem with running the lua script, otherwise nil.
func runLua(w http.ResponseWriter, req *http.Request, filename string, perm *permissions.Permissions, luapool *lStatePool) error {

	// Retrieve a Lua state
	L := luapool.Get()
	defer luapool.Put(L)

	// Export functions to the Lua state
	exportCommonFunctions(w, req, filename, perm, L, luapool)

	// Run the script
	if err := L.DoFile(filename); err != nil {
		// Logging and/or HTTP response is handled elsewhere
		return err
	}

	return nil
}

// Run a Lua string as a HTTP handler. Also has access to the userstate and permissions.
// Returns an error if there was a problem with running the lua script, otherwise nil.
func runLuaString(w http.ResponseWriter, req *http.Request, script string, perm *permissions.Permissions, luapool *lStatePool) error {

	// Retrieve a Lua state
	L := luapool.Get()

	// Give no filename (an empty string will be handled correctly by the function).
	exportCommonFunctions(w, req, "", perm, L, luapool)

	// Run the script
	if err := L.DoString(script); err != nil {
		// Close the Lua state
		L.Close()

		// Logging and/or HTTP response is handled elsewhere
		return err
	}

	// TODO Figure out if the Lua state should rather be put back in either case
	// Only put the Lua state back if there were no errors
	luapool.Put(L)

	return nil
}

// Run a Lua file as a configuration script. Also has access to the userstate and permissions.
// Returns an error if there was a problem with running the lua script, otherwise nil.
func runConfiguration(filename string, perm *permissions.Permissions, luapool *lStatePool) error {

	// Retrieve a Lua state
	L := luapool.Get()

	// Retrieve the userstate
	userstate := perm.UserState()

	// Server configuration functions
	exportServerConfigFunctions(L, perm, filename, luapool)

	// Other basic system functions, like log()
	exportBasicSystemFunctions(L)

	// Simpleredis data structures (could be used for storing server stats)
	exportList(L, userstate)
	exportSet(L, userstate)
	exportHash(L, userstate)
	exportKeyValue(L, userstate)

	// Run the script
	if err := L.DoFile(filename); err != nil {
		// Close the Lua state
		L.Close()

		// Logging and/or HTTP response is handled elsewhere
		return err
	}

	// Only put the Lua state back if there were no errors
	luapool.Put(L)

	return nil
}

/*
 * Return the functions available in the given Lua code as
 * functions in a map that can be used by templates.
 *
 * Note that the lua functions must only accept and return strings
 * and that only the first returned value will be accessible.
 * The Lua functions may take an optional number of arguments.
 */
func luaFunctionMap(w http.ResponseWriter, req *http.Request, luadata []byte, filename string, perm *permissions.Permissions, luapool *lStatePool) (template.FuncMap, error) {

	// Retrieve a Lua state
	L := luapool.Get()
	defer luapool.Put(L)

	// Prepare an empty map of functions (and variables)
	funcs := make(template.FuncMap)

	// Give no filename (an empty string will be handled correctly by the function).
	exportCommonFunctions(w, req, filename, perm, L, luapool)

	// Run the script
	if err := L.DoString(string(luadata)); err != nil {
		// Close the Lua state
		L.Close()

		// Logging and/or HTTP response is handled elsewhere
		return funcs, err
	}

	// Extract the available functions from the Lua state
	globalTable := L.G.Global
	globalTable.ForEach(func(key, value lua.LValue) {

		// Check if the current value is a string variable
		if luaString, ok := value.(lua.LString); ok {

			// Store the variable in the same map as the functions (string -> interface)
			// for ease of use together with templates.
			funcs[key.String()] = luaString.String()

			// Check if the current value is a function
		} else if luaFunc, ok := value.(*lua.LFunction); ok {

			// Only export the functions defined in the given Lua code,
			// not all the global functions. IsG is true if the function is global.
			if !luaFunc.IsG {

				functionName := key.String()

				// Register the function, with a variable number of string arguments
				// Functions returning (string, error) are supported by html.template
				funcs[functionName] = func(args ...string) (string, error) {

					// Create a brand new Lua state
					L2 := luapool.New()
					defer L2.Close()

					// Set up a new Lua state with the current http.ResponseWriter and *http.Request
					exportCommonFunctions(w, req, filename, perm, L2, luapool)

					// Push the Lua function to run
					L2.Push(luaFunc)

					// Push the given arguments
					for _, arg := range args {
						L2.Push(lua.LString(arg))
					}

					// Run the Lua function
					if err := L2.PCall(len(args), lua.MultRet, nil); err != nil {
						// If calling the function did not work out, return the infostring and error
						return infostring(functionName, args), err
					}

					// Empty return value if no values were returned
					retval := ""

					// Return the first of the returned arguments, as a string
					if L2.GetTop() >= 1 {
						retval = L2.ToString(1)
					}

					if DEBUG_MODE && VERBOSE {
						log.Info(infostring(functionName, args) + " -> \"" + retval + "\"")
					}

					// No return value, return an empty string and nil
					return retval, nil
				}
			}
		}
	})

	// Return the map of functions
	return funcs, nil
}
<commit_msg>Lua tables can now also be used by Amber templates<commit_after>package main

import (
	"bytes"
	"html/template"
	"net/http"

	log "github.com/Sirupsen/logrus"
	"github.com/xyproto/permissions2"
	"github.com/yuin/gopher-lua"
)

// Retrieve all the arguments given to a lua function
// and gather the strings in a buffer.
func arguments2buffer(L *lua.LState) bytes.Buffer {
	var buf bytes.Buffer
	top := L.GetTop()
	// Add all the string arguments to the buffer
	for i := 1; i <= top; i++ {
		buf.WriteString(L.Get(i).String())
		if i != top {
			buf.WriteString(" ")
		}
	}
	buf.WriteString("\n")
	return buf
}

// Convert a string slice to a lua table
func strings2table(L *lua.LState, sl []string) *lua.LTable {
	table := L.NewTable()
	for _, element := range sl {
		table.Append(lua.LString(element))
	}
	return table
}

// Convert a map[string]string to a lua table
func map2table(L *lua.LState, m map[string]string) *lua.LTable {
	table := L.NewTable()
	for key, value := range m {
		L.RawSet(table, lua.LString(key), lua.LString(value))
	}
	return table
}

// Return a *lua.LState object that contains several exposed functions
func exportCommonFunctions(w http.ResponseWriter, req *http.Request, filename string, perm *permissions.Permissions, L *lua.LState, luapool *lStatePool) {

	// Retrieve the userstate
	userstate := perm.UserState()

	// Make basic functions, like print, available to the Lua script.
	// Only exports functions that can relate to HTTP responses or requests.
	exportBasicWeb(w, req, L, filename)

	// Functions for serving files in the same directory as a script
	exportServeFile(w, req, L, filename, perm, luapool)

	// Make other basic functions available
	exportBasicSystemFunctions(L)

	// Functions for rendering markdown or amber
	exportRenderFunctions(w, req, L)

	// Make the functions related to userstate available to the Lua script
	exportUserstate(w, req, L, userstate)

	// Simpleredis data structures
	exportList(L, userstate)
	exportSet(L, userstate)
	exportHash(L, userstate)
	exportKeyValue(L, userstate)
}

// Run a Lua file as a HTTP handler. Also has access to the userstate and permissions.
// Returns an error if there was a problem with running the lua script, otherwise nil.
func runLua(w http.ResponseWriter, req *http.Request, filename string, perm *permissions.Permissions, luapool *lStatePool) error {

	// Retrieve a Lua state
	L := luapool.Get()
	defer luapool.Put(L)

	// Export functions to the Lua state
	exportCommonFunctions(w, req, filename, perm, L, luapool)

	// Run the script
	if err := L.DoFile(filename); err != nil {
		// Logging and/or HTTP response is handled elsewhere
		return err
	}

	return nil
}

// Run a Lua string as a HTTP handler. Also has access to the userstate and permissions.
// Returns an error if there was a problem with running the lua script, otherwise nil.
func runLuaString(w http.ResponseWriter, req *http.Request, script string, perm *permissions.Permissions, luapool *lStatePool) error {

	// Retrieve a Lua state
	L := luapool.Get()

	// Give no filename (an empty string will be handled correctly by the function).
	exportCommonFunctions(w, req, "", perm, L, luapool)

	// Run the script
	if err := L.DoString(script); err != nil {
		// Close the Lua state
		L.Close()

		// Logging and/or HTTP response is handled elsewhere
		return err
	}

	// TODO Figure out if the Lua state should rather be put back in either case
	// Only put the Lua state back if there were no errors
	luapool.Put(L)

	return nil
}

// Run a Lua file as a configuration script. Also has access to the userstate and permissions.
// Returns an error if there was a problem with running the lua script, otherwise nil.
func runConfiguration(filename string, perm *permissions.Permissions, luapool *lStatePool) error {

	// Retrieve a Lua state
	L := luapool.Get()

	// Retrieve the userstate
	userstate := perm.UserState()

	// Server configuration functions
	exportServerConfigFunctions(L, perm, filename, luapool)

	// Other basic system functions, like log()
	exportBasicSystemFunctions(L)

	// Simpleredis data structures (could be used for storing server stats)
	exportList(L, userstate)
	exportSet(L, userstate)
	exportHash(L, userstate)
	exportKeyValue(L, userstate)

	// Run the script
	if err := L.DoFile(filename); err != nil {
		// Close the Lua state
		L.Close()

		// Logging and/or HTTP response is handled elsewhere
		return err
	}

	// Only put the Lua state back if there were no errors
	luapool.Put(L)

	return nil
}

/*
 * Return the functions available in the given Lua code as
 * functions in a map that can be used by templates.
 *
 * Note that the lua functions must only accept and return strings
 * and that only the first returned value will be accessible.
 * The Lua functions may take an optional number of arguments.
 */
func luaFunctionMap(w http.ResponseWriter, req *http.Request, luadata []byte, filename string, perm *permissions.Permissions, luapool *lStatePool) (template.FuncMap, error) {

	// Retrieve a Lua state
	L := luapool.Get()
	defer luapool.Put(L)

	// Prepare an empty map of functions (and variables)
	funcs := make(template.FuncMap)

	// Give no filename (an empty string will be handled correctly by the function).
	exportCommonFunctions(w, req, filename, perm, L, luapool)

	// Run the script
	if err := L.DoString(string(luadata)); err != nil {
		// Close the Lua state
		L.Close()

		// Logging and/or HTTP response is handled elsewhere
		return funcs, err
	}

	// Extract the available functions from the Lua state
	globalTable := L.G.Global
	globalTable.ForEach(func(key, value lua.LValue) {

		// Check if the current value is a string variable
		if luaString, ok := value.(lua.LString); ok {

			// Store the variable in the same map as the functions (string -> interface)
			// for ease of use together with templates.
			funcs[key.String()] = luaString.String()

		} else if luaTable, ok := value.(*lua.LTable); ok {

			// Set up the possible mappings
			mss := make(map[string]string)
			msi := make(map[string]int)
			mis := make(map[int]string)
			mii := make(map[int]int)

			var skey, svalue lua.LString
			var ikey, ivalue lua.LNumber
			var has_skey, has_ikey, has_svalue, has_ivalue bool

			luaTable.ForEach(func(tkey, tvalue lua.LValue) {

				// Convert the keys and values to strings or ints
				skey, has_skey = tkey.(lua.LString)
				ikey, has_ikey = tkey.(lua.LNumber)
				svalue, has_svalue = tvalue.(lua.LString)
				ivalue, has_ivalue = tvalue.(lua.LNumber)

				// Store the right keys and values in the right maps
				if has_skey && has_svalue {
					mss[skey.String()] = svalue.String()
				} else if has_skey && has_ivalue {
					msi[skey.String()] = int(ivalue)
				} else if has_ikey && has_svalue {
					mis[int(ikey)] = svalue.String()
				} else if has_ikey && has_ivalue {
					mii[int(ikey)] = int(ivalue)
				}
			})

			// Make the first map that has values available
			if len(mss) > 0 {
				//log.Println(key, "STRING -> STRING map")
				funcs[key.String()] = mss
			} else if len(msi) > 0 {
				//log.Println(key, "STRING -> INT map")
				funcs[key.String()] = msi
			} else if len(mis) > 0 {
				//log.Println(key, "STRING -> INT map")
				funcs[key.String()] = mis
			} else if len(mii) > 0 {
				//log.Println(key, "INT -> INT map")
				funcs[key.String()] = mii
			}

			// Check if the current value is a function
		} else if luaFunc, ok := value.(*lua.LFunction); ok {

			// Only export the functions defined in the given Lua code,
			// not all the global functions. IsG is true if the function is global.
			if !luaFunc.IsG {

				functionName := key.String()

				// Register the function, with a variable number of string arguments
				// Functions returning (string, error) are supported by html.template
				funcs[functionName] = func(args ...string) (string, error) {

					// Create a brand new Lua state
					L2 := luapool.New()
					defer L2.Close()

					// Set up a new Lua state with the current http.ResponseWriter and *http.Request
					exportCommonFunctions(w, req, filename, perm, L2, luapool)

					// Push the Lua function to run
					L2.Push(luaFunc)

					// Push the given arguments
					for _, arg := range args {
						L2.Push(lua.LString(arg))
					}

					// Run the Lua function
					if err := L2.PCall(len(args), lua.MultRet, nil); err != nil {
						// If calling the function did not work out, return the infostring and error
						return infostring(functionName, args), err
					}

					// Empty return value if no values were returned
					retval := ""

					// Return the first of the returned arguments, as a string
					if L2.GetTop() >= 1 {
						retval = L2.ToString(1)
					}

					if DEBUG_MODE && VERBOSE {
						log.Info(infostring(functionName, args) + " -> \"" + retval + "\"")
					}

					// No return value, return an empty string and nil
					return retval, nil
				}
			}
		}
	})

	// Return the map of functions
	return funcs, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/subtle"
	"encoding/json"
	"fmt"
	"io"
	"mime"
	"net/http"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/badgerodon/ioutil"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/gorilla/sessions"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/julienschmidt/httprouter"
	"github.com/flynn/flynn/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/flynn/flynn/pkg/cors"
	"github.com/flynn/flynn/pkg/ctxhelper"
	"github.com/flynn/flynn/pkg/httphelper"
	"github.com/flynn/flynn/pkg/status"
)

type LoginInfo struct {
	Token string `json:"token" form:"token"`
}

func AssetReader(path string) (io.ReadSeeker, time.Time, error) {
	t := time.Time{}
	data, err := Asset(path)
	if err != nil {
		return nil, t, err
	}
	if fi, err := AssetInfo(path); err != nil {
		t = fi.ModTime()
	}
	return bytes.NewReader(data), t, nil
}

func APIHandler(conf *Config) http.Handler {
	api := &API{
		conf: conf,
	}
	router := httprouter.New()
	router2 := httprouter.New()

	prefixPath := func(p string) string {
		return path.Join(conf.PathPrefix, p)
	}

	router.HandlerFunc("GET", status.Path, status.HealthyHandler.ServeHTTP)

	router.POST(prefixPath("/user/sessions"), api.WrapHandler(api.Login))
	router.DELETE(prefixPath("/user/session"), api.WrapHandler(api.Logout))

	router.GET(prefixPath("/config"), api.WrapHandler(api.GetConfig))
	router.GET(prefixPath("/cert"), api.WrapHandler(api.GetCert))

	router.NotFound = router2.ServeHTTP
	router2.GET(prefixPath("/*path"), api.WrapHandler(api.ServeAsset))

	return httphelper.ContextInjector("dashboard",
		httphelper.NewRequestLogger(
			api.CorsHandler(router)))
}

type API struct {
	conf *Config
}

const ctxSessionKey = "session"

func (api *API) WrapHandler(handler httphelper.HandlerFunc) httprouter.Handle {
	return func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		ctx := w.(*httphelper.ResponseWriter).Context()
		log, _ := ctxhelper.LoggerFromContext(ctx)
		ctx = ctxhelper.NewContextParams(ctx, params)
		s, err := api.conf.SessionStore.Get(req, "session")
		if err != nil {
			log.Error(err.Error())
		}
		ctx = context.WithValue(ctx, ctxSessionKey, s)
		handler.ServeHTTP(ctx, w, req)
	}
}

func (api *API) SessionFromContext(ctx context.Context) *sessions.Session {
	return ctx.Value(ctxSessionKey).(*sessions.Session)
}

func (api *API) IsAuthenticated(ctx context.Context) bool {
	return api.SessionFromContext(ctx).Values["auth"] == true
}

func (api *API) SetAuthenticated(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	s := api.SessionFromContext(ctx)
	s.Values["auth"] = true
	s.Save(req, w)
}

func (api *API) UnsetAuthenticated(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	s := api.SessionFromContext(ctx)
	delete(s.Values, "auth")
	s.Save(req, w)
}

func (api *API) CorsHandler(main http.Handler) http.Handler {
	httpInterfaceURL := api.conf.InterfaceURL
	if strings.HasPrefix(api.conf.InterfaceURL, "https") {
		httpInterfaceURL = "http" + strings.TrimPrefix(api.conf.InterfaceURL, "https")
	}
	corsHandler := cors.Allow(&cors.Options{
		AllowOrigins:     []string{api.conf.InterfaceURL, httpInterfaceURL},
		AllowMethods:     []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"},
		AllowHeaders:     []string{"Authorization", "Accept", "Content-Type", "If-Match", "If-None-Match"},
		ExposeHeaders:    []string{"ETag"},
		AllowCredentials: true,
		MaxAge:           time.Hour,
	})
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		if strings.HasSuffix(req.URL.Path, "/ping") || req.Method == "OPTIONS" {
			httphelper.CORSAllowAllHandler(w, req)
			w.WriteHeader(200)
			return
		}
		corsHandler(w, req)
		main.ServeHTTP(w, req)
	})
}

func (api *API) ServeStatic(ctx context.Context, w http.ResponseWriter, req *http.Request, path string) {
	log, _ := ctxhelper.LoggerFromContext(ctx)
	data, t, err := AssetReader(path)
	if err != nil {
		log.Error(err.Error())
		w.WriteHeader(404)
		return
	}

	ext := filepath.Ext(path)
	if mimeType := mime.TypeByExtension(ext); mimeType != "" {
		w.Header().Add("Content-Type", mimeType)
	}
	if ext == ".html" {
		w.Header().Add("Cache-Control", "max-age=0")
	}

	http.ServeContent(w, req, path, t, data)
}

func (api *API) ServeTemplate(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	api.ServeStatic(ctx, w, req, filepath.Join("app", "build", "dashboard.html"))
}

func (api *API) ServeAsset(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	params, _ := ctxhelper.ParamsFromContext(ctx)
	path := params.ByName("path")
	if !strings.HasPrefix(path, "/assets/") {
		api.ServeTemplate(ctx, w, req)
		return
	}
	if strings.HasPrefix(path, "/assets/dashboard") && strings.HasSuffix(path, ".js") {
		api.ServeDashboardJs(ctx, w, req)
		return
	}
	api.ServeStatic(ctx, w, req, filepath.Join("app", "build", path))
}

func (api *API) Login(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	var info LoginInfo
	if err := json.NewDecoder(req.Body).Decode(&info); err != nil {
		httphelper.Error(w, err)
	}
	if len(info.Token) != len(api.conf.LoginToken) || subtle.ConstantTimeCompare([]byte(info.Token), []byte(api.conf.LoginToken)) != 1 {
		httphelper.Error(w, httphelper.JSONError{
			Code:    httphelper.UnauthorizedErrorCode,
			Message: "Invalid login token",
		})
		return
	}
	api.SetAuthenticated(ctx, w, req)
	if strings.Contains(req.Header.Get("Content-Type"), "form-urlencoded") {
		http.Redirect(w, req, api.conf.CookiePath, 302)
	} else {
		w.WriteHeader(200)
	}
}

func (api *API) Logout(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	api.UnsetAuthenticated(ctx, w, req)
	w.WriteHeader(200)
}

type OAuthToken struct {
	AccessToken string `json:"access_token"`
}

type ExpandedUser struct {
	Auths         map[string]*OAuthToken `json:"auths"`
	ControllerKey string                 `json:"controller_key"`
}

type UserConfig struct {
	User *ExpandedUser `json:"user,omitempty"`

	Endpoints          map[string]string `json:"endpoints"`
	DefaultRouteDomain string            `json:"default_route_domain"`
}

var baseConfig = UserConfig{
	Endpoints: map[string]string{
		"login":  "/user/sessions",
		"logout": "/user/session",
	},
}

func (api *API) GetConfig(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	config := baseConfig

	config.Endpoints["cluster_controller"] = fmt.Sprintf("https://%s", api.conf.ControllerDomain)
	config.DefaultRouteDomain = api.conf.DefaultRouteDomain

	if api.IsAuthenticated(ctx) {
		config.User = &ExpandedUser{}
		config.User.Auths = make(map[string]*OAuthToken)

		if api.conf.GithubToken != "" {
			config.User.Auths["github"] = &OAuthToken{AccessToken: api.conf.GithubToken}
		}

		config.User.ControllerKey = api.conf.ControllerKey
	}

	httphelper.JSON(w, 200, config)
}

func (api *API) GetCert(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "application/x-x509-ca-cert")
	w.Write(api.conf.CACert)
}

type DashboardConfig struct {
	AppName     string `json:"APP_NAME"`
	ApiServer   string `json:"API_SERVER"`
	PathPrefix  string `json:"PATH_PREFIX"`
	InstallCert bool   `json:"INSTALL_CERT"`
}

func (api *API) ServeDashboardJs(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	log, _ := ctxhelper.LoggerFromContext(ctx)
	path := filepath.Join("app", "build", "assets", filepath.Base(req.URL.Path))
	data, t, err := AssetReader(path)
	if err != nil {
		log.Error(err.Error())
		httphelper.Error(w, err)
		return
	}

	var jsConf bytes.Buffer
	jsConf.Write([]byte("window.DashboardConfig = "))
	json.NewEncoder(&jsConf).Encode(DashboardConfig{
		AppName:     api.conf.AppName,
		ApiServer:   api.conf.URL,
		PathPrefix:  api.conf.PathPrefix,
		InstallCert: len(api.conf.CACert) > 0,
	})
	jsConf.Write([]byte(";\n"))

	r := ioutil.NewMultiReadSeeker(bytes.NewReader(jsConf.Bytes()), data)

	http.ServeContent(w, req, path, t, r)
}
<commit_msg>dashboard: Optimize serving dashboard.js with static config<commit_after>package main

import (
	"bytes"
	"crypto/subtle"
	"encoding/json"
	"fmt"
	"io"
	"mime"
	"net/http"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/gorilla/sessions"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/julienschmidt/httprouter"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/jvatic/asset-matrix-go"
	"github.com/flynn/flynn/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/flynn/flynn/pkg/cors"
	"github.com/flynn/flynn/pkg/ctxhelper"
	"github.com/flynn/flynn/pkg/httphelper"
	"github.com/flynn/flynn/pkg/status"
)

type LoginInfo struct {
	Token string `json:"token" form:"token"`
}

func AssetReader(path string) (io.ReadSeeker, time.Time, error) {
	t := time.Time{}
	data, err := Asset(path)
	if err != nil {
		return nil, t, err
	}
	if fi, err := AssetInfo(path); err != nil {
		t = fi.ModTime()
	}
	return bytes.NewReader(data), t, nil
}

func APIHandler(conf *Config) http.Handler {
	api := &API{
		conf: conf,
	}
	if err := api.cacheDashboardJS(); err != nil {
		panic(err)
	}

	router := httprouter.New()
	router2 := httprouter.New()

	prefixPath := func(p string) string {
		return path.Join(conf.PathPrefix, p)
	}

	router.HandlerFunc("GET", status.Path, status.HealthyHandler.ServeHTTP)

	router.POST(prefixPath("/user/sessions"), api.WrapHandler(api.Login))
	router.DELETE(prefixPath("/user/session"), api.WrapHandler(api.Logout))

	router.GET(prefixPath("/config"), api.WrapHandler(api.GetConfig))
	router.GET(prefixPath("/cert"), api.WrapHandler(api.GetCert))

	router.NotFound = router2.ServeHTTP
	router2.GET(prefixPath("/*path"), api.WrapHandler(api.ServeAsset))

	return httphelper.ContextInjector("dashboard",
		httphelper.NewRequestLogger(
			api.CorsHandler(router)))
}

type API struct {
	conf               *Config
	dashboardJS        bytes.Buffer
	dashboardJSModTime time.Time
}

const ctxSessionKey = "session"

func (api *API) WrapHandler(handler httphelper.HandlerFunc) httprouter.Handle {
	return func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		ctx := w.(*httphelper.ResponseWriter).Context()
		log, _ := ctxhelper.LoggerFromContext(ctx)
		ctx = ctxhelper.NewContextParams(ctx, params)
		s, err := api.conf.SessionStore.Get(req, "session")
		if err != nil {
			log.Error(err.Error())
		}
		ctx = context.WithValue(ctx, ctxSessionKey, s)
		handler.ServeHTTP(ctx, w, req)
	}
}

func (api *API) SessionFromContext(ctx context.Context) *sessions.Session {
	return ctx.Value(ctxSessionKey).(*sessions.Session)
}

func (api *API) IsAuthenticated(ctx context.Context) bool {
	return api.SessionFromContext(ctx).Values["auth"] == true
}

func (api *API) SetAuthenticated(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	s := api.SessionFromContext(ctx)
	s.Values["auth"] = true
	s.Save(req, w)
}

func (api *API) UnsetAuthenticated(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	s := api.SessionFromContext(ctx)
	delete(s.Values, "auth")
	s.Save(req, w)
}

func (api *API) CorsHandler(main http.Handler) http.Handler {
	httpInterfaceURL := api.conf.InterfaceURL
	if strings.HasPrefix(api.conf.InterfaceURL, "https") {
		httpInterfaceURL = "http" + strings.TrimPrefix(api.conf.InterfaceURL, "https")
	}
	corsHandler := cors.Allow(&cors.Options{
		AllowOrigins:     []string{api.conf.InterfaceURL, httpInterfaceURL},
		AllowMethods:     []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"},
		AllowHeaders:     []string{"Authorization", "Accept", "Content-Type", "If-Match", "If-None-Match"},
		ExposeHeaders:    []string{"ETag"},
		AllowCredentials: true,
		MaxAge:           time.Hour,
	})
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		if strings.HasSuffix(req.URL.Path, "/ping") || req.Method == "OPTIONS" {
			httphelper.CORSAllowAllHandler(w, req)
			w.WriteHeader(200)
			return
		}
		corsHandler(w, req)
		main.ServeHTTP(w, req)
	})
}

func (api *API) ServeStatic(ctx context.Context, w http.ResponseWriter, req *http.Request, path string) {
	log, _ := ctxhelper.LoggerFromContext(ctx)
	data, t, err := AssetReader(path)
	if err != nil {
		log.Error(err.Error())
		w.WriteHeader(404)
		return
	}

	ext := filepath.Ext(path)
	if mimeType := mime.TypeByExtension(ext); mimeType != "" {
		w.Header().Add("Content-Type", mimeType)
	}
	if ext == ".html" {
		w.Header().Add("Cache-Control", "max-age=0")
	}

	http.ServeContent(w, req, path, t, data)
}

func (api *API) ServeTemplate(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	api.ServeStatic(ctx, w, req, filepath.Join("app", "build", "dashboard.html"))
}

func (api *API) ServeAsset(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	params, _ := ctxhelper.ParamsFromContext(ctx)
	path := params.ByName("path")
	if !strings.HasPrefix(path, "/assets/") {
		api.ServeTemplate(ctx, w, req)
		return
	}
	if strings.HasPrefix(path, "/assets/dashboard") && strings.HasSuffix(path, ".js") {
		api.ServeDashboardJs(ctx, w, req)
		return
	}
	api.ServeStatic(ctx, w, req, filepath.Join("app", "build", path))
}

func (api *API) Login(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	var info LoginInfo
	if err := json.NewDecoder(req.Body).Decode(&info); err != nil {
		httphelper.Error(w, err)
	}
	if len(info.Token) != len(api.conf.LoginToken) || subtle.ConstantTimeCompare([]byte(info.Token), []byte(api.conf.LoginToken)) != 1 {
		httphelper.Error(w, httphelper.JSONError{
			Code:    httphelper.UnauthorizedErrorCode,
			Message: "Invalid login token",
		})
		return
	}
	api.SetAuthenticated(ctx, w, req)
	if strings.Contains(req.Header.Get("Content-Type"), "form-urlencoded") {
		http.Redirect(w, req, api.conf.CookiePath, 302)
	} else {
		w.WriteHeader(200)
	}
}

func (api *API) Logout(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	api.UnsetAuthenticated(ctx, w, req)
	w.WriteHeader(200)
}

type OAuthToken struct {
	AccessToken string `json:"access_token"`
}

type ExpandedUser struct {
	Auths         map[string]*OAuthToken `json:"auths"`
	ControllerKey string                 `json:"controller_key"`
}

type UserConfig struct {
	User *ExpandedUser `json:"user,omitempty"`

	Endpoints          map[string]string `json:"endpoints"`
	DefaultRouteDomain string            `json:"default_route_domain"`
}

var baseConfig = UserConfig{
	Endpoints: map[string]string{
		"login":  "/user/sessions",
		"logout": "/user/session",
	},
}

func (api *API) GetConfig(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	config := baseConfig

	config.Endpoints["cluster_controller"] = fmt.Sprintf("https://%s", api.conf.ControllerDomain)
	config.DefaultRouteDomain = api.conf.DefaultRouteDomain

	if api.IsAuthenticated(ctx) {
		config.User = &ExpandedUser{}
		config.User.Auths = make(map[string]*OAuthToken)

		if api.conf.GithubToken != "" {
			config.User.Auths["github"] = &OAuthToken{AccessToken: api.conf.GithubToken}
		}

		config.User.ControllerKey = api.conf.ControllerKey
	}

	httphelper.JSON(w, 200, config)
}

func (api *API) GetCert(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "application/x-x509-ca-cert")
	w.Write(api.conf.CACert)
}

type DashboardConfig struct {
	AppName     string `json:"APP_NAME"`
	ApiServer   string `json:"API_SERVER"`
	PathPrefix  string `json:"PATH_PREFIX"`
	InstallCert bool   `json:"INSTALL_CERT"`
}

func (api *API) ServeDashboardJs(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	r := bytes.NewReader(api.dashboardJS.Bytes())
	http.ServeContent(w, req, req.URL.Path, api.dashboardJSModTime, r)
}

func (api *API) cacheDashboardJS() error {
	var manifest assetmatrix.Manifest
	manifestBytes, err := Asset(filepath.Join("app", "build", "assets", "manifest.json"))
	if err != nil {
		return err
	}
	if err := json.Unmarshal(manifestBytes, &manifest); err != nil {
		return err
	}

	var data bytes.Buffer
	path := filepath.Join("app", "build", "assets", manifest.Assets["dashboard.js"])
	js, t, err := AssetReader(path)
	if err != nil {
		return err
	}

	data.Write([]byte("window.DashboardConfig = "))
	json.NewEncoder(&data).Encode(DashboardConfig{
		AppName:     api.conf.AppName,
		ApiServer:   api.conf.URL,
		PathPrefix:  api.conf.PathPrefix,
		InstallCert: len(api.conf.CACert) > 0,
	})
	data.Write([]byte(";\n"))
	io.Copy(&data, js)

	api.dashboardJS = data
	api.dashboardJSModTime = t
	return nil
}
<|endoftext|>


<commit_before>package builds

import (
	"context"
	"fmt"

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

	exutil "github.com/openshift/origin/test/extended/util"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var _ = g.Describe("[sig-builds][Feature:Builds][Slow] completed builds should have digest of the image in their status", func() {
	defer g.GinkgoRecover()
	var (
		imageStreamFixture = exutil.FixturePath("testdata", "builds", "test-image-stream.json")
		stiBuildFixture    = exutil.FixturePath("testdata", "builds", "test-s2i-build.json")
		dockerBuildFixture = exutil.FixturePath("testdata", "builds", "test-docker-build.json")
		oc                 = exutil.NewCLI("build-sti-labels")
	)

	g.Context("", func() {

		g.BeforeEach(func() {
			exutil.PreTestDump()

			g.By("creating test imagestream")
			err := oc.Run("create").Args("-f", imageStreamFixture).Execute()
			o.Expect(err).NotTo(o.HaveOccurred())
		})

		g.AfterEach(func() {
			if g.CurrentGinkgoTestDescription().Failed {
				exutil.DumpPodStates(oc)
				exutil.DumpConfigMapStates(oc)
				exutil.DumpPodLogsStartingWith("", oc)
			}
		})

		g.Describe("S2I build", func() {
			g.Describe("started with normal log level", func() {
				testBuildDigest(oc, stiBuildFixture, 0)
			})

			g.Describe("started with log level >5", func() {
				testBuildDigest(oc, stiBuildFixture, 7)
			})
		})

		g.Describe("Docker build", func() {
			g.Describe("started with normal log level", func() {
				testBuildDigest(oc, dockerBuildFixture, 0)
			})

			g.Describe("started with log level >5", func() {
				testBuildDigest(oc, dockerBuildFixture, 7)
			})
		})
	})
})

func testBuildDigest(oc *exutil.CLI, buildFixture string, buildLogLevel uint) {
	g.It(fmt.Sprintf("should save the image digest when finished"), func() {
		g.By("creating test build")
		err := oc.Run("create").Args("-f", buildFixture).Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		logLevelArg := fmt.Sprintf("--build-loglevel=%d", buildLogLevel)
		g.By("starting a test build")
		br, err := exutil.StartBuildAndWait(oc, "test", logLevelArg)
		o.Expect(err).NotTo(o.HaveOccurred())
		br.AssertSuccess()

		g.By("checking that the image digest has been saved to the build status")
		o.Expect(br.Build.Status.Output.To).NotTo(o.BeNil())

		ist, err := oc.ImageClient().ImageV1().ImageStreamTags(oc.Namespace()).Get(context.Background(), "test:latest", v1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		o.Expect(br.Build.Status.Output.To.ImageDigest).To(o.Equal(ist.Image.Name))
	})
}
<commit_msg>Bug 1950095: Verify layer MIME types in built images<commit_after>package builds

import (
	"context"
	"fmt"

	manifestschema2 "github.com/docker/distribution/manifest/schema2"
	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/kubernetes/test/e2e/framework"

	exutil "github.com/openshift/origin/test/extended/util"
)

var _ = g.Describe("[sig-builds][Feature:Builds][Slow] completed builds should have digest of the image in their status", func() {
	defer g.GinkgoRecover()
	var (
		imageStreamFixture = exutil.FixturePath("testdata", "builds", "test-image-stream.json")
		stiBuildFixture    = exutil.FixturePath("testdata", "builds", "test-s2i-build.json")
		dockerBuildFixture = exutil.FixturePath("testdata", "builds", "test-docker-build.json")
		oc                 = exutil.NewCLI("build-sti-labels")
	)

	g.Context("", func() {

		g.BeforeEach(func() {
			exutil.PreTestDump()

			g.By("creating test imagestream")
			err := oc.Run("create").Args("-f", imageStreamFixture).Execute()
			o.Expect(err).NotTo(o.HaveOccurred())
		})

		g.AfterEach(func() {
			if g.CurrentGinkgoTestDescription().Failed {
				exutil.DumpPodStates(oc)
				exutil.DumpConfigMapStates(oc)
				exutil.DumpPodLogsStartingWith("", oc)
			}
		})

		g.Describe("S2I build", func() {
			g.Describe("started with normal log level", func() {
				testBuildDigest(oc, stiBuildFixture, 0)
			})

			g.Describe("started with log level >5", func() {
				testBuildDigest(oc, stiBuildFixture, 7)
			})
		})

		g.Describe("Docker build", func() {
			g.Describe("started with normal log level", func() {
				testBuildDigest(oc, dockerBuildFixture, 0)
			})

			g.Describe("started with log level >5", func() {
				testBuildDigest(oc, dockerBuildFixture, 7)
			})
		})
	})
})

func testBuildDigest(oc *exutil.CLI, buildFixture string, buildLogLevel uint) {
	g.It(fmt.Sprintf("should save the image digest when finished"), func() {
		g.By("creating test build")
		err := oc.Run("create").Args("-f", buildFixture).Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		logLevelArg := fmt.Sprintf("--build-loglevel=%d", buildLogLevel)
		g.By("starting a test build")
		br, err := exutil.StartBuildAndWait(oc, "test", logLevelArg)
		o.Expect(err).NotTo(o.HaveOccurred())
		br.AssertSuccess()

		g.By("checking that the image digest has been saved to the build status")
		o.Expect(br.Build.Status.Output.To).NotTo(o.BeNil())

		ist, err := oc.ImageClient().ImageV1().ImageStreamTags(oc.Namespace()).Get(context.Background(), "test:latest", metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		o.Expect(br.Build.Status.Output.To.ImageDigest).To(o.Equal(ist.Image.Name))

		g.By("checking that the image layers have valid docker v2schema2 MIME types")
		image, err := oc.AdminImageClient().ImageV1().Images().Get(context.Background(), br.Build.Status.Output.To.ImageDigest, metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		framework.Logf("media type for image %s: %s", image.Name, image.DockerImageManifestMediaType)
		for _, layer := range image.DockerImageLayers {
			framework.Logf("checking MIME type for layer %s", layer.Name)
			o.Expect(layer.MediaType).To(o.Equal(manifestschema2.MediaTypeLayer))
		}
	})
}
<|endoftext|>


<commit_before>package api

import (
	//	"gopkg.in/mgo.v2"
	"appengine/aetest"
	"appengine/datastore"
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/ErikBjare/Futarchio/src/db"
	"io/ioutil"
	"log"
	"net/http"
	"testing"
	"time"
)

func init() {
	_, err := http.Get("http://localhost:8080/api/0/init")
	if err != nil {
		log.Fatal(err)
	}
}

func TestAuth(t *testing.T) {
	client := &http.Client{}
	bodybuf := &bytes.Buffer{}
	bodybuf.Write([]byte("{\"username\": \"erb\", \"password\": \"password\"}"))
	resp, err := client.Post("http://localhost:8080/api/0/auth", "application/json", bodybuf)
	if err != nil {
		t.Fatal(err)
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatal(err)
	}

	msg := map[string]map[string]string{}
	//fmt.Println(string(body))
	err = json.Unmarshal(body, &msg)
	if err != nil {
		t.Fatal(err)
	}

	// Apparently required to allow the datastore time to be able to store Auth
	// Should probably be removed once Memcache is implemented
	time.Sleep(1 * time.Second)

	urls := []string{"http://localhost:8080/api/0/users", "http://localhost:8080/api/0/users/me"}
	for _, url := range urls {
		req, err := http.NewRequest("GET", url, nil)
		if err != nil {
			t.Fatal(err)
		}
		req.Header.Add("Authorization", msg["auth"]["key"])
		resp, err = client.Do(req)
		if err != nil {
			t.Error("Is the server running?")
			t.Fatal(err)
		}
		defer resp.Body.Close()
		if resp.StatusCode != 200 {
			t.Fatal(fmt.Sprintf("Status code was not 200, was %d with message: %s", resp.StatusCode, resp.Status))
		}

		body, err = ioutil.ReadAll(resp.Body)
		if err != nil {
			t.Error(err)
		}

		// The following comment line can be useful for debugging by printing body
		//log.Println(string(body))

		data := map[string][]interface{}{}
		err = json.Unmarshal(body, &data)
		if err != nil {
			t.Error(err)
		}
		if len(data["data"]) == 0 {
			t.Fatal(fmt.Sprintf("Got zero results or non-array"))
		}
	}
}

func BenchmarkAPICall(b *testing.B) {
	client := &http.Client{}
	for i := 0; i < b.N; i++ {
		req, err := http.NewRequest("GET", "http://localhost:8080/api/0/users", nil)
		if err != nil {
			b.Fatal(err)
		}
		req.Header.Add("Authorization", "Basic YWRtaW46YWRtaW4=")
		resp, err := client.Do(req)
		if err != nil {
			b.Error("Is the server running?")
			b.Fatal(err)
		}
		defer resp.Body.Close()
		if resp.StatusCode != 200 {
			b.Fatal(fmt.Sprintf("Status code was not 200, was %d with message: %s", resp.StatusCode, resp.Status))
		}

		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			b.Error(err)
		}
		data := map[string]interface{}{}
		err = json.Unmarshal(body, &data)
		if err != nil {
			b.Error(err)
		}
		if data["length"] == float64(0) {
			b.Fatal(fmt.Sprintf("Got zero results: %f", data["length"]))
		}
	}
}

func TestUsers(t *testing.T) {
	c, err := aetest.NewContext(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	key := datastore.NewKey(c, "User", "", 1, nil)
	_, err = datastore.Put(c, key, db.NewUser("erb", "secretpassword", "Erik", "erik@bjareho.lt", []string{}))
	if err != nil {
		t.Fatal(err)
	}

	var user db.User
	err = datastore.Get(c, key, &user)
	if err != nil {
		t.Error(err)
	}

	var users []db.User
	q := datastore.NewQuery("User").Filter("Email =", "erik@bjareho.lt")
	keys, err := q.GetAll(c, &users)
	if err != nil {
		t.Error(err)
	}

	if len(keys) == 0 {
		t.Error("Couldn't find Erik in database")
	} else if len(keys) > 1 {
		t.Error("More than one user with email erik@bjareho.lt in database")
	}
}

/*
func BenchmarkUserExistanceCycle(b *testing.B) {
	c, err := aetest.NewContext(nil)
	if err != nil {
		panic(err)
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		user := db.NewUser("tester", "password", "Tester", "test@example.com", []string{})
		userkey, err := datastore.Put(c, datastore.NewIncompleteKey(c, "User", nil), user)
		if err != nil {
			b.Error("Error when creating user")
			log.Println("I'm here")
		}
		datastore.Delete(c, userkey)
	}
}
*/
<commit_msg>Fixed tests<commit_after>package api

import (
	//	"gopkg.in/mgo.v2"
	"appengine/aetest"
	"appengine/datastore"
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/ErikBjare/Futarchio/src/db"
	"io/ioutil"
	"log"
	"net/http"
	"testing"
	"time"
)

func init() {
	_, err := http.Get("http://localhost:8080/api/0/init")
	if err != nil {
		log.Fatal(err)
	}
}

func TestAuth(t *testing.T) {
	client := &http.Client{}
	bodybuf := &bytes.Buffer{}
	bodybuf.Write([]byte("{\"username\": \"erb\", \"password\": \"password\"}"))
	resp, err := client.Post("http://localhost:8080/api/0/auth", "application/json", bodybuf)
	if err != nil {
		t.Fatal(err)
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatal(err)
	}

	msg := map[string]map[string]string{}
	//fmt.Println(string(body))
	err = json.Unmarshal(body, &msg)
	if err != nil {
		t.Fatal(err)
	}

	// Apparently required to allow the datastore time to be able to store Auth
	// Should probably be removed once Memcache is implemented
	time.Sleep(1 * time.Second)

	urls := []string{"http://localhost:8080/api/0/users", "http://localhost:8080/api/0/users/me"}
	for _, url := range urls {
		req, err := http.NewRequest("GET", url, nil)
		if err != nil {
			t.Fatal(err)
		}
		req.Header.Add("Authorization", msg["auth"]["key"])
		resp, err = client.Do(req)
		if err != nil {
			t.Error("Is the server running?")
			t.Fatal(err)
		}
		defer resp.Body.Close()
		if resp.StatusCode != 200 {
			t.Fatal(fmt.Sprintf("Status code was not 200, was %d with message: %s", resp.StatusCode, resp.Status))
		}

		body, err = ioutil.ReadAll(resp.Body)
		if err != nil {
			t.Error(err)
		}

		// The following comment line can be useful for debugging by printing body
		//log.Println(string(body))

		data := map[string][]interface{}{}
		err = json.Unmarshal(body, &data)
		if err != nil {
			t.Error(err)
		}
		if len(data["data"]) == 0 {
			t.Fatal(fmt.Sprintf("Got zero results or non-array"))
		}
	}
}

func BenchmarkAPICall(b *testing.B) {
	client := &http.Client{}
	for i := 0; i < b.N; i++ {
		req, err := http.NewRequest("GET", "http://localhost:8080/api/0/users", nil)
		if err != nil {
			b.Fatal(err)
		}
		req.Header.Add("Authorization", "Basic YWRtaW46YWRtaW4=")
		resp, err := client.Do(req)
		if err != nil {
			b.Error("Is the server running?")
			b.Fatal(err)
		}
		defer resp.Body.Close()
		if resp.StatusCode != 200 {
			b.Fatal(fmt.Sprintf("Status code was not 200, was %d with message: %s", resp.StatusCode, resp.Status))
		}

		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			b.Error(err)
		}
		data := map[string]interface{}{}
		err = json.Unmarshal(body, &data)
		if err != nil {
			b.Error(err)
		}
		if data["length"] == float64(0) {
			b.Fatal(fmt.Sprintf("Got zero results: %f", data["length"]))
		}
	}
}

func TestUsers(t *testing.T) {
	c, err := aetest.NewContext(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	key := datastore.NewKey(c, "User", "", 1, nil)
	_, err = datastore.Put(c, key, db.NewUser("erb", "secretpassword", "Erik", "erik@bjareho.lt"))
	if err != nil {
		t.Fatal(err)
	}

	var user db.User
	err = datastore.Get(c, key, &user)
	if err != nil {
		t.Error(err)
	}

	var users []db.User
	q := datastore.NewQuery("User").Filter("Email =", "erik@bjareho.lt")
	keys, err := q.GetAll(c, &users)
	if err != nil {
		t.Error(err)
	}

	if len(keys) == 0 {
		t.Error("Couldn't find Erik in database")
	} else if len(keys) > 1 {
		t.Error("More than one user with email erik@bjareho.lt in database")
	}
}

/*
func BenchmarkUserExistanceCycle(b *testing.B) {
	c, err := aetest.NewContext(nil)
	if err != nil {
		panic(err)
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		user := db.NewUser("tester", "password", "Tester", "test@example.com", []string{})
		userkey, err := datastore.Put(c, datastore.NewIncompleteKey(c, "User", nil), user)
		if err != nil {
			b.Error("Error when creating user")
			log.Println("I'm here")
		}
		datastore.Delete(c, userkey)
	}
}
*/
<|endoftext|>