<commit_before>// #ignore

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

// Run runs tests in the test directory.
//
// TODO(bradfitz): docs of some sort, once we figure out how we're changing
// headers of files
package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"go/build"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"sort"
	"strconv"
	"strings"
)

var (
	verbose     = flag.Bool("v", false, "verbose. if set, parallelism is set to 1.")
	numParallel = flag.Int("n", runtime.NumCPU(), "number of parallel tests to run")
	summary     = flag.Bool("summary", false, "show summary of results")
	showSkips   = flag.Bool("show_skips", false, "show skipped tests")
)

var (
	// gc and ld are [568][gl].
	gc, ld string

	// letter is the build.ArchChar
	letter string

	// dirs are the directories to look for *.go files in.
	// TODO(bradfitz): just use all directories?
	dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "bugs"}

	// ratec controls the max number of tests running at a time.
	ratec chan bool

	// toRun is the channel of tests to run.
	// It is nil until the first test is started.
	toRun chan *test
)

// maxTests is an upper bound on the total number of tests.
// It is used as a channel buffer size to make sure sends don't block.
const maxTests = 5000

func main() {
	flag.Parse()

	// Disable parallelism if printing
	if *verbose {
		*numParallel = 1
	}

	ratec = make(chan bool, *numParallel)
	var err error
	letter, err = build.ArchChar(build.Default.GOARCH)
	check(err)
	gc = letter + "g"
	ld = letter + "l"

	var tests []*test
	if flag.NArg() > 0 {
		for _, arg := range flag.Args() {
			if arg == "-" || arg == "--" {
				// Permit running either:
				// $ go run run.go - env.go
				// $ go run run.go -- env.go
				continue
			}
			if !strings.HasSuffix(arg, ".go") {
				log.Fatalf("can't yet deal with non-go file %q", arg)
			}
			dir, file := filepath.Split(arg)
			tests = append(tests, startTest(dir, file))
		}
	} else {
		for _, dir := range dirs {
			for _, baseGoFile := range goFiles(dir) {
				tests = append(tests, startTest(dir, baseGoFile))
			}
		}
	}

	failed := false
	resCount := map[string]int{}
	for _, test := range tests {
		<-test.donec
		_, isSkip := test.err.(skipError)
		errStr := "pass"
		if isSkip {
			errStr = "skip"
		}
		if test.err != nil {
			errStr = test.err.Error()
			if !isSkip {
				failed = true
			}
		}
		resCount[errStr]++
		if isSkip && !*verbose && !*showSkips {
			continue
		}
		if !*verbose && test.err == nil {
			continue
		}
		fmt.Printf("%-10s %-20s: %s\n", test.action, test.goFileName(), errStr)
	}

	if *summary {
		for k, v := range resCount {
			fmt.Printf("%5d %s\n", v, k)
		}
	}

	if failed {
		os.Exit(1)
	}
}

func toolPath(name string) string {
	p := filepath.Join(os.Getenv("GOROOT"), "bin", "tool", name)
	if _, err := os.Stat(p); err != nil {
		log.Fatalf("didn't find binary at %s", p)
	}
	return p
}

func goFiles(dir string) []string {
	f, err := os.Open(dir)
	check(err)
	dirnames, err := f.Readdirnames(-1)
	check(err)
	names := []string{}
	for _, name := range dirnames {
		if strings.HasSuffix(name, ".go") {
			names = append(names, name)
		}
	}
	sort.Strings(names)
	return names
}

// skipError describes why a test was skipped.
type skipError string

func (s skipError) Error() string { return string(s) }

func check(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

// test holds the state of a test.
type test struct {
	dir, gofile string
	donec       chan bool // closed when done

	src    string
	action string // "compile", "build", "run", "errorcheck"

	tempDir string
	err     error
}

// startTest
func startTest(dir, gofile string) *test {
	t := &test{
		dir:    dir,
		gofile: gofile,
		donec:  make(chan bool, 1),
	}
	if toRun == nil {
		toRun = make(chan *test, maxTests)
		go runTests()
	}
	select {
	case toRun <- t:
	default:
		panic("toRun buffer size (maxTests) is too small")
	}
	return t
}

// runTests runs tests in parallel, but respecting the order they
// were enqueued on the toRun channel.
func runTests() {
	for {
		ratec <- true
		t := <-toRun
		go func() {
			t.run()
			<-ratec
		}()
	}
}

var cwd, _ = os.Getwd()

func (t *test) goFileName() string {
	return filepath.Join(t.dir, t.gofile)
}

// run runs a test.
func (t *test) run() {
	defer close(t.donec)

	srcBytes, err := ioutil.ReadFile(t.goFileName())
	if err != nil {
		t.err = err
		return
	}
	t.src = string(srcBytes)
	if t.src[0] == '\n' {
		t.err = skipError("starts with newline")
		return
	}
	pos := strings.Index(t.src, "\n\n")
	if pos == -1 {
		t.err = errors.New("double newline not found")
		return
	}
	action := t.src[:pos]
	if strings.HasPrefix(action, "//") {
		action = action[2:]
	}

	var args []string
	f := strings.Fields(action)
	if len(f) > 0 {
		action = f[0]
		args = f[1:]
	}

	switch action {
	case "cmpout":
		action = "run" // the run case already looks for <dir>/<test>.out files
		fallthrough
	case "compile", "build", "run", "errorcheck":
		t.action = action
	default:
		t.err = skipError("skipped; unknown pattern: " + action)
		t.action = "??"
		return
	}

	t.makeTempDir()
	defer os.RemoveAll(t.tempDir)

	err = ioutil.WriteFile(filepath.Join(t.tempDir, t.gofile), srcBytes, 0644)
	check(err)

	useTmp := true
	runcmd := func(args ...string) ([]byte, error) {
		cmd := exec.Command(args[0], args[1:]...)
		var buf bytes.Buffer
		cmd.Stdout = &buf
		cmd.Stderr = &buf
		if useTmp {
			cmd.Dir = t.tempDir
		}
		cmd.Env = append(cmd.Env, "GOOS="+runtime.GOOS, "GOARCH="+runtime.GOARCH)
		err := cmd.Run()
		return buf.Bytes(), err
	}

	long := filepath.Join(cwd, t.goFileName())
	switch action {
	default:
		t.err = fmt.Errorf("unimplemented action %q", action)

	case "errorcheck":
		out, _ := runcmd("go", "tool", gc, "-e", "-o", "a."+letter, long)
		t.err = t.errorCheck(string(out), long, t.gofile)
		return

	case "compile":
		out, err := runcmd("go", "tool", gc, "-e", "-o", "a."+letter, long)
		if err != nil {
			t.err = fmt.Errorf("%s\n%s", err, out)
		}

	case "build":
		out, err := runcmd("go", "build", "-o", "a.exe", long)
		if err != nil {
			t.err = fmt.Errorf("%s\n%s", err, out)
		}

	case "run":
		useTmp = false
		out, err := runcmd(append([]string{"go", "run", t.goFileName()}, args...)...)
		if err != nil {
			t.err = fmt.Errorf("%s\n%s", err, out)
		}
		if string(out) != t.expectedOutput() {
			t.err = fmt.Errorf("incorrect output\n%s", out)
		}
	}
}

func (t *test) String() string {
	return filepath.Join(t.dir, t.gofile)
}

func (t *test) makeTempDir() {
	var err error
	t.tempDir, err = ioutil.TempDir("", "")
	check(err)
}

func (t *test) expectedOutput() string {
	filename := filepath.Join(t.dir, t.gofile)
	filename = filename[:len(filename)-len(".go")]
	filename += ".out"
	b, _ := ioutil.ReadFile(filename)
	return string(b)
}

func (t *test) errorCheck(outStr string, full, short string) (err error) {
	defer func() {
		if *verbose && err != nil {
			log.Printf("%s gc output:\n%s", t, outStr)
		}
	}()
	var errs []error

	var out []string
	// 6g error messages continue onto additional lines with leading tabs.
	// Split the output at the beginning of each line that doesn't begin with a tab.
	for _, line := range strings.Split(outStr, "\n") {
		if strings.HasPrefix(line, "\t") {
			out[len(out)-1] += "\n" + line
		} else {
			out = append(out, line)
		}
	}

	// Cut directory name.
	for i := range out {
		out[i] = strings.Replace(out[i], full, short, -1)
	}

	for _, we := range t.wantedErrors() {
		var errmsgs []string
		errmsgs, out = partitionStrings(we.filterRe, out)
		if len(errmsgs) == 0 {
			errs = append(errs, fmt.Errorf("%s:%d: missing error %q", we.file, we.lineNum, we.reStr))
			continue
		}
		matched := false
		for _, errmsg := range errmsgs {
			if we.re.MatchString(errmsg) {
				matched = true
			} else {
				out = append(out, errmsg)
			}
		}
		if !matched {
			errs = append(errs, fmt.Errorf("%s:%d: no match for %q in%s", we.file, we.lineNum, we.reStr, strings.Join(out, "\n")))
			continue
		}
	}

	if len(errs) == 0 {
		return nil
	}
	if len(errs) == 1 {
		return errs[0]
	}
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "\n")
	for _, err := range errs {
		fmt.Fprintf(&buf, "%s\n", err.Error())
	}
	return errors.New(buf.String())

}

func partitionStrings(rx *regexp.Regexp, strs []string) (matched, unmatched []string) {
	for _, s := range strs {
		if rx.MatchString(s) {
			matched = append(matched, s)
		} else {
			unmatched = append(unmatched, s)
		}
	}
	return
}

type wantedError struct {
	reStr    string
	re       *regexp.Regexp
	lineNum  int
	file     string
	filterRe *regexp.Regexp // /^file:linenum\b/m
}

var (
	errRx       = regexp.MustCompile(`// (?:GC_)?ERROR (.*)`)
	errQuotesRx = regexp.MustCompile(`"([^"]*)"`)
	lineRx      = regexp.MustCompile(`LINE(([+-])([0-9]+))?`)
)

func (t *test) wantedErrors() (errs []wantedError) {
	for i, line := range strings.Split(t.src, "\n") {
		lineNum := i + 1
		if strings.Contains(line, "////") {
			// double comment disables ERROR
			continue
		}
		m := errRx.FindStringSubmatch(line)
		if m == nil {
			continue
		}
		all := m[1]
		mm := errQuotesRx.FindAllStringSubmatch(all, -1)
		if mm == nil {
			log.Fatalf("invalid errchk line in %s: %s", t.goFileName(), line)
		}
		for _, m := range mm {
			rx := lineRx.ReplaceAllStringFunc(m[1], func(m string) string {
				n := lineNum
				if strings.HasPrefix(m, "LINE+") {
					delta, _ := strconv.Atoi(m[5:])
					n += delta
				} else if strings.HasPrefix(m, "LINE-") {
					delta, _ := strconv.Atoi(m[5:])
					n -= delta
				}
				return fmt.Sprintf("%s:%d", t.gofile, n)
			})
			filterPattern := fmt.Sprintf(`^(\w+/)?%s:%d[:[]`, t.gofile, lineNum)
			errs = append(errs, wantedError{
				reStr:    rx,
				re:       regexp.MustCompile(rx),
				filterRe: regexp.MustCompile(filterPattern),
				lineNum:  lineNum,
				file:     t.gofile,
			})
		}
	}

	return
}
<commit_msg>test/run: fix builders<commit_after>// #ignore

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

// Run runs tests in the test directory.
//
// TODO(bradfitz): docs of some sort, once we figure out how we're changing
// headers of files
package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"go/build"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"sort"
	"strconv"
	"strings"
)

var (
	verbose     = flag.Bool("v", false, "verbose. if set, parallelism is set to 1.")
	numParallel = flag.Int("n", runtime.NumCPU(), "number of parallel tests to run")
	summary     = flag.Bool("summary", false, "show summary of results")
	showSkips   = flag.Bool("show_skips", false, "show skipped tests")
)

var (
	// gc and ld are [568][gl].
	gc, ld string

	// letter is the build.ArchChar
	letter string

	// dirs are the directories to look for *.go files in.
	// TODO(bradfitz): just use all directories?
	dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "bugs"}

	// ratec controls the max number of tests running at a time.
	ratec chan bool

	// toRun is the channel of tests to run.
	// It is nil until the first test is started.
	toRun chan *test
)

// maxTests is an upper bound on the total number of tests.
// It is used as a channel buffer size to make sure sends don't block.
const maxTests = 5000

