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

package main

import (
	"bufio"
	"bytes"
	"errors"
	"flag"
	"fmt"
	"go/scanner"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"strings"

	"golang.org/x/tools/internal/imports"
)

var (
	// main operation modes
	list   = flag.Bool("l", false, "list files whose formatting differs from goimport's")
	write  = flag.Bool("w", false, "write result to (source) file instead of stdout")
	doDiff = flag.Bool("d", false, "display diffs instead of rewriting files")
	srcdir = flag.String("srcdir", "", "choose imports as if source code is from `dir`. When operating on a single file, dir may instead be the complete file name.")

	verbose bool // verbose logging

	cpuProfile     = flag.String("cpuprofile", "", "CPU profile output")
	memProfile     = flag.String("memprofile", "", "memory profile output")
	memProfileRate = flag.Int("memrate", 0, "if > 0, sets runtime.MemProfileRate")

	options = &imports.Options{
		TabWidth:  8,
		TabIndent: true,
		Comments:  true,
		Fragment:  true,
	}
	exitCode = 0
)

func init() {
	flag.BoolVar(&options.AllErrors, "e", false, "report all errors (not just the first 10 on different lines)")
	flag.StringVar(&options.Env.LocalPrefix, "local", "", "put imports beginning with this string after 3rd-party packages; comma-separated list")
	flag.BoolVar(&options.FormatOnly, "format-only", false, "if true, don't fix imports and only format. In this mode, goimports is effectively gofmt, with the addition that imports are grouped into sections.")
}

func report(err error) {
	scanner.PrintError(os.Stderr, err)
	exitCode = 2
}

func usage() {
	fmt.Fprintf(os.Stderr, "usage: goimports [flags] [path ...]\n")
	flag.PrintDefaults()
	os.Exit(2)
}

func isGoFile(f os.FileInfo) bool {
	// ignore non-Go files
	name := f.Name()
	return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}

// argumentType is which mode goimports was invoked as.
type argumentType int

const (
	// fromStdin means the user is piping their source into goimports.
	fromStdin argumentType = iota

	// singleArg is the common case from editors, when goimports is run on
	// a single file.
	singleArg

	// multipleArg is when the user ran "goimports file1.go file2.go"
	// or ran goimports on a directory tree.
	multipleArg
)

func processFile(filename string, in io.Reader, out io.Writer, argType argumentType) error {
	opt := options
	if argType == fromStdin {
		nopt := *options
		nopt.Fragment = true
		opt = &nopt
	}

	if in == nil {
		f, err := os.Open(filename)
		if err != nil {
			return err
		}
		defer f.Close()
		in = f
	}

	src, err := ioutil.ReadAll(in)
	if err != nil {
		return err
	}

	target := filename
	if *srcdir != "" {
		// Determine whether the provided -srcdirc is a directory or file
		// and then use it to override the target.
		//
		// See https://github.com/dominikh/go-mode.el/issues/146
		if isFile(*srcdir) {
			if argType == multipleArg {
				return errors.New("-srcdir value can't be a file when passing multiple arguments or when walking directories")
			}
			target = *srcdir
		} else if argType == singleArg && strings.HasSuffix(*srcdir, ".go") && !isDir(*srcdir) {
			// For a file which doesn't exist on disk yet, but might shortly.
			// e.g. user in editor opens $DIR/newfile.go and newfile.go doesn't yet exist on disk.
			// The goimports on-save hook writes the buffer to a temp file
			// first and runs goimports before the actual save to newfile.go.
			// The editor's buffer is named "newfile.go" so that is passed to goimports as:
			//      goimports -srcdir=/gopath/src/pkg/newfile.go /tmp/gofmtXXXXXXXX.go
			// and then the editor reloads the result from the tmp file and writes
			// it to newfile.go.
			target = *srcdir
		} else {
			// Pretend that file is from *srcdir in order to decide
			// visible imports correctly.
			target = filepath.Join(*srcdir, filepath.Base(filename))
		}
	}

	res, err := imports.Process(target, src, opt)
	if err != nil {
		return err
	}

	if !bytes.Equal(src, res) {
		// formatting has changed
		if *list {
			fmt.Fprintln(out, filename)
		}
		if *write {
			if argType == fromStdin {
				// filename is "<standard input>"
				return errors.New("can't use -w on stdin")
			}
			// On Windows, we need to re-set the permissions from the file. See golang/go#38225.
			var perms os.FileMode
			if fi, err := os.Stat(filename); err == nil {
				perms = fi.Mode() & os.ModePerm
			}
			err = ioutil.WriteFile(filename, res, perms)
			if err != nil {
				return err
			}
		}
		if *doDiff {
			if argType == fromStdin {
				filename = "stdin.go" // because <standard input>.orig looks silly
			}
			data, err := diff(src, res, filename)
			if err != nil {
				return fmt.Errorf("computing diff: %s", err)
			}
			fmt.Printf("diff -u %s %s\n", filepath.ToSlash(filename+".orig"), filepath.ToSlash(filename))
			out.Write(data)
		}
	}

	if !*list && !*write && !*doDiff {
		_, err = out.Write(res)
	}

	return err
}

func visitFile(path string, f os.FileInfo, err error) error {
	if err == nil && isGoFile(f) {
		err = processFile(path, nil, os.Stdout, multipleArg)
	}
	if err != nil {
		report(err)
	}
	return nil
}

func walkDir(path string) {
	filepath.Walk(path, visitFile)
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	// call gofmtMain in a separate function
	// so that it can use defer and have them
	// run before the exit.
	gofmtMain()
	os.Exit(exitCode)
}

// parseFlags parses command line flags and returns the paths to process.
// It's a var so that custom implementations can replace it in other files.
var parseFlags = func() []string {
	flag.BoolVar(&verbose, "v", false, "verbose logging")

	flag.Parse()
	return flag.Args()
}

func bufferedFileWriter(dest string) (w io.Writer, close func()) {
	f, err := os.Create(dest)
	if err != nil {
		log.Fatal(err)
	}
	bw := bufio.NewWriter(f)
	return bw, func() {
		if err := bw.Flush(); err != nil {
			log.Fatalf("error flushing %v: %v", dest, err)
		}
		if err := f.Close(); err != nil {
			log.Fatal(err)
		}
	}
}

func gofmtMain() {
	flag.Usage = usage
	paths := parseFlags()

	if *cpuProfile != "" {
		bw, flush := bufferedFileWriter(*cpuProfile)
		pprof.StartCPUProfile(bw)
		defer flush()
		defer pprof.StopCPUProfile()
	}
	// doTrace is a conditionally compiled wrapper around runtime/trace. It is
	// used to allow goimports to compile under gccgo, which does not support
	// runtime/trace. See https://golang.org/issue/15544.
	defer doTrace()()
	if *memProfileRate > 0 {
		runtime.MemProfileRate = *memProfileRate
		bw, flush := bufferedFileWriter(*memProfile)
		defer func() {
			runtime.GC() // materialize all statistics
			if err := pprof.WriteHeapProfile(bw); err != nil {
				log.Fatal(err)
			}
			flush()
		}()
	}

	if verbose {
		log.SetFlags(log.LstdFlags | log.Lmicroseconds)
		options.Env.Logf = log.Printf
	}
	if options.TabWidth < 0 {
		fmt.Fprintf(os.Stderr, "negative tabwidth %d\n", options.TabWidth)
		exitCode = 2
		return
	}

	if len(paths) == 0 {
		if err := processFile("<standard input>", os.Stdin, os.Stdout, fromStdin); err != nil {
			report(err)
		}
		return
	}

	argType := singleArg
	if len(paths) > 1 {
		argType = multipleArg
	}

	for _, path := range paths {
		switch dir, err := os.Stat(path); {
		case err != nil:
			report(err)
		case dir.IsDir():
			walkDir(path)
		default:
			if err := processFile(path, nil, os.Stdout, argType); err != nil {
				report(err)
			}
		}
	}
}

func writeTempFile(dir, prefix string, data []byte) (string, error) {
	file, err := ioutil.TempFile(dir, prefix)
	if err != nil {
		return "", err
	}
	_, err = file.Write(data)
	if err1 := file.Close(); err == nil {
		err = err1
	}
	if err != nil {
		os.Remove(file.Name())
		return "", err
	}
	return file.Name(), nil
}

func diff(b1, b2 []byte, filename string) (data []byte, err error) {
	f1, err := writeTempFile("", "gofmt", b1)
	if err != nil {
		return
	}
	defer os.Remove(f1)

	f2, err := writeTempFile("", "gofmt", b2)
	if err != nil {
		return
	}
	defer os.Remove(f2)

	cmd := "diff"
	if runtime.GOOS == "plan9" {
		cmd = "/bin/ape/diff"
	}

	data, err = exec.Command(cmd, "-u", f1, f2).CombinedOutput()
	if len(data) > 0 {
		// diff exits with a non-zero status when the files don't match.
		// Ignore that failure as long as we get output.
		return replaceTempFilename(data, filename)
	}
	return
}

// replaceTempFilename replaces temporary filenames in diff with actual one.
//
// --- /tmp/gofmt316145376	2017-02-03 19:13:00.280468375 -0500
// +++ /tmp/gofmt617882815	2017-02-03 19:13:00.280468375 -0500
// ...
// ->
// --- path/to/file.go.orig	2017-02-03 19:13:00.280468375 -0500
// +++ path/to/file.go	2017-02-03 19:13:00.280468375 -0500
// ...
func replaceTempFilename(diff []byte, filename string) ([]byte, error) {
	bs := bytes.SplitN(diff, []byte{'\n'}, 3)
	if len(bs) < 3 {
		return nil, fmt.Errorf("got unexpected diff for %s", filename)
	}
	// Preserve timestamps.
	var t0, t1 []byte
	if i := bytes.LastIndexByte(bs[0], '\t'); i != -1 {
		t0 = bs[0][i:]
	}
	if i := bytes.LastIndexByte(bs[1], '\t'); i != -1 {
		t1 = bs[1][i:]
	}
	// Always print filepath with slash separator.
	f := filepath.ToSlash(filename)
	bs[0] = []byte(fmt.Sprintf("--- %s%s", f+".orig", t0))
	bs[1] = []byte(fmt.Sprintf("+++ %s%s", f, t1))
	return bytes.Join(bs, []byte{'\n'}), nil
}

// isFile reports whether name is a file.
func isFile(name string) bool {
	fi, err := os.Stat(name)
	return err == nil && fi.Mode().IsRegular()
}

// isDir reports whether name is a directory.
func isDir(name string) bool {
	fi, err := os.Stat(name)
	return err == nil && fi.IsDir()
}
<commit_msg>goimport: prevent panic on parsing local prefix<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bufio"
	"bytes"
	"errors"
	"flag"
	"fmt"
	"go/scanner"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"strings"

	"golang.org/x/tools/internal/imports"
)

var (
	// main operation modes
	list   = flag.Bool("l", false, "list files whose formatting differs from goimport's")
	write  = flag.Bool("w", false, "write result to (source) file instead of stdout")
	doDiff = flag.Bool("d", false, "display diffs instead of rewriting files")
	srcdir = flag.String("srcdir", "", "choose imports as if source code is from `dir`. When operating on a single file, dir may instead be the complete file name.")

	verbose bool // verbose logging

	cpuProfile     = flag.String("cpuprofile", "", "CPU profile output")
	memProfile     = flag.String("memprofile", "", "memory profile output")
	memProfileRate = flag.Int("memrate", 0, "if > 0, sets runtime.MemProfileRate")

	options = &imports.Options{
		TabWidth:  8,
		TabIndent: true,
		Comments:  true,
		Fragment:  true,
		Env: &imports.ProcessEnv{},
	}
	exitCode = 0
)

func init() {
	flag.BoolVar(&options.AllErrors, "e", false, "report all errors (not just the first 10 on different lines)")
	flag.StringVar(&options.Env.LocalPrefix, "local", "", "put imports beginning with this string after 3rd-party packages; comma-separated list")
	flag.BoolVar(&options.FormatOnly, "format-only", false, "if true, don't fix imports and only format. In this mode, goimports is effectively gofmt, with the addition that imports are grouped into sections.")
}

func report(err error) {
	scanner.PrintError(os.Stderr, err)
	exitCode = 2
}

func usage() {
	fmt.Fprintf(os.Stderr, "usage: goimports [flags] [path ...]\n")
	flag.PrintDefaults()
	os.Exit(2)
}

func isGoFile(f os.FileInfo) bool {
	// ignore non-Go files
	name := f.Name()
	return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}

