<commit_before>package main

import (
	"fmt"
	"net/http"

	"code.google.com/p/go-uuid/uuid"
	"github.com/arschles/gbs/log"
	docker "github.com/fsouza/go-dockerclient"
	"github.com/gorilla/mux"
)

func buildHandler(workdir string, dockerCl *docker.Client) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		site, ok := mux.Vars(r)["site"]
		if !ok {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		org, ok := mux.Vars(r)["org"]
		if !ok {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		repo, ok := mux.Vars(r)["repo"]
		if !ok {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		log.Printf("building %s/%s/%s", site, org, repo)
		container, err := dockerCl.CreateContainer(docker.CreateContainerOptions{
			Name: fmt.Sprintf("build-%s-%s-%s-%s", site, org, repo, uuid.New()),
			Config: &docker.Config{
				Env:   []string{"GO15VENDOREXPERIMENT=1", "CGO_ENABLED=0", "SITE=" + site, "ORG=" + org, "REPO=" + repo},
				Cmd:   []string{"/bin/bash", "/pwd/build.sh"},
				Image: "golang:1.5.2",
				Mounts: []docker.Mount{
					docker.Mount{Name: "pwd", Source: workdir, Destination: "/pwd", Mode: "rx"},
				},
			},
			HostConfig: &docker.HostConfig{},
		})
		if err != nil {
			log.Errf("creating container (%s)", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		if err := dockerCl.StartContainer(container.ID, &docker.HostConfig{}); err != nil {
			log.Errf("starting container (%s)", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		if err := dockerCl.AttachToContainer(docker.AttachToContainerOptions{
			Container:    container.ID,
			OutputStream: w,
			ErrorStream:  w,
			Logs:         true,
			Stream:       true,
			Stdout:       true,
			Stderr:       true,
		}); err != nil {
			log.Errf("attaching to container (%s)", err)
			w.WriteHeader(http.StatusInternalServerError)
		}

		code, err := dockerCl.WaitContainer(container.ID)
		if err != nil {
			log.Errf("container exited with code %d (%s)", code, err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		// w.WriteHeader(http.StatusCreated)
		// w.Write([]byte(fmt.Sprintf("created as %s on the filesystem\n", repo)))
	})
}
<commit_msg>writing both to stdout and the client<commit_after>package main

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

	"code.google.com/p/go-uuid/uuid"
	"github.com/arschles/gbs/log"
	docker "github.com/fsouza/go-dockerclient"
	"github.com/gorilla/mux"
)

func buildHandler(workdir string, dockerCl *docker.Client) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		site, ok := mux.Vars(r)["site"]
		if !ok {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		org, ok := mux.Vars(r)["org"]
		if !ok {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		repo, ok := mux.Vars(r)["repo"]
		if !ok {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		log.Printf("building %s/%s/%s", site, org, repo)
		container, err := dockerCl.CreateContainer(docker.CreateContainerOptions{
			Name: fmt.Sprintf("build-%s-%s-%s-%s", site, org, repo, uuid.New()),
			Config: &docker.Config{
				Env:   []string{"GO15VENDOREXPERIMENT=1", "CGO_ENABLED=0", "SITE=" + site, "ORG=" + org, "REPO=" + repo},
				Cmd:   []string{"/bin/bash", "/pwd/build.sh"},
				Image: "golang:1.5.2",
				Mounts: []docker.Mount{
					docker.Mount{Name: "pwd", Source: workdir, Destination: "/pwd", Mode: "rx"},
				},
			},
			HostConfig: &docker.HostConfig{},
		})
		if err != nil {
			log.Errf("creating container (%s)", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		if err := dockerCl.StartContainer(container.ID, &docker.HostConfig{}); err != nil {
			log.Errf("starting container (%s)", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		if err := dockerCl.AttachToContainer(docker.AttachToContainerOptions{
			Container:    container.ID,
			OutputStream: io.MultiWriter(w, os.Stdout),
			ErrorStream:  io.MultiWriter(w, os.Stderr),
			Logs:         true,
			Stream:       true,
			Stdout:       true,
			Stderr:       true,
		}); err != nil {
			log.Errf("attaching to container (%s)", err)
			w.WriteHeader(http.StatusInternalServerError)
		}

		code, err := dockerCl.WaitContainer(container.ID)
		if err != nil {
			log.Errf("container exited with code %d (%s)", code, err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		// w.WriteHeader(http.StatusCreated)
		// w.Write([]byte(fmt.Sprintf("created as %s on the filesystem\n", repo)))
	})
}
<|endoftext|>


<commit_before>// +build go1.10

package godog

import (
	"bytes"
	"fmt"
	"go/build"
	"go/parser"
	"go/token"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"text/template"
	"time"
	"unicode"
)

var tooldir = findToolDir()
var compiler = filepath.Join(tooldir, "compile")
var linker = filepath.Join(tooldir, "link")
var gopaths = filepath.SplitList(build.Default.GOPATH)
var goarch = build.Default.GOARCH
var goroot = build.Default.GOROOT
var goos = build.Default.GOOS

var godogImportPath = "github.com/DATA-DOG/godog"
var runnerTemplate = template.Must(template.New("testmain").Parse(`package main

import (
	"github.com/DATA-DOG/godog"
	{{if .Contexts}}_test "{{.ImportPath}}"{{end}}
	"os"
)

func main() {
	status := godog.Run("{{ .Name }}", func (suite *godog.Suite) {
		os.Setenv("GODOG_TESTED_PACKAGE", "{{.ImportPath}}")
		{{range .Contexts}}
			_test.{{ . }}(suite)
		{{end}}
	})
	os.Exit(status)
}`))

// Build creates a test package like go test command at given target path.
// If there are no go files in tested directory, then
// it simply builds a godog executable to scan features.
//
// If there are go test files, it first builds a test
// package with standard go test command.
//
// Finally it generates godog suite executable which
// registers exported godog contexts from the test files
// of tested package.
//
// Returns the path to generated executable
func Build(bin string) error {
	abs, err := filepath.Abs(".")
	if err != nil {
		return err
	}

	// we allow package to be nil, if godog is run only when
	// there is a feature file in empty directory
	pkg := importPackage(abs)
	src, anyContexts, err := buildTestMain(pkg)
	if err != nil {
		return err
	}

	workdir := fmt.Sprintf(filepath.Join("%s", "godog-%d"), os.TempDir(), time.Now().UnixNano())
	testdir := workdir

	// if none of test files exist, or there are no contexts found
	// we will skip test package compilation, since it is useless
	if anyContexts {
		// first of all compile test package dependencies
		// that will save us many compilations for dependencies
		// go does it better
		out, err := exec.Command("go", "test", "-i").CombinedOutput()
		if err != nil {
			return fmt.Errorf("failed to compile package: %s, reason: %v, output: %s", pkg.Name, err, string(out))
		}

		// builds and compile the tested package.
		// generated test executable will be removed
		// since we do not need it for godog suite.
		// we also print back the temp WORK directory
		// go has built. We will reuse it for our suite workdir.
		out, err = exec.Command("go", "test", "-c", "-work", "-o", "/dev/null").CombinedOutput()
		if err != nil {
			return fmt.Errorf("failed to compile tested package: %s, reason: %v, output: %s", pkg.Name, err, string(out))
		}

		// extract go-build temporary directory as our workdir
		workdir = strings.TrimSpace(string(out))
		if !strings.HasPrefix(workdir, "WORK=") {
			return fmt.Errorf("expected WORK dir path, but got: %s", workdir)
		}
		workdir = strings.Replace(workdir, "WORK=", "", 1)
		testdir = filepath.Join(workdir, "b001")
	} else {
		// still need to create temporary workdir
		if err = os.MkdirAll(testdir, 0755); err != nil {
			return err
		}
	}
	defer os.RemoveAll(workdir)

	// replace _testmain.go file with our own
	testmain := filepath.Join(testdir, "_testmain.go")
	err = ioutil.WriteFile(testmain, src, 0644)
	if err != nil {
		return err
	}

	// godog library may not be imported in tested package
	// but we need it for our testmain package.
	// So we look it up in available source paths
	// including vendor directory, supported since 1.5.
	godogPkg, err := locatePackage(godogImportPath)
	if err != nil {
		return err
	}

	// make sure godog package archive is installed, gherkin
	// will be installed as dependency of godog
	cmd := exec.Command("go", "install", godogPkg.ImportPath)
	cmd.Env = os.Environ()
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("failed to install godog package: %s, reason: %v", string(out), err)
	}

	// compile godog testmain package archive
	// we do not depend on CGO so a lot of checks are not necessary
	testMainPkgOut := filepath.Join(testdir, "main.a")
	args := []string{
		"-o", testMainPkgOut,
		"-p", "main",
		"-complete",
	}

	var in *os.File
	cfg := filepath.Join(testdir, "importcfg.link")
	args = append(args, "-importcfg", cfg)
	if _, err := os.Stat(cfg); err == nil {
		// there were go sources
		in, err = os.OpenFile(cfg, os.O_APPEND|os.O_WRONLY, 0600)
		if err != nil {
			return err
		}
	} else {
		// there were no go sources in the directory
		// so we need to build all dependency tree ourselves
		in, err = os.Create(cfg)
		if err != nil {
			return err
		}
		fmt.Fprintln(in, "# import config")

		deps := make(map[string]string)
		if err := dependencies(godogPkg, deps); err != nil {
			return err
		}

		for pkgName, pkgObj := range deps {
			if i := strings.LastIndex(pkgName, "vendor/"); i != -1 {
				name := pkgName[i+7:]
				fmt.Fprintf(in, "importmap %s=%s\n", name, pkgName)
			}
			fmt.Fprintf(in, "packagefile %s=%s\n", pkgName, pkgObj)
		}
	}
	in.Close()

	args = append(args, "-pack", testmain)
	cmd = exec.Command(compiler, args...)
	cmd.Env = os.Environ()
	out, err = cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("failed to compile testmain package: %v - output: %s", err, string(out))
	}

	// link test suite executable
	args = []string{
		"-o", bin,
		"-importcfg", cfg,
		"-buildmode=exe",
	}
	args = append(args, testMainPkgOut)
	cmd = exec.Command(linker, args...)
	cmd.Env = os.Environ()

	out, err = cmd.CombinedOutput()
	if err != nil {
		msg := `failed to link test executable:
	reason: %s
	command: %s`
		return fmt.Errorf(msg, string(out), linker+" '"+strings.Join(args, "' '")+"'")
	}

	return nil
}

func locatePackage(name string) (*build.Package, error) {
	for _, p := range build.Default.SrcDirs() {
		abs, err := filepath.Abs(filepath.Join(p, name))
		if err != nil {
			continue
		}
		pkg, err := build.ImportDir(abs, 0)
		if err != nil {
			continue
		}
		return pkg, nil
	}

	// search vendor paths
	dir, err := filepath.Abs(".")
	if err != nil {
		return nil, err
	}

	for _, gopath := range gopaths {
		gopath = filepath.Join(gopath, "src")
		for strings.HasPrefix(dir, gopath) && dir != gopath {
			pkg, err := build.ImportDir(filepath.Join(dir, "vendor", name), 0)
			if err != nil {
				dir = filepath.Dir(dir)
				continue
			}
			return pkg, nil
		}
	}

	return nil, fmt.Errorf("failed to find %s package in any of:\n%s", name, strings.Join(build.Default.SrcDirs(), "\n"))
}

func importPackage(dir string) *build.Package {
	pkg, _ := build.ImportDir(dir, 0)

	// normalize import path for local import packages
	// taken from go source code
	// see: https://github.com/golang/go/blob/go1.7rc5/src/cmd/go/pkg.go#L279
	if pkg != nil && pkg.ImportPath == "." {
		pkg.ImportPath = path.Join("_", strings.Map(makeImportValid, filepath.ToSlash(dir)))
	}

	return pkg
}

// from go src
func makeImportValid(r rune) rune {
	// Should match Go spec, compilers, and ../../go/parser/parser.go:/isValidImport.
	const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
	if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
		return '_'
	}
	return r
}

func uniqStringList(strs []string) (unique []string) {
	uniq := make(map[string]void, len(strs))
	for _, s := range strs {
		if _, ok := uniq[s]; !ok {
			uniq[s] = void{}
			unique = append(unique, s)
		}
	}
	return
}

// buildTestMain if given package is valid
// it scans test files for contexts
// and produces a testmain source code.
func buildTestMain(pkg *build.Package) ([]byte, bool, error) {
	var contexts []string
	var importPath string
	name := "main"
	if nil != pkg {
		ctxs, err := processPackageTestFiles(
			pkg.TestGoFiles,
			pkg.XTestGoFiles,
		)
		if err != nil {
			return nil, false, err
		}
		contexts = ctxs
		importPath = pkg.ImportPath
		name = pkg.Name
	}

	data := struct {
		Name       string
		Contexts   []string
		ImportPath string
	}{name, contexts, importPath}

	var buf bytes.Buffer
	if err := runnerTemplate.Execute(&buf, data); err != nil {
		return nil, len(contexts) > 0, err
	}
	return buf.Bytes(), len(contexts) > 0, nil
}

// processPackageTestFiles runs through ast of each test
// file pack and looks for godog suite contexts to register
// on run
func processPackageTestFiles(packs ...[]string) ([]string, error) {
	var ctxs []string
	fset := token.NewFileSet()
	for _, pack := range packs {
		for _, testFile := range pack {
			node, err := parser.ParseFile(fset, testFile, nil, 0)
			if err != nil {
				return ctxs, err
			}

			ctxs = append(ctxs, astContexts(node)...)
		}
	}
	var failed []string
	for _, ctx := range ctxs {
		runes := []rune(ctx)
		if unicode.IsLower(runes[0]) {
			expected := append([]rune{unicode.ToUpper(runes[0])}, runes[1:]...)
			failed = append(failed, fmt.Sprintf("%s - should be: %s", ctx, string(expected)))
		}
	}
	if len(failed) > 0 {
		return ctxs, fmt.Errorf("godog contexts must be exported:\n\t%s", strings.Join(failed, "\n\t"))
	}
	return ctxs, nil
}

func findToolDir() string {
	if out, err := exec.Command("go", "env", "GOTOOLDIR").Output(); err != nil {
		return filepath.Clean(strings.TrimSpace(string(out)))
	}
	return filepath.Clean(build.ToolDir)
}

func dependencies(pkg *build.Package, visited map[string]string) error {
	visited[pkg.ImportPath] = pkg.PkgObj
	for _, name := range pkg.Imports {
		if _, ok := visited[name]; ok {
			continue
		}

		next, err := locatePackage(name)
		if err != nil {
			return err
		}

		visited[name] = pkg.PkgObj
		if err := dependencies(next, visited); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>support godog package in vendor directory<commit_after>// +build go1.10

package godog

import (
	"bytes"
	"fmt"
	"go/build"
	"go/parser"
	"go/token"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"text/template"
	"time"
	"unicode"
)

var tooldir = findToolDir()
var compiler = filepath.Join(tooldir, "compile")
var linker = filepath.Join(tooldir, "link")
var gopaths = filepath.SplitList(build.Default.GOPATH)
var goarch = build.Default.GOARCH
var goroot = build.Default.GOROOT
var goos = build.Default.GOOS

var godogImportPath = "github.com/DATA-DOG/godog"
var runnerTemplate = template.Must(template.New("testmain").Parse(`package main

import (
	"github.com/DATA-DOG/godog"
	{{if .Contexts}}_test "{{.ImportPath}}"{{end}}
	"os"
)

func main() {
	status := godog.Run("{{ .Name }}", func (suite *godog.Suite) {
		os.Setenv("GODOG_TESTED_PACKAGE", "{{.ImportPath}}")
		{{range .Contexts}}
			_test.{{ . }}(suite)
		{{end}}
	})
	os.Exit(status)
}`))

// Build creates a test package like go test command at given target path.
// If there are no go files in tested directory, then
// it simply builds a godog executable to scan features.
//
// If there are go test files, it first builds a test
// package with standard go test command.
//
// Finally it generates godog suite executable which
// registers exported godog contexts from the test files
// of tested package.
//
// Returns the path to generated executable
func Build(bin string) error {
	abs, err := filepath.Abs(".")
	if err != nil {
		return err
	}

	// we allow package to be nil, if godog is run only when
	// there is a feature file in empty directory
	pkg := importPackage(abs)
	src, anyContexts, err := buildTestMain(pkg)
	if err != nil {
		return err
	}

	workdir := fmt.Sprintf(filepath.Join("%s", "godog-%d"), os.TempDir(), time.Now().UnixNano())
	testdir := workdir

	// if none of test files exist, or there are no contexts found
	// we will skip test package compilation, since it is useless
	if anyContexts {
		// first of all compile test package dependencies
		// that will save us many compilations for dependencies
		// go does it better
		out, err := exec.Command("go", "test", "-i").CombinedOutput()
		if err != nil {
			return fmt.Errorf("failed to compile package: %s, reason: %v, output: %s", pkg.Name, err, string(out))
		}

		// builds and compile the tested package.
		// generated test executable will be removed
		// since we do not need it for godog suite.
		// we also print back the temp WORK directory
		// go has built. We will reuse it for our suite workdir.
		out, err = exec.Command("go", "test", "-c", "-work", "-o", "/dev/null").CombinedOutput()
		if err != nil {
			return fmt.Errorf("failed to compile tested package: %s, reason: %v, output: %s", pkg.Name, err, string(out))
		}

		// extract go-build temporary directory as our workdir
		workdir = strings.TrimSpace(string(out))
		if !strings.HasPrefix(workdir, "WORK=") {
			return fmt.Errorf("expected WORK dir path, but got: %s", workdir)
		}
		workdir = strings.Replace(workdir, "WORK=", "", 1)
		testdir = filepath.Join(workdir, "b001")
	} else {
		// still need to create temporary workdir
		if err = os.MkdirAll(testdir, 0755); err != nil {
			return err
		}
	}
	defer os.RemoveAll(workdir)

	// replace _testmain.go file with our own
	testmain := filepath.Join(testdir, "_testmain.go")
	err = ioutil.WriteFile(testmain, src, 0644)
	if err != nil {
		return err
	}

	// godog library may not be imported in tested package
	// but we need it for our testmain package.
	// So we look it up in available source paths
	// including vendor directory, supported since 1.5.
	godogPkg, err := locatePackage(godogImportPath)
	if err != nil {
		return err
	}

	// make sure godog package archive is installed, gherkin
	// will be installed as dependency of godog
	cmd := exec.Command("go", "install", "-i", godogPkg.ImportPath)
	cmd.Env = os.Environ()
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("failed to install godog package: %s, reason: %v", string(out), err)
	}

	// compile godog testmain package archive
	// we do not depend on CGO so a lot of checks are not necessary
	testMainPkgOut := filepath.Join(testdir, "main.a")
	args := []string{
		"-o", testMainPkgOut,
		"-p", "main",
		"-complete",
	}

	var in *os.File
	cfg := filepath.Join(testdir, "importcfg.link")
	args = append(args, "-importcfg", cfg)
	if _, err := os.Stat(cfg); err == nil {
		// there were go sources
		in, err = os.OpenFile(cfg, os.O_APPEND|os.O_WRONLY, 0600)
		if err != nil {
			return err
		}
	} else {
		// there were no go sources in the directory
		// so we need to build all dependency tree ourselves
		in, err = os.Create(cfg)
		if err != nil {
			return err
		}
		fmt.Fprintln(in, "# import config")

		deps := make(map[string]string)
		if err := dependencies(godogPkg, deps); err != nil {
			return err
		}

		for pkgName, pkgObj := range deps {
			if i := strings.LastIndex(pkgName, "vendor/"); i != -1 {
				name := pkgName[i+7:]
				fmt.Fprintf(in, "importmap %s=%s\n", name, pkgName)
			}
			fmt.Fprintf(in, "packagefile %s=%s\n", pkgName, pkgObj)
		}
	}
	in.Close()

	args = append(args, "-pack", testmain)
	cmd = exec.Command(compiler, args...)
	cmd.Env = os.Environ()
	out, err = cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("failed to compile testmain package: %v - output: %s", err, string(out))
	}

	// link test suite executable
	args = []string{
		"-o", bin,
		"-importcfg", cfg,
		"-buildmode=exe",
	}
	args = append(args, testMainPkgOut)
	cmd = exec.Command(linker, args...)
	cmd.Env = os.Environ()

	// in case if build is without contexts, need to remove import maps
	if testdir == workdir {
		data, err := ioutil.ReadFile(cfg)
		if err != nil {
			return err
		}

		lines := strings.Split(string(data), "\n")
		var fixed []string
		for _, line := range lines {
			if strings.Index(line, "importmap") == 0 {
				continue
			}
			fixed = append(fixed, line)
		}
		if err := ioutil.WriteFile(cfg, []byte(strings.Join(fixed, "\n")), 0600); err != nil {
			return err
		}
	}

	out, err = cmd.CombinedOutput()
	if err != nil {
		msg := `failed to link test executable:
	reason: %s
	command: %s`
		return fmt.Errorf(msg, string(out), linker+" '"+strings.Join(args, "' '")+"'")
	}

	return nil
}

func locatePackage(name string) (*build.Package, error) {
	for _, p := range build.Default.SrcDirs() {
		abs, err := filepath.Abs(filepath.Join(p, name))
		if err != nil {
			continue
		}
		pkg, err := build.ImportDir(abs, 0)
		if err != nil {
			continue
		}
		return pkg, nil
	}

	// search vendor paths
	dir, err := filepath.Abs(".")
	if err != nil {
		return nil, err
	}

	for _, gopath := range gopaths {
		gopath = filepath.Join(gopath, "src")
		for strings.HasPrefix(dir, gopath) && dir != gopath {
			pkg, err := build.ImportDir(filepath.Join(dir, "vendor", name), 0)
			if err != nil {
				dir = filepath.Dir(dir)
				continue
			}
			return pkg, nil
		}
	}

	return nil, fmt.Errorf("failed to find %s package in any of:\n%s", name, strings.Join(build.Default.SrcDirs(), "\n"))
}

func importPackage(dir string) *build.Package {
	pkg, _ := build.ImportDir(dir, 0)

	// normalize import path for local import packages
	// taken from go source code
	// see: https://github.com/golang/go/blob/go1.7rc5/src/cmd/go/pkg.go#L279
	if pkg != nil && pkg.ImportPath == "." {
		pkg.ImportPath = path.Join("_", strings.Map(makeImportValid, filepath.ToSlash(dir)))
	}

	return pkg
}

// from go src
func makeImportValid(r rune) rune {
	// Should match Go spec, compilers, and ../../go/parser/parser.go:/isValidImport.
	const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
	if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
		return '_'
	}
	return r
}

func uniqStringList(strs []string) (unique []string) {
	uniq := make(map[string]void, len(strs))
	for _, s := range strs {
		if _, ok := uniq[s]; !ok {
			uniq[s] = void{}
			unique = append(unique, s)
		}
	}
	return
}

// buildTestMain if given package is valid
// it scans test files for contexts
// and produces a testmain source code.
func buildTestMain(pkg *build.Package) ([]byte, bool, error) {
	var contexts []string
	var importPath string
	name := "main"
	if nil != pkg {
		ctxs, err := processPackageTestFiles(
			pkg.TestGoFiles,
			pkg.XTestGoFiles,
		)
		if err != nil {
			return nil, false, err
		}
		contexts = ctxs
		importPath = pkg.ImportPath
		name = pkg.Name
	}

	data := struct {
		Name       string
		Contexts   []string
		ImportPath string
	}{name, contexts, importPath}

	var buf bytes.Buffer
	if err := runnerTemplate.Execute(&buf, data); err != nil {
		return nil, len(contexts) > 0, err
	}
	return buf.Bytes(), len(contexts) > 0, nil
}

// processPackageTestFiles runs through ast of each test
// file pack and looks for godog suite contexts to register
// on run
func processPackageTestFiles(packs ...[]string) ([]string, error) {
	var ctxs []string
	fset := token.NewFileSet()
	for _, pack := range packs {
		for _, testFile := range pack {
			node, err := parser.ParseFile(fset, testFile, nil, 0)
			if err != nil {
				return ctxs, err
			}

			ctxs = append(ctxs, astContexts(node)...)
		}
	}
	var failed []string
	for _, ctx := range ctxs {
		runes := []rune(ctx)
		if unicode.IsLower(runes[0]) {
			expected := append([]rune{unicode.ToUpper(runes[0])}, runes[1:]...)
			failed = append(failed, fmt.Sprintf("%s - should be: %s", ctx, string(expected)))
		}
	}
	if len(failed) > 0 {
		return ctxs, fmt.Errorf("godog contexts must be exported:\n\t%s", strings.Join(failed, "\n\t"))
	}
	return ctxs, nil
}

func findToolDir() string {
	if out, err := exec.Command("go", "env", "GOTOOLDIR").Output(); err != nil {
		return filepath.Clean(strings.TrimSpace(string(out)))
	}
	return filepath.Clean(build.ToolDir)
}

func dependencies(pkg *build.Package, visited map[string]string) error {
	visited[pkg.ImportPath] = pkg.PkgObj
	for _, name := range pkg.Imports {
		if _, ok := visited[name]; ok {
			continue
		}

		next, err := locatePackage(name)
		if err != nil {
			return err
		}

		visited[name] = pkg.PkgObj
		if err := dependencies(next, visited); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package commands

import (
	"strings"

	"github.com/bwmarrin/discordgo"
)

var regions = map[string]string{
	"africa":    "465387147629953034",
	"america":   "465386843706359840",
	"asia":      "465386826006528001",
	"australia": "465387169888862230",
	"europe":    "465386794914152448",
}

// Region sets the specific region role for the user.
func Region(s *discordgo.Session, msg *discordgo.MessageCreate) bool {
	if !strings.HasPrefix(msg.Content, "!region ") {
		return false
	}

	region := strings.ToLower(msg.Content[len("!region "):])

	// Check to make sure the region is in the region map
	if _, ok := regions[region]; !ok {
		s.ChannelMessageSend(msg.ChannelID, "This is not a region!")
		return true
	}

	// Get the channel, this is used to get the guild ID
	c, _ := s.Channel(msg.ChannelID)

	// Check to see if user already has a region role
	user, _ := s.GuildMember(c.GuildID, msg.Author.ID)

	for _, role := range user.Roles {
		match := false

		// We also need to loop through our map because Discord doesn't
		// return roles as names but rather IDs.
		for _, id := range regions {
			if role == id {
				// Remove the role and set match to true
				s.GuildMemberRoleRemove(c.GuildID, msg.Author.ID, id)
				match = true
				break
			}
		}

		if match {
			break
		}
	}

	// Try to set the role
	err := s.GuildMemberRoleAdd(c.GuildID, msg.Author.ID, regions[region])

	if err != nil {
		s.ChannelMessageSend(msg.ChannelID, "The region role could not be set!")
		return true
	}

	s.ChannelMessageSend(msg.ChannelID, "Set region "+region+" for your account!")
	return true
}
<commit_msg>Improved error handling<commit_after>package commands

import (
	"strings"

	"github.com/bwmarrin/discordgo"
)

var regions = map[string]string{
	"africa":    "465387147629953034",
	"america":   "465386843706359840",
	"asia":      "465386826006528001",
	"australia": "465387169888862230",
	"europe":    "465386794914152448",
}

// Region sets the specific region role for the user.
func Region(s *discordgo.Session, msg *discordgo.MessageCreate) bool {
	if !strings.HasPrefix(msg.Content, "!region ") {
		return false
	}

	region := strings.ToLower(msg.Content[len("!region "):])

	// Check to make sure the region is in the region map
	if _, ok := regions[region]; !ok {
		s.ChannelMessageSend(msg.ChannelID, "This is not a region!")
		return true
	}

	// Check to see if user already has a region role
	user, err := s.GuildMember(guildID, msg.Author.ID)

	if err != nil {
		s.ChannelMessageSend(msg.ChannelID, "Error: User not found")
		return true
	}

	for _, role := range user.Roles {
		match := false

		// We also need to loop through our map because Discord doesn't
		// return roles as names but rather IDs.
		for _, id := range regions {
			if role == id {
				// Remove the role and set match to true
				s.GuildMemberRoleRemove(guildID, msg.Author.ID, id)
				match = true
				break
			}
		}

		if match {
			break
		}
	}

	// Try to set the role
	err = s.GuildMemberRoleAdd(guildID, msg.Author.ID, regions[region])

	if err != nil {
		s.ChannelMessageSend(msg.ChannelID, "The region role could not be set!")
		return true
	}

	s.ChannelMessageSend(msg.ChannelID, "Set region "+region+" for your account!")
	return true
}
<|endoftext|>


<commit_before>// Copyright 2020 PingCAP, Inc. Licensed under Apache-2.0.

package pdutil

import (
	"context"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"testing"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/pingcap/failpoint"
	"github.com/pingcap/kvproto/pkg/metapb"
	"github.com/pingcap/tidb/store/pdtypes"
	"github.com/pingcap/tidb/util/codec"
	"github.com/stretchr/testify/require"
)

func TestScheduler(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	scheduler := "balance-leader-scheduler"
	mock := func(context.Context, string, string, *http.Client, string, io.Reader) ([]byte, error) {
		return nil, errors.New("failed")
	}
	schedulerPauseCh := make(chan struct{})
	pdController := &PdController{addrs: []string{"", ""}, schedulerPauseCh: schedulerPauseCh}
	// As pdController.Client is nil, (*pdController).Close() can not be called directly.
	defer close(schedulerPauseCh)

	_, err := pdController.pauseSchedulersAndConfigWith(ctx, []string{scheduler}, nil, mock)
	require.EqualError(t, err, "failed")

	go func() {
		<-schedulerPauseCh
	}()
	err = pdController.resumeSchedulerWith(ctx, []string{scheduler}, mock)
	require.NoError(t, err)

	cfg := map[string]interface{}{
		"max-merge-region-keys":       0,
		"max-snapshot":                1,
		"enable-location-replacement": false,
		"max-pending-peer-count":      uint64(16),
	}
	_, err = pdController.pauseSchedulersAndConfigWith(ctx, []string{}, cfg, mock)
	require.Error(t, err)
	require.Regexp(t, "^failed to update PD", err.Error())
	go func() {
		<-schedulerPauseCh
	}()
	err = pdController.resumeSchedulerWith(ctx, []string{scheduler}, mock)
	require.NoError(t, err)

	_, err = pdController.listSchedulersWith(ctx, mock)
	require.EqualError(t, err, "failed")

	mock = func(context.Context, string, string, *http.Client, string, io.Reader) ([]byte, error) {
		return []byte(`["` + scheduler + `"]`), nil
	}

	_, err = pdController.pauseSchedulersAndConfigWith(ctx, []string{scheduler}, cfg, mock)
	require.NoError(t, err)

	// pauseSchedulersAndConfigWith will wait on chan schedulerPauseCh
	err = pdController.resumeSchedulerWith(ctx, []string{scheduler}, mock)
	require.NoError(t, err)

	schedulers, err := pdController.listSchedulersWith(ctx, mock)
	require.NoError(t, err)
	require.Len(t, schedulers, 1)
	require.Equal(t, scheduler, schedulers[0])
}

func TestGetClusterVersion(t *testing.T) {
	pdController := &PdController{addrs: []string{"", ""}} // two endpoints
	counter := 0
	mock := func(context.Context, string, string, *http.Client, string, io.Reader) ([]byte, error) {
		counter++
		if counter <= 1 {
			return nil, errors.New("mock error")
		}
		return []byte(`test`), nil
	}

	ctx := context.Background()
	respString, err := pdController.getClusterVersionWith(ctx, mock)
	require.NoError(t, err)
	require.Equal(t, "test", respString)

	mock = func(context.Context, string, string, *http.Client, string, io.Reader) ([]byte, error) {
		return nil, errors.New("mock error")
	}
	_, err = pdController.getClusterVersionWith(ctx, mock)
	require.Error(t, err)
}

func TestRegionCount(t *testing.T) {
	regions := &pdtypes.RegionTree{}
	regions.SetRegion(pdtypes.NewRegionInfo(&metapb.Region{
		Id:          1,
		StartKey:    codec.EncodeBytes(nil, []byte{1, 1}),
		EndKey:      codec.EncodeBytes(nil, []byte{1, 3}),
		RegionEpoch: &metapb.RegionEpoch{},
	}, nil))
	regions.SetRegion(pdtypes.NewRegionInfo(&metapb.Region{
		Id:          2,
		StartKey:    codec.EncodeBytes(nil, []byte{1, 3}),
		EndKey:      codec.EncodeBytes(nil, []byte{1, 5}),
		RegionEpoch: &metapb.RegionEpoch{},
	}, nil))
	regions.SetRegion(pdtypes.NewRegionInfo(&metapb.Region{
		Id:          3,
		StartKey:    codec.EncodeBytes(nil, []byte{2, 3}),
		EndKey:      codec.EncodeBytes(nil, []byte{3, 4}),
		RegionEpoch: &metapb.RegionEpoch{},
	}, nil))
	require.Equal(t, 3, len(regions.Regions))

	mock := func(
		_ context.Context, addr string, prefix string, _ *http.Client, _ string, _ io.Reader,
	) ([]byte, error) {
		query := fmt.Sprintf("%s/%s", addr, prefix)
		u, e := url.Parse(query)
		require.NoError(t, e, query)
		start := u.Query().Get("start_key")
		end := u.Query().Get("end_key")
		t.Log(hex.EncodeToString([]byte(start)))
		t.Log(hex.EncodeToString([]byte(end)))
		scanRegions := regions.ScanRange([]byte(start), []byte(end), 0)
		stats := pdtypes.RegionStats{Count: len(scanRegions)}
		ret, err := json.Marshal(stats)
		require.NoError(t, err)
		return ret, nil
	}

	pdController := &PdController{addrs: []string{"http://mock"}}
	ctx := context.Background()
	resp, err := pdController.getRegionCountWith(ctx, mock, []byte{}, []byte{})
	require.NoError(t, err)
	require.Equal(t, 3, resp)

	resp, err = pdController.getRegionCountWith(ctx, mock, []byte{0}, []byte{0xff})
	require.NoError(t, err)
	require.Equal(t, 3, resp)

	resp, err = pdController.getRegionCountWith(ctx, mock, []byte{1, 2}, []byte{1, 4})
	require.NoError(t, err)
	require.Equal(t, 2, resp)
}

func TestPDVersion(t *testing.T) {
	v := []byte("\"v4.1.0-alpha1\"\n")
	r := parseVersion(v)
	expectV := semver.New("4.1.0-alpha1")
	require.Equal(t, expectV.Major, r.Major)
	require.Equal(t, expectV.Minor, r.Minor)
	require.Equal(t, expectV.PreRelease, r.PreRelease)
}

func TestPDRequestRetry(t *testing.T) {
	ctx := context.Background()

	require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/br/pkg/pdutil/FastRetry", "return(true)"))
	defer func() {
		require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/br/pkg/pdutil/FastRetry"))
	}()

	count := 0
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		count++
		if count <= pdRequestRetryTime-1 {
			w.WriteHeader(http.StatusGatewayTimeout)
			return
		}
		w.WriteHeader(http.StatusOK)
	}))
	cli := http.DefaultClient
	taddr := ts.URL
	_, reqErr := pdRequest(ctx, taddr, "", cli, http.MethodGet, nil)
	require.NoError(t, reqErr)
	ts.Close()
	count = 0
	ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		count++
		if count <= pdRequestRetryTime+1 {
			w.WriteHeader(http.StatusGatewayTimeout)
			return
		}
		w.WriteHeader(http.StatusOK)
	}))
	defer ts.Close()
	taddr = ts.URL
	_, reqErr = pdRequest(ctx, taddr, "", cli, http.MethodGet, nil)
	require.Error(t, reqErr)
}

func TestStoreInfo(t *testing.T) {
	storeInfo := pdtypes.StoreInfo{
		Status: &pdtypes.StoreStatus{
			Capacity:  pdtypes.ByteSize(1024),
			Available: pdtypes.ByteSize(1024),
		},
		Store: &pdtypes.MetaStore{
			StateName: "Tombstone",
		},
	}
	mock := func(
		_ context.Context, addr string, prefix string, _ *http.Client, _ string, _ io.Reader,
	) ([]byte, error) {
		query := fmt.Sprintf("%s/%s", addr, prefix)
		require.Equal(t, "http://mock/pd/api/v1/store/1", query)
		ret, err := json.Marshal(storeInfo)
		require.NoError(t, err)
		return ret, nil
	}

	pdController := &PdController{addrs: []string{"http://mock"}}
	ctx := context.Background()
	resp, err := pdController.getStoreInfoWith(ctx, mock, 1)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.NotNil(t, resp.Status)
	require.Equal(t, "Tombstone", resp.Store.StateName)
	require.Equal(t, uint64(1024), uint64(resp.Status.Available))
}

func TestPauseSchedulersByKeyRange(t *testing.T) {
	const ttl = time.Second

	labelExpires := make(map[string]time.Time)

	httpSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method == http.MethodDelete {
			ruleID := strings.TrimPrefix(r.URL.Path, "/"+regionLabelPrefix+"/")
			print(ruleID)
			delete(labelExpires, ruleID)
			return
		}
		var labelRule LabelRule
		err := json.NewDecoder(r.Body).Decode(&labelRule)
		require.NoError(t, err)
		require.Len(t, labelRule.Labels, 1)
		regionLabel := labelRule.Labels[0]
		require.Equal(t, "schedule", regionLabel.Key)
		require.Equal(t, "deny", regionLabel.Value)
		reqTTL, err := time.ParseDuration(regionLabel.TTL)
		require.NoError(t, err)
		if reqTTL == 0 {
			delete(labelExpires, labelRule.ID)
		} else {
			require.Equal(t, ttl, reqTTL)
			if expire, ok := labelExpires[labelRule.ID]; ok {
				require.True(t, expire.After(time.Now()), "should not expire before now")
			}
			labelExpires[labelRule.ID] = time.Now().Add(ttl)
		}
	}))
	defer httpSrv.Close()

	pdController := &PdController{addrs: []string{httpSrv.URL}, cli: http.DefaultClient}
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	done, err := pdController.pauseSchedulerByKeyRangeWithTTL(ctx, []byte{0, 0, 0, 0}, []byte{0xff, 0xff, 0xff, 0xff}, ttl)
	require.NoError(t, err)
	time.Sleep(ttl * 3)
	cancel()
	<-done
	require.Len(t, labelExpires, 0)
}
<commit_msg>pdutil: fix unstable test TestPauseSchedulersByKeyRange (#35949)<commit_after>// Copyright 2020 PingCAP, Inc. Licensed under Apache-2.0.

package pdutil

import (
	"context"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/pingcap/failpoint"
	"github.com/pingcap/kvproto/pkg/metapb"
	"github.com/pingcap/tidb/store/pdtypes"
	"github.com/pingcap/tidb/util/codec"
	"github.com/stretchr/testify/require"
)

func TestScheduler(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	scheduler := "balance-leader-scheduler"
	mock := func(context.Context, string, string, *http.Client, string, io.Reader) ([]byte, error) {
		return nil, errors.New("failed")
	}
	schedulerPauseCh := make(chan struct{})
	pdController := &PdController{addrs: []string{"", ""}, schedulerPauseCh: schedulerPauseCh}
	// As pdController.Client is nil, (*pdController).Close() can not be called directly.
	defer close(schedulerPauseCh)

	_, err := pdController.pauseSchedulersAndConfigWith(ctx, []string{scheduler}, nil, mock)
	require.EqualError(t, err, "failed")

	go func() {
		<-schedulerPauseCh
	}()
	err = pdController.resumeSchedulerWith(ctx, []string{scheduler}, mock)
	require.NoError(t, err)

	cfg := map[string]interface{}{
		"max-merge-region-keys":       0,
		"max-snapshot":                1,
		"enable-location-replacement": false,
		"max-pending-peer-count":      uint64(16),
	}
	_, err = pdController.pauseSchedulersAndConfigWith(ctx, []string{}, cfg, mock)
	require.Error(t, err)
	require.Regexp(t, "^failed to update PD", err.Error())
	go func() {
		<-schedulerPauseCh
	}()
	err = pdController.resumeSchedulerWith(ctx, []string{scheduler}, mock)
	require.NoError(t, err)

	_, err = pdController.listSchedulersWith(ctx, mock)
	require.EqualError(t, err, "failed")

	mock = func(context.Context, string, string, *http.Client, string, io.Reader) ([]byte, error) {
		return []byte(`["` + scheduler + `"]`), nil
	}

	_, err = pdController.pauseSchedulersAndConfigWith(ctx, []string{scheduler}, cfg, mock)
	require.NoError(t, err)

	// pauseSchedulersAndConfigWith will wait on chan schedulerPauseCh
	err = pdController.resumeSchedulerWith(ctx, []string{scheduler}, mock)
	require.NoError(t, err)

	schedulers, err := pdController.listSchedulersWith(ctx, mock)
	require.NoError(t, err)
	require.Len(t, schedulers, 1)
	require.Equal(t, scheduler, schedulers[0])
}

func TestGetClusterVersion(t *testing.T) {
	pdController := &PdController{addrs: []string{"", ""}} // two endpoints
	counter := 0
	mock := func(context.Context, string, string, *http.Client, string, io.Reader) ([]byte, error) {
		counter++
		if counter <= 1 {
			return nil, errors.New("mock error")
		}
		return []byte(`test`), nil
	}

	ctx := context.Background()
	respString, err := pdController.getClusterVersionWith(ctx, mock)
	require.NoError(t, err)
	require.Equal(t, "test", respString)

	mock = func(context.Context, string, string, *http.Client, string, io.Reader) ([]byte, error) {
		return nil, errors.New("mock error")
	}
	_, err = pdController.getClusterVersionWith(ctx, mock)
	require.Error(t, err)
}

func TestRegionCount(t *testing.T) {
	regions := &pdtypes.RegionTree{}
	regions.SetRegion(pdtypes.NewRegionInfo(&metapb.Region{
		Id:          1,
		StartKey:    codec.EncodeBytes(nil, []byte{1, 1}),
		EndKey:      codec.EncodeBytes(nil, []byte{1, 3}),
		RegionEpoch: &metapb.RegionEpoch{},
	}, nil))
	regions.SetRegion(pdtypes.NewRegionInfo(&metapb.Region{
		Id:          2,
		StartKey:    codec.EncodeBytes(nil, []byte{1, 3}),
		EndKey:      codec.EncodeBytes(nil, []byte{1, 5}),
		RegionEpoch: &metapb.RegionEpoch{},
	}, nil))
	regions.SetRegion(pdtypes.NewRegionInfo(&metapb.Region{
		Id:          3,
		StartKey:    codec.EncodeBytes(nil, []byte{2, 3}),
		EndKey:      codec.EncodeBytes(nil, []byte{3, 4}),
		RegionEpoch: &metapb.RegionEpoch{},
	}, nil))
	require.Equal(t, 3, len(regions.Regions))

	mock := func(
		_ context.Context, addr string, prefix string, _ *http.Client, _ string, _ io.Reader,
	) ([]byte, error) {
		query := fmt.Sprintf("%s/%s", addr, prefix)
		u, e := url.Parse(query)
		require.NoError(t, e, query)
		start := u.Query().Get("start_key")
		end := u.Query().Get("end_key")
		t.Log(hex.EncodeToString([]byte(start)))
		t.Log(hex.EncodeToString([]byte(end)))
		scanRegions := regions.ScanRange([]byte(start), []byte(end), 0)
		stats := pdtypes.RegionStats{Count: len(scanRegions)}
		ret, err := json.Marshal(stats)
		require.NoError(t, err)
		return ret, nil
	}

	pdController := &PdController{addrs: []string{"http://mock"}}
	ctx := context.Background()
	resp, err := pdController.getRegionCountWith(ctx, mock, []byte{}, []byte{})
	require.NoError(t, err)
	require.Equal(t, 3, resp)

	resp, err = pdController.getRegionCountWith(ctx, mock, []byte{0}, []byte{0xff})
	require.NoError(t, err)
	require.Equal(t, 3, resp)

	resp, err = pdController.getRegionCountWith(ctx, mock, []byte{1, 2}, []byte{1, 4})
	require.NoError(t, err)
	require.Equal(t, 2, resp)
}

func TestPDVersion(t *testing.T) {
	v := []byte("\"v4.1.0-alpha1\"\n")
	r := parseVersion(v)
	expectV := semver.New("4.1.0-alpha1")
	require.Equal(t, expectV.Major, r.Major)
	require.Equal(t, expectV.Minor, r.Minor)
	require.Equal(t, expectV.PreRelease, r.PreRelease)
}

func TestPDRequestRetry(t *testing.T) {
	ctx := context.Background()

	require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/br/pkg/pdutil/FastRetry", "return(true)"))
	defer func() {
		require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/br/pkg/pdutil/FastRetry"))
	}()

	count := 0
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		count++
		if count <= pdRequestRetryTime-1 {
			w.WriteHeader(http.StatusGatewayTimeout)
			return
		}
		w.WriteHeader(http.StatusOK)
	}))
	cli := http.DefaultClient
	taddr := ts.URL
	_, reqErr := pdRequest(ctx, taddr, "", cli, http.MethodGet, nil)
	require.NoError(t, reqErr)
	ts.Close()
	count = 0
	ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		count++
		if count <= pdRequestRetryTime+1 {
			w.WriteHeader(http.StatusGatewayTimeout)
			return
		}
		w.WriteHeader(http.StatusOK)
	}))
	defer ts.Close()
	taddr = ts.URL
	_, reqErr = pdRequest(ctx, taddr, "", cli, http.MethodGet, nil)
	require.Error(t, reqErr)
}