func main() {
	flag.Parse()

	// Disable parallelism if printing
	if *verbose {
		*numParallel = 1
	}

	ratec = make(chan bool, *numParallel)
	var err error
	letter, err = build.ArchChar(build.Default.GOARCH)
	check(err)
	gc = letter + "g"
	ld = letter + "l"

	var tests []*test
	if flag.NArg() > 0 {
		for _, arg := range flag.Args() {
			if arg == "-" || arg == "--" {
				// Permit running either:
				// $ go run run.go - env.go
				// $ go run run.go -- env.go
				continue
			}
			if !strings.HasSuffix(arg, ".go") {
				log.Fatalf("can't yet deal with non-go file %q", arg)
			}
			dir, file := filepath.Split(arg)
			tests = append(tests, startTest(dir, file))
		}
	} else {
		for _, dir := range dirs {
			for _, baseGoFile := range goFiles(dir) {
				tests = append(tests, startTest(dir, baseGoFile))
			}
		}
	}

	failed := false
	resCount := map[string]int{}
	for _, test := range tests {
		<-test.donec
		_, isSkip := test.err.(skipError)
		errStr := "pass"
		if isSkip {
			errStr = "skip"
		}
		if test.err != nil {
			errStr = test.err.Error()
			if !isSkip {
				failed = true
			}
		}
		resCount[errStr]++
		if isSkip && !*verbose && !*showSkips {
			continue
		}
		if !*verbose && test.err == nil {
			continue
		}
		fmt.Printf("%-10s %-20s: %s\n", test.action, test.goFileName(), errStr)
	}

	if *summary {
		for k, v := range resCount {
			fmt.Printf("%5d %s\n", v, k)
		}
	}

	if failed {
		os.Exit(1)
	}
}

func toolPath(name string) string {
	p := filepath.Join(os.Getenv("GOROOT"), "bin", "tool", name)
	if _, err := os.Stat(p); err != nil {
		log.Fatalf("didn't find binary at %s", p)
	}
	return p
}

func goFiles(dir string) []string {
	f, err := os.Open(dir)
	check(err)
	dirnames, err := f.Readdirnames(-1)
	check(err)
	names := []string{}
	for _, name := range dirnames {
		if strings.HasSuffix(name, ".go") {
			names = append(names, name)
		}
	}
	sort.Strings(names)
	return names
}

// skipError describes why a test was skipped.
type skipError string

func (s skipError) Error() string { return string(s) }

func check(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

// test holds the state of a test.
type test struct {
	dir, gofile string
	donec       chan bool // closed when done

	src    string
	action string // "compile", "build", "run", "errorcheck"

	tempDir string
	err     error
}

// startTest
func startTest(dir, gofile string) *test {
	t := &test{
		dir:    dir,
		gofile: gofile,
		donec:  make(chan bool, 1),
	}
	if toRun == nil {
		toRun = make(chan *test, maxTests)
		go runTests()
	}
	select {
	case toRun <- t:
	default:
		panic("toRun buffer size (maxTests) is too small")
	}
	return t
}

// runTests runs tests in parallel, but respecting the order they
// were enqueued on the toRun channel.
func runTests() {
	for {
		ratec <- true
		t := <-toRun
		go func() {
			t.run()
			<-ratec
		}()
	}
}

var cwd, _ = os.Getwd()

func (t *test) goFileName() string {
	return filepath.Join(t.dir, t.gofile)
}

// run runs a test.
func (t *test) run() {
	defer close(t.donec)

	srcBytes, err := ioutil.ReadFile(t.goFileName())
	if err != nil {
		t.err = err
		return
	}
	t.src = string(srcBytes)
	if t.src[0] == '\n' {
		t.err = skipError("starts with newline")
		return
	}
	pos := strings.Index(t.src, "\n\n")
	if pos == -1 {
		t.err = errors.New("double newline not found")
		return
	}
	action := t.src[:pos]
	if strings.HasPrefix(action, "//") {
		action = action[2:]
	}

	var args []string
	f := strings.Fields(action)
	if len(f) > 0 {
		action = f[0]
		args = f[1:]
	}

	switch action {
	case "cmpout":
		action = "run" // the run case already looks for <dir>/<test>.out files
		fallthrough
	case "compile", "build", "run", "errorcheck":
		t.action = action
	default:
		t.err = skipError("skipped; unknown pattern: " + action)
		t.action = "??"
		return
	}

	t.makeTempDir()
	defer os.RemoveAll(t.tempDir)

	err = ioutil.WriteFile(filepath.Join(t.tempDir, t.gofile), srcBytes, 0644)
	check(err)

	// A few tests (of things like the environment) require these to be set.
	os.Setenv("GOOS", runtime.GOOS)
	os.Setenv("GOARCH", runtime.GOARCH)

	useTmp := true
	runcmd := func(args ...string) ([]byte, error) {
		cmd := exec.Command(args[0], args[1:]...)
		var buf bytes.Buffer
		cmd.Stdout = &buf
		cmd.Stderr = &buf
		if useTmp {
			cmd.Dir = t.tempDir
		}
		err := cmd.Run()
		return buf.Bytes(), err
	}

	long := filepath.Join(cwd, t.goFileName())
	switch action {
	default:
		t.err = fmt.Errorf("unimplemented action %q", action)

	case "errorcheck":
		out, _ := runcmd("go", "tool", gc, "-e", "-o", "a."+letter, long)
		t.err = t.errorCheck(string(out), long, t.gofile)
		return

	case "compile":
		out, err := runcmd("go", "tool", gc, "-e", "-o", "a."+letter, long)
		if err != nil {
			t.err = fmt.Errorf("%s\n%s", err, out)
		}

	case "build":
		out, err := runcmd("go", "build", "-o", "a.exe", long)
		if err != nil {
			t.err = fmt.Errorf("%s\n%s", err, out)
		}

	case "run":
		useTmp = false
		out, err := runcmd(append([]string{"go", "run", t.goFileName()}, args...)...)
		if err != nil {
			t.err = fmt.Errorf("%s\n%s", err, out)
		}
		if string(out) != t.expectedOutput() {
			t.err = fmt.Errorf("incorrect output\n%s", out)
		}
	}
}

func (t *test) String() string {
	return filepath.Join(t.dir, t.gofile)
}

func (t *test) makeTempDir() {
	var err error
	t.tempDir, err = ioutil.TempDir("", "")
	check(err)
}

func (t *test) expectedOutput() string {
	filename := filepath.Join(t.dir, t.gofile)
	filename = filename[:len(filename)-len(".go")]
	filename += ".out"
	b, _ := ioutil.ReadFile(filename)
	return string(b)
}

func (t *test) errorCheck(outStr string, full, short string) (err error) {
	defer func() {
		if *verbose && err != nil {
			log.Printf("%s gc output:\n%s", t, outStr)
		}
	}()
	var errs []error

	var out []string
	// 6g error messages continue onto additional lines with leading tabs.
	// Split the output at the beginning of each line that doesn't begin with a tab.
	for _, line := range strings.Split(outStr, "\n") {
		if strings.HasPrefix(line, "\t") {
			out[len(out)-1] += "\n" + line
		} else {
			out = append(out, line)
		}
	}

	// Cut directory name.
	for i := range out {
		out[i] = strings.Replace(out[i], full, short, -1)
	}

	for _, we := range t.wantedErrors() {
		var errmsgs []string
		errmsgs, out = partitionStrings(we.filterRe, out)
		if len(errmsgs) == 0 {
			errs = append(errs, fmt.Errorf("%s:%d: missing error %q", we.file, we.lineNum, we.reStr))
			continue
		}
		matched := false
		for _, errmsg := range errmsgs {
			if we.re.MatchString(errmsg) {
				matched = true
			} else {
				out = append(out, errmsg)
			}
		}
		if !matched {
			errs = append(errs, fmt.Errorf("%s:%d: no match for %q in%s", we.file, we.lineNum, we.reStr, strings.Join(out, "\n")))
			continue
		}
	}

	if len(errs) == 0 {
		return nil
	}
	if len(errs) == 1 {
		return errs[0]
	}
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "\n")
	for _, err := range errs {
		fmt.Fprintf(&buf, "%s\n", err.Error())
	}
	return errors.New(buf.String())

}

func partitionStrings(rx *regexp.Regexp, strs []string) (matched, unmatched []string) {
	for _, s := range strs {
		if rx.MatchString(s) {
			matched = append(matched, s)
		} else {
			unmatched = append(unmatched, s)
		}
	}
	return
}

type wantedError struct {
	reStr    string
	re       *regexp.Regexp
	lineNum  int
	file     string
	filterRe *regexp.Regexp // /^file:linenum\b/m
}

var (
	errRx       = regexp.MustCompile(`// (?:GC_)?ERROR (.*)`)
	errQuotesRx = regexp.MustCompile(`"([^"]*)"`)
	lineRx      = regexp.MustCompile(`LINE(([+-])([0-9]+))?`)
)

func (t *test) wantedErrors() (errs []wantedError) {
	for i, line := range strings.Split(t.src, "\n") {
		lineNum := i + 1
		if strings.Contains(line, "////") {
			// double comment disables ERROR
			continue
		}
		m := errRx.FindStringSubmatch(line)
		if m == nil {
			continue
		}
		all := m[1]
		mm := errQuotesRx.FindAllStringSubmatch(all, -1)
		if mm == nil {
			log.Fatalf("invalid errchk line in %s: %s", t.goFileName(), line)
		}
		for _, m := range mm {
			rx := lineRx.ReplaceAllStringFunc(m[1], func(m string) string {
				n := lineNum
				if strings.HasPrefix(m, "LINE+") {
					delta, _ := strconv.Atoi(m[5:])
					n += delta
				} else if strings.HasPrefix(m, "LINE-") {
					delta, _ := strconv.Atoi(m[5:])
					n -= delta
				}
				return fmt.Sprintf("%s:%d", t.gofile, n)
			})
			filterPattern := fmt.Sprintf(`^(\w+/)?%s:%d[:[]`, t.gofile, lineNum)
			errs = append(errs, wantedError{
				reStr:    rx,
				re:       regexp.MustCompile(rx),
				filterRe: regexp.MustCompile(filterPattern),
				lineNum:  lineNum,
				file:     t.gofile,
			})
		}
	}

	return
}
<|endoftext|>


<commit_before>package clicommand

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/buildkite/agent/v3/agent"
	"github.com/buildkite/agent/v3/api"
	"github.com/buildkite/agent/v3/bootstrap/shell"
	"github.com/buildkite/agent/v3/cliconfig"
	"github.com/buildkite/agent/v3/env"
	"github.com/buildkite/agent/v3/redaction"
	"github.com/buildkite/agent/v3/retry"
	"github.com/buildkite/agent/v3/stdin"
	"github.com/urfave/cli"
)

var PipelineUploadHelpDescription = `Usage:

   buildkite-agent pipeline upload [file] [options...]

Description:

   Allows you to change the pipeline of a running build by uploading either a
   YAML (recommended) or JSON configuration file. If no configuration file is
   provided, the command looks for the file in the following locations:

   - buildkite.yml
   - buildkite.yaml
   - buildkite.json
   - .buildkite/pipeline.yml
   - .buildkite/pipeline.yaml
   - .buildkite/pipeline.json
   - buildkite/pipeline.yml
   - buildkite/pipeline.yaml
   - buildkite/pipeline.json

   You can also pipe build pipelines to the command allowing you to create
   scripts that generate dynamic pipelines.

Example:

   $ buildkite-agent pipeline upload
   $ buildkite-agent pipeline upload my-custom-pipeline.yml
   $ ./script/dynamic_step_generator | buildkite-agent pipeline upload`

type PipelineUploadConfig struct {
	FilePath        string   `cli:"arg:0" label:"upload paths"`
	Replace         bool     `cli:"replace"`
	Job             string   `cli:"job"`
	DryRun          bool     `cli:"dry-run"`
	NoInterpolation bool     `cli:"no-interpolation"`
	RedactedVars    []string `cli:"redacted-vars" normalize:"list"`

	// Global flags
	Debug       bool     `cli:"debug"`
	NoColor     bool     `cli:"no-color"`
	Experiments []string `cli:"experiment" normalize:"list"`
	Profile     string   `cli:"profile"`

	// API config
	DebugHTTP        bool   `cli:"debug-http"`
	AgentAccessToken string `cli:"agent-access-token" validate:"required"`
	Endpoint         string `cli:"endpoint" validate:"required"`
	NoHTTP2          bool   `cli:"no-http2"`
}