// argumentType is which mode goimports was invoked as.
type argumentType int

const (
	// fromStdin means the user is piping their source into goimports.
	fromStdin argumentType = iota

	// singleArg is the common case from editors, when goimports is run on
	// a single file.
	singleArg

	// multipleArg is when the user ran "goimports file1.go file2.go"
	// or ran goimports on a directory tree.
	multipleArg
)

func processFile(filename string, in io.Reader, out io.Writer, argType argumentType) error {
	opt := options
	if argType == fromStdin {
		nopt := *options
		nopt.Fragment = true
		opt = &nopt
	}

	if in == nil {
		f, err := os.Open(filename)
		if err != nil {
			return err
		}
		defer f.Close()
		in = f
	}

	src, err := ioutil.ReadAll(in)
	if err != nil {
		return err
	}

	target := filename
	if *srcdir != "" {
		// Determine whether the provided -srcdirc is a directory or file
		// and then use it to override the target.
		//
		// See https://github.com/dominikh/go-mode.el/issues/146
		if isFile(*srcdir) {
			if argType == multipleArg {
				return errors.New("-srcdir value can't be a file when passing multiple arguments or when walking directories")
			}
			target = *srcdir
		} else if argType == singleArg && strings.HasSuffix(*srcdir, ".go") && !isDir(*srcdir) {
			// For a file which doesn't exist on disk yet, but might shortly.
			// e.g. user in editor opens $DIR/newfile.go and newfile.go doesn't yet exist on disk.
			// The goimports on-save hook writes the buffer to a temp file
			// first and runs goimports before the actual save to newfile.go.
			// The editor's buffer is named "newfile.go" so that is passed to goimports as:
			//      goimports -srcdir=/gopath/src/pkg/newfile.go /tmp/gofmtXXXXXXXX.go
			// and then the editor reloads the result from the tmp file and writes
			// it to newfile.go.
			target = *srcdir
		} else {
			// Pretend that file is from *srcdir in order to decide
			// visible imports correctly.
			target = filepath.Join(*srcdir, filepath.Base(filename))
		}
	}

	res, err := imports.Process(target, src, opt)
	if err != nil {
		return err
	}

	if !bytes.Equal(src, res) {
		// formatting has changed
		if *list {
			fmt.Fprintln(out, filename)
		}
		if *write {
			if argType == fromStdin {
				// filename is "<standard input>"
				return errors.New("can't use -w on stdin")
			}
			// On Windows, we need to re-set the permissions from the file. See golang/go#38225.
			var perms os.FileMode
			if fi, err := os.Stat(filename); err == nil {
				perms = fi.Mode() & os.ModePerm
			}
			err = ioutil.WriteFile(filename, res, perms)
			if err != nil {
				return err
			}
		}
		if *doDiff {
			if argType == fromStdin {
				filename = "stdin.go" // because <standard input>.orig looks silly
			}
			data, err := diff(src, res, filename)
			if err != nil {
				return fmt.Errorf("computing diff: %s", err)
			}
			fmt.Printf("diff -u %s %s\n", filepath.ToSlash(filename+".orig"), filepath.ToSlash(filename))
			out.Write(data)
		}
	}

	if !*list && !*write && !*doDiff {
		_, err = out.Write(res)
	}

	return err
}

func visitFile(path string, f os.FileInfo, err error) error {
	if err == nil && isGoFile(f) {
		err = processFile(path, nil, os.Stdout, multipleArg)
	}
	if err != nil {
		report(err)
	}
	return nil
}

func walkDir(path string) {
	filepath.Walk(path, visitFile)
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	// call gofmtMain in a separate function
	// so that it can use defer and have them
	// run before the exit.
	gofmtMain()
	os.Exit(exitCode)
}

// parseFlags parses command line flags and returns the paths to process.
// It's a var so that custom implementations can replace it in other files.
var parseFlags = func() []string {
	flag.BoolVar(&verbose, "v", false, "verbose logging")

	flag.Parse()
	return flag.Args()
}

func bufferedFileWriter(dest string) (w io.Writer, close func()) {
	f, err := os.Create(dest)
	if err != nil {
		log.Fatal(err)
	}
	bw := bufio.NewWriter(f)
	return bw, func() {
		if err := bw.Flush(); err != nil {
			log.Fatalf("error flushing %v: %v", dest, err)
		}
		if err := f.Close(); err != nil {
			log.Fatal(err)
		}
	}
}

func gofmtMain() {
	flag.Usage = usage
	paths := parseFlags()

	if *cpuProfile != "" {
		bw, flush := bufferedFileWriter(*cpuProfile)
		pprof.StartCPUProfile(bw)
		defer flush()
		defer pprof.StopCPUProfile()
	}
	// doTrace is a conditionally compiled wrapper around runtime/trace. It is
	// used to allow goimports to compile under gccgo, which does not support
	// runtime/trace. See https://golang.org/issue/15544.
	defer doTrace()()
	if *memProfileRate > 0 {
		runtime.MemProfileRate = *memProfileRate
		bw, flush := bufferedFileWriter(*memProfile)
		defer func() {
			runtime.GC() // materialize all statistics
			if err := pprof.WriteHeapProfile(bw); err != nil {
				log.Fatal(err)
			}
			flush()
		}()
	}

	if verbose {
		log.SetFlags(log.LstdFlags | log.Lmicroseconds)
		options.Env.Logf = log.Printf
	}
	if options.TabWidth < 0 {
		fmt.Fprintf(os.Stderr, "negative tabwidth %d\n", options.TabWidth)
		exitCode = 2
		return
	}

	if len(paths) == 0 {
		if err := processFile("<standard input>", os.Stdin, os.Stdout, fromStdin); err != nil {
			report(err)
		}
		return
	}

	argType := singleArg
	if len(paths) > 1 {
		argType = multipleArg
	}

	for _, path := range paths {
		switch dir, err := os.Stat(path); {
		case err != nil:
			report(err)
		case dir.IsDir():
			walkDir(path)
		default:
			if err := processFile(path, nil, os.Stdout, argType); err != nil {
				report(err)
			}
		}
	}
}

func writeTempFile(dir, prefix string, data []byte) (string, error) {
	file, err := ioutil.TempFile(dir, prefix)
	if err != nil {
		return "", err
	}
	_, err = file.Write(data)
	if err1 := file.Close(); err == nil {
		err = err1
	}
	if err != nil {
		os.Remove(file.Name())
		return "", err
	}
	return file.Name(), nil
}

func diff(b1, b2 []byte, filename string) (data []byte, err error) {
	f1, err := writeTempFile("", "gofmt", b1)
	if err != nil {
		return
	}
	defer os.Remove(f1)

	f2, err := writeTempFile("", "gofmt", b2)
	if err != nil {
		return
	}
	defer os.Remove(f2)

	cmd := "diff"
	if runtime.GOOS == "plan9" {
		cmd = "/bin/ape/diff"
	}

	data, err = exec.Command(cmd, "-u", f1, f2).CombinedOutput()
	if len(data) > 0 {
		// diff exits with a non-zero status when the files don't match.
		// Ignore that failure as long as we get output.
		return replaceTempFilename(data, filename)
	}
	return
}

// replaceTempFilename replaces temporary filenames in diff with actual one.
//
// --- /tmp/gofmt316145376	2017-02-03 19:13:00.280468375 -0500
// +++ /tmp/gofmt617882815	2017-02-03 19:13:00.280468375 -0500
// ...
// ->
// --- path/to/file.go.orig	2017-02-03 19:13:00.280468375 -0500
// +++ path/to/file.go	2017-02-03 19:13:00.280468375 -0500
// ...
func replaceTempFilename(diff []byte, filename string) ([]byte, error) {
	bs := bytes.SplitN(diff, []byte{'\n'}, 3)
	if len(bs) < 3 {
		return nil, fmt.Errorf("got unexpected diff for %s", filename)
	}
	// Preserve timestamps.
	var t0, t1 []byte
	if i := bytes.LastIndexByte(bs[0], '\t'); i != -1 {
		t0 = bs[0][i:]
	}
	if i := bytes.LastIndexByte(bs[1], '\t'); i != -1 {
		t1 = bs[1][i:]
	}
	// Always print filepath with slash separator.
	f := filepath.ToSlash(filename)
	bs[0] = []byte(fmt.Sprintf("--- %s%s", f+".orig", t0))
	bs[1] = []byte(fmt.Sprintf("+++ %s%s", f, t1))
	return bytes.Join(bs, []byte{'\n'}), nil
}

// isFile reports whether name is a file.
func isFile(name string) bool {
	fi, err := os.Stat(name)
	return err == nil && fi.Mode().IsRegular()
}

// isDir reports whether name is a directory.
func isDir(name string) bool {
	fi, err := os.Stat(name)
	return err == nil && fi.IsDir()
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

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

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

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

package main

import (
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"

	utilflag "k8s.io/apiserver/pkg/util/flag"
	"k8s.io/apiserver/pkg/util/logs"
	"k8s.io/kubernetes/pkg/util"
	"k8s.io/kubernetes/pkg/version/verflag"

	"github.com/spf13/pflag"
)

// HyperKube represents a single binary that can morph/manage into multiple
// servers.
type HyperKube struct {
	Name string // The executable name, used for help and soft-link invocation
	Long string // A long description of the binary.  It will be world wrapped before output.

	servers             []Server
	baseFlags           *pflag.FlagSet
	out                 io.Writer
	helpFlagVal         bool
	makeSymlinksFlagVal bool
}

// AddServer adds a server to the HyperKube object.
func (hk *HyperKube) AddServer(s *Server) {
	hk.servers = append(hk.servers, *s)
	hk.servers[len(hk.servers)-1].hk = hk
}

// FindServer will find a specific server named name.
func (hk *HyperKube) FindServer(name string) (*Server, error) {
	for _, s := range hk.servers {
		if s.Name() == name || s.AlternativeName == name {
			return &s, nil
		}
	}
	return nil, fmt.Errorf("Server not found: %s", name)
}

// Servers returns a list of all of the registered servers
func (hk *HyperKube) Servers() []Server {
	return hk.servers
}

// Flags returns a flagset for "global" flags.
func (hk *HyperKube) Flags() *pflag.FlagSet {
	if hk.baseFlags == nil {
		hk.baseFlags = pflag.NewFlagSet(hk.Name, pflag.ContinueOnError)
		hk.baseFlags.SetOutput(ioutil.Discard)
		hk.baseFlags.SetNormalizeFunc(utilflag.WordSepNormalizeFunc)
		hk.baseFlags.BoolVarP(&hk.helpFlagVal, "help", "h", false, "help for "+hk.Name)
		hk.baseFlags.BoolVar(&hk.makeSymlinksFlagVal, "make-symlinks", false, "create a symlink for each server in current directory")
		hk.baseFlags.MarkHidden("make-symlinks") // hide this flag from appearing in servers' usage output

		// These will add all of the "global" flags (defined with both the
		// flag and pflag packages) to the new flag set we have.
		hk.baseFlags.AddGoFlagSet(flag.CommandLine)
		hk.baseFlags.AddFlagSet(pflag.CommandLine)

	}
	return hk.baseFlags
}

// Out returns the io.Writer that is used for all usage/error information
func (hk *HyperKube) Out() io.Writer {
	if hk.out == nil {
		hk.out = os.Stderr
	}
	return hk.out
}

// SetOut sets the output writer for all usage/error information
func (hk *HyperKube) SetOut(w io.Writer) {
	hk.out = w
}

// Print is a convenience method to Print to the defined output
func (hk *HyperKube) Print(i ...interface{}) {
	fmt.Fprint(hk.Out(), i...)
}

// Println is a convenience method to Println to the defined output
func (hk *HyperKube) Println(i ...interface{}) {
	fmt.Fprintln(hk.Out(), i...)
}

// Printf is a convenience method to Printf to the defined output
func (hk *HyperKube) Printf(format string, i ...interface{}) {
	fmt.Fprintf(hk.Out(), format, i...)
}

// Run the server.  This will pick the appropriate server and run it.
func (hk *HyperKube) Run(args []string) error {
	// If we are called directly, parse all flags up to the first real
	// argument.  That should be the server to run.
	command := args[0]
	baseCommand := path.Base(command)
	serverName := baseCommand
	args = args[1:]
	if serverName == hk.Name {

		baseFlags := hk.Flags()
		baseFlags.SetInterspersed(false) // Only parse flags up to the next real command
		err := baseFlags.Parse(args)
		if err != nil || hk.helpFlagVal {
			if err != nil {
				hk.Println("Error:", err)
			}
			hk.Usage()
			return err
		}

		if hk.makeSymlinksFlagVal {
			return hk.MakeSymlinks(command)
		}

		verflag.PrintAndExitIfRequested()

		args = baseFlags.Args()
		if len(args) > 0 && len(args[0]) > 0 {
			serverName = args[0]
			baseCommand = baseCommand + " " + serverName
			args = args[1:]
		} else {
			err = errors.New("no server specified")
			hk.Printf("Error: %v\n\n", err)
			hk.Usage()
			return err
		}
	}

	s, err := hk.FindServer(serverName)
	if err != nil {
		hk.Printf("Error: %v\n\n", err)
		hk.Usage()
		return err
	}

	s.Flags().AddFlagSet(hk.Flags())
	err = s.Flags().Parse(args)
	if err != nil || hk.helpFlagVal {
		if err != nil {
			hk.Printf("Error: %v\n\n", err)
		}
		s.Usage()
		return err
	}

	verflag.PrintAndExitIfRequested()

	logs.InitLogs()
	defer logs.FlushLogs()

	err = s.Run(s, s.Flags().Args())
	if err != nil {
		hk.Println("Error:", err)
	}

	return err
}

// RunToExit will run the hyperkube and then call os.Exit with an appropriate exit code.
func (hk *HyperKube) RunToExit(args []string) {
	err := hk.Run(args)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err.Error())
		os.Exit(1)
	}
	os.Exit(0)
}