func TestStoreInfo(t *testing.T) {
	storeInfo := pdtypes.StoreInfo{
		Status: &pdtypes.StoreStatus{
			Capacity:  pdtypes.ByteSize(1024),
			Available: pdtypes.ByteSize(1024),
		},
		Store: &pdtypes.MetaStore{
			StateName: "Tombstone",
		},
	}
	mock := func(
		_ context.Context, addr string, prefix string, _ *http.Client, _ string, _ io.Reader,
	) ([]byte, error) {
		query := fmt.Sprintf("%s/%s", addr, prefix)
		require.Equal(t, "http://mock/pd/api/v1/store/1", query)
		ret, err := json.Marshal(storeInfo)
		require.NoError(t, err)
		return ret, nil
	}

	pdController := &PdController{addrs: []string{"http://mock"}}
	ctx := context.Background()
	resp, err := pdController.getStoreInfoWith(ctx, mock, 1)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.NotNil(t, resp.Status)
	require.Equal(t, "Tombstone", resp.Store.StateName)
	require.Equal(t, uint64(1024), uint64(resp.Status.Available))
}

func TestPauseSchedulersByKeyRange(t *testing.T) {
	const ttl = time.Second

	labelExpires := make(map[string]time.Time)

	var (
		mu      sync.Mutex
		deleted bool
	)

	httpSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		mu.Lock()
		defer mu.Unlock()
		if deleted {
			return
		}
		if r.Method == http.MethodDelete {
			ruleID := strings.TrimPrefix(r.URL.Path, "/"+regionLabelPrefix+"/")
			delete(labelExpires, ruleID)
			deleted = true
			return
		}
		var labelRule LabelRule
		err := json.NewDecoder(r.Body).Decode(&labelRule)
		require.NoError(t, err)
		require.Len(t, labelRule.Labels, 1)
		regionLabel := labelRule.Labels[0]
		require.Equal(t, "schedule", regionLabel.Key)
		require.Equal(t, "deny", regionLabel.Value)
		reqTTL, err := time.ParseDuration(regionLabel.TTL)
		require.NoError(t, err)
		if reqTTL == 0 {
			delete(labelExpires, labelRule.ID)
		} else {
			require.Equal(t, ttl, reqTTL)
			if expire, ok := labelExpires[labelRule.ID]; ok {
				require.True(t, expire.After(time.Now()), "should not expire before now")
			}
			labelExpires[labelRule.ID] = time.Now().Add(ttl)
		}
	}))
	defer httpSrv.Close()

	pdController := &PdController{addrs: []string{httpSrv.URL}, cli: http.DefaultClient}
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	done, err := pdController.pauseSchedulerByKeyRangeWithTTL(ctx, []byte{0, 0, 0, 0}, []byte{0xff, 0xff, 0xff, 0xff}, ttl)
	require.NoError(t, err)
	time.Sleep(ttl * 3)
	cancel()
	<-done
	require.Len(t, labelExpires, 0)
}
<|endoftext|>