var PipelineUploadCommand = cli.Command{
	Name:        "upload",
	Usage:       "Uploads a description of a build pipeline adds it to the currently running build after the current job.",
	Description: PipelineUploadHelpDescription,
	Flags: []cli.Flag{
		cli.BoolFlag{
			Name:   "replace",
			Usage:  "Replace the rest of the existing pipeline with the steps uploaded. Jobs that are already running are not removed.",
			EnvVar: "BUILDKITE_PIPELINE_REPLACE",
		},
		cli.StringFlag{
			Name:   "job",
			Value:  "",
			Usage:  "The job that is making the changes to its build",
			EnvVar: "BUILDKITE_JOB_ID",
		},
		cli.BoolFlag{
			Name:   "dry-run",
			Usage:  "Rather than uploading the pipeline, it will be echoed to stdout",
			EnvVar: "BUILDKITE_PIPELINE_UPLOAD_DRY_RUN",
		},
		cli.BoolFlag{
			Name:   "no-interpolation",
			Usage:  "Skip variable interpolation the pipeline when uploaded",
			EnvVar: "BUILDKITE_PIPELINE_NO_INTERPOLATION",
		},

		// API Flags
		AgentAccessTokenFlag,
		EndpointFlag,
		NoHTTP2Flag,
		DebugHTTPFlag,

		// Global flags
		NoColorFlag,
		DebugFlag,
		ExperimentsFlag,
		ProfileFlag,
		RedactedVars,
	},
	Action: func(c *cli.Context) {
		// The configuration will be loaded into this struct
		cfg := PipelineUploadConfig{}

		l := CreateLogger(&cfg)

		// Load the configuration
		if err := cliconfig.Load(c, l, &cfg); err != nil {
			l.Fatal("%s", err)
		}

		// Setup any global configuration options
		done := HandleGlobalFlags(l, cfg)
		defer done()

		// Find the pipeline file either from STDIN or the first
		// argument
		var input []byte
		var err error
		var filename string

		if cfg.FilePath != "" {
			l.Info("Reading pipeline config from \"%s\"", cfg.FilePath)

			filename = filepath.Base(cfg.FilePath)
			input, err = ioutil.ReadFile(cfg.FilePath)
			if err != nil {
				l.Fatal("Failed to read file: %s", err)
			}
		} else if stdin.IsReadable() {
			l.Info("Reading pipeline config from STDIN")

			// Actually read the file from STDIN
			input, err = ioutil.ReadAll(os.Stdin)
			if err != nil {
				l.Fatal("Failed to read from STDIN: %s", err)
			}
		} else {
			l.Info("Searching for pipeline config...")

			paths := []string{
				"buildkite.yml",
				"buildkite.yaml",
				"buildkite.json",
				filepath.FromSlash(".buildkite/pipeline.yml"),
				filepath.FromSlash(".buildkite/pipeline.yaml"),
				filepath.FromSlash(".buildkite/pipeline.json"),
				filepath.FromSlash("buildkite/pipeline.yml"),
				filepath.FromSlash("buildkite/pipeline.yaml"),
				filepath.FromSlash("buildkite/pipeline.json"),
			}

			// Collect all the files that exist
			exists := []string{}
			for _, path := range paths {
				if _, err := os.Stat(path); err == nil {
					exists = append(exists, path)
				}
			}

			// If more than 1 of the config files exist, throw an
			// error. There can only be one!!
			if len(exists) > 1 {
				l.Fatal("Found multiple configuration files: %s. Please only have 1 configuration file present.", strings.Join(exists, ", "))
			} else if len(exists) == 0 {
				l.Fatal("Could not find a default pipeline configuration file. See `buildkite-agent pipeline upload --help` for more information.")
			}

			found := exists[0]

			l.Info("Found config file \"%s\"", found)

			// Read the default file
			filename = path.Base(found)
			input, err = ioutil.ReadFile(found)
			if err != nil {
				l.Fatal("Failed to read file \"%s\" (%s)", found, err)
			}
		}

		// Make sure the file actually has something in it
		if len(input) == 0 {
			l.Fatal("Config file is empty")
		}

		// Load environment to pass into parser
		environ := env.FromSlice(os.Environ())

		// resolve BUILDKITE_COMMIT based on the local git repo
		if commitRef, ok := environ.Get(`BUILDKITE_COMMIT`); ok {
			cmdOut, err := exec.Command(`git`, `rev-parse`, commitRef).Output()
			if err != nil {
				l.Warn("Error running git rev-parse %q: %v", commitRef, err)
			} else {
				trimmedCmdOut := strings.TrimSpace(string(cmdOut))
				l.Info("Updating BUILDKITE_COMMIT to %q", trimmedCmdOut)
				environ.Set(`BUILDKITE_COMMIT`, trimmedCmdOut)
			}
		}

		src := filename
		if src == "" {
			src = "(stdin)"
		}

		// Parse the pipeline
		result, err := agent.PipelineParser{
			Env:             environ,
			Filename:        filename,
			Pipeline:        input,
			NoInterpolation: cfg.NoInterpolation,
		}.Parse()
		if err != nil {
			l.Fatal("Pipeline parsing of \"%s\" failed (%s)", src, err)
		}

		// In dry-run mode we just output the generated pipeline to stdout
		if cfg.DryRun {
			enc := json.NewEncoder(os.Stdout)
			enc.SetIndent("", "  ")

			// Dump json indented to stdout. All logging happens to stderr
			// this can be used with other tools to get interpolated json
			if err := enc.Encode(result); err != nil {
				l.Fatal("%#v", err)
			}

			return
		}

		if len(cfg.RedactedVars) > 0 {
			needles := redaction.GetKeyValuesToRedact(shell.StderrLogger, cfg.RedactedVars, env.FromSlice(os.Environ()).ToMap())
			serialisedPipeline, err := result.MarshalJSON()

			if err != nil {
				l.Fatal("Couldn’t scan the %q pipeline for redacted variables. This parsed pipeline could not be serialized, ensure the pipeline YAML is valid, or ignore interpolated secrets for this upload by passing --redacted-vars=''. (%s)", src, err)
			}

			stringifiedserialisedPipeline := string(serialisedPipeline)

			for needleKey, needle := range needles {
				if strings.Contains(stringifiedserialisedPipeline, needle) {
					l.Fatal("Couldn’t upload the %q pipeline. Refusing to upload pipeline containing the value interpolated from the %q environment variable. Ensure your pipeline does not include secret values or interpolated secret values.", src, needleKey)
				}
			}
		}

		// Check we have a job id set if not in dry run
		if cfg.Job == "" {
			l.Fatal("Missing job parameter. Usually this is set in the environment for a Buildkite job via BUILDKITE_JOB_ID.")
		}

		// Check we have an agent access token if not in dry run
		if cfg.AgentAccessToken == "" {
			l.Fatal("Missing agent-access-token parameter. Usually this is set in the environment for a Buildkite job via BUILDKITE_AGENT_ACCESS_TOKEN.")
		}

		// Create the API client
		client := api.NewClient(l, loadAPIClientConfig(cfg, `AgentAccessToken`))

		// Generate a UUID that will identify this pipeline change. We
		// do this outside of the retry loop because we want this UUID
		// to be the same for each attempt at updating the pipeline.
		uuid := api.NewUUID()

		// Retry the pipeline upload a few times before giving up
		err = retry.Do(func(s *retry.Stats) error {
			_, err = client.UploadPipeline(cfg.Job, &api.Pipeline{UUID: uuid, Pipeline: result, Replace: cfg.Replace})
			if err != nil {
				l.Warn("%s (%s)", err, s)

				// 422 responses will always fail no need to retry
				if apierr, ok := err.(*api.ErrorResponse); ok && apierr.Response.StatusCode == 422 {
					l.Error("Unrecoverable error, skipping retries")
					s.Break()
				}
			}

			return err
			// On a server error, it means there is downtime or other problems, we
			// need to retry. Let's retry every 5 seconds, for a total of 5 minutes.
		}, &retry.Config{Maximum: 60, Interval: 5 * time.Second})
		if err != nil {
			l.Fatal("Failed to upload and process pipeline: %s", err)
		}

		l.Info("Successfully uploaded and parsed pipeline config")
	},
}
<commit_msg>Make secret value rejection on pipeline upload optional<commit_after>package clicommand

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/buildkite/agent/v3/agent"
	"github.com/buildkite/agent/v3/api"
	"github.com/buildkite/agent/v3/bootstrap/shell"
	"github.com/buildkite/agent/v3/cliconfig"
	"github.com/buildkite/agent/v3/env"
	"github.com/buildkite/agent/v3/redaction"
	"github.com/buildkite/agent/v3/retry"
	"github.com/buildkite/agent/v3/stdin"
	"github.com/urfave/cli"
)

var PipelineUploadHelpDescription = `Usage:

   buildkite-agent pipeline upload [file] [options...]

Description:

   Allows you to change the pipeline of a running build by uploading either a
   YAML (recommended) or JSON configuration file. If no configuration file is
   provided, the command looks for the file in the following locations:

   - buildkite.yml
   - buildkite.yaml
   - buildkite.json
   - .buildkite/pipeline.yml
   - .buildkite/pipeline.yaml
   - .buildkite/pipeline.json
   - buildkite/pipeline.yml
   - buildkite/pipeline.yaml
   - buildkite/pipeline.json

   You can also pipe build pipelines to the command allowing you to create
   scripts that generate dynamic pipelines.

Example:

   $ buildkite-agent pipeline upload
   $ buildkite-agent pipeline upload my-custom-pipeline.yml
   $ ./script/dynamic_step_generator | buildkite-agent pipeline upload`

type PipelineUploadConfig struct {
	FilePath        string   `cli:"arg:0" label:"upload paths"`
	Replace         bool     `cli:"replace"`
	Job             string   `cli:"job"`
	DryRun          bool     `cli:"dry-run"`
	NoInterpolation bool     `cli:"no-interpolation"`
	RedactedVars    []string `cli:"redacted-vars" normalize:"list"`
	RejectSecrets   bool     `cli:"reject-secrets"`

	// Global flags
	Debug       bool     `cli:"debug"`
	NoColor     bool     `cli:"no-color"`
	Experiments []string `cli:"experiment" normalize:"list"`
	Profile     string   `cli:"profile"`

	// API config
	DebugHTTP        bool   `cli:"debug-http"`
	AgentAccessToken string `cli:"agent-access-token" validate:"required"`
	Endpoint         string `cli:"endpoint" validate:"required"`
	NoHTTP2          bool   `cli:"no-http2"`
}