// Usage will write out a summary for all servers that this binary supports.
func (hk *HyperKube) Usage() {
	tt := `{{if .Long}}{{.Long | trim | wrap ""}}
{{end}}Usage

  {{.Name}} <server> [flags]

Servers
{{range .Servers}}
  {{.Name}}
{{.Long | trim | wrap "    "}}{{end}}
Call '{{.Name}} --make-symlinks' to create symlinks for each server in the local directory.
Call '{{.Name}} <server> --help' for help on a specific server.
`
	util.ExecuteTemplate(hk.Out(), tt, hk)
}

// MakeSymlinks will create a symlink for each registered hyperkube server in the local directory.
func (hk *HyperKube) MakeSymlinks(command string) error {
	wd, err := os.Getwd()
	if err != nil {
		return err
	}

	var errs bool
	for _, s := range hk.servers {
		link := path.Join(wd, s.Name())

		err := os.Symlink(command, link)
		if err != nil {
			errs = true
			hk.Println(err)
		}
	}

	if errs {
		return errors.New("Error creating one or more symlinks.")
	}
	return nil
}
<commit_msg>ineffectual assignment to baseCommand, delete it<commit_after>/*
Copyright 2014 The Kubernetes Authors.

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

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

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

package main

import (
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"

	utilflag "k8s.io/apiserver/pkg/util/flag"
	"k8s.io/apiserver/pkg/util/logs"
	"k8s.io/kubernetes/pkg/util"
	"k8s.io/kubernetes/pkg/version/verflag"

	"github.com/spf13/pflag"
)

// HyperKube represents a single binary that can morph/manage into multiple
// servers.
type HyperKube struct {
	Name string // The executable name, used for help and soft-link invocation
	Long string // A long description of the binary.  It will be world wrapped before output.

	servers             []Server
	baseFlags           *pflag.FlagSet
	out                 io.Writer
	helpFlagVal         bool
	makeSymlinksFlagVal bool
}

// AddServer adds a server to the HyperKube object.
func (hk *HyperKube) AddServer(s *Server) {
	hk.servers = append(hk.servers, *s)
	hk.servers[len(hk.servers)-1].hk = hk
}

// FindServer will find a specific server named name.
func (hk *HyperKube) FindServer(name string) (*Server, error) {
	for _, s := range hk.servers {
		if s.Name() == name || s.AlternativeName == name {
			return &s, nil
		}
	}
	return nil, fmt.Errorf("Server not found: %s", name)
}

// Servers returns a list of all of the registered servers
func (hk *HyperKube) Servers() []Server {
	return hk.servers
}

// Flags returns a flagset for "global" flags.
func (hk *HyperKube) Flags() *pflag.FlagSet {
	if hk.baseFlags == nil {
		hk.baseFlags = pflag.NewFlagSet(hk.Name, pflag.ContinueOnError)
		hk.baseFlags.SetOutput(ioutil.Discard)
		hk.baseFlags.SetNormalizeFunc(utilflag.WordSepNormalizeFunc)
		hk.baseFlags.BoolVarP(&hk.helpFlagVal, "help", "h", false, "help for "+hk.Name)
		hk.baseFlags.BoolVar(&hk.makeSymlinksFlagVal, "make-symlinks", false, "create a symlink for each server in current directory")
		hk.baseFlags.MarkHidden("make-symlinks") // hide this flag from appearing in servers' usage output

		// These will add all of the "global" flags (defined with both the
		// flag and pflag packages) to the new flag set we have.
		hk.baseFlags.AddGoFlagSet(flag.CommandLine)
		hk.baseFlags.AddFlagSet(pflag.CommandLine)

	}
	return hk.baseFlags
}

// Out returns the io.Writer that is used for all usage/error information
func (hk *HyperKube) Out() io.Writer {
	if hk.out == nil {
		hk.out = os.Stderr
	}
	return hk.out
}

// SetOut sets the output writer for all usage/error information
func (hk *HyperKube) SetOut(w io.Writer) {
	hk.out = w
}

// Print is a convenience method to Print to the defined output
func (hk *HyperKube) Print(i ...interface{}) {
	fmt.Fprint(hk.Out(), i...)
}

// Println is a convenience method to Println to the defined output
func (hk *HyperKube) Println(i ...interface{}) {
	fmt.Fprintln(hk.Out(), i...)
}

// Printf is a convenience method to Printf to the defined output
func (hk *HyperKube) Printf(format string, i ...interface{}) {
	fmt.Fprintf(hk.Out(), format, i...)
}

// Run the server.  This will pick the appropriate server and run it.
func (hk *HyperKube) Run(args []string) error {
	// If we are called directly, parse all flags up to the first real
	// argument.  That should be the server to run.
	command := args[0]
	serverName := path.Base(command)
	args = args[1:]
	if serverName == hk.Name {

		baseFlags := hk.Flags()
		baseFlags.SetInterspersed(false) // Only parse flags up to the next real command
		err := baseFlags.Parse(args)
		if err != nil || hk.helpFlagVal {
			if err != nil {
				hk.Println("Error:", err)
			}
			hk.Usage()
			return err
		}

		if hk.makeSymlinksFlagVal {
			return hk.MakeSymlinks(command)
		}

		verflag.PrintAndExitIfRequested()

		args = baseFlags.Args()
		if len(args) > 0 && len(args[0]) > 0 {
			serverName = args[0]
			args = args[1:]
		} else {
			err = errors.New("no server specified")
			hk.Printf("Error: %v\n\n", err)
			hk.Usage()
			return err
		}
	}

	s, err := hk.FindServer(serverName)
	if err != nil {
		hk.Printf("Error: %v\n\n", err)
		hk.Usage()
		return err
	}

	s.Flags().AddFlagSet(hk.Flags())
	err = s.Flags().Parse(args)
	if err != nil || hk.helpFlagVal {
		if err != nil {
			hk.Printf("Error: %v\n\n", err)
		}
		s.Usage()
		return err
	}

	verflag.PrintAndExitIfRequested()

	logs.InitLogs()
	defer logs.FlushLogs()

	err = s.Run(s, s.Flags().Args())
	if err != nil {
		hk.Println("Error:", err)
	}

	return err
}

// RunToExit will run the hyperkube and then call os.Exit with an appropriate exit code.
func (hk *HyperKube) RunToExit(args []string) {
	err := hk.Run(args)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err.Error())
		os.Exit(1)
	}
	os.Exit(0)
}

// Usage will write out a summary for all servers that this binary supports.
func (hk *HyperKube) Usage() {
	tt := `{{if .Long}}{{.Long | trim | wrap ""}}
{{end}}Usage

  {{.Name}} <server> [flags]

Servers
{{range .Servers}}
  {{.Name}}
{{.Long | trim | wrap "    "}}{{end}}
Call '{{.Name}} --make-symlinks' to create symlinks for each server in the local directory.
Call '{{.Name}} <server> --help' for help on a specific server.
`
	util.ExecuteTemplate(hk.Out(), tt, hk)
}

// MakeSymlinks will create a symlink for each registered hyperkube server in the local directory.
func (hk *HyperKube) MakeSymlinks(command string) error {
	wd, err := os.Getwd()
	if err != nil {
		return err
	}

	var errs bool
	for _, s := range hk.servers {
		link := path.Join(wd, s.Name())

		err := os.Symlink(command, link)
		if err != nil {
			errs = true
			hk.Println(err)
		}
	}

	if errs {
		return errors.New("Error creating one or more symlinks.")
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package backups

import (
	"fmt"
	"io"
	"os"

	"github.com/juju/cmd"
	"github.com/juju/errors"
	"launchpad.net/gnuflag"

	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/state/backups"
)

const uploadDoc = `
"upload" sends a backup archive file to remote storage.
`

// UploadCommand is the sub-command for uploading a backup archive.
type UploadCommand struct {
	CommandBase
	// Filename is where to find the archive to upload.
	Filename string
	// ShowMeta indicates that the uploaded metadata should be printed.
	ShowMeta bool
	// Quiet indicates that the new backup ID should not be printed.
	Quiet bool
}

// SetFlags implements Command.SetFlags.
func (c *UploadCommand) SetFlags(f *gnuflag.FlagSet) {
	f.BoolVar(&c.ShowMeta, "verbose", false, "show the uploaded metadata")
	f.BoolVar(&c.Quiet, "quiet", false, "do not print the new backup ID")
}

// Info implements Command.Info.
func (c *UploadCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "upload",
		Args:    "<filename>",
		Purpose: "store a backup archive file remotely in juju",
		Doc:     uploadDoc,
	}
}

// Init implements Command.Init.
func (c *UploadCommand) Init(args []string) error {
	if len(args) == 0 {
		return errors.New("backup filename not specified")
	}
	filename, args := args[0], args[1:]
	if err := cmd.CheckEmpty(args); err != nil {
		return errors.Trace(err)
	}
	c.Filename = filename
	return nil
}

// Run implements Command.Run.
func (c *UploadCommand) Run(ctx *cmd.Context) error {
	client, err := c.NewAPIClient()
	if err != nil {
		return errors.Trace(err)
	}
	defer client.Close()

	archive, meta, err := c.getArchive(c.Filename)
	if err != nil {
		return errors.Trace(err)
	}
	defer archive.Close()

	if c.ShowMeta {
		fmt.Fprintln(ctx.Stdout, "Uploaded metadata:")
		c.dumpMetadata(ctx, meta)
		fmt.Fprintln(ctx.Stdout)
	}

	// Upload the archive.
	id, err := client.Upload(archive, *meta)
	if err != nil {
		return errors.Trace(err)
	}

	if c.Quiet {
		fmt.Fprintln(ctx.Stdout, id)
		return nil
	}

	// Pull the stored metadata.
	stored, err := client.Info(id)
	if err != nil {
		return errors.Trace(err)
	}

	c.dumpMetadata(ctx, stored)
	return nil
}

func (c *UploadCommand) getArchive(filename string) (io.ReadCloser, *params.BackupsMetadataResult, error) {

	archive, err := os.Open(filename)
	if err != nil {
		return nil, nil, errors.Trace(err)
	}

	// Extract the metadata.
	ad, err := backups.NewArchiveDataReader(archive)
	if err != nil {
		return nil, nil, errors.Trace(err)
	}
	_, err = archive.Seek(0, os.SEEK_SET)
	if err != nil {
		return nil, nil, errors.Trace(err)
	}
	meta, err := ad.Metadata()
	if err != nil {
		if !errors.IsNotFound(err) {
			return nil, nil, errors.Trace(err)
		}
		meta, err = backups.BuildMetadata(archive)
		if err != nil {
			return nil, nil, errors.Trace(err)
		}
	} else {
		// Make sure the file info is set.
		fileMeta, err := backups.BuildMetadata(archive)
		if err != nil {
			return nil, nil, errors.Trace(err)
		}
		if meta.Size() == int64(0) {
			if err := meta.SetFileInfo(fileMeta.Size(), "", ""); err != nil {
				return nil, nil, errors.Trace(err)
			}
		}
		if meta.Checksum() == "" {
			err := meta.SetFileInfo(0, fileMeta.Checksum(), fileMeta.ChecksumFormat())
			if err != nil {
				return nil, nil, errors.Trace(err)
			}
		}
		if meta.Finished == nil || meta.Finished.IsZero() {
			meta.Finished = fileMeta.Finished
		}
	}
	_, err = archive.Seek(0, os.SEEK_SET)
	if err != nil {
		return nil, nil, errors.Trace(err)
	}

	// Pack the metadata into a result.
	var metaResult params.BackupsMetadataResult
	metaResult.UpdateFromMetadata(meta)

	return archive, &metaResult, nil
}
<commit_msg>Use a new API client for the Info call.<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package backups

import (
	"fmt"
	"io"
	"os"

	"github.com/juju/cmd"
	"github.com/juju/errors"
	"launchpad.net/gnuflag"

	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/state/backups"
)

const uploadDoc = `
"upload" sends a backup archive file to remote storage.
`

// UploadCommand is the sub-command for uploading a backup archive.
type UploadCommand struct {
	CommandBase
	// Filename is where to find the archive to upload.
	Filename string
	// ShowMeta indicates that the uploaded metadata should be printed.
	ShowMeta bool
	// Quiet indicates that the new backup ID should not be printed.
	Quiet bool
}

// SetFlags implements Command.SetFlags.
func (c *UploadCommand) SetFlags(f *gnuflag.FlagSet) {
	f.BoolVar(&c.ShowMeta, "verbose", false, "show the uploaded metadata")
	f.BoolVar(&c.Quiet, "quiet", false, "do not print the new backup ID")
}

// Info implements Command.Info.
func (c *UploadCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "upload",
		Args:    "<filename>",
		Purpose: "store a backup archive file remotely in juju",
		Doc:     uploadDoc,
	}
}

// Init implements Command.Init.
func (c *UploadCommand) Init(args []string) error {
	if len(args) == 0 {
		return errors.New("backup filename not specified")
	}
	filename, args := args[0], args[1:]
	if err := cmd.CheckEmpty(args); err != nil {
		return errors.Trace(err)
	}
	c.Filename = filename
	return nil
}

// Run implements Command.Run.
func (c *UploadCommand) Run(ctx *cmd.Context) error {
	client, err := c.NewAPIClient()
	if err != nil {
		return errors.Trace(err)
	}
	defer client.Close()

	archive, meta, err := c.getArchive(c.Filename)
	if err != nil {
		return errors.Trace(err)
	}
	defer archive.Close()

	if c.ShowMeta {
		fmt.Fprintln(ctx.Stdout, "Uploaded metadata:")
		c.dumpMetadata(ctx, meta)
		fmt.Fprintln(ctx.Stdout)
	}

	// Upload the archive.
	id, err := client.Upload(archive, *meta)
	if err != nil {
		return errors.Trace(err)
	}

	if c.Quiet {
		fmt.Fprintln(ctx.Stdout, id)
		return nil
	}

	// Pull the stored metadata.
	stored, err := c.getStoredMetadata(id)
	if err != nil {
		return errors.Trace(err)
	}

	c.dumpMetadata(ctx, stored)
	return nil
}

func (c *UploadCommand) getStoredMetadata(id string) (*params.BackupsMetadataResult, error) {
	// TODO(ericsnow) lp-1399722 This should be addressed.
	// There is at least anecdotal evidence that we cannot use an API
	// client for more than a single request. So we use a new client
	// for download.
	client, err := c.NewAPIClient()
	if err != nil {
		return errors.Trace(err)
	}
	defer client.Close()

	stored, err := client.Info(id)
	return stored, errors.Trace(err)
}

func (c *UploadCommand) getArchive(filename string) (io.ReadCloser, *params.BackupsMetadataResult, error) {

	archive, err := os.Open(filename)
	if err != nil {
		return nil, nil, errors.Trace(err)
	}

	// Extract the metadata.
	ad, err := backups.NewArchiveDataReader(archive)
	if err != nil {
		return nil, nil, errors.Trace(err)
	}
	_, err = archive.Seek(0, os.SEEK_SET)
	if err != nil {
		return nil, nil, errors.Trace(err)
	}
	meta, err := ad.Metadata()
	if err != nil {
		if !errors.IsNotFound(err) {
			return nil, nil, errors.Trace(err)
		}
		meta, err = backups.BuildMetadata(archive)
		if err != nil {
			return nil, nil, errors.Trace(err)
		}
	} else {
		// Make sure the file info is set.
		fileMeta, err := backups.BuildMetadata(archive)
		if err != nil {
			return nil, nil, errors.Trace(err)
		}
		if meta.Size() == int64(0) {
			if err := meta.SetFileInfo(fileMeta.Size(), "", ""); err != nil {
				return nil, nil, errors.Trace(err)
			}
		}
		if meta.Checksum() == "" {
			err := meta.SetFileInfo(0, fileMeta.Checksum(), fileMeta.ChecksumFormat())
			if err != nil {
				return nil, nil, errors.Trace(err)
			}
		}
		if meta.Finished == nil || meta.Finished.IsZero() {
			meta.Finished = fileMeta.Finished
		}
	}
	_, err = archive.Seek(0, os.SEEK_SET)
	if err != nil {
		return nil, nil, errors.Trace(err)
	}

	// Pack the metadata into a result.
	var metaResult params.BackupsMetadataResult
	metaResult.UpdateFromMetadata(meta)

	return archive, &metaResult, nil
}
<|endoftext|>


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

package main

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
)

type pullRequestConfig struct {
	// cvr control version repository
	cvr CVR

	// logger of the pull request
	logger *logrus.Entry

	// commentTrigger is the comment that must be present to test
	// this pull request, if comment is empty then no comment is needed
	commentTrigger RepoComment

	// postOnSuccess is the comment to post if test finished correctly
	postOnSuccess string

	// postOnFailure is the comment to post if test failed
	postOnFailure string

	// whitelist is the list of users whose pull request can be tested
	whitelist string
}

// pullRequest represents a pull request
// present in a control version repository
type pullRequest struct {
	// repoBranch of the pull request
	repoBranch

	// info of the pull request
	info pullRequestInfo

	// number of the pull request
	number int

	// commits in the pull request
	commits []repoCommit

	// whitelist is the list of users whose pull request can be tested
	whitelist string

	// commentTrigger is the comment that must be present to trigger the test
	// also it is used to ensure that the pull request is tested only when
	// the comment appears after the list of commits
	commentTrigger *RepoComment

	// postOnSuccess is the comment to post if test finished correctly
	postOnSuccess string

	// postOnFailure is the comment to post if test failed
	postOnFailure string
}

// newPullRequest returns a new pullRequest object
func newPullRequest(pr int, config pullRequestConfig) (*pullRequest, error) {
	logger := config.logger
	cvr := config.cvr

	logger.Debugf("creating pull request: %d", pr)

	i, err := cvr.getPullRequestInfo(pr)
	if err != nil {
		return nil, fmt.Errorf("failed to get pull request info %s", err)
	}

	logger.Debugf("pull request %d info: %+v", pr, i)

	commits, err := cvr.getPullRequestCommits(pr)
	if err != nil {
		return nil, fmt.Errorf("failed to get pull request commits %s", err)
	}

	var shas string
	for _, c := range commits {
		shas += c.sha + ","
	}

	var commentTrigger *RepoComment
	if len(config.commentTrigger.Comment) != 0 {
		comment, err := cvr.getLatestPullRequestComment(pr, config.commentTrigger.User,
			config.commentTrigger.Comment)
		if err == nil {
			commentTrigger = &comment
		}
	}

	logger.Debugf("pull request %d comment trigger: %#v", pr, commentTrigger)

	return &pullRequest{
		repoBranch: repoBranch{
			info:        repoBranchInfo{sha: shas},
			name:        i.branch,
			cvr:         cvr,
			beingTested: false,
			logger:      logger,
		},
		info:           i,
		number:         pr,
		commits:        commits,
		whitelist:      config.whitelist,
		commentTrigger: commentTrigger,
		postOnFailure:  config.postOnFailure,
		postOnSuccess:  config.postOnSuccess,
	}, nil
}

func (pr *pullRequest) authorInWhitelist() bool {
	for _, u := range strings.Split(pr.whitelist, ",") {
		switch u {
		// check if user is a member of the organization
		case "@":
			author := pr.info.author
			ok, err := pr.repoBranch.cvr.isMember(author)
			if ok {
				return true
			}
			if err != nil {
				pr.repoBranch.logger.Errorf("unable to know if user %s is a member of the organization: %s",
					author, err)
			}
		// any pull request can be tested
		case "*":
			return true

		// specific users
		default:
			if u == pr.info.author {
				return true
			}
		}
	}

	return false
}

// canBeTested returns an error if the pull request cannot be tested
func (pr *pullRequest) canBeTested() error {
	if !pr.info.mergeable {
		return fmt.Errorf("the pull request is not mergeable")
	}

	commitsLen := len(pr.commits)
	if commitsLen == 0 {
		return fmt.Errorf("there are no commits to test")
	}

	latestCommit := pr.commits[commitsLen-1]
	if pr.commentTrigger != nil && pr.commentTrigger.time.Unix() < latestCommit.time.Unix() {
		return fmt.Errorf("there are new commits after latest comment trigger %+v", *pr.commentTrigger)
	}

	if pr.info.state == "closed" {
		return fmt.Errorf("the state of pull request %d is %s", pr.number, pr.info.state)
	}

	if len(pr.whitelist) > 0 && !pr.authorInWhitelist() {
		return fmt.Errorf("the author of the pull request is not in the whitelist")
	}

	return nil
}

// download the source code of the revision inside the specific directory path
func (pr *pullRequest) download(path string) error {
	pr.repoBranch.logger.Debugf("downloading pull request %d", pr.number)

	return pr.repoBranch.cvr.downloadPullRequest(pr.number, pr.repoBranch.name, path)
}

func (pr *pullRequest) test(config stageConfig, stages map[string]stage) error {
	pr.repoBranch.logger.Debugf("testing pull request %d", pr.number)

	envars := []string{fmt.Sprintf("LOCALCI_PR_NUMBER=%d", pr.number)}
	config.env = append(config.env, envars...)

	err := pr.repoBranch.test(config, stages)
	postMsg := pr.postOnSuccess
	if err != nil {
		postMsg = pr.postOnFailure
	}

	if e := pr.repoBranch.cvr.createComment(pr.number, postMsg); e != nil {
		pr.repoBranch.logger.Errorf("failed to create comment on pull request %d: %s",
			pr.number, e)
	}

	return err
}

func (pr *pullRequest) id() string {
	return strconv.Itoa(pr.number)
}

func (pr *pullRequest) equal(r interface{}) bool {
	rpr, ok := r.(*pullRequest)
	if !ok {
		return false
	}

	return pr.repoBranch.equal((revision)(&rpr.repoBranch))
}

func (pr *pullRequest) logDirName() string {
	return pr.id()
}
<commit_msg>localCI: do not try to post empty messages<commit_after>// Copyright (c) 2017 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
)

type pullRequestConfig struct {
	// cvr control version repository
	cvr CVR

	// logger of the pull request
	logger *logrus.Entry

	// commentTrigger is the comment that must be present to test
	// this pull request, if comment is empty then no comment is needed
	commentTrigger RepoComment

	// postOnSuccess is the comment to post if test finished correctly
	postOnSuccess string

	// postOnFailure is the comment to post if test failed
	postOnFailure string

	// whitelist is the list of users whose pull request can be tested
	whitelist string
}

// pullRequest represents a pull request
// present in a control version repository
type pullRequest struct {
	// repoBranch of the pull request
	repoBranch

	// info of the pull request
	info pullRequestInfo

	// number of the pull request
	number int

	// commits in the pull request
	commits []repoCommit

	// whitelist is the list of users whose pull request can be tested
	whitelist string

	// commentTrigger is the comment that must be present to trigger the test
	// also it is used to ensure that the pull request is tested only when
	// the comment appears after the list of commits
	commentTrigger *RepoComment

	// postOnSuccess is the comment to post if test finished correctly
	postOnSuccess string

	// postOnFailure is the comment to post if test failed
	postOnFailure string
}

// newPullRequest returns a new pullRequest object
func newPullRequest(pr int, config pullRequestConfig) (*pullRequest, error) {
	logger := config.logger
	cvr := config.cvr

	logger.Debugf("creating pull request: %d", pr)

	i, err := cvr.getPullRequestInfo(pr)
	if err != nil {
		return nil, fmt.Errorf("failed to get pull request info %s", err)
	}

	logger.Debugf("pull request %d info: %+v", pr, i)

	commits, err := cvr.getPullRequestCommits(pr)
	if err != nil {
		return nil, fmt.Errorf("failed to get pull request commits %s", err)
	}

	var shas string
	for _, c := range commits {
		shas += c.sha + ","
	}

	var commentTrigger *RepoComment
	if len(config.commentTrigger.Comment) != 0 {
		comment, err := cvr.getLatestPullRequestComment(pr, config.commentTrigger.User,
			config.commentTrigger.Comment)
		if err == nil {
			commentTrigger = &comment
		}
	}

	logger.Debugf("pull request %d comment trigger: %#v", pr, commentTrigger)

	return &pullRequest{
		repoBranch: repoBranch{
			info:        repoBranchInfo{sha: shas},
			name:        i.branch,
			cvr:         cvr,
			beingTested: false,
			logger:      logger,
		},
		info:           i,
		number:         pr,
		commits:        commits,
		whitelist:      config.whitelist,
		commentTrigger: commentTrigger,
		postOnFailure:  config.postOnFailure,
		postOnSuccess:  config.postOnSuccess,
	}, nil
}

func (pr *pullRequest) authorInWhitelist() bool {
	for _, u := range strings.Split(pr.whitelist, ",") {
		switch u {
		// check if user is a member of the organization
		case "@":
			author := pr.info.author
			ok, err := pr.repoBranch.cvr.isMember(author)
			if ok {
				return true
			}
			if err != nil {
				pr.repoBranch.logger.Errorf("unable to know if user %s is a member of the organization: %s",
					author, err)
			}
		// any pull request can be tested
		case "*":
			return true

		// specific users
		default:
			if u == pr.info.author {
				return true
			}
		}
	}

	return false
}

// canBeTested returns an error if the pull request cannot be tested
func (pr *pullRequest) canBeTested() error {
	if !pr.info.mergeable {
		return fmt.Errorf("the pull request is not mergeable")
	}

	commitsLen := len(pr.commits)
	if commitsLen == 0 {
		return fmt.Errorf("there are no commits to test")
	}

	latestCommit := pr.commits[commitsLen-1]
	if pr.commentTrigger != nil && pr.commentTrigger.time.Unix() < latestCommit.time.Unix() {
		return fmt.Errorf("there are new commits after latest comment trigger %+v", *pr.commentTrigger)
	}

	if pr.info.state == "closed" {
		return fmt.Errorf("the state of pull request %d is %s", pr.number, pr.info.state)
	}

	if len(pr.whitelist) > 0 && !pr.authorInWhitelist() {
		return fmt.Errorf("the author of the pull request is not in the whitelist")
	}

	return nil
}

// download the source code of the revision inside the specific directory path
func (pr *pullRequest) download(path string) error {
	pr.repoBranch.logger.Debugf("downloading pull request %d", pr.number)

	return pr.repoBranch.cvr.downloadPullRequest(pr.number, pr.repoBranch.name, path)
}

func (pr *pullRequest) test(config stageConfig, stages map[string]stage) error {
	pr.repoBranch.logger.Debugf("testing pull request %d", pr.number)

	envars := []string{fmt.Sprintf("LOCALCI_PR_NUMBER=%d", pr.number)}
	config.env = append(config.env, envars...)

	err := pr.repoBranch.test(config, stages)
	postMsg := pr.postOnSuccess
	if err != nil {
		postMsg = pr.postOnFailure
	}

	// do not try to post empty messages
	if len(postMsg) == 0 {
		return err
	}

	if e := pr.repoBranch.cvr.createComment(pr.number, postMsg); e != nil {
		pr.repoBranch.logger.Errorf("failed to create comment on pull request %d: %s",
			pr.number, e)
	}

	return err
}

func (pr *pullRequest) id() string {
	return strconv.Itoa(pr.number)
}

func (pr *pullRequest) equal(r interface{}) bool {
	rpr, ok := r.(*pullRequest)
	if !ok {
		return false
	}

	return pr.repoBranch.equal((revision)(&rpr.repoBranch))
}

func (pr *pullRequest) logDirName() string {
	return pr.id()
}
<|endoftext|>


<commit_before>// mailbox_server - to receive open click.
/*
Usage:

	mailbox_server [flags]

The flags are:

	`-p`: Http port, default is `:8801`

*/
package main