<commit_before>package slave

import (
	"fmt"
	"github.com/KIT-MAMID/mamid/msp"
	"golang.org/x/sys/unix"
	"os/exec"
	"strings"
)

type ProcessManager struct {
	killChan         chan msp.PortNumber
	command          string
	dataDir          string
	runningProcesses map[msp.PortNumber]*exec.Cmd
}

func NewProcessManager(command string, dataDir string) *ProcessManager {
	return &ProcessManager{
		killChan:         make(chan msp.PortNumber),
		command:          command,
		dataDir:          dataDir,
		runningProcesses: make(map[msp.PortNumber]*exec.Cmd),
	}
}

func (p *ProcessManager) Run() {
	go func() {
		for {
			port := <-p.killChan
			delete(p.runningProcesses, port)
		}
	}()
}

func (p *ProcessManager) SpawnProcess(m msp.Mongod) error {
	dbDir := fmt.Sprintf("%s/%s/%s", p.dataDir, DataDBDir, m.ReplicaSetName)
	if err := unix.Access(dbDir, unix.R_OK|unix.W_OK|unix.X_OK); err != nil {
		if err := unix.Mkdir(dbDir, 0700); err != nil {
			panic(fmt.Sprintf("Could not create a readable and writable directory at %s", dbDir))
		}
	}

	escName := strings.Replace(m.ReplicaSetName, "'", "'\\''", -1)
	sh := fmt.Sprintf("/usr/bin/env %s --dbpath '%s/%s/%s' --port %d --replSet '%s'", p.command, p.dataDir, DataDBDir, escName, m.Port, escName)
	cmd := exec.Command("/bin/sh", "-c", sh)
	err := cmd.Start()
	if err != nil {
		return err
	}

	go func() {
		cmd.Process.Wait()
		p.killChan <- m.Port
	}()

	p.runningProcesses[m.Port] = cmd
	return nil
}