var PipelineUploadCommand = cli.Command{
	Name:        "upload",
	Usage:       "Uploads a description of a build pipeline adds it to the currently running build after the current job",
	Description: PipelineUploadHelpDescription,
	Flags: []cli.Flag{
		cli.BoolFlag{
			Name:   "replace",
			Usage:  "Replace the rest of the existing pipeline with the steps uploaded. Jobs that are already running are not removed.",
			EnvVar: "BUILDKITE_PIPELINE_REPLACE",
		},
		cli.StringFlag{
			Name:   "job",
			Value:  "",
			Usage:  "The job that is making the changes to its build",
			EnvVar: "BUILDKITE_JOB_ID",
		},
		cli.BoolFlag{
			Name:   "dry-run",
			Usage:  "Rather than uploading the pipeline, it will be echoed to stdout",
			EnvVar: "BUILDKITE_PIPELINE_UPLOAD_DRY_RUN",
		},
		cli.BoolFlag{
			Name:   "no-interpolation",
			Usage:  "Skip variable interpolation the pipeline when uploaded",
			EnvVar: "BUILDKITE_PIPELINE_NO_INTERPOLATION",
		},
		cli.BoolFlag{
			Name:   "reject-secrets",
			Usage:  "When true, fail the pipeline upload early if the the pipeline contains secrets",
			EnvVar: "BUILDKITE_AGENT_PIPELINE_UPLOAD_REJECT_SECRETS",
		},

		// API Flags
		AgentAccessTokenFlag,
		EndpointFlag,
		NoHTTP2Flag,
		DebugHTTPFlag,

		// Global flags
		NoColorFlag,
		DebugFlag,
		ExperimentsFlag,
		ProfileFlag,
		RedactedVars,
	},
	Action: func(c *cli.Context) {
		// The configuration will be loaded into this struct
		cfg := PipelineUploadConfig{}

		l := CreateLogger(&cfg)

		// Load the configuration
		if err := cliconfig.Load(c, l, &cfg); err != nil {
			l.Fatal("%s", err)
		}

		// Setup any global configuration options
		done := HandleGlobalFlags(l, cfg)
		defer done()

		// Find the pipeline file either from STDIN or the first
		// argument
		var input []byte
		var err error
		var filename string

		if cfg.FilePath != "" {
			l.Info("Reading pipeline config from \"%s\"", cfg.FilePath)

			filename = filepath.Base(cfg.FilePath)
			input, err = ioutil.ReadFile(cfg.FilePath)
			if err != nil {
				l.Fatal("Failed to read file: %s", err)
			}
		} else if stdin.IsReadable() {
			l.Info("Reading pipeline config from STDIN")

			// Actually read the file from STDIN
			input, err = ioutil.ReadAll(os.Stdin)
			if err != nil {
				l.Fatal("Failed to read from STDIN: %s", err)
			}
		} else {
			l.Info("Searching for pipeline config...")

			paths := []string{
				"buildkite.yml",
				"buildkite.yaml",
				"buildkite.json",
				filepath.FromSlash(".buildkite/pipeline.yml"),
				filepath.FromSlash(".buildkite/pipeline.yaml"),
				filepath.FromSlash(".buildkite/pipeline.json"),
				filepath.FromSlash("buildkite/pipeline.yml"),
				filepath.FromSlash("buildkite/pipeline.yaml"),
				filepath.FromSlash("buildkite/pipeline.json"),
			}

			// Collect all the files that exist
			exists := []string{}
			for _, path := range paths {
				if _, err := os.Stat(path); err == nil {
					exists = append(exists, path)
				}
			}

			// If more than 1 of the config files exist, throw an
			// error. There can only be one!!
			if len(exists) > 1 {
				l.Fatal("Found multiple configuration files: %s. Please only have 1 configuration file present.", strings.Join(exists, ", "))
			} else if len(exists) == 0 {
				l.Fatal("Could not find a default pipeline configuration file. See `buildkite-agent pipeline upload --help` for more information.")
			}

			found := exists[0]

			l.Info("Found config file \"%s\"", found)

			// Read the default file
			filename = path.Base(found)
			input, err = ioutil.ReadFile(found)
			if err != nil {
				l.Fatal("Failed to read file \"%s\" (%s)", found, err)
			}
		}

		// Make sure the file actually has something in it
		if len(input) == 0 {
			l.Fatal("Config file is empty")
		}

		// Load environment to pass into parser
		environ := env.FromSlice(os.Environ())

		// resolve BUILDKITE_COMMIT based on the local git repo
		if commitRef, ok := environ.Get(`BUILDKITE_COMMIT`); ok {
			cmdOut, err := exec.Command(`git`, `rev-parse`, commitRef).Output()
			if err != nil {
				l.Warn("Error running git rev-parse %q: %v", commitRef, err)
			} else {
				trimmedCmdOut := strings.TrimSpace(string(cmdOut))
				l.Info("Updating BUILDKITE_COMMIT to %q", trimmedCmdOut)
				environ.Set(`BUILDKITE_COMMIT`, trimmedCmdOut)
			}
		}

		src := filename
		if src == "" {
			src = "(stdin)"
		}

		// Parse the pipeline
		result, err := agent.PipelineParser{
			Env:             environ,
			Filename:        filename,
			Pipeline:        input,
			NoInterpolation: cfg.NoInterpolation,
		}.Parse()
		if err != nil {
			l.Fatal("Pipeline parsing of \"%s\" failed (%s)", src, err)
		}

		if len(cfg.RedactedVars) > 0 {
			needles := redaction.GetKeyValuesToRedact(shell.StderrLogger, cfg.RedactedVars, env.FromSlice(os.Environ()).ToMap())
			serialisedPipeline, err := result.MarshalJSON()

			if err != nil {
				l.Fatal("Couldn’t scan the %q pipeline for redacted variables. This parsed pipeline could not be serialized, ensure the pipeline YAML is valid, or ignore interpolated secrets for this upload by passing --redacted-vars=''. (%s)", src, err)
			}

			stringifiedserialisedPipeline := string(serialisedPipeline)

			secretsFound := make([]string, 0, len(needles))
			for needleKey, needle := range needles {
				if strings.Contains(stringifiedserialisedPipeline, needle) {
					secretsFound = append(secretsFound, needleKey)
				}
			}

			if len(secretsFound) > 0 {
				if cfg.RejectSecrets {
					l.Fatal("Pipeline %q contains values interpolated from the following secret environment variables: %v, and cannot be uploaded to Buildkite", src, secretsFound)
				} else {
					l.Warn("Pipeline %q contains values interpolated from the following secret environment variables: %v, which could leak sensitive information into the Buildkite UI.", src, secretsFound)
					l.Warn("This pipeline will still be uploaded, but if you'd like to to prevent this from happening, you can use the `--reject-secrets` cli flag, or the `BUILDKITE_AGENT_PIPELINE_UPLOAD_REJECT_SECRETS` environment variable, which will make the `buildkite-agent pipeline upload` command fail if it finds secrets in the pipeline.")
					l.Warn("The behaviour in the above flags will become default in Buildkite Agent v4")
				}
			}
		}

		// In dry-run mode we just output the generated pipeline to stdout
		if cfg.DryRun {
			enc := json.NewEncoder(os.Stdout)
			enc.SetIndent("", "  ")

			// Dump json indented to stdout. All logging happens to stderr
			// this can be used with other tools to get interpolated json
			if err := enc.Encode(result); err != nil {
				l.Fatal("%#v", err)
			}

			return
		}

		// Check we have a job id set if not in dry run
		if cfg.Job == "" {
			l.Fatal("Missing job parameter. Usually this is set in the environment for a Buildkite job via BUILDKITE_JOB_ID.")
		}

		// Check we have an agent access token if not in dry run
		if cfg.AgentAccessToken == "" {
			l.Fatal("Missing agent-access-token parameter. Usually this is set in the environment for a Buildkite job via BUILDKITE_AGENT_ACCESS_TOKEN.")
		}

		// Create the API client
		client := api.NewClient(l, loadAPIClientConfig(cfg, `AgentAccessToken`))

		// Generate a UUID that will identify this pipeline change. We
		// do this outside of the retry loop because we want this UUID
		// to be the same for each attempt at updating the pipeline.
		uuid := api.NewUUID()

		// Retry the pipeline upload a few times before giving up
		err = retry.Do(func(s *retry.Stats) error {
			_, err = client.UploadPipeline(cfg.Job, &api.Pipeline{UUID: uuid, Pipeline: result, Replace: cfg.Replace})
			if err != nil {
				l.Warn("%s (%s)", err, s)

				// 422 responses will always fail no need to retry
				if apierr, ok := err.(*api.ErrorResponse); ok && apierr.Response.StatusCode == 422 {
					l.Error("Unrecoverable error, skipping retries")
					s.Break()
				}
			}

			return err
			// On a server error, it means there is downtime or other problems, we
			// need to retry. Let's retry every 5 seconds, for a total of 5 minutes.
		}, &retry.Config{Maximum: 60, Interval: 5 * time.Second})
		if err != nil {
			l.Fatal("Failed to upload and process pipeline: %s", err)
		}

		l.Info("Successfully uploaded and parsed pipeline config")
	},
}
<|endoftext|>


<commit_before>package app

import (
	"exp/html"
	"fmt"
	"io"
	"net/http"
	"strings"
)

func debug(w http.ResponseWriter, r *http.Request) {
	node, err := html.Parse(strings.NewReader(manageChecklistsHtml))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	debugNodeFull(w, node, 0)
}

func debugNodeFull(w io.Writer, node *html.Node, n int) {
	if node == nil {
		return
	}
	for i := 0; i < n; i++ {
		fmt.Fprintf(w, " ")
	}
	debugNode(w, node)
	debugNodeFull(w, node.FirstChild, n+2)
	debugNodeFull(w, node.NextSibling, n)
}

func debugNode(w io.Writer, node *html.Node) {
	fmt.Fprintf(w, "%s ", nodeTypeToString(node.Type))
	if node.Type == html.ElementNode {
		fmt.Fprintf(w, "<%s", node.DataAtom.String())
		for _, attr := range node.Attr {
			fmt.Fprintf(w, " %s=\"%s\"", attr.Key, attr.Val)
		}
		fmt.Fprintf(w, ">")
	}

	fmt.Fprintf(w, ": %s\n", node.Data)
}

func nodeTypeToString(nodeType html.NodeType) string {
	switch nodeType {
	case html.ErrorNode:
		return "Error"
	case html.TextNode:
		return "Text"
	case html.DocumentNode:
		return "Document"
	case html.ElementNode:
		return "Element"
	case html.CommentNode:
		return "Comment"
	case html.DoctypeNode:
		return "Doctype"
	}
	return "<UnknownType>"
}
<commit_msg>Add DFS traversal to debug code.<commit_after>package app

import (
	"exp/html"
	"fmt"
	"io"
	"net/http"
	"strings"
)

func debug(w http.ResponseWriter, r *http.Request) {
	rootNode, err := html.Parse(strings.NewReader(manageChecklistsHtml))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	checkListIds := make(map[string]bool)
	for node, depth := rootNode, 0; node != nil; node, depth = nextNode(node, depth) {
		for i := 0; i < depth; i++ {
			fmt.Fprintf(w, "  ")
		}
		debugNode(w, node)
		checkListId := findCheckListId(node)
		if checkListId != nil {
			checkListIds[checkListId] = true
		}
	}
}

// Returns the next node in a depth-first traversal.
func nextNode(node *html.Node, depth int) (*html.Node, int) {
	if node.FirstChild != nil {
		return node.FirstChild, depth+1
	}
	if node.NextSibling != nil {
		return node.NextSibling, depth
	}
	for node = node.Parent; node != nil; node = node.Parent {
		depth -= 1
		if node.NextSibling != nil {
			return node.NextSibling, depth
		}
	}
	return nil, 0
}

func debugNode(w io.Writer, node *html.Node) {
	fmt.Fprintf(w, "%s ", nodeTypeToString(node.Type))
	if node.Type == html.ElementNode {
		fmt.Fprintf(w, "<%s", node.DataAtom.String())
		for _, attr := range node.Attr {
			fmt.Fprintf(w, " %s=\"%s\"", attr.Key, attr.Val)
		}
		fmt.Fprintf(w, ">")
	}

	fmt.Fprintf(w, ": %s\n", node.Data)
}

func nodeTypeToString(nodeType html.NodeType) string {
	switch nodeType {
	case html.ErrorNode:
		return "Error"
	case html.TextNode:
		return "Text"
	case html.DocumentNode:
		return "Document"
	case html.ElementNode:
		return "Element"
	case html.CommentNode:
		return "Comment"
	case html.DoctypeNode:
		return "Doctype"
	}
	return "<UnknownType>"
}
<|endoftext|>


<commit_before>package allocdir

import (
	"os"
	"syscall"
)

// linkDir hardlinks src to dst. The src and dst must be on the same filesystem.
func linkDir(src, dst string) error {
	return syscall.Link(src, dst)
}

// unlinkDir removes a directory link.
func unlinkDir(dir string) error {
	return syscall.Unlink(dir)
}

// createSecretDir creates the secrets dir folder at the given path
func createSecretDir(dir string) error {
	return os.MkdirAll(dir, 0777)
}

// removeSecretDir removes the secrets dir folder
func removeSecretDir(dir string) error {
	return os.RemoveAll(dir)
}
<commit_msg>fix linting failure in test case file<commit_after>package main

import "fmt"

func main() {
	fmt.Println("Hello World")
}
<|endoftext|>


<commit_before>package client

import (
	"fmt"
	"log"
	"strings"
	"sync"
	"time"

	"github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/client/driver"
	"github.com/hashicorp/nomad/client/fingerprint"
	cstructs "github.com/hashicorp/nomad/client/structs"
	"github.com/hashicorp/nomad/nomad/structs"
)

// FingerprintManager runs a client fingerprinters on a continuous basis, and
// updates the client when the node has changed
type FingerprintManager struct {
	getConfig  func() *config.Config
	node       *structs.Node
	nodeLock   sync.Mutex
	shutdownCh chan struct{}

	// updateNodeAttributes is a callback to the client to update the state of its
	// associated node
	updateNodeAttributes func(*cstructs.FingerprintResponse) *structs.Node

	// updateNodeFromDriver is a callback to the client to update the state of a
	// specific driver for the node
	updateNodeFromDriver func(string, *structs.DriverInfo, *structs.DriverInfo) *structs.Node
	logger               *log.Logger
}