import (
	"encoding/hex"
	"flag"
	"log"
	"net/http"
	"net/url"
	"regexp"

	_ "github.com/go-sql-driver/mysql"
	"github.com/toomore/mailbox/campaign"
	"github.com/toomore/mailbox/reader"
	"github.com/toomore/mailbox/utils"
)

var (
	httpPort = flag.String("p", ":8801", "Http port")
	cExpr    = regexp.MustCompile(`/(read|door)/([0-9a-z]+)`)
)

func read(w http.ResponseWriter, r *http.Request) {
	v, _ := url.ParseQuery(r.Header.Get("X-Args"))

	var hm string
	match := cExpr.FindStringSubmatch(r.Header.Get("X-Uri"))
	if len(match) >= 3 {
		hm = match[2]
	}
	w.WriteHeader(http.StatusNotFound)
	hmbyte, _ := hex.DecodeString(hm)
	if campaign.CheckMac(hmbyte, v.Get("c"), v) {
		log.Println("[read] Pass")
		reader.Save(v.Get("c"), v.Get("u"), r.Header.Get("X-Real-Ip"), r.Header.Get("User-Agent"))
	} else {
		log.Println("[read] Hash Fail!!!")
	}
	log.Printf("%+v", r)
	log.Println(r.Header.Get("X-Real-Ip"))
	log.Println(r.Header.Get("User-Agent"))
}