func (p *ProcessManager) RunningProcesses() []msp.PortNumber {
	ports := make([]msp.PortNumber, 0, len(p.runningProcesses))
	for port := range p.runningProcesses {
		ports = append(ports, port)
	}
	return ports
}

func (p *ProcessManager) KillProcess(port msp.PortNumber) error {
	if cmd, exists := p.runningProcesses[port]; exists {
		delete(p.runningProcesses, port)
		return cmd.Process.Kill()
	}
	return nil
}

// killProcess is destructive. Even when there was an error (already killed, stuck state, permissions lost), we do not care. The error is purely informational that _something_ went wrong.
// This function is to be used for complete clean restart/shutdown only.
func (p *ProcessManager) KillProcesses() error {
	var err error = nil
	for _, cmd := range p.runningProcesses {
		curErr := cmd.Process.Kill()
		if err == nil {
			err = curErr
		}
	}
	p.runningProcesses = make(map[msp.PortNumber]*exec.Cmd)
	return err
}
<commit_msg>FIX: processmanager: do not spawn mongods in shell<commit_after>package slave

import (
	"fmt"
	"github.com/KIT-MAMID/mamid/msp"
	"golang.org/x/sys/unix"
	"os/exec"
)

type ProcessManager struct {
	killChan         chan msp.PortNumber
	command          string
	dataDir          string
	runningProcesses map[msp.PortNumber]*exec.Cmd
}

func NewProcessManager(command string, dataDir string) *ProcessManager {
	return &ProcessManager{
		killChan:         make(chan msp.PortNumber),
		command:          command,
		dataDir:          dataDir,
		runningProcesses: make(map[msp.PortNumber]*exec.Cmd),
	}
}

func (p *ProcessManager) Run() {
	go func() {
		for {
			port := <-p.killChan
			delete(p.runningProcesses, port)
		}
	}()
}

func (p *ProcessManager) SpawnProcess(m msp.Mongod) error {
	dbDir := fmt.Sprintf("%s/%s/%s", p.dataDir, DataDBDir, m.ReplicaSetName)
	if err := unix.Access(dbDir, unix.R_OK|unix.W_OK|unix.X_OK); err != nil {
		if err := unix.Mkdir(dbDir, 0700); err != nil {
			panic(fmt.Sprintf("Could not create a readable and writable directory at %s", dbDir))
		}
	}

	cmd := exec.Command(p.command, "--dbpath", dbDir, "--port", fmt.Sprintf("%d", m.Port), "--replSet", m.ReplicaSetName)
	err := cmd.Start()
	if err != nil {
		return err
	}

	go func() {
		cmd.Process.Wait()
		p.killChan <- m.Port
	}()

	p.runningProcesses[m.Port] = cmd
	return nil
}

func (p *ProcessManager) RunningProcesses() []msp.PortNumber {
	ports := make([]msp.PortNumber, 0, len(p.runningProcesses))
	for port := range p.runningProcesses {
		ports = append(ports, port)
	}
	return ports
}

func (p *ProcessManager) KillProcess(port msp.PortNumber) error {
	if cmd, exists := p.runningProcesses[port]; exists {
		delete(p.runningProcesses, port)
		return cmd.Process.Kill()
	}
	return nil
}

// killProcess is destructive. Even when there was an error (already killed, stuck state, permissions lost), we do not care. The error is purely informational that _something_ went wrong.
// This function is to be used for complete clean restart/shutdown only.
func (p *ProcessManager) KillProcesses() error {
	var err error = nil
	for _, cmd := range p.runningProcesses {
		curErr := cmd.Process.Kill()
		if err == nil {
			err = curErr
		}
	}
	p.runningProcesses = make(map[msp.PortNumber]*exec.Cmd)
	return err
}
<|endoftext|>


<commit_before>// +build !windows

package net_test

import (
	"errors"
	"strings"

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

	. "github.com/cloudfoundry/bosh-agent/platform/net"
	fakearp "github.com/cloudfoundry/bosh-agent/platform/net/arp/fakes"
	fakenet "github.com/cloudfoundry/bosh-agent/platform/net/fakes"
	boship "github.com/cloudfoundry/bosh-agent/platform/net/ip"
	fakeip "github.com/cloudfoundry/bosh-agent/platform/net/ip/fakes"
	"github.com/cloudfoundry/bosh-agent/platform/net/netfakes"
	boshsettings "github.com/cloudfoundry/bosh-agent/settings"
	boshlog "github.com/cloudfoundry/bosh-utils/logger"
	fakesys "github.com/cloudfoundry/bosh-utils/system/fakes"
)