// NewFingerprintManager is a constructor that creates and returns an instance
// of FingerprintManager
func NewFingerprintManager(getConfig func() *config.Config,
	node *structs.Node,
	shutdownCh chan struct{},
	updateNodeAttributes func(*cstructs.FingerprintResponse) *structs.Node,
	updateNodeFromDriver func(string, *structs.DriverInfo, *structs.DriverInfo) *structs.Node,
	logger *log.Logger) *FingerprintManager {
	return &FingerprintManager{
		getConfig:            getConfig,
		updateNodeAttributes: updateNodeAttributes,
		updateNodeFromDriver: updateNodeFromDriver,
		node:                 node,
		shutdownCh:           shutdownCh,
		logger:               logger,
	}
}

// Run starts the process of fingerprinting the node. It does an initial pass,
// identifying whitelisted and blacklisted fingerprints/drivers. Then, for
// those which require periotic checking, it starts a periodic process for
// each.
func (fp *FingerprintManager) Run() error {
	// First, set up all fingerprints
	cfg := fp.getConfig()
	whitelistFingerprints := cfg.ReadStringListToMap("fingerprint.whitelist")
	whitelistFingerprintsEnabled := len(whitelistFingerprints) > 0
	blacklistFingerprints := cfg.ReadStringListToMap("fingerprint.blacklist")

	fp.logger.Printf("[DEBUG] client.fingerprint_manager: built-in fingerprints: %v", fingerprint.BuiltinFingerprints())

	var availableFingerprints []string
	var skippedFingerprints []string
	for _, name := range fingerprint.BuiltinFingerprints() {
		// Skip modules that are not in the whitelist if it is enabled.
		if _, ok := whitelistFingerprints[name]; whitelistFingerprintsEnabled && !ok {
			skippedFingerprints = append(skippedFingerprints, name)
			continue
		}
		// Skip modules that are in the blacklist
		if _, ok := blacklistFingerprints[name]; ok {
			skippedFingerprints = append(skippedFingerprints, name)
			continue
		}

		availableFingerprints = append(availableFingerprints, name)
	}

	if err := fp.setupFingerprinters(availableFingerprints); err != nil {
		return err
	}

	if len(skippedFingerprints) != 0 {
		fp.logger.Printf("[DEBUG] client.fingerprint_manager: fingerprint modules skipped due to white/blacklist: %v", skippedFingerprints)
	}

	// Next, set up drivers
	// Build the white/blacklists of drivers.
	whitelistDrivers := cfg.ReadStringListToMap("driver.whitelist")
	whitelistDriversEnabled := len(whitelistDrivers) > 0
	blacklistDrivers := cfg.ReadStringListToMap("driver.blacklist")

	var availDrivers []string
	var skippedDrivers []string

	for name := range driver.BuiltinDrivers {
		// Skip fingerprinting drivers that are not in the whitelist if it is
		// enabled.
		if _, ok := whitelistDrivers[name]; whitelistDriversEnabled && !ok {
			skippedDrivers = append(skippedDrivers, name)
			continue
		}
		// Skip fingerprinting drivers that are in the blacklist
		if _, ok := blacklistDrivers[name]; ok {
			skippedDrivers = append(skippedDrivers, name)
			continue
		}

		availDrivers = append(availDrivers, name)
	}

	if err := fp.setupDrivers(availDrivers); err != nil {
		return err
	}

	if len(skippedDrivers) > 0 {
		fp.logger.Printf("[DEBUG] client.fingerprint_manager: drivers skipped due to white/blacklist: %v", skippedDrivers)
	}
	return nil
}

// setupFingerprints is used to fingerprint the node to see if these attributes are
// supported
func (fm *FingerprintManager) setupFingerprinters(fingerprints []string) error {
	var appliedFingerprints []string

	for _, name := range fingerprints {
		f, err := fingerprint.NewFingerprint(name, fm.logger)

		if err != nil {
			fm.logger.Printf("[ERR] client.fingerprint_manager: fingerprinting for %v failed: %+v", name, err)
			return err
		}

		detected, err := fm.fingerprint(name, f)
		if err != nil {
			return err
		}

		// log the fingerprinters which have been applied
		if detected {
			appliedFingerprints = append(appliedFingerprints, name)
		}

		p, period := f.Periodic()
		if p {
			go fm.runFingerprint(f, period, name)
		}
	}

	fm.logger.Printf("[DEBUG] client.fingerprint_manager: detected fingerprints %v", appliedFingerprints)
	return nil
}

// setupDrivers is used to fingerprint the node to see if these drivers are
// supported
func (fm *FingerprintManager) setupDrivers(drivers []string) error {
	var availDrivers []string
	driverCtx := driver.NewDriverContext("", "", fm.getConfig(), fm.node, fm.logger, nil)
	for _, name := range drivers {

		d, err := driver.NewDriver(name, driverCtx)
		if err != nil {
			return err
		}

		detected, err := fm.fingerprintDriver(name, d)
		if err != nil {
			fm.logger.Printf("[DEBUG] client.fingerprint_manager: fingerprinting driver %v failed: %+v", name, err)
			return err
		}

		// For all drivers without health checking enabled , create a driver
		// info which matches its fingerprint status. Later, for drivers that
		// have the health check interface implemented, a periodic health check
		// will be run
		if _, isHealthCheck := d.(fingerprint.HealthCheck); !isHealthCheck {
			healthInfo := &structs.DriverInfo{
				Healthy:    detected,
				UpdateTime: time.Now(),
			}
			if node := fm.updateNodeFromDriver(name, nil, healthInfo); node != nil {
				fm.nodeLock.Lock()
				fm.node = node
				fm.nodeLock.Unlock()
			}
		}

		// Start a periodic watcher to detect changes to a drivers health and
		// attributes.
		go fm.watchDriver(d, name)

		// Log the fingerprinters which have been applied
		if detected {
			availDrivers = append(availDrivers, name)
		}
	}

	fm.logger.Printf("[DEBUG] client.fingerprint_manager: detected drivers %v", availDrivers)
	return nil
}

// runFingerprint runs each fingerprinter individually on an ongoing basis
func (fm *FingerprintManager) runFingerprint(f fingerprint.Fingerprint, period time.Duration, name string) {
	fm.logger.Printf("[DEBUG] client.fingerprint_manager: fingerprinting %s every %v", name, period)

	timer := time.NewTimer(period)
	defer timer.Stop()

	for {
		select {
		case <-timer.C:
			timer.Reset(period)

			_, err := fm.fingerprint(name, f)
			if err != nil {
				fm.logger.Printf("[DEBUG] client.fingerprint_manager: periodic fingerprinting for %v failed: %+v", name, err)
				continue
			}

		case <-fm.shutdownCh:
			return
		}
	}
}

// fingerprint does an initial fingerprint of the client. If the fingerprinter
// is meant to be run continuously, a process is launched to perform this
// fingerprint on an ongoing basis in the background.
func (fm *FingerprintManager) fingerprint(name string, f fingerprint.Fingerprint) (bool, error) {
	var response cstructs.FingerprintResponse

	fm.nodeLock.Lock()
	request := &cstructs.FingerprintRequest{Config: fm.getConfig(), Node: fm.node}
	err := f.Fingerprint(request, &response)
	fm.nodeLock.Unlock()

	if err != nil {
		return false, err
	}

	if node := fm.updateNodeAttributes(&response); node != nil {
		fm.nodeLock.Lock()
		fm.node = node
		fm.nodeLock.Unlock()
	}

	return response.Detected, nil
}

// watchDrivers facilitates the different periods between fingerprint and
// health checking a driver
func (fm *FingerprintManager) watchDriver(d driver.Driver, name string) {
	var fingerprintTicker, healthTicker <-chan time.Time

	// Determine whether the fingerprinter is periodic and health checking
	isPeriodic, fingerprintPeriod := d.Periodic()
	hc, isHealthCheck := d.(fingerprint.HealthCheck)

	// Nothing to do since the state of this driver will never change
	if !isPeriodic && !isHealthCheck {
		return
	}

	// Setup the required tickers
	if isPeriodic {
		ticker := time.NewTicker(fingerprintPeriod)
		fingerprintTicker = ticker.C
		defer ticker.Stop()
		fm.logger.Printf("[DEBUG] client.fingerprint_manager: fingerprinting driver %s every %v", name, fingerprintPeriod)
	}
	if isHealthCheck {
		// Determine the interval at which to health check
		req := &cstructs.HealthCheckIntervalRequest{}
		var resp cstructs.HealthCheckIntervalResponse

		if err := hc.GetHealthCheckInterval(req, &resp); err != nil {
			fm.logger.Printf("[ERR] client.fingerprint_manager: error getting health check interval for driver %s: %v", name, err)
		} else if resp.Eligible {
			ticker := time.NewTicker(resp.Period)
			healthTicker = ticker.C
			defer ticker.Stop()
			fm.logger.Printf("[DEBUG] client.fingerprint_manager: health checking driver %s every %v", name, resp.Period)
		}
	}

	for {
		select {
		case <-fm.shutdownCh:
			return
		case <-fingerprintTicker:
			if _, err := fm.fingerprintDriver(name, d); err != nil {
				fm.logger.Printf("[DEBUG] client.fingerprint_manager: periodic fingerprinting for driver %v failed: %+v", name, err)
			}
		case <-healthTicker:
			// Determine if we should run the health check
			fm.nodeLock.Lock()
			driver, detected := fm.node.Drivers[name]
			if detected && driver != nil {
				detected = driver.Detected
			}
			fm.nodeLock.Unlock()

			if detected {
				if err := fm.runDriverHealthCheck(name, hc); err != nil {
					fm.logger.Printf("[DEBUG] client.fingerprint_manager: health checking for %v failed: %v", name, err)
				}
			} else {
				// If the driver is undetected, change the health status to unhealthy
				// only once.
				healthInfo := &structs.DriverInfo{
					Healthy:           false,
					HealthDescription: fmt.Sprintf("Driver %s is not detected", name),
					UpdateTime:        time.Now(),
				}
				if node := fm.updateNodeFromDriver(name, nil, healthInfo); node != nil {
					fm.nodeLock.Lock()
					fm.node = node
					fm.nodeLock.Unlock()
				}
			}
		}
	}
}

// fingerprintDriver is a temporary solution to move towards DriverInfo and
// away from annotating a node's attributes to demonstrate support for a
// particular driver. Takes the FingerprintResponse and converts it to the
// proper DriverInfo update and then sets the prefix attributes as well
func (fm *FingerprintManager) fingerprintDriver(name string, f fingerprint.Fingerprint) (bool, error) {
	var response cstructs.FingerprintResponse

	fm.nodeLock.Lock()
	request := &cstructs.FingerprintRequest{Config: fm.getConfig(), Node: fm.node}
	err := f.Fingerprint(request, &response)
	fm.nodeLock.Unlock()

	if err != nil {
		return false, err
	}

	if node := fm.updateNodeAttributes(&response); node != nil {
		fm.nodeLock.Lock()
		fm.node = node
		fm.nodeLock.Unlock()
	}

	// COMPAT: Remove in 0.9: As of Nomad 0.8 there is a temporary measure to
	// update all driver attributes to its corresponding driver info object,
	// as eventually all drivers will need to
	// support this. Doing this so that we can enable this iteratively and also
	// in a backwards compatible way, where node attributes for drivers will
	// eventually be phased out.
	strippedAttributes := make(map[string]string, 0)
	for k, v := range response.Attributes {
		copy := k
		strings.Replace(copy, "driver.", "", 1)
		strippedAttributes[k] = v
	}

	di := &structs.DriverInfo{
		Attributes: strippedAttributes,
		Detected:   response.Detected,
	}
	if node := fm.updateNodeFromDriver(name, di, nil); node != nil {
		fm.nodeLock.Lock()
		fm.node = node
		fm.nodeLock.Unlock()
	}

	return response.Detected, nil
}

// runDriverHealthCheck checks the health of the specified resource.
func (fm *FingerprintManager) runDriverHealthCheck(name string, hc fingerprint.HealthCheck) error {
	request := &cstructs.HealthCheckRequest{}
	var response cstructs.HealthCheckResponse
	if err := hc.HealthCheck(request, &response); err != nil {
		return err
	}

	// Update the status of the node irregardless if there was an error- in the
	// case of periodic health checks, an error will occur if a health check
	// fails
	if node := fm.updateNodeFromDriver(name, nil, response.Drivers[name]); node != nil {
		fm.nodeLock.Lock()
		fm.node = node
		fm.nodeLock.Unlock()
	}

	return nil
}
<commit_msg>always set initial health status for every driver<commit_after>package client