func door(w http.ResponseWriter, r *http.Request) {
	v, _ := url.ParseQuery(r.Header.Get("X-Args"))
	if v.Get("t") != "a" {
		log.Println("No `t`", v.Get("t"))
		return
	}

	var hm string
	match := cExpr.FindStringSubmatch(r.Header.Get("X-Uri"))
	log.Println(match)
	if len(match) >= 3 {
		hm = match[2]
	}
	hmbyte, _ := hex.DecodeString(hm)
	if campaign.CheckMac(hmbyte, v.Get("c"), v) {
		utils.GetConn().Query(`INSERT INTO doors(cid,uid,linkid,ip,agent) VALUES(?,?,?,?,?)`,
			v.Get("c"), v.Get("u"), v.Get("l"), r.Header.Get("X-Real-Ip"), r.Header.Get("User-Agent"))
		log.Println("[door] Pass")
	} else {
		log.Println("[door] Hash Fail!!!")
	}
	log.Printf("%+v", r)
	log.Println(r.Header.Get("X-Real-Ip"))
	log.Println(r.Header.Get("User-Agent"))
	rows, err := utils.GetConn().Query(`SELECT url FROM links WHERE cid=? AND id=?`, v.Get("c"), v.Get("l"))
	if err == nil {
		for rows.Next() {
			var url string
			rows.Scan(&url)
			http.Redirect(w, r, url, http.StatusSeeOther)
		}
	}
}

func main() {
	flag.Parse()
	http.HandleFunc("/read/", read)
	http.HandleFunc("/door/", door)
	log.Println("HTTP Port:", *httpPort)
	log.Println(http.ListenAndServe(*httpPort, nil))
}
<commit_msg>Add linkscache<commit_after>// mailbox_server - to receive open click.
/*
Usage:

	mailbox_server [flags]

The flags are:

	`-p`: Http port, default is `:8801`

*/
package main

import (
	"encoding/hex"
	"flag"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"regexp"

	_ "github.com/go-sql-driver/mysql"
	"github.com/toomore/mailbox/campaign"
	"github.com/toomore/mailbox/reader"
	"github.com/toomore/mailbox/utils"
)

var (
	cExpr      = regexp.MustCompile(`/(read|door)/([0-9a-z]+)`)
	httpPort   = flag.String("p", ":8801", "Http port")
	linksCache = make(map[string]string)
)

func read(w http.ResponseWriter, r *http.Request) {
	v, _ := url.ParseQuery(r.Header.Get("X-Args"))

	var hm string
	match := cExpr.FindStringSubmatch(r.Header.Get("X-Uri"))
	if len(match) >= 3 {
		hm = match[2]
	}
	w.WriteHeader(http.StatusNotFound)
	hmbyte, _ := hex.DecodeString(hm)
	if campaign.CheckMac(hmbyte, v.Get("c"), v) {
		log.Println("[read] Pass")
		reader.Save(v.Get("c"), v.Get("u"), r.Header.Get("X-Real-Ip"), r.Header.Get("User-Agent"))
	} else {
		log.Println("[read] Hash Fail!!!")
	}
	log.Printf("%+v", r)
	log.Println(r.Header.Get("X-Real-Ip"))
	log.Println(r.Header.Get("User-Agent"))
}

func door(w http.ResponseWriter, r *http.Request) {
	v, _ := url.ParseQuery(r.Header.Get("X-Args"))
	if v.Get("t") != "a" {
		log.Println("No `t`", v.Get("t"))
		return
	}

	var hm string
	match := cExpr.FindStringSubmatch(r.Header.Get("X-Uri"))
	log.Println(match)
	if len(match) >= 3 {
		hm = match[2]
	}
	hmbyte, _ := hex.DecodeString(hm)
	if campaign.CheckMac(hmbyte, v.Get("c"), v) {
		utils.GetConn().Query(`INSERT INTO doors(cid,uid,linkid,ip,agent) VALUES(?,?,?,?,?)`,
			v.Get("c"), v.Get("u"), v.Get("l"), r.Header.Get("X-Real-Ip"), r.Header.Get("User-Agent"))
		log.Println("[door] Pass")
	} else {
		log.Println("[door] Hash Fail!!!")
	}
	log.Printf("%+v", r)
	log.Println(r.Header.Get("X-Real-Ip"))
	log.Println(r.Header.Get("User-Agent"))

	linksCacheKey := fmt.Sprintf("%s%s", v.Get("c"), v.Get("l"))
	if url, ok := linksCache[linksCacheKey]; ok {
		log.Println("Using door cache", linksCacheKey)
		http.Redirect(w, r, url, http.StatusSeeOther)
		return
	}

	rows, err := utils.GetConn().Query(`SELECT url FROM links WHERE cid=? AND id=?`, v.Get("c"), v.Get("l"))
	if err == nil {
		for rows.Next() {
			var url string
			rows.Scan(&url)
			linksCache[linksCacheKey] = url
			log.Println("Find door", linksCacheKey)
			http.Redirect(w, r, url, http.StatusSeeOther)
			return
		}
	}
}

func main() {
	flag.Parse()
	http.HandleFunc("/read/", read)
	http.HandleFunc("/door/", door)
	log.Println("HTTP Port:", *httpPort)
	log.Println(http.ListenAndServe(*httpPort, nil))
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors All rights reserved.

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

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

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

package cmd

import (
	"context"
	"os"
	"os/signal"
	"path/filepath"
	"runtime"
	"strconv"
	"time"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"

	"k8s.io/minikube/pkg/drivers/kic/oci"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/exit"
	"k8s.io/minikube/pkg/minikube/localpath"
	"k8s.io/minikube/pkg/minikube/machine"
	"k8s.io/minikube/pkg/minikube/service"
	"k8s.io/minikube/pkg/minikube/tunnel"
	"k8s.io/minikube/pkg/minikube/tunnel/kic"
)

var cleanup bool

// tunnelCmd represents the tunnel command
var tunnelCmd = &cobra.Command{
	Use:   "tunnel",
	Short: "tunnel makes services of type LoadBalancer accessible on localhost",
	Long:  `tunnel creates a route to services deployed with type LoadBalancer and sets their Ingress to their ClusterIP. for a detailed example see https://minikube.sigs.k8s.io/docs/tasks/loadbalancer`,
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		RootCmd.PersistentPreRun(cmd, args)
	},
	Run: func(cmd *cobra.Command, args []string) {
		manager := tunnel.NewManager()

		if cleanup {
			glog.Info("Checking for tunnels to cleanup...")
			if err := manager.CleanupNotRunningTunnels(); err != nil {
				glog.Errorf("error cleaning up: %s", err)
			}
			return
		}

		glog.Infof("Creating docker machine client...")
		api, err := machine.NewAPIClient()
		if err != nil {
			exit.WithError("error creating machine client", err)
		}
		glog.Infof("Creating k8s client...")

		// Tunnel uses the k8s clientset to query the API server for services in the LoadBalancerEmulator.
		// We define the tunnel and minikube error free if the API server responds within a second.
		// This also contributes to better UX, the tunnel status check can happen every second and
		// doesn't hang on the API server call during startup and shutdown time or if there is a temporary error.
		clientset, err := service.K8s.GetClientset(1 * time.Second)
		if err != nil {
			exit.WithError("error creating clientset", err)
		}

		cfg, err := config.Load(viper.GetString(config.MachineProfile))
		if err != nil {
			exit.WithError("Error getting config", err)
		}

		ctrlC := make(chan os.Signal, 1)
		signal.Notify(ctrlC, os.Interrupt)
		ctx, cancel := context.WithCancel(context.Background())
		go func() {
			<-ctrlC
			cancel()
		}()

		if runtime.GOOS == "darwin" && cfg.VMDriver == "docker" {
			// do not ignore error
			port, _ := oci.HostPortBinding("docker", "minikube", 22)
			sshPort := strconv.Itoa(port)
			sshKey := filepath.Join(localpath.MiniPath(), "machines", cfg.Name, "id_rsa")

			kicTunnel := kic.NewTunnel(ctx, sshPort, sshKey, clientset.CoreV1())
			kicTunnel.Start()

			return
		}

		done, err := manager.StartTunnel(ctx, cfg.Name, api, config.DefaultLoader, clientset.CoreV1())
		if err != nil {
			exit.WithError("error starting tunnel", err)
		}
		<-done
	},
}