var _ = Describe("UbuntuNetManager (IPv6)", func() {
	var (
		fs                            *fakesys.FakeFileSystem
		cmdRunner                     *fakesys.FakeCmdRunner
		ipResolver                    *fakeip.FakeResolver
		addressBroadcaster            *fakearp.FakeAddressBroadcaster
		interfaceAddrsProvider        *fakeip.FakeInterfaceAddressesProvider
		kernelIPv6                    *fakenet.FakeKernelIPv6
		netManager                    UbuntuNetManager
		interfaceConfigurationCreator InterfaceConfigurationCreator
		fakeMACAddressDetector        *netfakes.FakeMACAddressDetector
	)

	stubInterfaces := func(physicalInterfaces map[string]boshsettings.Network) {
		addresses := map[string]string{}
		for iface, networkSettings := range physicalInterfaces {
			addresses[networkSettings.Mac] = iface
		}

		fakeMACAddressDetector.DetectMacAddressesReturns(addresses, nil)
	}

	BeforeEach(func() {
		fs = fakesys.NewFakeFileSystem()
		cmdRunner = fakesys.NewFakeCmdRunner()
		ipResolver = &fakeip.FakeResolver{}
		logger := boshlog.NewLogger(boshlog.LevelNone)
		fakeMACAddressDetector = &netfakes.FakeMACAddressDetector{}
		interfaceConfigurationCreator = NewInterfaceConfigurationCreator(logger)
		addressBroadcaster = &fakearp.FakeAddressBroadcaster{}
		interfaceAddrsProvider = &fakeip.FakeInterfaceAddressesProvider{}
		interfaceAddrsValidator := boship.NewInterfaceAddressesValidator(interfaceAddrsProvider)
		dnsValidator := NewDNSValidator(fs)
		kernelIPv6 = &fakenet.FakeKernelIPv6{}
		netManager = NewUbuntuNetManager(
			fs,
			cmdRunner,
			ipResolver,
			fakeMACAddressDetector,
			interfaceConfigurationCreator,
			interfaceAddrsValidator,
			dnsValidator,
			addressBroadcaster,
			kernelIPv6,
			logger,
		).(UbuntuNetManager)
	})

	scrubMultipleLines := func(in string) string {
		return strings.Replace(in, "\n\n\n", "\n\n", -1)
	}

	Describe("SetupNetworking", func() {
		BeforeEach(func() {
			err := fs.WriteFileString("/etc/resolv.conf", "nameserver 8.8.8.8\nnameserver 9.9.9.9")
			Expect(err).ToNot(HaveOccurred())

			err = fs.WriteFileString("/boot/grub/grub.cfg", "")
			Expect(err).ToNot(HaveOccurred())

			interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{
				boship.NewSimpleInterfaceAddress("ethstatic1", "2601:646:100:e8e8::103"),
				boship.NewSimpleInterfaceAddress("ethstatic2", "1.2.3.4"),
				boship.NewSimpleInterfaceAddress("ethstatic3", "2601:646:100:eeee::10"),
			}
		})

		It("enables IPv6 if there are any IPv6 addresses", func() {
			static1Net := boshsettings.Network{
				Type:    "manual",
				IP:      "2601:646:100:e8e8::103",
				Netmask: "ffff:ffff:ffff:ffff:0000:0000:0000:0000",
				Gateway: "2601:646:100:e8e8::",
				Default: []string{"gateway", "dns"},
				DNS:     []string{"8.8.8.8", "9.9.9.9"},
				Mac:     "mac1",
			}

			stubInterfaces(map[string]boshsettings.Network{
				"ethstatic1": static1Net,
			})

			err := netManager.SetupNetworking(boshsettings.Networks{"net1": static1Net}, nil)
			Expect(err).ToNot(HaveOccurred())

			Expect(kernelIPv6.Enabled).To(BeTrue())
		})

		It("returns error if enabling IPv6 in kernel fails", func() {
			static1Net := boshsettings.Network{
				Type:    "manual",
				IP:      "2601:646:100:e8e8::103",
				Netmask: "ffff:ffff:ffff:ffff:0000:0000:0000:0000",
				Gateway: "2601:646:100:e8e8::",
				Default: []string{"gateway", "dns"},
				DNS:     []string{"8.8.8.8", "9.9.9.9"},
				Mac:     "mac1",
			}

			stubInterfaces(map[string]boshsettings.Network{
				"ethstatic1": static1Net,
			})

			kernelIPv6.EnableErr = errors.New("fake-err")

			err := netManager.SetupNetworking(boshsettings.Networks{"net1": static1Net}, nil)
			Expect(err).To(HaveOccurred())
			Expect(err.Error()).To(ContainSubstring("fake-err"))
		})

		It("does not enable IPv6 if there aren't any IPv6 addresses", func() {
			static1Net := boshsettings.Network{
				Type:    "manual",
				IP:      "1.2.3.4",
				Default: nil,
				Netmask: "255.255.255.0",
				Gateway: "3.4.5.6",
				Mac:     "mac2",
			}

			stubInterfaces(map[string]boshsettings.Network{
				"ethstatic2": static1Net,
			})

			err := netManager.SetupNetworking(boshsettings.Networks{"net1": static1Net}, nil)
			Expect(err).ToNot(HaveOccurred())

			Expect(kernelIPv6.Enabled).To(BeFalse())
		})

		It("writes /etc/network/interfaces with static inet6 configuration when manual network is used", func() {
			static1Net := boshsettings.Network{
				Type:    "manual",
				IP:      "2601:646:100:e8e8::103",
				Netmask: "ffff:ffff:ffff:ffff:0000:0000:0000:0000",
				Gateway: "2601:646:100:e8e8::",
				Default: []string{"gateway", "dns"},
				DNS:     []string{"8.8.8.8", "9.9.9.9"},
				Mac:     "mac1",
			}
			static2Net := boshsettings.Network{
				Type:    "manual",
				IP:      "1.2.3.4",
				Default: nil,
				Netmask: "255.255.255.0",
				Gateway: "3.4.5.6",
				Mac:     "mac2",
			}
			static3Net := boshsettings.Network{
				Type:    "manual",
				IP:      "2601:646:100:eeee::10",
				Netmask: "ffff:ffff:ffff:ffff:ffff:0000:0000:0000",
				Gateway: "2601:646:100:eeee::",
				Default: []string{},
				DNS:     []string{"8.8.8.8", "9.9.9.9"},
				Mac:     "mac3",
			}

			stubInterfaces(map[string]boshsettings.Network{
				"ethstatic1": static1Net,
				"ethstatic2": static2Net,
				"ethstatic3": static3Net,
			})

			err := netManager.SetupNetworking(boshsettings.Networks{
				"net1": static1Net,
				"net2": static2Net,
				"net3": static3Net,
			}, nil)
			Expect(err).ToNot(HaveOccurred())

			matches, err := fs.Ls("/etc/systemd/network/")
			Expect(err).NotTo(HaveOccurred())
			Expect(matches).To(ConsistOf(
				"/etc/systemd/network/10_ethstatic1.network",
				"/etc/systemd/network/10_ethstatic2.network",
				"/etc/systemd/network/10_ethstatic3.network",
			))
			networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_ethstatic1.network")
			Expect(networkConfig).ToNot(BeNil())
			Expect(scrubMultipleLines(networkConfig.StringContents())).To(Equal(`# Generated by bosh-agent
[Match]
Name=ethstatic1

[Address]
Address=2601:646:100:e8e8::103/64

[Network]
Gateway=2601:646:100:e8e8::
IPv6AcceptRA=true
DNS=8.8.8.8
DNS=9.9.9.9

[Route]
`))
			networkConfig = fs.GetFileTestStat("/etc/systemd/network/10_ethstatic2.network")
			Expect(networkConfig).ToNot(BeNil())
			Expect(scrubMultipleLines(networkConfig.StringContents())).To(Equal(`# Generated by bosh-agent
[Match]
Name=ethstatic2

[Address]
Address=1.2.3.4/24

[Network]

DNS=8.8.8.8
DNS=9.9.9.9

[Route]
`))
			networkConfig = fs.GetFileTestStat("/etc/systemd/network/10_ethstatic3.network")
			Expect(networkConfig).ToNot(BeNil())
			Expect(scrubMultipleLines(networkConfig.StringContents())).To(Equal(`# Generated by bosh-agent
[Match]
Name=ethstatic3

[Address]
Address=2601:646:100:eeee::10/80

[Network]

IPv6AcceptRA=true
DNS=8.8.8.8
DNS=9.9.9.9

[Route]
`))
		})

		It("configures postup routes for static network", func() {
			static1Net := boshsettings.Network{
				Type:    "manual",
				IP:      "2601:646:100:e8e8::103",
				Netmask: "ffff:ffff:ffff:ffff:0000:0000:0000:0000",
				Gateway: "2601:646:100:e8e8::",
				Default: []string{"gateway", "dns"},
				DNS:     []string{"8.8.8.8", "9.9.9.9"},
				Mac:     "mac1",
				Routes: []boshsettings.Route{
					boshsettings.Route{
						Destination: "2001:db8:1234::",
						Gateway:     "2601:646:100:e8e8::",
						Netmask:     "ffff:ffff:ffff:0000:0000:0000:0000:0000",
					},
				},
			}

			stubInterfaces(map[string]boshsettings.Network{
				"ethstatic1": static1Net,
			})

			err := netManager.SetupNetworking(boshsettings.Networks{
				"net1": static1Net,
			}, nil)
			Expect(err).ToNot(HaveOccurred())

			matches, err := fs.Ls("/etc/systemd/network/")
			Expect(err).NotTo(HaveOccurred())
			Expect(matches).To(ConsistOf(
				"/etc/systemd/network/10_ethstatic1.network",
			))
			networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_ethstatic1.network")
			Expect(networkConfig).ToNot(BeNil())
			Expect(scrubMultipleLines(networkConfig.StringContents())).To(Equal(`# Generated by bosh-agent
[Match]
Name=ethstatic1

[Address]
Address=2601:646:100:e8e8::103/64

[Network]
Gateway=2601:646:100:e8e8::
IPv6AcceptRA=true
DNS=8.8.8.8
DNS=9.9.9.9

[Route]

Destination=2001:db8:1234::/48
Gateway=2601:646:100:e8e8::
`))
		})
	})
})
<commit_msg>Fixup ipv6 systemd-networkd expectations<commit_after>// +build !windows

package net_test

import (
	"errors"
	"strings"

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

	. "github.com/cloudfoundry/bosh-agent/platform/net"
	fakearp "github.com/cloudfoundry/bosh-agent/platform/net/arp/fakes"
	fakenet "github.com/cloudfoundry/bosh-agent/platform/net/fakes"
	boship "github.com/cloudfoundry/bosh-agent/platform/net/ip"
	fakeip "github.com/cloudfoundry/bosh-agent/platform/net/ip/fakes"
	"github.com/cloudfoundry/bosh-agent/platform/net/netfakes"
	boshsettings "github.com/cloudfoundry/bosh-agent/settings"
	boshlog "github.com/cloudfoundry/bosh-utils/logger"
	fakesys "github.com/cloudfoundry/bosh-utils/system/fakes"
)