import (
	"fmt"
	"log"
	"strings"
	"sync"
	"time"

	"github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/client/driver"
	"github.com/hashicorp/nomad/client/fingerprint"
	cstructs "github.com/hashicorp/nomad/client/structs"
	"github.com/hashicorp/nomad/nomad/structs"
)

// FingerprintManager runs a client fingerprinters on a continuous basis, and
// updates the client when the node has changed
type FingerprintManager struct {
	getConfig  func() *config.Config
	node       *structs.Node
	nodeLock   sync.Mutex
	shutdownCh chan struct{}

	// updateNodeAttributes is a callback to the client to update the state of its
	// associated node
	updateNodeAttributes func(*cstructs.FingerprintResponse) *structs.Node

	// updateNodeFromDriver is a callback to the client to update the state of a
	// specific driver for the node
	updateNodeFromDriver func(string, *structs.DriverInfo, *structs.DriverInfo) *structs.Node
	logger               *log.Logger
}

// NewFingerprintManager is a constructor that creates and returns an instance
// of FingerprintManager
func NewFingerprintManager(getConfig func() *config.Config,
	node *structs.Node,
	shutdownCh chan struct{},
	updateNodeAttributes func(*cstructs.FingerprintResponse) *structs.Node,
	updateNodeFromDriver func(string, *structs.DriverInfo, *structs.DriverInfo) *structs.Node,
	logger *log.Logger) *FingerprintManager {
	return &FingerprintManager{
		getConfig:            getConfig,
		updateNodeAttributes: updateNodeAttributes,
		updateNodeFromDriver: updateNodeFromDriver,
		node:                 node,
		shutdownCh:           shutdownCh,
		logger:               logger,
	}
}

// Run starts the process of fingerprinting the node. It does an initial pass,
// identifying whitelisted and blacklisted fingerprints/drivers. Then, for
// those which require periotic checking, it starts a periodic process for
// each.
func (fp *FingerprintManager) Run() error {
	// First, set up all fingerprints
	cfg := fp.getConfig()
	whitelistFingerprints := cfg.ReadStringListToMap("fingerprint.whitelist")
	whitelistFingerprintsEnabled := len(whitelistFingerprints) > 0
	blacklistFingerprints := cfg.ReadStringListToMap("fingerprint.blacklist")

	fp.logger.Printf("[DEBUG] client.fingerprint_manager: built-in fingerprints: %v", fingerprint.BuiltinFingerprints())

	var availableFingerprints []string
	var skippedFingerprints []string
	for _, name := range fingerprint.BuiltinFingerprints() {
		// Skip modules that are not in the whitelist if it is enabled.
		if _, ok := whitelistFingerprints[name]; whitelistFingerprintsEnabled && !ok {
			skippedFingerprints = append(skippedFingerprints, name)
			continue
		}
		// Skip modules that are in the blacklist
		if _, ok := blacklistFingerprints[name]; ok {
			skippedFingerprints = append(skippedFingerprints, name)
			continue
		}

		availableFingerprints = append(availableFingerprints, name)
	}

	if err := fp.setupFingerprinters(availableFingerprints); err != nil {
		return err
	}

	if len(skippedFingerprints) != 0 {
		fp.logger.Printf("[DEBUG] client.fingerprint_manager: fingerprint modules skipped due to white/blacklist: %v", skippedFingerprints)
	}

	// Next, set up drivers
	// Build the white/blacklists of drivers.
	whitelistDrivers := cfg.ReadStringListToMap("driver.whitelist")
	whitelistDriversEnabled := len(whitelistDrivers) > 0
	blacklistDrivers := cfg.ReadStringListToMap("driver.blacklist")

	var availDrivers []string
	var skippedDrivers []string

	for name := range driver.BuiltinDrivers {
		// Skip fingerprinting drivers that are not in the whitelist if it is
		// enabled.
		if _, ok := whitelistDrivers[name]; whitelistDriversEnabled && !ok {
			skippedDrivers = append(skippedDrivers, name)
			continue
		}
		// Skip fingerprinting drivers that are in the blacklist
		if _, ok := blacklistDrivers[name]; ok {
			skippedDrivers = append(skippedDrivers, name)
			continue
		}

		availDrivers = append(availDrivers, name)
	}

	if err := fp.setupDrivers(availDrivers); err != nil {
		return err
	}

	if len(skippedDrivers) > 0 {
		fp.logger.Printf("[DEBUG] client.fingerprint_manager: drivers skipped due to white/blacklist: %v", skippedDrivers)
	}
	return nil
}

// setupFingerprints is used to fingerprint the node to see if these attributes are
// supported
func (fm *FingerprintManager) setupFingerprinters(fingerprints []string) error {
	var appliedFingerprints []string

	for _, name := range fingerprints {
		f, err := fingerprint.NewFingerprint(name, fm.logger)

		if err != nil {
			fm.logger.Printf("[ERR] client.fingerprint_manager: fingerprinting for %v failed: %+v", name, err)
			return err
		}

		detected, err := fm.fingerprint(name, f)
		if err != nil {
			return err
		}

		// log the fingerprinters which have been applied
		if detected {
			appliedFingerprints = append(appliedFingerprints, name)
		}

		p, period := f.Periodic()
		if p {
			go fm.runFingerprint(f, period, name)
		}
	}

	fm.logger.Printf("[DEBUG] client.fingerprint_manager: detected fingerprints %v", appliedFingerprints)
	return nil
}

// setupDrivers is used to fingerprint the node to see if these drivers are
// supported
func (fm *FingerprintManager) setupDrivers(drivers []string) error {
	var availDrivers []string
	driverCtx := driver.NewDriverContext("", "", fm.getConfig(), fm.node, fm.logger, nil)
	for _, name := range drivers {

		d, err := driver.NewDriver(name, driverCtx)
		if err != nil {
			return err
		}

		detected, err := fm.fingerprintDriver(name, d)
		if err != nil {
			fm.logger.Printf("[DEBUG] client.fingerprint_manager: fingerprinting driver %v failed: %+v", name, err)
			return err
		}

		// Set the initial health check status to be the driver detected status.
		// Later, the periodic health checker will update this value for drivers
		// where health checks are enabled.
		healthInfo := &structs.DriverInfo{
			Healthy:    detected,
			UpdateTime: time.Now(),
		}
		if node := fm.updateNodeFromDriver(name, nil, healthInfo); node != nil {
			fm.nodeLock.Lock()
			fm.node = node
			fm.nodeLock.Unlock()
		}

		// Start a periodic watcher to detect changes to a drivers health and
		// attributes.
		go fm.watchDriver(d, name)

		// Log the fingerprinters which have been applied
		if detected {
			availDrivers = append(availDrivers, name)
		}
	}

	fm.logger.Printf("[DEBUG] client.fingerprint_manager: detected drivers %v", availDrivers)
	return nil
}

// runFingerprint runs each fingerprinter individually on an ongoing basis
func (fm *FingerprintManager) runFingerprint(f fingerprint.Fingerprint, period time.Duration, name string) {
	fm.logger.Printf("[DEBUG] client.fingerprint_manager: fingerprinting %s every %v", name, period)

	timer := time.NewTimer(period)
	defer timer.Stop()

	for {
		select {
		case <-timer.C:
			timer.Reset(period)

			_, err := fm.fingerprint(name, f)
			if err != nil {
				fm.logger.Printf("[DEBUG] client.fingerprint_manager: periodic fingerprinting for %v failed: %+v", name, err)
				continue
			}

		case <-fm.shutdownCh:
			return
		}
	}
}

// fingerprint does an initial fingerprint of the client. If the fingerprinter
// is meant to be run continuously, a process is launched to perform this
// fingerprint on an ongoing basis in the background.
func (fm *FingerprintManager) fingerprint(name string, f fingerprint.Fingerprint) (bool, error) {
	var response cstructs.FingerprintResponse

	fm.nodeLock.Lock()
	request := &cstructs.FingerprintRequest{Config: fm.getConfig(), Node: fm.node}
	err := f.Fingerprint(request, &response)
	fm.nodeLock.Unlock()

	if err != nil {
		return false, err
	}

	if node := fm.updateNodeAttributes(&response); node != nil {
		fm.nodeLock.Lock()
		fm.node = node
		fm.nodeLock.Unlock()
	}

	return response.Detected, nil
}

// watchDrivers facilitates the different periods between fingerprint and
// health checking a driver
func (fm *FingerprintManager) watchDriver(d driver.Driver, name string) {
	var fingerprintTicker, healthTicker <-chan time.Time

	// Determine whether the fingerprinter is periodic and health checking
	isPeriodic, fingerprintPeriod := d.Periodic()
	hc, isHealthCheck := d.(fingerprint.HealthCheck)

	// Nothing to do since the state of this driver will never change
	if !isPeriodic && !isHealthCheck {
		return
	}

	// Setup the required tickers
	if isPeriodic {
		ticker := time.NewTicker(fingerprintPeriod)
		fingerprintTicker = ticker.C
		defer ticker.Stop()
		fm.logger.Printf("[DEBUG] client.fingerprint_manager: fingerprinting driver %s every %v", name, fingerprintPeriod)
	}
	if isHealthCheck {
		// Determine the interval at which to health check
		req := &cstructs.HealthCheckIntervalRequest{}
		var resp cstructs.HealthCheckIntervalResponse

		if err := hc.GetHealthCheckInterval(req, &resp); err != nil {
			fm.logger.Printf("[ERR] client.fingerprint_manager: error getting health check interval for driver %s: %v", name, err)
		} else if resp.Eligible {
			ticker := time.NewTicker(resp.Period)
			healthTicker = ticker.C
			defer ticker.Stop()
			fm.logger.Printf("[DEBUG] client.fingerprint_manager: health checking driver %s every %v", name, resp.Period)
		}
	}

	for {
		select {
		case <-fm.shutdownCh:
			return
		case <-fingerprintTicker:
			if _, err := fm.fingerprintDriver(name, d); err != nil {
				fm.logger.Printf("[DEBUG] client.fingerprint_manager: periodic fingerprinting for driver %v failed: %+v", name, err)
			}
		case <-healthTicker:
			// Determine if we should run the health check
			fm.nodeLock.Lock()
			driver, detected := fm.node.Drivers[name]
			if detected && driver != nil {
				detected = driver.Detected
			}
			fm.nodeLock.Unlock()

			if detected {
				if err := fm.runDriverHealthCheck(name, hc); err != nil {
					fm.logger.Printf("[DEBUG] client.fingerprint_manager: health checking for %v failed: %v", name, err)
				}
			} else {
				// If the driver is undetected, change the health status to unhealthy
				// only once.
				healthInfo := &structs.DriverInfo{
					Healthy:           false,
					HealthDescription: fmt.Sprintf("Driver %s is not detected", name),
					UpdateTime:        time.Now(),
				}
				if node := fm.updateNodeFromDriver(name, nil, healthInfo); node != nil {
					fm.nodeLock.Lock()
					fm.node = node
					fm.nodeLock.Unlock()
				}
			}
		}
	}
}

// fingerprintDriver is a temporary solution to move towards DriverInfo and
// away from annotating a node's attributes to demonstrate support for a
// particular driver. Takes the FingerprintResponse and converts it to the
// proper DriverInfo update and then sets the prefix attributes as well
func (fm *FingerprintManager) fingerprintDriver(name string, f fingerprint.Fingerprint) (bool, error) {
	var response cstructs.FingerprintResponse

	fm.nodeLock.Lock()
	request := &cstructs.FingerprintRequest{Config: fm.getConfig(), Node: fm.node}
	err := f.Fingerprint(request, &response)
	fm.nodeLock.Unlock()

	if err != nil {
		return false, err
	}

	if node := fm.updateNodeAttributes(&response); node != nil {
		fm.nodeLock.Lock()
		fm.node = node
		fm.nodeLock.Unlock()
	}

	// COMPAT: Remove in 0.9: As of Nomad 0.8 there is a temporary measure to
	// update all driver attributes to its corresponding driver info object,
	// as eventually all drivers will need to
	// support this. Doing this so that we can enable this iteratively and also
	// in a backwards compatible way, where node attributes for drivers will
	// eventually be phased out.
	strippedAttributes := make(map[string]string, 0)
	for k, v := range response.Attributes {
		copy := k
		strings.Replace(copy, "driver.", "", 1)
		strippedAttributes[k] = v
	}

	di := &structs.DriverInfo{
		Attributes: strippedAttributes,
		Detected:   response.Detected,
	}
	if node := fm.updateNodeFromDriver(name, di, nil); node != nil {
		fm.nodeLock.Lock()
		fm.node = node
		fm.nodeLock.Unlock()
	}

	return response.Detected, nil
}