func init() {
	tunnelCmd.Flags().BoolVarP(&cleanup, "cleanup", "c", false, "call with cleanup=true to remove old tunnels")
}
<commit_msg>Add error checking to kic cmd/tunnel<commit_after>/*
Copyright 2018 The Kubernetes Authors All rights reserved.

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

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

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

package cmd

import (
	"context"
	"os"
	"os/signal"
	"path/filepath"
	"runtime"
	"strconv"
	"time"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"

	"k8s.io/minikube/pkg/drivers/kic/oci"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/exit"
	"k8s.io/minikube/pkg/minikube/localpath"
	"k8s.io/minikube/pkg/minikube/machine"
	"k8s.io/minikube/pkg/minikube/service"
	"k8s.io/minikube/pkg/minikube/tunnel"
	"k8s.io/minikube/pkg/minikube/tunnel/kic"
)

var cleanup bool

// tunnelCmd represents the tunnel command
var tunnelCmd = &cobra.Command{
	Use:   "tunnel",
	Short: "tunnel makes services of type LoadBalancer accessible on localhost",
	Long:  `tunnel creates a route to services deployed with type LoadBalancer and sets their Ingress to their ClusterIP. for a detailed example see https://minikube.sigs.k8s.io/docs/tasks/loadbalancer`,
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		RootCmd.PersistentPreRun(cmd, args)
	},
	Run: func(cmd *cobra.Command, args []string) {
		manager := tunnel.NewManager()

		if cleanup {
			glog.Info("Checking for tunnels to cleanup...")
			if err := manager.CleanupNotRunningTunnels(); err != nil {
				glog.Errorf("error cleaning up: %s", err)
			}
			return
		}

		glog.Infof("Creating docker machine client...")
		api, err := machine.NewAPIClient()
		if err != nil {
			exit.WithError("error creating machine client", err)
		}
		glog.Infof("Creating k8s client...")

		// Tunnel uses the k8s clientset to query the API server for services in the LoadBalancerEmulator.
		// We define the tunnel and minikube error free if the API server responds within a second.
		// This also contributes to better UX, the tunnel status check can happen every second and
		// doesn't hang on the API server call during startup and shutdown time or if there is a temporary error.
		clientset, err := service.K8s.GetClientset(1 * time.Second)
		if err != nil {
			exit.WithError("error creating clientset", err)
		}

		cfg, err := config.Load(viper.GetString(config.MachineProfile))
		if err != nil {
			exit.WithError("Error getting config", err)
		}

		ctrlC := make(chan os.Signal, 1)
		signal.Notify(ctrlC, os.Interrupt)
		ctx, cancel := context.WithCancel(context.Background())
		go func() {
			<-ctrlC
			cancel()
		}()

		if runtime.GOOS == "darwin" && cfg.VMDriver == "docker" {
			port, err := oci.HostPortBinding("docker", "minikube", 22)
			if err != nil {
				exit.WithError("error getting ssh port", err)
			}
			sshPort := strconv.Itoa(port)
			sshKey := filepath.Join(localpath.MiniPath(), "machines", cfg.Name, "id_rsa")

			kicTunnel := kic.NewTunnel(ctx, sshPort, sshKey, clientset.CoreV1())
			err = kicTunnel.Start()
			if err != nil {
				exit.WithError("error starting tunnel", err)
			}

			return
		}

		done, err := manager.StartTunnel(ctx, cfg.Name, api, config.DefaultLoader, clientset.CoreV1())
		if err != nil {
			exit.WithError("error starting tunnel", err)
		}
		<-done
	},
}

func init() {
	tunnelCmd.Flags().BoolVarP(&cleanup, "cleanup", "c", false, "call with cleanup=true to remove old tunnels")
}
<|endoftext|>


<commit_before>//+build integration

package integration

import (
	"flag"
	"fmt"
	"io/ioutil"
	"testing"
	"time"

	ct "github.com/google/certificate-transparency/go"
	"github.com/google/certificate-transparency/go/client"
	"github.com/google/certificate-transparency/go/jsonclient"
	"golang.org/x/net/context"
)

var httpServerFlag = flag.String("ct_http_server", "localhost:8092", "Server address:port")
var pubKey = flag.String("public_key_file", "", "Name of file containing log's public key")

func TestCTIntegration(t *testing.T) {
	flag.Parse()

	logURI := "http://" + (*httpServerFlag)

	opts := jsonclient.Options{}
	if *pubKey != "" {
		pubkey, err := ioutil.ReadFile(*pubKey)
		if err != nil {
			t.Fatalf("Failed to get public key contents: ", err)
		}
		opts.PublicKey = string(pubkey)
	}
	logClient, err := client.New(logURI, nil, opts)
	if err != nil {
		t.Fatalf("Failed to create LogClient instance: ", err)
	}
	ctx := context.Background()

	// Stage 0: get accepted roots, which should just be the fake CA.
	roots, err := logClient.GetAcceptedRoots(ctx)
	if err != nil {
		t.Fatalf("Failed to get roots: %v", err)
	}
	if len(roots) != 1 {
		t.Errorf("len(GetAcceptableRoots())=%d; want 1", len(roots))
	}

	// Stage 1: get the STH, which should be empty.
	sth, err := logClient.GetSTH()
	if err != nil {
		t.Fatalf("Failed to get STH: %v", err)
	}
	if sth.Version != 0 {
		t.Errorf("sth.Version=%v; want V1(0)", sth.Version)
	}
	if sth.TreeSize != 0 {
		t.Errorf("sth.TreeSize=%d; want 0", sth.TreeSize)
	}
	when := ctTimestampToTime(sth.Timestamp)
	fmt.Printf("%v: Got STH: %x\n", when, sth.Version, sth.TreeSize, sth.SHA256RootHash)
	fmt.Printf("%v\n", signatureToString(&sth.TreeHeadSignature))

	// Stage 2: add a cert, get an SCT
	// TODO(drysdale)

	// Stage 3: keep getting the STH until tree size becomes 1.
	// TODO(drysdale)

	// Stage 4: get a consistency proof from 0->1.
	// TODO(drysdale)

	// Stage 5: add certificates 2, 3, 4, 5,...N, for some random N in [4,25]
	// TODO(drysdale)

	// Stage 6: keep getting the STH until tree size becomes N.
	// TODO(drysdale)

	// Stage 7: get a consistency proof from 1->N.
	// TODO(drysdale)

	// Stage 8: get entries [1, N]
	// TODO(drysdale)

	// Stage 9: get an audit proof for cert M, randomly chosen in [1,N]
	// TODO(drysdale)
}

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

func signatureToString(signed *ct.DigitallySigned) string {
	return fmt.Sprintf("Signature: Hash=%v Sign=%v Value=%x", signed.Algorithm.Hash, signed.Algorithm.Signature, signed.Signature)
}
<commit_msg>integration: update for API change in CT client<commit_after>//+build integration

package integration

import (
	"flag"
	"fmt"
	"io/ioutil"
	"testing"
	"time"

	ct "github.com/google/certificate-transparency/go"
	"github.com/google/certificate-transparency/go/client"
	"github.com/google/certificate-transparency/go/jsonclient"
	"golang.org/x/net/context"
)

var httpServerFlag = flag.String("ct_http_server", "localhost:8092", "Server address:port")
var pubKey = flag.String("public_key_file", "", "Name of file containing log's public key")

func TestCTIntegration(t *testing.T) {
	flag.Parse()

	logURI := "http://" + (*httpServerFlag)

	opts := jsonclient.Options{}
	if *pubKey != "" {
		pubkey, err := ioutil.ReadFile(*pubKey)
		if err != nil {
			t.Fatalf("Failed to get public key contents: ", err)
		}
		opts.PublicKey = string(pubkey)
	}
	logClient, err := client.New(logURI, nil, opts)
	if err != nil {
		t.Fatalf("Failed to create LogClient instance: ", err)
	}
	ctx := context.Background()

	// Stage 0: get accepted roots, which should just be the fake CA.
	roots, err := logClient.GetAcceptedRoots(ctx)
	if err != nil {
		t.Fatalf("Failed to get roots: %v", err)
	}
	if len(roots) != 1 {
		t.Errorf("len(GetAcceptableRoots())=%d; want 1", len(roots))
	}

	// Stage 1: get the STH, which should be empty.
	sth, err := logClient.GetSTH(ctx)
	if err != nil {
		t.Fatalf("Failed to get STH: %v", err)
	}
	if sth.Version != 0 {
		t.Errorf("sth.Version=%v; want V1(0)", sth.Version)
	}
	if sth.TreeSize != 0 {
		t.Errorf("sth.TreeSize=%d; want 0", sth.TreeSize)
	}
	when := ctTimestampToTime(sth.Timestamp)
	fmt.Printf("%v: Got STH: %x\n", when, sth.Version, sth.TreeSize, sth.SHA256RootHash)
	fmt.Printf("%v\n", signatureToString(&sth.TreeHeadSignature))

	// Stage 2: add a cert, get an SCT
	// TODO(drysdale)

	// Stage 3: keep getting the STH until tree size becomes 1.
	// TODO(drysdale)

	// Stage 4: get a consistency proof from 0->1.
	// TODO(drysdale)

	// Stage 5: add certificates 2, 3, 4, 5,...N, for some random N in [4,25]
	// TODO(drysdale)

	// Stage 6: keep getting the STH until tree size becomes N.
	// TODO(drysdale)

	// Stage 7: get a consistency proof from 1->N.
	// TODO(drysdale)

	// Stage 8: get entries [1, N]
	// TODO(drysdale)

	// Stage 9: get an audit proof for cert M, randomly chosen in [1,N]
	// TODO(drysdale)
}

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

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


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

package integration

import (
	"fmt"
	"os"
	"strings"
)

func (s *S) getInstallerConfig() string {
	// if no provisioner is set, add an extra host, so Tsuru can build platforms
	hosts := len(provisioners)
	if hosts == 0 {
		hosts = 1
	}
	for _, m := range clusterManagers {
		if req, ok := m.(interface {
			RequiredNodes() int
		}); ok {
			hosts += req.RequiredNodes()
		}
	}
	return fmt.Sprintf(`driver:
  name: virtualbox
  options:
    virtualbox-cpu-count: 2
    virtualbox-memory: 2048
docker-flags:
  - experimental
hosts:
  apps:
    size: %d
components:
  tsuru-image: tsuru/api:latest
  install-dashboard: false
`, hosts)
}

func (s *S) getPlatforms() []string {
	availablePlatforms := []string{
		"tsuru/python",
		"tsuru/go",
		"tsuru/buildpack",
		"tsuru/cordova",
		"tsuru/elixir",
		"tsuru/java",
		"tsuru/nodejs",
		"tsuru/php",
		"tsuru/play",
		"tsuru/pypy",
		"tsuru/python3",
		"tsuru/ruby",
		"tsuru/static",
		"tsuru/perl",
	}
	if _, ok := os.LookupEnv(integrationEnvID + "platforms"); !ok {
		return availablePlatforms
	}
	platforms := s.env.All("platforms")
	selectedPlatforms := make([]string, 0, len(availablePlatforms))
	for _, name := range platforms {
		name = strings.Trim(name, " ")
		for i, platform := range availablePlatforms {
			if name == platform || "tsuru/"+name == platform {
				selectedPlatforms = append(selectedPlatforms, platform)
				availablePlatforms = append(availablePlatforms[:i], availablePlatforms[i+1:]...)
				break
			}
		}
	}
	return selectedPlatforms
}

func (s *S) getProvisioners() []string {
	availableProvisioners := []string{"docker"}
	if _, ok := os.LookupEnv(integrationEnvID + "provisioners"); !ok {
		return availableProvisioners
	}
	selectedProvisioners := make([]string, 0, len(availableProvisioners))
	for _, provisioner := range s.env.All("provisioners") {
		provisioner = strings.Trim(provisioner, " ")
		for i, item := range availableProvisioners {
			if item == provisioner {
				selectedProvisioners = append(selectedProvisioners, provisioner)
				availableProvisioners = append(availableProvisioners[:i], availableProvisioners[i+1:]...)
				break
			}
		}
	}
	return selectedProvisioners
}

func (s *S) getClusterManagers() []ClusterManager {
	availableClusterManagers := map[string]ClusterManager{
		"gce":      &GceClusterManager{env: s.env},
		"minikube": &MinikubeClusterManager{env: s.env},
		"swarm":    &SwarmClusterManager{env: s.env},
	}
	managers := make([]ClusterManager, 0, len(availableClusterManagers))
	clusters := s.env.All("clusters")
	for _, cluster := range clusters {
		cluster = strings.Trim(cluster, " ")
		manager := availableClusterManagers[cluster]
		if manager == nil {
			continue
		}
		managers = append(managers, manager)
		delete(availableClusterManagers, cluster)
	}
	return managers
}

func installerName(env *Environment) string {
	name := env.Get("installername")
	if name == "" {
		name = "tsuru"
	}
	return name
}

func (s *S) config() {
	env := NewEnvironment()
	if !env.Has("enabled") {
		return
	}
	s.env = env
	platforms = s.getPlatforms()
	provisioners = s.getProvisioners()
	clusterManagers = s.getClusterManagers()
	installerConfig = s.getInstallerConfig()
}
<commit_msg>integration: enable swarm cluster if no cluster is set<commit_after>// Copyright 2017 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package integration

import (
	"fmt"
	"os"
	"strings"
)

func (s *S) getInstallerConfig() string {
	// if no provisioner is set, add an extra host, so Tsuru can build platforms
	hosts := len(provisioners)
	if hosts == 0 {
		hosts = 1
	}
	for _, m := range clusterManagers {
		if req, ok := m.(interface {
			RequiredNodes() int
		}); ok {
			hosts += req.RequiredNodes()
		}
	}
	return fmt.Sprintf(`driver:
  name: virtualbox
  options:
    virtualbox-cpu-count: 2
    virtualbox-memory: 2048
docker-flags:
  - experimental
hosts:
  apps:
    size: %d
components:
  tsuru-image: tsuru/api:latest
  install-dashboard: false
`, hosts)
}

func (s *S) getPlatforms() []string {
	availablePlatforms := []string{
		"tsuru/python",
		"tsuru/go",
		"tsuru/buildpack",
		"tsuru/cordova",
		"tsuru/elixir",
		"tsuru/java",
		"tsuru/nodejs",
		"tsuru/php",
		"tsuru/play",
		"tsuru/pypy",
		"tsuru/python3",
		"tsuru/ruby",
		"tsuru/static",
		"tsuru/perl",
	}
	if _, ok := os.LookupEnv(integrationEnvID + "platforms"); !ok {
		return availablePlatforms
	}
	platforms := s.env.All("platforms")
	selectedPlatforms := make([]string, 0, len(availablePlatforms))
	for _, name := range platforms {
		name = strings.Trim(name, " ")
		for i, platform := range availablePlatforms {
			if name == platform || "tsuru/"+name == platform {
				selectedPlatforms = append(selectedPlatforms, platform)
				availablePlatforms = append(availablePlatforms[:i], availablePlatforms[i+1:]...)
				break
			}
		}
	}
	return selectedPlatforms
}

func (s *S) getProvisioners() []string {
	availableProvisioners := []string{"docker"}
	if _, ok := os.LookupEnv(integrationEnvID + "provisioners"); !ok {
		return availableProvisioners
	}
	selectedProvisioners := make([]string, 0, len(availableProvisioners))
	for _, provisioner := range s.env.All("provisioners") {
		provisioner = strings.Trim(provisioner, " ")
		for i, item := range availableProvisioners {
			if item == provisioner {
				selectedProvisioners = append(selectedProvisioners, provisioner)
				availableProvisioners = append(availableProvisioners[:i], availableProvisioners[i+1:]...)
				break
			}
		}
	}
	return selectedProvisioners
}

func (s *S) getClusterManagers() []ClusterManager {
	availableClusterManagers := map[string]ClusterManager{
		"gce":      &GceClusterManager{env: s.env},
		"minikube": &MinikubeClusterManager{env: s.env},
		"swarm":    &SwarmClusterManager{env: s.env},
	}
	if _, ok := os.LookupEnv(integrationEnvID + "clusters"); !ok {
		return []ClusterManager{availableClusterManagers["swarm"]}
	}
	managers := make([]ClusterManager, 0, len(availableClusterManagers))
	clusters := s.env.All("clusters")
	for _, cluster := range clusters {
		cluster = strings.Trim(cluster, " ")
		manager := availableClusterManagers[cluster]
		if manager == nil {
			continue
		}
		managers = append(managers, manager)
		delete(availableClusterManagers, cluster)
	}
	return managers
}

func installerName(env *Environment) string {
	name := env.Get("installername")
	if name == "" {
		name = "tsuru"
	}
	return name
}

func (s *S) config() {
	env := NewEnvironment()
	if !env.Has("enabled") {
		return
	}
	s.env = env
	platforms = s.getPlatforms()
	provisioners = s.getProvisioners()
	clusterManagers = s.getClusterManagers()
	installerConfig = s.getInstallerConfig()
}
<|endoftext|>


<commit_before>package read

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"runtime"

	"github.com/go-task/task/v2/internal/taskfile"

	"gopkg.in/yaml.v2"
)

var (
	// ErrIncludedTaskfilesCantHaveIncludes is returned when a included Taskfile contains includes
	ErrIncludedTaskfilesCantHaveIncludes = errors.New("task: Included Taskfiles can't have includes. Please, move the include to the main Taskfile")
)

// Taskfile reads a Taskfile for a given directory
func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
	path := filepath.Join(dir, entrypoint)
	if _, err := os.Stat(path); err != nil {
		return nil, fmt.Errorf(`task: No Taskfile found on "%s". Use "task --init" to create a new one`, path)
	}
	t, err := readTaskfile(path)
	if err != nil {
		return nil, err
	}

	for namespace, includedTask := range t.Includes {
		path = filepath.Join(dir, includedTask.Taskfile)
		info, err := os.Stat(path)
		if err != nil {
			return nil, err
		}
		if info.IsDir() {
			path = filepath.Join(path, "Taskfile.yml")
		}
		includedTaskfile, err := readTaskfile(path)
		if err != nil {
			return nil, err
		}
		if len(includedTaskfile.Includes) > 0 {
			return nil, ErrIncludedTaskfilesCantHaveIncludes
		}

		for _, task := range includedTaskfile.Tasks {
			task.Dir = filepath.Join(includedTask.Dir, task.Dir)
		}

		if err = taskfile.Merge(t, includedTaskfile, namespace); err != nil {
			return nil, err
		}
	}

	path = filepath.Join(dir, fmt.Sprintf("Taskfile_%s.yml", runtime.GOOS))
	if _, err = os.Stat(path); err == nil {
		osTaskfile, err := readTaskfile(path)
		if err != nil {
			return nil, err
		}
		if err = taskfile.Merge(t, osTaskfile); err != nil {
			return nil, err
		}
	}

	for name, task := range t.Tasks {
		task.Task = name
	}

	return t, nil
}

func readTaskfile(file string) (*taskfile.Taskfile, error) {
	f, err := os.Open(file)
	if err != nil {
		return nil, err
	}
	var t taskfile.Taskfile
	return &t, yaml.NewDecoder(f).Decode(&t)
}
<commit_msg>Added os-related files for included taskfiles<commit_after>package read

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/go-task/task/v2/internal/taskfile"

	"gopkg.in/yaml.v2"
)

var (
	// ErrIncludedTaskfilesCantHaveIncludes is returned when a included Taskfile contains includes
	ErrIncludedTaskfilesCantHaveIncludes = errors.New("task: Included Taskfiles can't have includes. Please, move the include to the main Taskfile")
)

// Taskfile reads a Taskfile for a given directory
func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
	path := filepath.Join(dir, entrypoint)
	if _, err := os.Stat(path); err != nil {
		return nil, fmt.Errorf(`task: No Taskfile found on "%s". Use "task --init" to create a new one`, path)
	}
	t, err := readTaskfile(path)
	if err != nil {
		return nil, err
	}

	for namespace, includedTask := range t.Includes {
		path = filepath.Join(dir, includedTask.Taskfile)
		info, err := os.Stat(path)
		if err != nil {
			return nil, err
		}
		if info.IsDir() {
			path = filepath.Join(path, "Taskfile.yml")
		}
		includedTaskfile, err := readTaskfile(path)
		if err != nil {
			return nil, err
		}
		if len(includedTaskfile.Includes) > 0 {
			return nil, ErrIncludedTaskfilesCantHaveIncludes
		}

		includedTaskDirectory := filepath.Dir(path)
		includedTaskFileName := strings.TrimSuffix(filepath.Base(path), filepath.Ext(path))
		path = filepath.Join(includedTaskDirectory, fmt.Sprintf("%s_%s.yml", includedTaskFileName, runtime.GOOS))
		if _, err = os.Stat(path); err == nil {
			osIncludedTaskfile, err := readTaskfile(path)
			if err != nil {
				return nil, err
			}
			if err = taskfile.Merge(includedTaskfile, osIncludedTaskfile); err != nil {
				return nil, err
			}
		}

		for _, task := range includedTaskfile.Tasks {
			task.Dir = filepath.Join(includedTask.Dir, task.Dir)
		}

		if err = taskfile.Merge(t, includedTaskfile, namespace); err != nil {
			return nil, err
		}
	}

	path = filepath.Join(dir, fmt.Sprintf("Taskfile_%s.yml", runtime.GOOS))
	if _, err = os.Stat(path); err == nil {
		osTaskfile, err := readTaskfile(path)
		if err != nil {
			return nil, err
		}
		if err = taskfile.Merge(t, osTaskfile); err != nil {
			return nil, err
		}
	}

	for name, task := range t.Tasks {
		task.Task = name
	}

	return t, nil
}

func readTaskfile(file string) (*taskfile.Taskfile, error) {
	f, err := os.Open(file)
	if err != nil {
		return nil, err
	}
	var t taskfile.Taskfile
	return &t, yaml.NewDecoder(f).Decode(&t)
}
<|endoftext|>


<commit_before>package tddbc

import (
	"fmt"
)

type OpenRange struct {
	lower int
	upper int
}

func NewOpenRange(lower, upper int) *OpenRange {
	return &OpenRange{lower, upper}
}

func (r *OpenRange) String() string {
	return fmt.Sprintf("[%d,%d]", r.lower, r.upper)
}

func (r *OpenRange) Contains(target int) bool {
	return r.lower < target && target < r.upper
}

func (r *OpenRange) Equal(target OpenRange) bool {
	return r.lower == target.lower && r.upper == target.upper
}
<commit_msg>開区間が別の開区間と接続しているか確認するテストケースを実装<commit_after>package tddbc

import (
	"fmt"
)

type OpenRange struct {
	lower int
	upper int
}

func NewOpenRange(lower, upper int) *OpenRange {
	return &OpenRange{lower, upper}
}

func (r *OpenRange) String() string {
	return fmt.Sprintf("[%d,%d]", r.lower, r.upper)
}

func (r *OpenRange) Contains(target int) bool {
	return r.lower < target && target < r.upper
}

func (r *OpenRange) Equal(target OpenRange) bool {
	return r.lower == target.lower && r.upper == target.upper
}

func (r *OpenRange) IsConnectedTo(target OpenRange) bool {
	return true
}
<|endoftext|>


<commit_before>// Basic data structures for Go.
package main

import (
	"errors"
	"fmt"
	"math/rand"
	// "time"
)

// Letters for quick lookup.
// These are the letters marked on top of the board.  "I" is
// always left out, likely to avoid confusion with "1".
const BoardLetters = "ABCDEFGHJKLMNOPQRST"
const ActionChars = "cbw"

// Here it would be nice if the type system allows us to say
// "integer with one of these value: 0, 1, 2.
type Color int

const (
	// Stone colors. Used for board state.
	Empty = 0 // No stone here.
	Black = 1
	White = 2
)

// Point where a stone can be placed.
type Point struct {
	X int // Left to right, 0-18 (marked as 1-19 on board.)
	Y int // Bottom to top, 0-18 (ABCDEFGHJKLMNOPQRST.)
}

type Size struct {
	Width  int
	Height int
}

// A move. (Place or capture stone.)
type Move struct {
	Point
	Color
}

func (move Move) String() string {
	return FormatMove(move)
}

func ParseMove(text string) (Move, error) {
	// letter := text[0]
	// number := text[1:len(text)-1]
	// color := text[len(text)-1]

	return Move{}, errors.New("Parser not written")
}

func FormatMove(move Move) string {
	// Todo: check if values are out of range?
	letter := BoardLetters[move.X]
	number := move.Y + 1
	stoneColor := ActionChars[move.Color]

	return fmt.Sprintf("%c%d%c", letter, number, stoneColor)
}

type Group struct {
	Color
	Points    map[Point]bool
	Liberties map[Point]bool
}

// The board is just a cache of moves. We can recompute the board from
// the moves.
type Game struct {
	Size
	Board map[Point]Color
	Moves []Move
	// Here we should have a stack (or slice) of boards so that we have
	// a history of previous board states.

	// + We need to keep score somehow. (Number of captured stones for
	// each player.)
}

func (game *Game) AddMove(move Move) {
	game.Moves = append(game.Moves, move)
	game.Board[move.Point] = move.Color
}

func (game *Game) AddMoves(moves []Move) {
	for _, move := range moves {
		game.AddMove(move)
	}
}

func (game *Game) PrintBoard() {
	colorChars := ".bw"

	fmt.Print("   ")
	for x := 0; x < game.Width; x++ {
		fmt.Printf(" %c", BoardLetters[x])
	}
	fmt.Println()

	for y := game.Height - 1; y >= 0; y-- {
		line := ""
		for x := 0; x < game.Width; x++ {
			line += fmt.Sprintf(" %c", colorChars[game.Board[Point{x, y}]])
		}
		fmt.Printf(" %2d%s\n", y+1, line)
	}
}

func NewGame(boardSize Size) Game {
	return Game{
		Size:  boardSize,
		Board: make(map[Point]Color),
	}
}

// returns a list of random moves
// the moves are not guaranteed to be valid
// this function can place multiple stones on top of each other
// this is used for debugging detection of invalid moves
// and for implementing grouping and detection of dead groups
func (game *Game) generateRandomMoves(numMoves int) []Move {
	// rand.Seed(time.Now().UnixNano())

	moves := []Move{}
	for i := 0; i < numMoves; i++ {
		randomColor := Color(rand.Intn(2) + 1) // either black or white stone
		randomX := rand.Intn(game.Width)
		randomY := rand.Intn(game.Height)
		moves = append(moves, Move{Point{randomX, randomY}, randomColor})
	}
	return moves
}

// finds up to four neighbors for a reference point (east, north, west, south)
// does not check whether the reference point is actually on the board
func (game *Game) findNeighbors(point Point) []Point {
	neighbors := []Point{}
	// east
	if point.X < game.Width-1 {
		neighbors = append(neighbors, Point{point.X + 1, point.Y})
	}
	// north
	if point.Y < game.Height-1 {
		neighbors = append(neighbors, Point{point.X, point.Y + 1})
	}
	// west
	if point.X > 0 {
		neighbors = append(neighbors, Point{point.X - 1, point.Y})
	}
	// south
	if point.Y > 0 {
		neighbors = append(neighbors, Point{point.X, point.Y - 1})
	}
	return neighbors
}

func (game *Game) findCapturedGroups() {

	groups := []Group{}
	groupedPoints := make(map[Point]bool)

	for point, color := range game.Board {
		if color != 0 {
			if !groupedPoints[point] {
				group := Group{
					Color:     color,
					Points:    make(map[Point]bool),
					Liberties: make(map[Point]bool),
				}

				todo := []Point{point}

				for len(todo) > 0 {
					// pop the last element
					// see https://github.com/golang/go/wiki/SliceTricks
					point, todo = todo[len(todo)-1], todo[:len(todo)-1]

					if !groupedPoints[point] {
						_color := game.Board[point]
						if _color == 0 {
							group.Liberties[point] = true
						} else if _color == group.Color {
							group.Points[point] = true
							groupedPoints[point] = true
							// extend the todo array by neighbors
							todo = append(todo, game.findNeighbors(point)...)
						}
					}
				}
				groups = append(groups, group)
			}
		}
	}

	// for debugging
	boardNumLiberties := make([]int, game.Width*game.Height)

	for _, group := range groups {
		numLiberties := len(group.Liberties)
		for point, _ := range group.Points {
			boardNumLiberties[point.Y*game.Width+point.X] = numLiberties
		}
	}

	fmt.Println("\nLiberties of groups:\n")

	for y := game.Height - 1; y >= 0; y-- {
		fmt.Print("   ")
		for x := 0; x < game.Width; x++ {
			numLiberties := boardNumLiberties[y*game.Width+x]
			if numLiberties == 0 {
				if game.Board[Point{x, y}] == Empty {
					fmt.Print(" .")
				} else {
					fmt.Print(" *")
				}
			} else {
				fmt.Printf("%2d", boardNumLiberties[y*game.Width+x])
			}
		}
		fmt.Print("\n")
	}
}

func main() {
	game := NewGame(Size{9, 11})

	moves := game.generateRandomMoves(135)

	fmt.Println()
	fmt.Println("Moves:", moves)
	fmt.Println()
	game.AddMoves(moves)
	game.PrintBoard()
	game.findCapturedGroups()

	// fmt.Println(game.Board[Point{}])
	// fmt.Println(game.Board[Point{1, 1}])

	// fmt.Println(moves)
}
<commit_msg>Added NewGroup().<commit_after>// Basic data structures for Go.
package main

import (
	"errors"
	"fmt"
	"math/rand"
	// "time"
)

// Letters for quick lookup.
// These are the letters marked on top of the board.  "I" is
// always left out, likely to avoid confusion with "1".
const BoardLetters = "ABCDEFGHJKLMNOPQRST"
const ActionChars = "cbw"

// Here it would be nice if the type system allows us to say
// "integer with one of these value: 0, 1, 2.
type Color int

const (
	// Stone colors. Used for board state.
	Empty = 0 // No stone here.
	Black = 1
	White = 2
)

// Point where a stone can be placed.
type Point struct {
	X int // Left to right, 0-18 (marked as 1-19 on board.)
	Y int // Bottom to top, 0-18 (ABCDEFGHJKLMNOPQRST.)
}

type Size struct {
	Width  int
	Height int
}

// A move. (Place or capture stone.)
type Move struct {
	Point
	Color
}

func (move Move) String() string {
	return FormatMove(move)
}

func ParseMove(text string) (Move, error) {
	// letter := text[0]
	// number := text[1:len(text)-1]
	// color := text[len(text)-1]

	return Move{}, errors.New("Parser not written")
}

func FormatMove(move Move) string {
	// Todo: check if values are out of range?
	letter := BoardLetters[move.X]
	number := move.Y + 1
	stoneColor := ActionChars[move.Color]

	return fmt.Sprintf("%c%d%c", letter, number, stoneColor)
}

type Group struct {
	Color
	Points    map[Point]bool
	Liberties map[Point]bool
}

func NewGroup(color Color) Group {
	return Group{
		Color:     color,
		Points:    make(map[Point]bool),
		Liberties: make(map[Point]bool),
	}
}

// The board is just a cache of moves. We can recompute the board from
// the moves.
type Game struct {
	Size
	Board map[Point]Color
	Moves []Move
	// Here we should have a stack (or slice) of boards so that we have
	// a history of previous board states.

	// + We need to keep score somehow. (Number of captured stones for
	// each player.)
}

func (game *Game) AddMove(move Move) {
	game.Moves = append(game.Moves, move)
	game.Board[move.Point] = move.Color
}

func (game *Game) AddMoves(moves []Move) {
	for _, move := range moves {
		game.AddMove(move)
	}
}

func (game *Game) PrintBoard() {
	colorChars := ".bw"

	fmt.Print("   ")
	for x := 0; x < game.Width; x++ {
		fmt.Printf(" %c", BoardLetters[x])
	}
	fmt.Println()

	for y := game.Height - 1; y >= 0; y-- {
		line := ""
		for x := 0; x < game.Width; x++ {
			line += fmt.Sprintf(" %c", colorChars[game.Board[Point{x, y}]])
		}
		fmt.Printf(" %2d%s\n", y+1, line)
	}
}

func NewGame(boardSize Size) Game {
	return Game{
		Size:  boardSize,
		Board: make(map[Point]Color),
	}
}

// returns a list of random moves
// the moves are not guaranteed to be valid
// this function can place multiple stones on top of each other
// this is used for debugging detection of invalid moves
// and for implementing grouping and detection of dead groups
func (game *Game) generateRandomMoves(numMoves int) []Move {
	// rand.Seed(time.Now().UnixNano())

	moves := []Move{}
	for i := 0; i < numMoves; i++ {
		randomColor := Color(rand.Intn(2) + 1) // either black or white stone
		randomX := rand.Intn(game.Width)
		randomY := rand.Intn(game.Height)
		moves = append(moves, Move{Point{randomX, randomY}, randomColor})
	}
	return moves
}

// finds up to four neighbors for a reference point (east, north, west, south)
// does not check whether the reference point is actually on the board
func (game *Game) findNeighbors(point Point) []Point {
	neighbors := []Point{}
	// east
	if point.X < game.Width-1 {
		neighbors = append(neighbors, Point{point.X + 1, point.Y})
	}
	// north
	if point.Y < game.Height-1 {
		neighbors = append(neighbors, Point{point.X, point.Y + 1})
	}
	// west
	if point.X > 0 {
		neighbors = append(neighbors, Point{point.X - 1, point.Y})
	}
	// south
	if point.Y > 0 {
		neighbors = append(neighbors, Point{point.X, point.Y - 1})
	}
	return neighbors
}

func (game *Game) findCapturedGroups() {

	groups := []Group{}
	groupedPoints := make(map[Point]bool)

	for point, color := range game.Board {
		if color != 0 {
			if !groupedPoints[point] {
				group := NewGroup(color)

				todo := []Point{point}

				for len(todo) > 0 {
					// pop the last element
					// see https://github.com/golang/go/wiki/SliceTricks
					point, todo = todo[len(todo)-1], todo[:len(todo)-1]

					if !groupedPoints[point] {
						_color := game.Board[point]
						if _color == 0 {
							group.Liberties[point] = true
						} else if _color == group.Color {
							group.Points[point] = true
							groupedPoints[point] = true
							// extend the todo array by neighbors
							todo = append(todo, game.findNeighbors(point)...)
						}
					}
				}
				groups = append(groups, group)
			}
		}
	}

	// for debugging
	boardNumLiberties := make([]int, game.Width*game.Height)

	for _, group := range groups {
		numLiberties := len(group.Liberties)
		for point, _ := range group.Points {
			boardNumLiberties[point.Y*game.Width+point.X] = numLiberties
		}
	}

	fmt.Println("\nLiberties of groups:\n")

	for y := game.Height - 1; y >= 0; y-- {
		fmt.Print("   ")
		for x := 0; x < game.Width; x++ {
			numLiberties := boardNumLiberties[y*game.Width+x]
			if numLiberties == 0 {
				if game.Board[Point{x, y}] == Empty {
					fmt.Print(" .")
				} else {
					fmt.Print(" *")
				}
			} else {
				fmt.Printf("%2d", boardNumLiberties[y*game.Width+x])
			}
		}
		fmt.Print("\n")
	}
}

func main() {
	game := NewGame(Size{9, 11})

	moves := game.generateRandomMoves(135)

	fmt.Println()
	fmt.Println("Moves:", moves)
	fmt.Println()
	game.AddMoves(moves)
	game.PrintBoard()
	game.findCapturedGroups()

	// fmt.Println(game.Board[Point{}])
	// fmt.Println(game.Board[Point{1, 1}])

	// fmt.Println(moves)
}
<|endoftext|>