var _ = Describe("UbuntuNetManager (IPv6)", func() {
	var (
		fs                            *fakesys.FakeFileSystem
		cmdRunner                     *fakesys.FakeCmdRunner
		ipResolver                    *fakeip.FakeResolver
		addressBroadcaster            *fakearp.FakeAddressBroadcaster
		interfaceAddrsProvider        *fakeip.FakeInterfaceAddressesProvider
		kernelIPv6                    *fakenet.FakeKernelIPv6
		netManager                    UbuntuNetManager
		interfaceConfigurationCreator InterfaceConfigurationCreator
		fakeMACAddressDetector        *netfakes.FakeMACAddressDetector
	)

	stubInterfaces := func(physicalInterfaces map[string]boshsettings.Network) {
		addresses := map[string]string{}
		for iface, networkSettings := range physicalInterfaces {
			addresses[networkSettings.Mac] = iface
		}

		fakeMACAddressDetector.DetectMacAddressesReturns(addresses, nil)
	}

	BeforeEach(func() {
		fs = fakesys.NewFakeFileSystem()
		cmdRunner = fakesys.NewFakeCmdRunner()
		ipResolver = &fakeip.FakeResolver{}
		logger := boshlog.NewLogger(boshlog.LevelNone)
		fakeMACAddressDetector = &netfakes.FakeMACAddressDetector{}
		interfaceConfigurationCreator = NewInterfaceConfigurationCreator(logger)
		addressBroadcaster = &fakearp.FakeAddressBroadcaster{}
		interfaceAddrsProvider = &fakeip.FakeInterfaceAddressesProvider{}
		interfaceAddrsValidator := boship.NewInterfaceAddressesValidator(interfaceAddrsProvider)
		dnsValidator := NewDNSValidator(fs)
		kernelIPv6 = &fakenet.FakeKernelIPv6{}
		netManager = NewUbuntuNetManager(
			fs,
			cmdRunner,
			ipResolver,
			fakeMACAddressDetector,
			interfaceConfigurationCreator,
			interfaceAddrsValidator,
			dnsValidator,
			addressBroadcaster,
			kernelIPv6,
			logger,
		).(UbuntuNetManager)
	})

	scrubMultipleLines := func(in string) string {
		return strings.Replace(in, "\n\n\n", "\n\n", -1)
	}

	Describe("SetupNetworking", func() {
		BeforeEach(func() {
			err := fs.WriteFileString("/etc/resolv.conf", "nameserver 8.8.8.8\nnameserver 9.9.9.9")
			Expect(err).ToNot(HaveOccurred())

			err = fs.WriteFileString("/boot/grub/grub.cfg", "")
			Expect(err).ToNot(HaveOccurred())

			interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{
				boship.NewSimpleInterfaceAddress("ethstatic1", "2601:646:100:e8e8::103"),
				boship.NewSimpleInterfaceAddress("ethstatic2", "1.2.3.4"),
				boship.NewSimpleInterfaceAddress("ethstatic3", "2601:646:100:eeee::10"),
			}
		})

		It("enables IPv6 if there are any IPv6 addresses", func() {
			static1Net := boshsettings.Network{
				Type:    "manual",
				IP:      "2601:646:100:e8e8::103",
				Netmask: "ffff:ffff:ffff:ffff:0000:0000:0000:0000",
				Gateway: "2601:646:100:e8e8::",
				Default: []string{"gateway", "dns"},
				DNS:     []string{"8.8.8.8", "9.9.9.9"},
				Mac:     "mac1",
			}

			stubInterfaces(map[string]boshsettings.Network{
				"ethstatic1": static1Net,
			})

			err := netManager.SetupNetworking(boshsettings.Networks{"net1": static1Net}, nil)
			Expect(err).ToNot(HaveOccurred())

			Expect(kernelIPv6.Enabled).To(BeTrue())
		})

		It("returns error if enabling IPv6 in kernel fails", func() {
			static1Net := boshsettings.Network{
				Type:    "manual",
				IP:      "2601:646:100:e8e8::103",
				Netmask: "ffff:ffff:ffff:ffff:0000:0000:0000:0000",
				Gateway: "2601:646:100:e8e8::",
				Default: []string{"gateway", "dns"},
				DNS:     []string{"8.8.8.8", "9.9.9.9"},
				Mac:     "mac1",
			}

			stubInterfaces(map[string]boshsettings.Network{
				"ethstatic1": static1Net,
			})

			kernelIPv6.EnableErr = errors.New("fake-err")

			err := netManager.SetupNetworking(boshsettings.Networks{"net1": static1Net}, nil)
			Expect(err).To(HaveOccurred())
			Expect(err.Error()).To(ContainSubstring("fake-err"))
		})

		It("does not enable IPv6 if there aren't any IPv6 addresses", func() {
			static1Net := boshsettings.Network{
				Type:    "manual",
				IP:      "1.2.3.4",
				Default: nil,
				Netmask: "255.255.255.0",
				Gateway: "3.4.5.6",
				Mac:     "mac2",
			}

			stubInterfaces(map[string]boshsettings.Network{
				"ethstatic2": static1Net,
			})

			err := netManager.SetupNetworking(boshsettings.Networks{"net1": static1Net}, nil)
			Expect(err).ToNot(HaveOccurred())

			Expect(kernelIPv6.Enabled).To(BeFalse())
		})

		It("writes /etc/network/interfaces with static inet6 configuration when manual network is used", func() {
			static1Net := boshsettings.Network{
				Type:    "manual",
				IP:      "2601:646:100:e8e8::103",
				Netmask: "ffff:ffff:ffff:ffff:0000:0000:0000:0000",
				Gateway: "2601:646:100:e8e8::",
				Default: []string{"gateway", "dns"},
				DNS:     []string{"8.8.8.8", "9.9.9.9"},
				Mac:     "mac1",
			}
			static2Net := boshsettings.Network{
				Type:    "manual",
				IP:      "1.2.3.4",
				Default: nil,
				Netmask: "255.255.255.0",
				Gateway: "3.4.5.6",
				Mac:     "mac2",
			}
			static3Net := boshsettings.Network{
				Type:    "manual",
				IP:      "2601:646:100:eeee::10",
				Netmask: "ffff:ffff:ffff:ffff:ffff:0000:0000:0000",
				Gateway: "2601:646:100:eeee::",
				Default: []string{},
				DNS:     []string{"8.8.8.8", "9.9.9.9"},
				Mac:     "mac3",
			}

			stubInterfaces(map[string]boshsettings.Network{
				"ethstatic1": static1Net,
				"ethstatic2": static2Net,
				"ethstatic3": static3Net,
			})

			err := netManager.SetupNetworking(boshsettings.Networks{
				"net1": static1Net,
				"net2": static2Net,
				"net3": static3Net,
			}, nil)
			Expect(err).ToNot(HaveOccurred())

			matches, err := fs.Ls("/etc/systemd/network/")
			Expect(err).NotTo(HaveOccurred())
			Expect(matches).To(ConsistOf(
				"/etc/systemd/network/10_ethstatic1.network",
				"/etc/systemd/network/10_ethstatic2.network",
				"/etc/systemd/network/10_ethstatic3.network",
			))
			networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_ethstatic1.network")
			Expect(networkConfig).ToNot(BeNil())
			Expect(scrubMultipleLines(networkConfig.StringContents())).To(Equal(`# Generated by bosh-agent
[Match]
Name=ethstatic1

[Address]
Address=2601:646:100:e8e8::103/64

[Network]
Gateway=2601:646:100:e8e8::
IPv6AcceptRA=true
DNS=8.8.8.8
DNS=9.9.9.9

`))
			networkConfig = fs.GetFileTestStat("/etc/systemd/network/10_ethstatic2.network")
			Expect(networkConfig).ToNot(BeNil())
			Expect(scrubMultipleLines(networkConfig.StringContents())).To(Equal(`# Generated by bosh-agent
[Match]
Name=ethstatic2

[Address]
Address=1.2.3.4/24

[Network]

DNS=8.8.8.8
DNS=9.9.9.9

`))
			networkConfig = fs.GetFileTestStat("/etc/systemd/network/10_ethstatic3.network")
			Expect(networkConfig).ToNot(BeNil())
			Expect(scrubMultipleLines(networkConfig.StringContents())).To(Equal(`# Generated by bosh-agent
[Match]
Name=ethstatic3

[Address]
Address=2601:646:100:eeee::10/80

[Network]

IPv6AcceptRA=true
DNS=8.8.8.8
DNS=9.9.9.9

`))
		})

		It("configures postup routes for static network", func() {
			static1Net := boshsettings.Network{
				Type:    "manual",
				IP:      "2601:646:100:e8e8::103",
				Netmask: "ffff:ffff:ffff:ffff:0000:0000:0000:0000",
				Gateway: "2601:646:100:e8e8::",
				Default: []string{"gateway", "dns"},
				DNS:     []string{"8.8.8.8", "9.9.9.9"},
				Mac:     "mac1",
				Routes: []boshsettings.Route{
					boshsettings.Route{
						Destination: "2001:db8:1234::",
						Gateway:     "2601:646:100:e8e8::",
						Netmask:     "ffff:ffff:ffff:0000:0000:0000:0000:0000",
					},
				},
			}

			stubInterfaces(map[string]boshsettings.Network{
				"ethstatic1": static1Net,
			})

			err := netManager.SetupNetworking(boshsettings.Networks{
				"net1": static1Net,
			}, nil)
			Expect(err).ToNot(HaveOccurred())

			matches, err := fs.Ls("/etc/systemd/network/")
			Expect(err).NotTo(HaveOccurred())
			Expect(matches).To(ConsistOf(
				"/etc/systemd/network/10_ethstatic1.network",
			))
			networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_ethstatic1.network")
			Expect(networkConfig).ToNot(BeNil())
			Expect(scrubMultipleLines(networkConfig.StringContents())).To(Equal(`# Generated by bosh-agent
[Match]
Name=ethstatic1

[Address]
Address=2601:646:100:e8e8::103/64

[Network]
Gateway=2601:646:100:e8e8::
IPv6AcceptRA=true
DNS=8.8.8.8
DNS=9.9.9.9

[Route]
Destination=2001:db8:1234::/48
Gateway=2601:646:100:e8e8::
`))
		})
	})
})
<|endoftext|>


<commit_before>package tddbc

import (
	"testing"
)

func Test正常_開区間を作成し下端点が取得できることを確認する(t *testing.T) {
	var data = []struct {
		lower    int
		upper    int
		expected int
	}{
		{3, 8, 3},
		{1, 8, 1},
		{2, 8, 2},
	}

	for _, d := range data {
		expected := d.expected
		target := NewOpenRange(d.lower, d.upper)
		if target.lower != expected {
			t.Errorf("failed get lower expected=%d, actual=%d\n", expected, target.lower)
		}
	}
}

func Test正常_開区間を作成し上端点が取得できることを確認する(t *testing.T) {
	var data = []struct {
		lower    int
		upper    int
		expected int
	}{
		{3, 8, 8},
		{3, 9, 9},
		{3, 7, 7},
	}

	for _, d := range data {
		expected := d.expected
		target := NewOpenRange(d.lower, d.upper)
		if target.upper != expected {
			t.Errorf("failed get lower expected=%d, actual=%d\n", expected, target.lower)
		}
	}
}

func Test正常_開区間の文字列表記が取得できることを確認する(t *testing.T) {
	var data = []struct {
		lower    int
		upper    int
		expected string
	}{
		{3, 8, "[3,8]"},
		{3, 9, "[3,9]"},
	}

	for _, d := range data {
		expected := d.expected
		target := NewOpenRange(d.lower, d.upper)
		actual := target.String()
		if actual != expected {
			t.Errorf("failed get lower expected=%s, actual=%s\n", expected, actual)
		}
	}
}

func Test正常_開区間に指定した整数を含むか確認する(t *testing.T) {
	var data = []struct {
		lower    int
		upper    int
		value    int
		expected bool
	}{
		{3, 8, 4, true},
		{3, 8, 7, true},
		{3, 8, 2, false},
		{3, 8, 9, false},
		{3, 8, 3, false},
		{3, 8, 8, false},
	}

	for _, d := range data {
		expected := d.expected
		value := d.value
		target := NewOpenRange(d.lower, d.upper)
		actual := target.Contains(value)
		if actual != expected {
			t.Errorf("failed %s value=%d expected=%t actual=%t\n", target, value, expected, actual)
		}
	}
}

func Test正常_開区間が別の開区間と等しいか確認する(t *testing.T) {
	var data = []struct {
		target   OpenRange
		value    OpenRange
		expected bool
	}{
		{OpenRange{3, 8}, OpenRange{3, 8}, true},
		{OpenRange{3, 8}, OpenRange{4, 8}, false},
		{OpenRange{3, 8}, OpenRange{3, 9}, false},
		{OpenRange{3, 8}, OpenRange{2, 8}, false},
	}
	for _, d := range data {
		expected := d.expected
		value := d.value
		target := d.target
		actual := target.Equal(value)
		if actual != expected {
			t.Errorf("failed %v value=%v expected=%t actual=%t\n", target, value, expected, actual)
		}
	}
}
<commit_msg>上端点がテスト対象の上端点より低いパターンを追加<commit_after>package tddbc

import (
	"testing"
)

func Test正常_開区間を作成し下端点が取得できることを確認する(t *testing.T) {
	var data = []struct {
		lower    int
		upper    int
		expected int
	}{
		{3, 8, 3},
		{1, 8, 1},
		{2, 8, 2},
	}

	for _, d := range data {
		expected := d.expected
		target := NewOpenRange(d.lower, d.upper)
		if target.lower != expected {
			t.Errorf("failed get lower expected=%d, actual=%d\n", expected, target.lower)
		}
	}
}

func Test正常_開区間を作成し上端点が取得できることを確認する(t *testing.T) {
	var data = []struct {
		lower    int
		upper    int
		expected int
	}{
		{3, 8, 8},
		{3, 9, 9},
		{3, 7, 7},
	}

	for _, d := range data {
		expected := d.expected
		target := NewOpenRange(d.lower, d.upper)
		if target.upper != expected {
			t.Errorf("failed get lower expected=%d, actual=%d\n", expected, target.lower)
		}
	}
}

func Test正常_開区間の文字列表記が取得できることを確認する(t *testing.T) {
	var data = []struct {
		lower    int
		upper    int
		expected string
	}{
		{3, 8, "[3,8]"},
		{3, 9, "[3,9]"},
	}

	for _, d := range data {
		expected := d.expected
		target := NewOpenRange(d.lower, d.upper)
		actual := target.String()
		if actual != expected {
			t.Errorf("failed get lower expected=%s, actual=%s\n", expected, actual)
		}
	}
}

func Test正常_開区間に指定した整数を含むか確認する(t *testing.T) {
	var data = []struct {
		lower    int
		upper    int
		value    int
		expected bool
	}{
		{3, 8, 4, true},
		{3, 8, 7, true},
		{3, 8, 2, false},
		{3, 8, 9, false},
		{3, 8, 3, false},
		{3, 8, 8, false},
	}

	for _, d := range data {
		expected := d.expected
		value := d.value
		target := NewOpenRange(d.lower, d.upper)
		actual := target.Contains(value)
		if actual != expected {
			t.Errorf("failed %s value=%d expected=%t actual=%t\n", target, value, expected, actual)
		}
	}
}

func Test正常_開区間が別の開区間と等しいか確認する(t *testing.T) {
	var data = []struct {
		target   OpenRange
		value    OpenRange
		expected bool
	}{
		{OpenRange{3, 8}, OpenRange{3, 8}, true},
		{OpenRange{3, 8}, OpenRange{4, 8}, false},
		{OpenRange{3, 8}, OpenRange{3, 9}, false},
		{OpenRange{3, 8}, OpenRange{2, 8}, false},
		{OpenRange{3, 8}, OpenRange{3, 7}, false},
	}
	for _, d := range data {
		expected := d.expected
		value := d.value
		target := d.target
		actual := target.Equal(value)
		if actual != expected {
			t.Errorf("failed %v value=%v expected=%t actual=%t\n", target, value, expected, actual)
		}
	}
}
<|endoftext|>


<commit_before>package main

type FontMap struct {
	row int
	col int
}

type FontTable struct {
	Table map[rune]FontMap
	Width int
	High  int
}

type FontMgr struct {
}

var cTable map[string]FontTable
var cFontName string

type FontInterface interface {
	Init(fontName string)
	Width() int
	High() int
	Col(char rune) int
	Row(char rune) int
}