// runDriverHealthCheck checks the health of the specified resource.
func (fm *FingerprintManager) runDriverHealthCheck(name string, hc fingerprint.HealthCheck) error {
	request := &cstructs.HealthCheckRequest{}
	var response cstructs.HealthCheckResponse
	if err := hc.HealthCheck(request, &response); err != nil {
		return err
	}

	// Update the status of the node irregardless if there was an error- in the
	// case of periodic health checks, an error will occur if a health check
	// fails
	if node := fm.updateNodeFromDriver(name, nil, response.Drivers[name]); node != nil {
		fm.nodeLock.Lock()
		fm.node = node
		fm.nodeLock.Unlock()
	}

	return nil
}
<|endoftext|>


<commit_before>package crypto

import (
	"bytes"
	"crypto/rand"
	"testing"
)

// TestTreeBuilder builds a tree and gets the merkle root.
func TestTreeBuilder(t *testing.T) {
	tree := NewTree()
	tree.PushObject("a")
	tree.PushObject("b")
	_ = tree.Root()

	bytes := [][]byte{{1}, {2}}
	_ = MerkleRoot(bytes)

	// correctness is assumed, as it's tested by the merkletree package. This
	// function is really for code coverage.
}

// TestCalculateLeaves probes the CalulateLeaves function.
func TestCalculateLeaves(t *testing.T) {
	tests := []struct {
		size, expSegs uint64
	}{
		{0, 1},
		{63, 1},
		{64, 1},
		{65, 2},
		{127, 2},
		{128, 2},
		{129, 3},
	}

	for i, test := range tests {
		if segs := CalculateLeaves(test.size); segs != test.expSegs {
			t.Errorf("miscalculation for test %v: expected %v, got %v", i, test.expSegs, segs)
		}
	}
}

// TestStorageProof builds a storage proof and checks that it verifies
// correctly.
func TestStorageProof(t *testing.T) {
	// generate proof data
	numSegments := uint64(7)
	data := make([]byte, numSegments*SegmentSize)
	rand.Read(data)
	rootHash, err := ReaderMerkleRoot(bytes.NewReader(data))
	if err != nil {
		t.Fatal(err)
	}

	// create and verify proofs for all indices
	for i := uint64(0); i < numSegments; i++ {
		baseSegment, hashSet, err := BuildReaderProof(bytes.NewReader(data), i)
		if err != nil {
			t.Error(err)
			continue
		}
		if !VerifySegment(baseSegment, hashSet, numSegments, i, rootHash) {
			t.Error("Proof", i, "did not pass verification")
		}
	}

	// Try an incorrect proof.
	baseSegment, hashSet, err := BuildReaderProof(bytes.NewReader(data), 3)
	if err != nil {
		t.Fatal(err)
	}
	if VerifySegment(baseSegment, hashSet, numSegments, 4, rootHash) {
		t.Error("Verified a bad proof")
	}
}

// TestPaddedStorageProof builds a storage proof that has padding in the last
// segment, and then requests a proof on the padded segment.
func TestPaddedStorageProof(t *testing.T) {
	// generate proof data
	data := make([]byte, (2*SegmentSize)+10)
	rand.Read(data)
	rootHash, err := ReaderMerkleRoot(bytes.NewReader(data))
	if err != nil {
		t.Fatal(err)
	}

	// create and verify a proof for the last index.
	baseSegment, hashSet, err := BuildReaderProof(bytes.NewReader(data), 2)
	if err != nil {
		t.Error(err)
	}
	if !VerifySegment(baseSegment, hashSet, 3, 2, rootHash) {
		t.Error("padded segment proof failed")
	}
}
<commit_msg>Add tests for MerkleTree.ReadSegments<commit_after>package crypto

import (
	"bytes"
	"crypto/rand"
	"testing"
)

// TestTreeBuilder builds a tree and gets the merkle root.
func TestTreeBuilder(t *testing.T) {
	tree := NewTree()
	tree.PushObject("a")
	tree.PushObject("b")
	_ = tree.Root()

	bytes := [][]byte{{1}, {2}}
	_ = MerkleRoot(bytes)

	// correctness is assumed, as it's tested by the merkletree package. This
	// function is really for code coverage.
}

// TestCalculateLeaves probes the CalulateLeaves function.
func TestCalculateLeaves(t *testing.T) {
	tests := []struct {
		size, expSegs uint64
	}{
		{0, 1},
		{63, 1},
		{64, 1},
		{65, 2},
		{127, 2},
		{128, 2},
		{129, 3},
	}

	for i, test := range tests {
		if segs := CalculateLeaves(test.size); segs != test.expSegs {
			t.Errorf("miscalculation for test %v: expected %v, got %v", i, test.expSegs, segs)
		}
	}
}

// TestStorageProof builds a storage proof and checks that it verifies
// correctly.
func TestStorageProof(t *testing.T) {
	// generate proof data
	numSegments := uint64(7)
	data := make([]byte, numSegments*SegmentSize)
	rand.Read(data)
	rootHash, err := ReaderMerkleRoot(bytes.NewReader(data))
	if err != nil {
		t.Fatal(err)
	}

	// create and verify proofs for all indices
	for i := uint64(0); i < numSegments; i++ {
		baseSegment, hashSet, err := BuildReaderProof(bytes.NewReader(data), i)
		if err != nil {
			t.Error(err)
			continue
		}
		if !VerifySegment(baseSegment, hashSet, numSegments, i, rootHash) {
			t.Error("Proof", i, "did not pass verification")
		}
	}

	// Try an incorrect proof.
	baseSegment, hashSet, err := BuildReaderProof(bytes.NewReader(data), 3)
	if err != nil {
		t.Fatal(err)
	}
	if VerifySegment(baseSegment, hashSet, numSegments, 4, rootHash) {
		t.Error("Verified a bad proof")
	}
}

// TestPaddedStorageProof builds a storage proof that has padding in the last
// segment, and then requests a proof on the padded segment.
func TestPaddedStorageProof(t *testing.T) {
	// generate proof data
	data := make([]byte, (2*SegmentSize)+10)
	rand.Read(data)
	rootHash, err := ReaderMerkleRoot(bytes.NewReader(data))
	if err != nil {
		t.Fatal(err)
	}

	// create and verify a proof for the last index.
	baseSegment, hashSet, err := BuildReaderProof(bytes.NewReader(data), 2)
	if err != nil {
		t.Error(err)
	}
	if !VerifySegment(baseSegment, hashSet, 3, 2, rootHash) {
		t.Error("padded segment proof failed")
	}
}

// TestReadSegments tests MerkleTree.ReadSegments by building a MerkleTree and
// comparing its root with the root returned from ReaderMerkleRoot.
func TestReadSegments(t *testing.T) {
	// Generate leaf data.
	numSegments := 7
	data := make([]byte, numSegments*SegmentSize)
	rand.Read(data)

	// Generate merkle tree root using MerkleTree.ReadSegments and
	// MerkleTree.Root.
	m := NewTree()
	err := m.ReadSegments(bytes.NewReader(data))
	if err != nil {
		t.Fatal(err)
	}
	rootHash := m.Root()

	// Generate merkle tree root using ReaderMerkleRoot
	expectedHash, err := ReaderMerkleRoot(bytes.NewReader(data))
	if err != nil {
		t.Fatal(err)
	}

	// Compare roots
	if rootHash != expectedHash {
		t.Errorf("root hashes not consistent: expected %s, got %s\n", expectedHash, rootHash)
	}
}

// TestPartialLastLeafReadSegments tests MerkleTree.ReadSements for data that
// doesn't fit evently into a whole number of leaves. It does so by building a
// MerkleTree with MerkleTree.ReadSegments and comparing its root with the root
// returned from ReaderMerkleRoot.
func TestPartialLastLeafReadSegments(t *testing.T) {
	// Generate leaf data that doesn't fit evenly into n leaves.
	numSegments := 2.5
	data := make([]byte, int(numSegments*SegmentSize))
	rand.Read(data)

	// Generate merkle tree root using MerkleTree.ReadSegments and
	// MerkleTree.Root.
	m := NewTree()
	err := m.ReadSegments(bytes.NewReader(data))
	if err != nil {
		t.Fatal(err)
	}
	rootHash := m.Root()

	// Generate merkle tree root using ReaderMerkleRoot
	expectedHash, err := ReaderMerkleRoot(bytes.NewReader(data))
	if err != nil {
		t.Fatal(err)
	}

	// Compare roots
	if rootHash != expectedHash {
		t.Errorf("root hashes not consistent: expected %s, got %s\n", expectedHash, rootHash)
	}
}
<|endoftext|>


<commit_before>package nvim

import (
	"fmt"
	"strconv"
	"strings"
	"sync"

	"nvim-go/config"

	"github.com/garyburd/neovim-go/vim"
)

var (
	tbuffer vim.Buffer
	twindow vim.Window
)

// Terminal configure of open the terminal.
type Terminal struct {
	v    *vim.Vim
	cmd  []string
	mode string
	// Dir specifies the working directory of the command on terminal.
	Dir string
	// Width split window width for open the terminal window.
	Width int64
	// Height split window height for open the terminal window.
	Height int64
}

// NewTerminal return the initialize Neovim terminal config.
func NewTerminal(vim *vim.Vim, command []string, mode string) *Terminal {
	return &Terminal{
		v:    vim,
		cmd:  command,
		mode: mode,
	}
}

// Run runs the command in the terminal buffer.
func (t *Terminal) Run() error {
	if t.Dir != "" {
		defer chdir(t.v, t.Dir)()
	}

	var (
		b      vim.Buffer
		w      vim.Window
		pos    = config.TerminalPosition
		height = config.TerminalHeight
		width  = config.TerminalWidth
	)

	// Creates a new pipeline
	p := t.v.NewPipeline()
	p.CurrentBuffer(&b)
	p.CurrentWindow(&w)
	if err := p.Wait(); err != nil {
		return err
	}

	if twindow != 0 {
		p.SetCurrentWindow(twindow)
		p.SetBufferOption(tbuffer, "modified", false)
		p.Call("termopen", nil, strings.Join(t.cmd, " "))
		p.SetBufferOption(tbuffer, "modified", true)
	} else {
		// Set split window position. (defalut: botright)
		vcmd := pos + " "

		t.Height = height
		t.Width = width

		switch {
		case t.Height != int64(0) && t.mode == "split":
			vcmd += strconv.FormatInt(t.Height, 10)
		case t.Width != int64(0) && t.mode == "vsplit":
			vcmd += strconv.FormatInt(t.Width, 10)
		case strings.Index(t.mode, "split") == -1:
			return fmt.Errorf("%s mode is not supported", t.mode)
		}

		// Create terminal buffer and spawn command.
		vcmd += t.mode + " | terminal " + strings.Join(t.cmd, " ")
		p.Command(vcmd)

		// Get terminal buffer and windows information.
		p.CurrentBuffer(&tbuffer)
		p.CurrentWindow(&twindow)
		if err := p.Wait(); err != nil {
			return err
		}

		// Workaround for "autocmd BufEnter term://* startinsert"
		if config.TerminalStartInsert {
			p.Command("stopinsert")
		}

		p.SetBufferOption(tbuffer, "filetype", "terminal")
		p.SetBufferOption(tbuffer, "buftype", "nofile")
		p.SetBufferOption(tbuffer, "bufhidden", "delete")
		p.SetBufferOption(tbuffer, "buflisted", false)
		p.SetBufferOption(tbuffer, "swapfile", false)

		p.SetWindowOption(twindow, "list", false)
		p.SetWindowOption(twindow, "number", false)
		p.SetWindowOption(twindow, "relativenumber", false)
		p.SetWindowOption(twindow, "winfixheight", true)

		// Cleanup cursor highlighting
		// TODO(zchee): Can use p.AddBufferHighlight?
		p.Command("hi TermCursor gui=NONE guifg=NONE guibg=NONE")
		p.Command("hi! TermCursorNC gui=NONE guifg=NONE guibg=NONE")

		// Cleanup autocmd for terminal buffer
		// The following autocmd is defined only in the terminal buffer local
		p.Command("autocmd! * <buffer>")
		// Set autocmd of automatically insert mode
		p.Command("autocmd WinEnter <buffer> startinsert")
		// Set autoclose buffer if the current buffer is only terminal
		// TODO(zchee): convert to rpc way
		p.Command("autocmd WinEnter <buffer> if winnr('$') == 1 | quit | endif")
	}

	// Set buffer name, filetype and options
	p.SetBufferName(tbuffer, "__GO_TERMINAL__")

	// Refocus coding buffer and stop insert mode
	p.SetCurrentWindow(w)
	p.Command("stopinsert")

	return p.Wait()
}

// chdir changes vim current working directory.
// The returned function restores working directory to `getcwd()` result path
// and unlocks the mutex.
func chdir(v *vim.Vim, dir string) func() {
	var (
		m   sync.Mutex
		cwd interface{}
	)
	m.Lock()
	if err := v.Eval("getcwd()", &cwd); err != nil {
		Echoerr(v, "GoTerminal: %v", err)
	}
	v.ChangeDirectory(dir)
	return func() {
		v.ChangeDirectory(cwd.(string))
		m.Unlock()
	}
}
<commit_msg>Terminal: Move set buffer|window option to 'Terminal' struct<commit_after>package nvim

import (
	"fmt"
	"strconv"
	"strings"
	"sync"

	"nvim-go/config"

	"github.com/garyburd/neovim-go/vim"
)

var (
	tbuffer vim.Buffer
	twindow vim.Window
)

// Terminal configure of open the terminal.
type Terminal struct {
	v    *vim.Vim
	cmd  []string
	mode string
	// Dir specifies the working directory of the command on terminal.
	Dir string
	// Width split window width for open the terminal window.
	Width int64
	// Height split window height for open the terminal window.
	Height int64

	Filetype  string
	Buftype   string
	Bufhidden string
	Buflisted bool
	Swapfile  bool

	List           bool
	Number         bool
	Relativenumber bool
	Winfixheight   bool
}

// NewTerminal return the initialize Neovim terminal config.
func NewTerminal(vim *vim.Vim, command []string, mode string) *Terminal {
	return &Terminal{
		v:    vim,
		cmd:  command,
		mode: mode,

		Filetype:  "terminal",
		Buftype:   "nofile",
		Bufhidden: "delete",
		Buflisted: false,
		Swapfile:  false,

		List:           false,
		Number:         false,
		Relativenumber: false,
		Winfixheight:   true,
	}
}

// Run runs the command in the terminal buffer.
func (t *Terminal) Run() error {
	if t.Dir != "" {
		defer chdir(t.v, t.Dir)()
	}

	var (
		b      vim.Buffer
		w      vim.Window
		pos    = config.TerminalPosition
		height = config.TerminalHeight
		width  = config.TerminalWidth
	)

	// Creates a new pipeline
	p := t.v.NewPipeline()
	p.CurrentBuffer(&b)
	p.CurrentWindow(&w)
	if err := p.Wait(); err != nil {
		return err
	}

	if twindow != 0 {
		p.SetCurrentWindow(twindow)
		p.SetBufferOption(tbuffer, "modified", false)
		p.Call("termopen", nil, strings.Join(t.cmd, " "))
		p.SetBufferOption(tbuffer, "modified", true)
	} else {
		// Set split window position. (defalut: botright)
		vcmd := pos + " "

		t.Height = height
		t.Width = width

		switch {
		case t.Height != int64(0) && t.mode == "split":
			vcmd += strconv.FormatInt(t.Height, 10)
		case t.Width != int64(0) && t.mode == "vsplit":
			vcmd += strconv.FormatInt(t.Width, 10)
		case strings.Index(t.mode, "split") == -1:
			return fmt.Errorf("%s mode is not supported", t.mode)
		}

		// Create terminal buffer and spawn command.
		vcmd += t.mode + " | terminal " + strings.Join(t.cmd, " ")
		p.Command(vcmd)

		// Get terminal buffer and windows information.
		p.CurrentBuffer(&tbuffer)
		p.CurrentWindow(&twindow)
		if err := p.Wait(); err != nil {
			return err
		}

		// Workaround for "autocmd BufEnter term://* startinsert"
		if config.TerminalStartInsert {
			p.Command("stopinsert")
		}

		p.SetBufferOption(tbuffer, "filetype", t.Filetype)
		p.SetBufferOption(tbuffer, "buftype", t.Buftype)
		p.SetBufferOption(tbuffer, "bufhidden", t.Bufhidden)
		p.SetBufferOption(tbuffer, "buflisted", t.Buflisted)
		p.SetBufferOption(tbuffer, "swapfile", t.Swapfile)

		p.SetWindowOption(twindow, "list", t.List)
		p.SetWindowOption(twindow, "number", t.Number)
		p.SetWindowOption(twindow, "relativenumber", t.Relativenumber)
		p.SetWindowOption(twindow, "winfixheight", t.Winfixheight)

		// Cleanup cursor highlighting
		// TODO(zchee): Can use p.AddBufferHighlight?
		p.Command("hi TermCursor gui=NONE guifg=NONE guibg=NONE")
		p.Command("hi! TermCursorNC gui=NONE guifg=NONE guibg=NONE")

		// Cleanup autocmd for terminal buffer
		// The following autocmd is defined only in the terminal buffer local
		p.Command("autocmd! * <buffer>")
		// Set autocmd of automatically insert mode
		p.Command("autocmd WinEnter <buffer> startinsert")
		// Set autoclose buffer if the current buffer is only terminal
		// TODO(zchee): convert to rpc way
		p.Command("autocmd WinEnter <buffer> if winnr('$') == 1 | quit | endif")
	}

	// Set buffer name, filetype and options
	p.SetBufferName(tbuffer, "__GO_TERMINAL__")

	// Refocus coding buffer and stop insert mode
	p.SetCurrentWindow(w)
	p.Command("stopinsert")

	return p.Wait()
}

// chdir changes vim current working directory.
// The returned function restores working directory to `getcwd()` result path
// and unlocks the mutex.
func chdir(v *vim.Vim, dir string) func() {
	var (
		m   sync.Mutex
		cwd interface{}
	)
	m.Lock()
	if err := v.Eval("getcwd()", &cwd); err != nil {
		Echoerr(v, "GoTerminal: %v", err)
	}
	v.ChangeDirectory(dir)
	return func() {
		v.ChangeDirectory(cwd.(string))
		m.Unlock()
	}
}
<|endoftext|>


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

// # $G $F.go && $L $F.$A && ./$A.out readfile.go
// # This is some data we can recognize

package main

func main() {
	var s string;
	var ok bool;

	s, ok = sys.readfile("readfile.go");
	if !ok {
		print("couldn't readfile\n");
		sys.Exit(1)
	}
	start_of_file :=
		"// $G $F.go && $L $F.$A && ./$A.out readfile.go\n" +
		"// # This is some data we can recognize\n" +
		"\n" +
		"package main\n";
	if s[0:102] != start_of_file {
		print("wrong data\n");
		sys.Exit(1)
	}
}
<commit_msg>sys.readfile has been removed.  Remove the test case for it. It was disabled last week anyhow.<commit_after><|endoftext|>


<commit_before>package cookie

import (
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

// Secure cookie expects the value to be pipe deliminated
func TestSecureCookieFormat(t *testing.T) {
	req, _ := http.NewRequest("GET", "http://example.com/", nil)
	req.Header.Set("Cookie", "user=malformeduservalue")
	if _, ok := SecureCookie(req, "user", "foobar"); ok {
		t.Fatalf("TestSecureCookieFormat expected false instead %v", ok)
	}
}

func cookiePartInSlice(cookieSlice []string, part string) bool {
	for i := range cookieSlice {
		entry := strings.TrimSpace(cookieSlice[i])
		if entry == part {
			return true
		}
	}
	return false
}

type SetCookieTest struct {
	name     string
	value    string
	age      int64
	path     string
	secret   string
	secure   bool
	httponly bool
}

// SetSecureCookiePath can set cookies with different flags
func TestSetSecureCookiePath(t *testing.T) {
	tests := []SetCookieTest{
		SetCookieTest{
			name:     "username",
			value:    "foo",
			age:      int64(3600),
			path:     "/",
			secret:   "12345secret",
			secure:   false,
			httponly: false,
		},
		SetCookieTest{
			name:     "username",
			value:    "foo",
			age:      int64(3600),
			path:     "/",
			secret:   "12345secret",
			secure:   true,
			httponly: false,
		},
		SetCookieTest{
			name:     "username",
			value:    "foo",
			age:      int64(3600),
			path:     "/",
			secret:   "12345secret",
			secure:   false,
			httponly: true,
		},
		SetCookieTest{
			name:     "username",
			value:    "foo",
			age:      int64(3600),
			path:     "/",
			secret:   "12345secret",
			secure:   true,
			httponly: true,
		},
	}

	for _, test := range tests {
		handler := func(w http.ResponseWriter, r *http.Request) {
			SetSecureCookiePathWithFlags(w, test.name, test.value, test.age, test.path, test.secret, test.secure, test.httponly)
			io.WriteString(w, "testing..")
		}

		req := httptest.NewRequest("GET", "http://example.com/foo", nil)
		w := httptest.NewRecorder()
		handler(w, req)

		resp := w.Result()
		cookie := resp.Header.Get("Set-Cookie")

		cookieSlice := strings.Split(cookie, ";")
		httpOnlyExists := cookiePartInSlice(cookieSlice, "HttpOnly")
		if httpOnlyExists != test.httponly {
			t.Logf("In test case: %+v\n", test)
			t.Fatalf("TestSetSecureCookiePath expected HttpOnly to be set, it isn't, Cookie: %s", cookie)
		}
		secureExists := cookiePartInSlice(cookieSlice, "Secure")
		if secureExists != test.secure {
			t.Logf("In test case: %+v\n", test)
			t.Fatalf("TestSetSecureCookiePath expected Secure to be set, it isn't, Cookie: %s", cookie)
		}
	}
}
<commit_msg>Minor simplification by gofmt -s<commit_after>package cookie

import (
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

// Secure cookie expects the value to be pipe deliminated
func TestSecureCookieFormat(t *testing.T) {
	req, _ := http.NewRequest("GET", "http://example.com/", nil)
	req.Header.Set("Cookie", "user=malformeduservalue")
	if _, ok := SecureCookie(req, "user", "foobar"); ok {
		t.Fatalf("TestSecureCookieFormat expected false instead %v", ok)
	}
}

func cookiePartInSlice(cookieSlice []string, part string) bool {
	for i := range cookieSlice {
		entry := strings.TrimSpace(cookieSlice[i])
		if entry == part {
			return true
		}
	}
	return false
}

type SetCookieTest struct {
	name     string
	value    string
	age      int64
	path     string
	secret   string
	secure   bool
	httponly bool
}

// SetSecureCookiePath can set cookies with different flags
func TestSetSecureCookiePath(t *testing.T) {
	tests := []SetCookieTest{
		{
			name:     "username",
			value:    "foo",
			age:      int64(3600),
			path:     "/",
			secret:   "12345secret",
			secure:   false,
			httponly: false,
		},
		{
			name:     "username",
			value:    "foo",
			age:      int64(3600),
			path:     "/",
			secret:   "12345secret",
			secure:   true,
			httponly: false,
		},
		{
			name:     "username",
			value:    "foo",
			age:      int64(3600),
			path:     "/",
			secret:   "12345secret",
			secure:   false,
			httponly: true,
		},
		{
			name:     "username",
			value:    "foo",
			age:      int64(3600),
			path:     "/",
			secret:   "12345secret",
			secure:   true,
			httponly: true,
		},
	}

	for _, test := range tests {
		handler := func(w http.ResponseWriter, r *http.Request) {
			SetSecureCookiePathWithFlags(w, test.name, test.value, test.age, test.path, test.secret, test.secure, test.httponly)
			io.WriteString(w, "testing..")
		}

		req := httptest.NewRequest("GET", "http://example.com/foo", nil)
		w := httptest.NewRecorder()
		handler(w, req)

		resp := w.Result()
		cookie := resp.Header.Get("Set-Cookie")

		cookieSlice := strings.Split(cookie, ";")
		httpOnlyExists := cookiePartInSlice(cookieSlice, "HttpOnly")
		if httpOnlyExists != test.httponly {
			t.Logf("In test case: %+v\n", test)
			t.Fatalf("TestSetSecureCookiePath expected HttpOnly to be set, it isn't, Cookie: %s", cookie)
		}
		secureExists := cookiePartInSlice(cookieSlice, "Secure")
		if secureExists != test.secure {
			t.Logf("In test case: %+v\n", test)
			t.Fatalf("TestSetSecureCookiePath expected Secure to be set, it isn't, Cookie: %s", cookie)
		}
	}
}
<|endoftext|>