func (f *FontMgr) Init(fontName string) {

	font6x8 := map[rune]FontMap{
		' ': {0, 0}, '!': {0, 1}, '"': {0, 2}, '#': {0, 3}, '$': {0, 4}, '%': {0, 5},
		'&': {0, 6}, '\'': {0, 7}, '(': {0, 8}, ')': {0, 9}, '*': {0, 10}, '+': {0, 11},
		',': {0, 12}, '-': {0, 13}, '.': {0, 14}, '/': {0, 15},

		'0': {1, 0}, '1': {1, 1}, '2': {1, 2}, '3': {1, 3}, '4': {1, 4}, '5': {1, 5},
		'6': {1, 6}, '7': {1, 7}, '8': {1, 8}, '9': {1, 9}, ':': {1, 10}, ';': {1, 11},
		'<': {1, 12}, '=': {1, 13}, '>': {1, 14}, '?': {1, 15},

		'@': {2, 0}, 'A': {2, 1}, 'B': {2, 2}, 'C': {2, 3}, 'D': {2, 4}, 'E': {2, 5},
		'F': {2, 6}, 'G': {2, 7}, 'H': {2, 8}, 'I': {2, 9}, 'J': {2, 10}, 'K': {2, 11},
		'L': {2, 12}, 'M': {2, 13}, 'N': {2, 14}, 'O': {2, 15},

		'P': {3, 0}, 'Q': {3, 1}, 'R': {3, 2}, 'S': {3, 3}, 'T': {3, 4}, 'U': {3, 5},
		'V': {3, 6}, 'W': {3, 7}, 'X': {3, 8}, 'Y': {3, 9}, 'Z': {3, 10}, '[': {3, 11},
		'\\': {3, 12}, ']': {3, 13}, '^': {3, 14}, '_': {3, 15},

		'`': {4, 0}, 'a': {4, 1}, 'b': {4, 2}, 'c': {4, 3}, 'd': {4, 4}, 'e': {4, 5},
		'f': {4, 6}, 'g': {4, 7}, 'h': {4, 8}, 'i': {4, 9}, 'j': {4, 10}, 'k': {4, 11},
		'l': {4, 12}, 'm': {4, 13}, 'n': {4, 14}, 'o': {4, 15},

		'p': {5, 0}, 'q': {5, 1}, 'r': {5, 2}, 's': {5, 3}, 't': {5, 4}, 'u': {5, 5},
		'v': {5, 6}, 'w': {5, 7}, 'x': {5, 8}, 'y': {5, 9}, 'z': {5, 10}, '{': {5, 11},
		'|': {6, 12}, '}': {5, 13}, '~': {5, 14}, '±': {5, 15},
	}

	font4x7 := map[rune]FontMap{
		' ': {0, 0}, '!': {0, 1}, '"': {0, 2}, '#': {0, 3}, '$': {0, 4}, '%': {0, 5},
		'&': {0, 6}, '\'': {0, 7}, '(': {0, 8}, ')': {0, 9}, '*': {0, 10}, '+': {0, 11},
		',': {0, 12}, '-': {0, 13}, '.': {0, 14}, '/': {0, 15},

		'0': {0, 16}, '1': {0, 17}, '2': {0, 18}, '3': {0, 19}, '4': {0, 20}, '5': {0, 21},
		'6': {0, 22}, '7': {0, 23}, '8': {0, 24}, '9': {0, 25}, ':': {0, 26}, ';': {0, 27},
		'<': {0, 28}, '=': {0, 29}, '>': {0, 30}, '?': {0, 31},

		'@': {1, 0}, 'A': {1, 1}, 'B': {1, 2}, 'C': {1, 3}, 'D': {1, 4}, 'E': {1, 5},
		'F': {1, 6}, 'G': {1, 7}, 'H': {1, 8}, 'I': {1, 9}, 'J': {1, 10}, 'K': {1, 11},
		'L': {1, 12}, 'M': {1, 13}, 'N': {1, 14}, 'O': {1, 15},

		'P': {1, 16}, 'Q': {1, 17}, 'R': {1, 18}, 'S': {1, 19}, 'T': {1, 20}, 'U': {1, 21},
		'V': {1, 22}, 'W': {1, 23}, 'X': {1, 24}, 'Y': {1, 25}, 'Z': {1, 26}, '[': {1, 27},
		'\\': {1, 28}, ']': {1, 29}, '†': {1, 30}, '_': {1, 31},

		'£': {2, 0}, 'a': {2, 1}, 'b': {2, 2}, 'c': {2, 3}, 'd': {2, 4}, 'e': {2, 5},
		'f': {2, 6}, 'g': {2, 7}, 'h': {2, 8}, 'i': {2, 9}, 'j': {2, 10}, 'k': {2, 11},
		'l': {2, 12}, 'm': {2, 13}, 'n': {2, 14}, 'o': {2, 15},

		'p': {2, 16}, 'q': {2, 17}, 'r': {2, 18}, 's': {2, 19}, 't': {2, 20}, 'u': {2, 21},
		'v': {2, 22}, 'w': {2, 23}, 'x': {2, 24}, 'y': {2, 25}, 'z': {2, 26}, '{': {2, 27},
		'|': {2, 28}, '}': {2, 29}, '~': {2, 30}, '±': {2, 31},
	}

	font6x6 := map[rune]FontMap{
		' ': {0, 0}, '!': {0, 1}, '"': {0, 2}, '#': {0, 3}, '$': {0, 4}, '%': {0, 5},
		'&': {0, 6}, '\'': {0, 7}, '(': {0, 8}, ')': {0, 9}, '*': {0, 10}, '+': {0, 11},
		',': {0, 12}, '-': {0, 13}, '.': {0, 14}, '/': {0, 15},

		'0': {0, 16}, '1': {0, 17}, '2': {0, 18}, '3': {0, 19}, '4': {0, 20}, '5': {0, 21},
		'6': {0, 22}, '7': {0, 23}, '8': {0, 24}, '9': {0, 25}, ':': {0, 26},

		'@': {1, 0}, 'A': {1, 1}, 'B': {1, 2}, 'C': {1, 3}, 'D': {1, 4}, 'E': {1, 5},
		'F': {1, 6}, 'G': {1, 7}, 'H': {1, 8}, 'I': {1, 9}, 'J': {1, 10}, 'K': {1, 11},
		'L': {1, 12}, 'M': {1, 13}, 'N': {1, 14}, 'O': {1, 15},

		'P': {1, 16}, 'Q': {1, 17}, 'R': {1, 18}, 'S': {1, 19}, 'T': {1, 20}, 'U': {1, 21},
		'V': {1, 22}, 'W': {1, 23}, 'X': {1, 24}, 'Y': {1, 25}, 'Z': {1, 26},

		'`': {2, 0}, 'a': {2, 1}, 'b': {2, 2}, 'c': {2, 3}, 'd': {2, 4}, 'e': {2, 5},
		'f': {2, 6}, 'g': {2, 7}, 'h': {2, 8}, 'i': {2, 9}, 'j': {2, 10}, 'k': {2, 11},
		'l': {2, 12}, 'm': {2, 13}, 'n': {2, 14}, 'o': {2, 15},

		'p': {2, 16}, 'q': {2, 17}, 'r': {2, 18}, 's': {2, 19}, 't': {2, 20}, 'u': {2, 21},
		'v': {2, 22}, 'w': {2, 23}, 'x': {2, 24}, 'y': {2, 25}, 'z': {2, 26},
	}

	cTable = map[string]FontTable{
		"font4x7": {font4x7, 4, 7},
		"font6x6": {font6x6, 6, 6},
		"font6x8": {font6x8, 6, 8},
		"font6x7": {font6x8, 6, 7},
	}

	cFontName = fontName
}

func (f *FontMgr) Width() int {
	return cTable[cFontName].Width
}

func (f *FontMgr) High() int {
	return cTable[cFontName].High
}

func (f *FontMgr) Col(char rune) int {
	return cTable[cFontName].Table[char].col
}

func (f *FontMgr) Row(char rune) int {
	return cTable[cFontName].Table[char].row
}
<commit_msg>Load font from images.<commit_after>package main

import (
	"image"
	"log"
	"os"
)

type FontMap struct {
	row int
	col int
}

type FontTable struct {
	Table map[rune]FontMap
	Width int
	High  int
}

type FontMgr struct {
}

var cTable map[string]FontTable
var cFontName string
var cImageReader image.Image

type FontInterface interface {
	Init(fontName string) image.Image
	Width() int
	High() int
	Col(char rune) int
	Row(char rune) int
}

func (f *FontMgr) Init(fontName string) image.Image {

	font6x8 := map[rune]FontMap{
		' ': {0, 0}, '!': {0, 1}, '"': {0, 2}, '#': {0, 3}, '$': {0, 4}, '%': {0, 5},
		'&': {0, 6}, '\'': {0, 7}, '(': {0, 8}, ')': {0, 9}, '*': {0, 10}, '+': {0, 11},
		',': {0, 12}, '-': {0, 13}, '.': {0, 14}, '/': {0, 15},

		'0': {1, 0}, '1': {1, 1}, '2': {1, 2}, '3': {1, 3}, '4': {1, 4}, '5': {1, 5},
		'6': {1, 6}, '7': {1, 7}, '8': {1, 8}, '9': {1, 9}, ':': {1, 10}, ';': {1, 11},
		'<': {1, 12}, '=': {1, 13}, '>': {1, 14}, '?': {1, 15},

		'@': {2, 0}, 'A': {2, 1}, 'B': {2, 2}, 'C': {2, 3}, 'D': {2, 4}, 'E': {2, 5},
		'F': {2, 6}, 'G': {2, 7}, 'H': {2, 8}, 'I': {2, 9}, 'J': {2, 10}, 'K': {2, 11},
		'L': {2, 12}, 'M': {2, 13}, 'N': {2, 14}, 'O': {2, 15},

		'P': {3, 0}, 'Q': {3, 1}, 'R': {3, 2}, 'S': {3, 3}, 'T': {3, 4}, 'U': {3, 5},
		'V': {3, 6}, 'W': {3, 7}, 'X': {3, 8}, 'Y': {3, 9}, 'Z': {3, 10}, '[': {3, 11},
		'\\': {3, 12}, ']': {3, 13}, '^': {3, 14}, '_': {3, 15},

		'`': {4, 0}, 'a': {4, 1}, 'b': {4, 2}, 'c': {4, 3}, 'd': {4, 4}, 'e': {4, 5},
		'f': {4, 6}, 'g': {4, 7}, 'h': {4, 8}, 'i': {4, 9}, 'j': {4, 10}, 'k': {4, 11},
		'l': {4, 12}, 'm': {4, 13}, 'n': {4, 14}, 'o': {4, 15},

		'p': {5, 0}, 'q': {5, 1}, 'r': {5, 2}, 's': {5, 3}, 't': {5, 4}, 'u': {5, 5},
		'v': {5, 6}, 'w': {5, 7}, 'x': {5, 8}, 'y': {5, 9}, 'z': {5, 10}, '{': {5, 11},
		'|': {6, 12}, '}': {5, 13}, '~': {5, 14}, '±': {5, 15},
	}

	font4x7 := map[rune]FontMap{
		' ': {0, 0}, '!': {0, 1}, '"': {0, 2}, '#': {0, 3}, '$': {0, 4}, '%': {0, 5},
		'&': {0, 6}, '\'': {0, 7}, '(': {0, 8}, ')': {0, 9}, '*': {0, 10}, '+': {0, 11},
		',': {0, 12}, '-': {0, 13}, '.': {0, 14}, '/': {0, 15},

		'0': {0, 16}, '1': {0, 17}, '2': {0, 18}, '3': {0, 19}, '4': {0, 20}, '5': {0, 21},
		'6': {0, 22}, '7': {0, 23}, '8': {0, 24}, '9': {0, 25}, ':': {0, 26}, ';': {0, 27},
		'<': {0, 28}, '=': {0, 29}, '>': {0, 30}, '?': {0, 31},

		'@': {1, 0}, 'A': {1, 1}, 'B': {1, 2}, 'C': {1, 3}, 'D': {1, 4}, 'E': {1, 5},
		'F': {1, 6}, 'G': {1, 7}, 'H': {1, 8}, 'I': {1, 9}, 'J': {1, 10}, 'K': {1, 11},
		'L': {1, 12}, 'M': {1, 13}, 'N': {1, 14}, 'O': {1, 15},

		'P': {1, 16}, 'Q': {1, 17}, 'R': {1, 18}, 'S': {1, 19}, 'T': {1, 20}, 'U': {1, 21},
		'V': {1, 22}, 'W': {1, 23}, 'X': {1, 24}, 'Y': {1, 25}, 'Z': {1, 26}, '[': {1, 27},
		'\\': {1, 28}, ']': {1, 29}, '†': {1, 30}, '_': {1, 31},

		'£': {2, 0}, 'a': {2, 1}, 'b': {2, 2}, 'c': {2, 3}, 'd': {2, 4}, 'e': {2, 5},
		'f': {2, 6}, 'g': {2, 7}, 'h': {2, 8}, 'i': {2, 9}, 'j': {2, 10}, 'k': {2, 11},
		'l': {2, 12}, 'm': {2, 13}, 'n': {2, 14}, 'o': {2, 15},

		'p': {2, 16}, 'q': {2, 17}, 'r': {2, 18}, 's': {2, 19}, 't': {2, 20}, 'u': {2, 21},
		'v': {2, 22}, 'w': {2, 23}, 'x': {2, 24}, 'y': {2, 25}, 'z': {2, 26}, '{': {2, 27},
		'|': {2, 28}, '}': {2, 29}, '~': {2, 30}, '±': {2, 31},
	}

	font6x6 := map[rune]FontMap{
		' ': {0, 0}, '!': {0, 1}, '"': {0, 2}, '#': {0, 3}, '$': {0, 4}, '%': {0, 5},
		'&': {0, 6}, '\'': {0, 7}, '(': {0, 8}, ')': {0, 9}, '*': {0, 10}, '+': {0, 11},
		',': {0, 12}, '-': {0, 13}, '.': {0, 14}, '/': {0, 15},

		'0': {0, 16}, '1': {0, 17}, '2': {0, 18}, '3': {0, 19}, '4': {0, 20}, '5': {0, 21},
		'6': {0, 22}, '7': {0, 23}, '8': {0, 24}, '9': {0, 25}, ':': {0, 26},

		'@': {1, 0}, 'A': {1, 1}, 'B': {1, 2}, 'C': {1, 3}, 'D': {1, 4}, 'E': {1, 5},
		'F': {1, 6}, 'G': {1, 7}, 'H': {1, 8}, 'I': {1, 9}, 'J': {1, 10}, 'K': {1, 11},
		'L': {1, 12}, 'M': {1, 13}, 'N': {1, 14}, 'O': {1, 15},

		'P': {1, 16}, 'Q': {1, 17}, 'R': {1, 18}, 'S': {1, 19}, 'T': {1, 20}, 'U': {1, 21},
		'V': {1, 22}, 'W': {1, 23}, 'X': {1, 24}, 'Y': {1, 25}, 'Z': {1, 26},

		'`': {2, 0}, 'a': {2, 1}, 'b': {2, 2}, 'c': {2, 3}, 'd': {2, 4}, 'e': {2, 5},
		'f': {2, 6}, 'g': {2, 7}, 'h': {2, 8}, 'i': {2, 9}, 'j': {2, 10}, 'k': {2, 11},
		'l': {2, 12}, 'm': {2, 13}, 'n': {2, 14}, 'o': {2, 15},

		'p': {2, 16}, 'q': {2, 17}, 'r': {2, 18}, 's': {2, 19}, 't': {2, 20}, 'u': {2, 21},
		'v': {2, 22}, 'w': {2, 23}, 'x': {2, 24}, 'y': {2, 25}, 'z': {2, 26},
	}

	cTable = map[string]FontTable{
		"font4x7": {font4x7, 4, 7},
		"font6x6": {font6x6, 6, 6},
		"font6x8": {font6x8, 6, 8},
		"font6x7": {font6x8, 6, 7},
	}

	cFontName = fontName

	reader, err := os.Open("fonts/" + fontName + ".png")
	if err != nil {
		log.Fatal(err)
	}
	defer reader.Close()

	// Decode fonts table.
	cImageReader, _, err := image.Decode(reader)
	if err != nil {
		log.Fatal(err)
	}

	return cImageReader
}

func (f *FontMgr) Width() int {
	return cTable[cFontName].Width
}

func (f *FontMgr) High() int {
	return cTable[cFontName].High
}

func (f *FontMgr) Col(char rune) int {
	return cTable[cFontName].Table[char].col
}

func (f *FontMgr) Row(char rune) int {
	return cTable[cFontName].Table[char].row
}
<|endoftext|>


<commit_before>package app

import (
	"errors"
	"github.com/Sirupsen/logrus"
	"github.com/Tapjoy/lane"
	"github.com/hashicorp/memberlist"
	"math"
	"math/rand"
	"sort"
	"sync"
	"time"
)

const NOPARTITIONS string = "no available partitions"

type Partitions struct {
	partitions     *lane.PQueue
	partitionCount int
	sync.RWMutex
}

type Partition struct {
	Id       int
	LastUsed time.Time
}

func InitPartitions(cfg *Config, queueName string) *Partitions {
	part := &Partitions{
		partitions:     lane.NewPQueue(lane.MINPQ),
		partitionCount: 0,
	}
	// We'll initially allocate the minimum amount
	minPartitions, _ := cfg.GetMinPartitions(queueName)
	part.Lock()
	part.makePartitions(cfg, queueName, minPartitions)
	part.Unlock()
	return part
}

func (part *Partitions) PartitionCount() int {
	return part.partitionCount
}
func (part *Partitions) GetPartition(cfg *Config, queueName string, list *memberlist.Memberlist) (int, int, *Partition, error) {

	//get the node position and the node count
	nodePosition, nodeCount := getNodePosition(list)

	//calculate the range that our node is responsible for
	step := math.MaxInt64 / nodeCount
	nodeBottom := nodePosition * step
	nodeTop := (nodePosition + 1) * step
	myPartition, partition, totalPartitions, err := part.getPartitionPosition(cfg, queueName)
	if err != nil {
		logrus.Error(err)
	}

	// calculate my range for the given number
	node_range := nodeTop - nodeBottom
	nodeStep := node_range / totalPartitions
	partitionBottom := nodeStep*myPartition + nodeBottom
	partitionTop := nodeStep*(myPartition+1) + nodeBottom
	return partitionBottom, partitionTop, partition, err
}

//helper method to get the node position
func getNodePosition(list *memberlist.Memberlist) (int, int) {
	// figure out which node we are
	// grab and sort the node names
	nodes := list.Members()
	var nodeNames []string
	for _, node := range nodes {
		nodeNames = append(nodeNames, node.Name)
	}
	// sort our nodes so that we have a canonical ordering
	// node failure will cause more dupes
	sort.Strings(nodeNames)
	// find our index position
	nodePosition := sort.SearchStrings(nodeNames, list.LocalNode().Name)
	nodeCount := len(nodeNames)
	return nodePosition, nodeCount
}

func (part *Partitions) getPartitionPosition(cfg *Config, queueName string) (int, *Partition, int, error) {
	//iterate over the partitions and then increase or decrease the number of partitions

	//TODO move loging out of the sync operation for better throughput
	myPartition := -1

	var err error
	poppedPartition, _ := part.partitions.Pop()
	var workingPartition *Partition
	if poppedPartition != nil {
		workingPartition = poppedPartition.(*Partition)
	} else {
		// this seems a little scary
		return myPartition, workingPartition, part.partitionCount, errors.New(NOPARTITIONS)
	}
	visTimeout, _ := cfg.GetVisibilityTimeout(queueName)
	if time.Since(workingPartition.LastUsed).Seconds() > visTimeout {
		myPartition = workingPartition.Id
	} else {
		part.partitions.Push(workingPartition, workingPartition.LastUsed.UnixNano())
		part.Lock()
		maxPartitions, _ := cfg.GetMaxPartitions(queueName)
		if part.partitionCount < maxPartitions {
			workingPartition := new(Partition)
			workingPartition.Id = part.partitionCount
			myPartition = workingPartition.Id
			part.partitionCount = part.partitionCount + 1
		} else {
			err = errors.New(NOPARTITIONS)
		}
		part.Unlock()
	}
	return myPartition, workingPartition, part.partitionCount, err
}
func (part *Partitions) PushPartition(cfg *Config, queueName string, partition *Partition, lock bool) {
	if lock {
		partition.LastUsed = time.Now()
		part.partitions.Push(partition, partition.LastUsed.UnixNano())
	} else {
		visTimeout, _ := cfg.GetVisibilityTimeout(queueName)
		unlockTime := int(visTimeout)
		partition.LastUsed = time.Now().Add(-(time.Duration(unlockTime) * time.Second))
		part.partitions.Push(partition, partition.LastUsed.UnixNano())
	}
}

func (part *Partitions) makePartitions(cfg *Config, queueName string, partitionsToMake int) {
	var initialTime time.Time
	maxPartitions, _ := cfg.GetMaxPartitions(queueName)
	offset := part.partitionCount
	for partitionId := offset; partitionId < offset+partitionsToMake; partitionId++ {
		if maxPartitions > partitionId {
			partition := new(Partition)
			partition.Id = partitionId
			partition.LastUsed = initialTime
			part.partitions.Push(partition, rand.Int63n(100000))
			part.partitionCount = part.partitionCount + 1
		}
	}
}
func (part *Partitions) syncPartitions(cfg *Config, queueName string) {

	part.Lock()
	maxPartitions, _ := cfg.GetMaxPartitions(queueName)
	minPartitions, _ := cfg.GetMinPartitions(queueName)
	maxPartitionAge, _ := cfg.GetMaxPartitionAge(queueName)

	var partsRemoved int
	for partsRemoved = 0; maxPartitions < part.partitionCount; partsRemoved++ {
		_, _ = part.partitions.Pop()
	}
	part.partitionCount = part.partitionCount - partsRemoved

	if part.partitionCount < minPartitions {
		part.makePartitions(cfg, queueName, minPartitions-part.partitionCount)
	}

	// Partition Aging logic
	// pop a partition
	var workingPartition *Partition
	poppedPartition, _ := part.partitions.Pop()
	if poppedPartition != nil {
		workingPartition = poppedPartition.(*Partition)
	} else {
		// this seems a little scary. we do a similiar thing in getPartitionPosition
		return
	}
	part.partitionCount = part.partitionCount - 1

	// check if the partition is older than the max age ( but not a fresh partition )
	// if true pop the next partition, continue until this condition
	for time.Since(workingPartition.LastUsed).Seconds() > maxPartitionAge && part.partitionCount >= minPartitions {
		poppedPartition, _ = part.partitions.Pop()
		if poppedPartition != nil {
			workingPartition = poppedPartition.(*Partition)
		}
		part.partitionCount = part.partitionCount - 1
	}
	//when false push the last popped partition
	part.partitions.Push(workingPartition, workingPartition.LastUsed.UnixNano())
	part.partitionCount = part.partitionCount + 1
	part.Unlock()
}
<commit_msg>Removing no partitions logspam<commit_after>package app

import (
	"errors"
	"github.com/Sirupsen/logrus"
	"github.com/Tapjoy/lane"
	"github.com/hashicorp/memberlist"
	"math"
	"math/rand"
	"sort"
	"sync"
	"time"
)

const NOPARTITIONS string = "no available partitions"

type Partitions struct {
	partitions     *lane.PQueue
	partitionCount int
	sync.RWMutex
}

type Partition struct {
	Id       int
	LastUsed time.Time
}

func InitPartitions(cfg *Config, queueName string) *Partitions {
	part := &Partitions{
		partitions:     lane.NewPQueue(lane.MINPQ),
		partitionCount: 0,
	}
	// We'll initially allocate the minimum amount
	minPartitions, _ := cfg.GetMinPartitions(queueName)
	part.Lock()
	part.makePartitions(cfg, queueName, minPartitions)
	part.Unlock()
	return part
}

func (part *Partitions) PartitionCount() int {
	return part.partitionCount
}
func (part *Partitions) GetPartition(cfg *Config, queueName string, list *memberlist.Memberlist) (int, int, *Partition, error) {

	//get the node position and the node count
	nodePosition, nodeCount := getNodePosition(list)

	//calculate the range that our node is responsible for
	step := math.MaxInt64 / nodeCount
	nodeBottom := nodePosition * step
	nodeTop := (nodePosition + 1) * step
	myPartition, partition, totalPartitions, err := part.getPartitionPosition(cfg, queueName)
	if err != nil && err.Error() != NOPARTITIONS {
		logrus.Error(err)
	}

	// calculate my range for the given number
	node_range := nodeTop - nodeBottom
	nodeStep := node_range / totalPartitions
	partitionBottom := nodeStep*myPartition + nodeBottom
	partitionTop := nodeStep*(myPartition+1) + nodeBottom
	return partitionBottom, partitionTop, partition, err
}

//helper method to get the node position
func getNodePosition(list *memberlist.Memberlist) (int, int) {
	// figure out which node we are
	// grab and sort the node names
	nodes := list.Members()
	var nodeNames []string
	for _, node := range nodes {
		nodeNames = append(nodeNames, node.Name)
	}
	// sort our nodes so that we have a canonical ordering
	// node failure will cause more dupes
	sort.Strings(nodeNames)
	// find our index position
	nodePosition := sort.SearchStrings(nodeNames, list.LocalNode().Name)
	nodeCount := len(nodeNames)
	return nodePosition, nodeCount
}

func (part *Partitions) getPartitionPosition(cfg *Config, queueName string) (int, *Partition, int, error) {
	//iterate over the partitions and then increase or decrease the number of partitions

	//TODO move loging out of the sync operation for better throughput
	myPartition := -1

	var err error
	poppedPartition, _ := part.partitions.Pop()
	var workingPartition *Partition
	if poppedPartition != nil {
		workingPartition = poppedPartition.(*Partition)
	} else {
		// this seems a little scary
		return myPartition, workingPartition, part.partitionCount, errors.New(NOPARTITIONS)
	}
	visTimeout, _ := cfg.GetVisibilityTimeout(queueName)
	if time.Since(workingPartition.LastUsed).Seconds() > visTimeout {
		myPartition = workingPartition.Id
	} else {
		part.partitions.Push(workingPartition, workingPartition.LastUsed.UnixNano())
		part.Lock()
		maxPartitions, _ := cfg.GetMaxPartitions(queueName)
		if part.partitionCount < maxPartitions {
			workingPartition := new(Partition)
			workingPartition.Id = part.partitionCount
			myPartition = workingPartition.Id
			part.partitionCount = part.partitionCount + 1
		} else {
			err = errors.New(NOPARTITIONS)
		}
		part.Unlock()
	}
	return myPartition, workingPartition, part.partitionCount, err
}
func (part *Partitions) PushPartition(cfg *Config, queueName string, partition *Partition, lock bool) {
	if lock {
		partition.LastUsed = time.Now()
		part.partitions.Push(partition, partition.LastUsed.UnixNano())
	} else {
		visTimeout, _ := cfg.GetVisibilityTimeout(queueName)
		unlockTime := int(visTimeout)
		partition.LastUsed = time.Now().Add(-(time.Duration(unlockTime) * time.Second))
		part.partitions.Push(partition, partition.LastUsed.UnixNano())
	}
}

func (part *Partitions) makePartitions(cfg *Config, queueName string, partitionsToMake int) {
	var initialTime time.Time
	maxPartitions, _ := cfg.GetMaxPartitions(queueName)
	offset := part.partitionCount
	for partitionId := offset; partitionId < offset+partitionsToMake; partitionId++ {
		if maxPartitions > partitionId {
			partition := new(Partition)
			partition.Id = partitionId
			partition.LastUsed = initialTime
			part.partitions.Push(partition, rand.Int63n(100000))
			part.partitionCount = part.partitionCount + 1
		}
	}
}
func (part *Partitions) syncPartitions(cfg *Config, queueName string) {

	part.Lock()
	maxPartitions, _ := cfg.GetMaxPartitions(queueName)
	minPartitions, _ := cfg.GetMinPartitions(queueName)
	maxPartitionAge, _ := cfg.GetMaxPartitionAge(queueName)

	var partsRemoved int
	for partsRemoved = 0; maxPartitions < part.partitionCount; partsRemoved++ {
		_, _ = part.partitions.Pop()
	}
	part.partitionCount = part.partitionCount - partsRemoved

	if part.partitionCount < minPartitions {
		part.makePartitions(cfg, queueName, minPartitions-part.partitionCount)
	}

	// Partition Aging logic
	// pop a partition
	var workingPartition *Partition
	poppedPartition, _ := part.partitions.Pop()
	if poppedPartition != nil {
		workingPartition = poppedPartition.(*Partition)
	} else {
		// this seems a little scary. we do a similiar thing in getPartitionPosition
		return
	}
	part.partitionCount = part.partitionCount - 1

	// check if the partition is older than the max age ( but not a fresh partition )
	// if true pop the next partition, continue until this condition
	for time.Since(workingPartition.LastUsed).Seconds() > maxPartitionAge && part.partitionCount >= minPartitions {
		poppedPartition, _ = part.partitions.Pop()
		if poppedPartition != nil {
			workingPartition = poppedPartition.(*Partition)
		}
		part.partitionCount = part.partitionCount - 1
	}
	//when false push the last popped partition
	part.partitions.Push(workingPartition, workingPartition.LastUsed.UnixNano())
	part.partitionCount = part.partitionCount + 1
	part.Unlock()
}
<|endoftext|>