<commit_before>// The go-vcs program exposes go-vcs's library functionality through a
// command-line interface.
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path/filepath"

	"strings"

	"github.com/kr/text"
	"sourcegraph.com/sourcegraph/go-diff/diff"
	"sourcegraph.com/sourcegraph/go-vcs/vcs"
	_ "sourcegraph.com/sourcegraph/go-vcs/vcs/gitcmd"
	//_ "sourcegraph.com/sourcegraph/go-vcs/vcs/git"
	_ "sourcegraph.com/sourcegraph/go-vcs/vcs/hg"
)

var (
	sshKeyFile = flag.String("i", "", "ssh key file")
	chdir      = flag.String("C", "", "change directory to this dir before doing anything")
)

func main() {
	log.SetFlags(0)
	flag.Parse()

	if flag.NArg() == 0 {
		log.Fatal("Must specify a subcommand.")
	}

	if *chdir != "" {
		if err := os.Chdir(*chdir); err != nil {
			log.Fatal("Chdir:", err)
		}
	}

	subcmd := flag.Arg(0)
	args := flag.Args()[1:]
	switch subcmd {
	case "git-clone-mirror":
		if len(args) != 2 {
			log.Fatal("git-clone requires 2 args: clone URL and dir.")
		}
		cloneURLStr, dir := args[0], args[1]

		cloneURL, err := url.Parse(cloneURLStr)
		if err != nil {
			log.Fatal(err)
		}

		if _, err := os.Stat(dir); !os.IsNotExist(err) {
			log.Fatalf("Clone destination dir must not exist: %s.", dir)
		}
		if _, err := os.Stat(filepath.Join(dir, "..")); err != nil {
			log.Fatalf("Clone destination dir parent must exist: %s.", filepath.Join(dir, ".."))
		}

		log.Printf("Cloning %s to %s...", cloneURL, dir)

		opt := vcs.CloneOpt{}
		if *sshKeyFile != "" {
			key, err := ioutil.ReadFile(*sshKeyFile)
			if err != nil {
				log.Fatal(err)
			}
			opt.RemoteOpts.SSH = &vcs.SSHConfig{PrivateKey: key}
		}
		repo, err := vcs.Clone("git", cloneURL.String(), dir, opt)
		if err != nil {
			log.Fatal(err)
		}

		log.Printf("Cloned: %T.", repo)

	case "show":
		if len(args) != 1 {
			log.Fatal("show takes 1 argument (revspec).")
		}
		revspec := args[0]

		repo, err := vcs.Open("git", ".")
		if err != nil {
			log.Fatal(err)
		}

		commitID, err := repo.ResolveRevision(revspec)
		if err != nil {
			log.Fatal(err)
		}

		commit, err := repo.GetCommit(commitID)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Revspec %q resolves to commit %s:\n", revspec, commitID)
		printCommit(commit)

	case "show-file":
		if len(args) != 2 {
			log.Fatal("show-file takes 2 arguments.")
		}

		repo, err := vcs.Open("git", ".")
		if err != nil {
			log.Fatal(err)
		}

		rev, err := repo.ResolveRevision(args[0])
		if err != nil {
			log.Fatal(err)
		}

		fs, err := repo.FileSystem(rev)
		if err != nil {
			log.Fatal(err)
		}

		f, err := fs.Open(args[1])
		if err != nil {
			log.Fatal(err)
		}

		b, err := ioutil.ReadAll(f)
		if err != nil {
			log.Fatal(err)
		}
		if _, err := os.Stdout.Write(b); err != nil {
			log.Fatal(err)
		}
	case "log":
		if len(args) != 0 {
			log.Fatal("log takes no arguments.")
		}

		repo, err := vcs.Open("git", ".")
		if err != nil {
			log.Fatal(err)
		}

		master, err := repo.ResolveRevision("master")
		if err != nil {
			log.Fatal(err)
		}

		commits, total, err := repo.Commits(vcs.CommitsOptions{Head: master})
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Commits (%d total):\n", total)
		for _, c := range commits {
			printCommit(c)
		}

	case "blame":
		newestCommit, file := args[0], args[1]

		repo, err := vcs.Open("git", ".")
		if err != nil {
			log.Fatal(err)
		}

		newestCommitID, err := repo.ResolveRevision(newestCommit)
		if err != nil {
			log.Fatal(err)
		}

		hunks, err := repo.(vcs.Blamer).BlameFile(file, &vcs.BlameOptions{NewestCommit: newestCommitID})
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Hunks (%d total):\n", len(hunks))
		for _, c := range hunks {
			printHunk(c)
		}

	case "diff", "diffstat":
		if len(args) != 2 {
			log.Fatalf("%s takes 2 args (base and head), behavior is like `git diff base...head` (note triple dot).", subcmd)
		}
		baseRev := args[0]
		headRev := args[1]

		repo, err := vcs.Open("git", ".")
		if err != nil {
			log.Fatal(err)
		}

		base, err := repo.ResolveRevision(baseRev)
		if err != nil {
			log.Fatal(err)
		}
		head, err := repo.ResolveRevision(headRev)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("git diff %s..%s", base, head)

		vdiff, err := repo.(vcs.Differ).Diff(base, head, &vcs.DiffOptions{ExcludeReachableFromBoth: true})
		if err != nil {
			log.Fatal(err)
		}

		switch subcmd {
		case "diff":
			fmt.Println(vdiff.Raw)
		case "diffstat":
			pdiff, err := diff.ParseMultiFileDiff([]byte(vdiff.Raw))
			if err != nil {
				log.Fatal(err)
			}
			for _, fdiff := range pdiff {
				var name string
				if fdiff.NewName == "/dev/null" {
					name = fdiff.OrigName
				} else {
					name = fdiff.NewName
				}
				fmt.Printf("%-50s    ", name)
				st := fdiff.Stat()
				const w = 30
				total := st.Added + st.Changed + st.Deleted
				if st.Added > 0 {
					st.Added = (st.Added*w)/total + 1
				}
				if st.Changed > 0 {
					st.Changed = (st.Changed*w)/total + 1
				}
				if st.Deleted > 0 {
					st.Deleted = (st.Deleted*w)/total + 1
				}
				fmt.Print(strings.Repeat("+", st.Added), strings.Repeat("Δ", st.Changed), strings.Repeat("-", st.Deleted), "\n")
			}
		}

	case "branches":
		if len(args) != 0 {
			log.Fatal("branches takes no arguments.")
		}

		repo, err := vcs.Open("hg", ".")
		if err != nil {
			log.Fatal(err)
		}

		branches, err := repo.Branches()
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Branches (%d total):\n", len(branches))
		for _, b := range branches {
			fmt.Printf("%s %s\n", b.Head, b.Name)
		}
	}
}

func printCommit(c *vcs.Commit) {
	fmt.Printf("%s\n%s <%s> at %s\n%s\n\n", c.ID, c.Author.Name, c.Author.Email, c.Author.Date, text.Indent(c.Message, "\t"))
}

func printHunk(h *vcs.Hunk) {
	fmt.Printf("L%d-%d b%d-%d\t%s\t%v\n", h.StartLine, h.EndLine, h.StartByte, h.EndByte, h.CommitID, h.Author)
}
<commit_msg>use HEAD, enable libgit2<commit_after>// The go-vcs program exposes go-vcs's library functionality through a
// command-line interface.
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path/filepath"

	"strings"

	"github.com/kr/text"
	"sourcegraph.com/sourcegraph/go-diff/diff"
	"sourcegraph.com/sourcegraph/go-vcs/vcs"
	_ "sourcegraph.com/sourcegraph/go-vcs/vcs/git"
	_ "sourcegraph.com/sourcegraph/go-vcs/vcs/gitcmd"
	_ "sourcegraph.com/sourcegraph/go-vcs/vcs/hg"
)

var (
	sshKeyFile = flag.String("i", "", "ssh key file")
	chdir      = flag.String("C", "", "change directory to this dir before doing anything")
)

func main() {
	log.SetFlags(0)
	flag.Parse()

	if flag.NArg() == 0 {
		log.Fatal("Must specify a subcommand.")
	}

	if *chdir != "" {
		if err := os.Chdir(*chdir); err != nil {
			log.Fatal("Chdir:", err)
		}
	}

	subcmd := flag.Arg(0)
	args := flag.Args()[1:]
	switch subcmd {
	case "git-clone-mirror":
		if len(args) != 2 {
			log.Fatal("git-clone requires 2 args: clone URL and dir.")
		}
		cloneURLStr, dir := args[0], args[1]

		cloneURL, err := url.Parse(cloneURLStr)
		if err != nil {
			log.Fatal(err)
		}

		if _, err := os.Stat(dir); !os.IsNotExist(err) {
			log.Fatalf("Clone destination dir must not exist: %s.", dir)
		}
		if _, err := os.Stat(filepath.Join(dir, "..")); err != nil {
			log.Fatalf("Clone destination dir parent must exist: %s.", filepath.Join(dir, ".."))
		}

		log.Printf("Cloning %s to %s...", cloneURL, dir)

		opt := vcs.CloneOpt{}
		if *sshKeyFile != "" {
			key, err := ioutil.ReadFile(*sshKeyFile)
			if err != nil {
				log.Fatal(err)
			}
			opt.RemoteOpts.SSH = &vcs.SSHConfig{PrivateKey: key}
		}
		repo, err := vcs.Clone("git", cloneURL.String(), dir, opt)
		if err != nil {
			log.Fatal(err)
		}

		log.Printf("Cloned: %T.", repo)

	case "show":
		if len(args) != 1 {
			log.Fatal("show takes 1 argument (revspec).")
		}
		revspec := args[0]

		repo, err := vcs.Open("git", ".")
		if err != nil {
			log.Fatal(err)
		}

		commitID, err := repo.ResolveRevision(revspec)
		if err != nil {
			log.Fatal(err)
		}

		commit, err := repo.GetCommit(commitID)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Revspec %q resolves to commit %s:\n", revspec, commitID)
		printCommit(commit)

	case "show-file":
		if len(args) != 2 {
			log.Fatal("show-file takes 2 arguments.")
		}

		repo, err := vcs.Open("git", ".")
		if err != nil {
			log.Fatal(err)
		}

		rev, err := repo.ResolveRevision(args[0])
		if err != nil {
			log.Fatal(err)
		}

		fs, err := repo.FileSystem(rev)
		if err != nil {
			log.Fatal(err)
		}

		f, err := fs.Open(args[1])
		if err != nil {
			log.Fatal(err)
		}

		b, err := ioutil.ReadAll(f)
		if err != nil {
			log.Fatal(err)
		}
		if _, err := os.Stdout.Write(b); err != nil {
			log.Fatal(err)
		}
	case "log":
		if len(args) != 0 {
			log.Fatal("log takes no arguments.")
		}

		repo, err := vcs.Open("git", ".")
		if err != nil {
			log.Fatal(err)
		}

		master, err := repo.ResolveRevision("HEAD")
		if err != nil {
			log.Fatal(err)
		}

		commits, total, err := repo.Commits(vcs.CommitsOptions{Head: master, N: 250})
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Commits (%d total):\n", total)
		for _, c := range commits {
			printCommit(c)
		}

	case "blame":
		newestCommit, file := args[0], args[1]

		repo, err := vcs.Open("git", ".")
		if err != nil {
			log.Fatal(err)
		}

		newestCommitID, err := repo.ResolveRevision(newestCommit)
		if err != nil {
			log.Fatal(err)
		}

		hunks, err := repo.(vcs.Blamer).BlameFile(file, &vcs.BlameOptions{NewestCommit: newestCommitID})
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Hunks (%d total):\n", len(hunks))
		for _, c := range hunks {
			printHunk(c)
		}

	case "diff", "diffstat":
		if len(args) != 2 {
			log.Fatalf("%s takes 2 args (base and head), behavior is like `git diff base...head` (note triple dot).", subcmd)
		}
		baseRev := args[0]
		headRev := args[1]

		repo, err := vcs.Open("git", ".")
		if err != nil {
			log.Fatal(err)
		}

		base, err := repo.ResolveRevision(baseRev)
		if err != nil {
			log.Fatal(err)
		}
		head, err := repo.ResolveRevision(headRev)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("git diff %s..%s", base, head)

		vdiff, err := repo.(vcs.Differ).Diff(base, head, &vcs.DiffOptions{ExcludeReachableFromBoth: true})
		if err != nil {
			log.Fatal(err)
		}

		switch subcmd {
		case "diff":
			fmt.Println(vdiff.Raw)
		case "diffstat":
			pdiff, err := diff.ParseMultiFileDiff([]byte(vdiff.Raw))
			if err != nil {
				log.Fatal(err)
			}
			for _, fdiff := range pdiff {
				var name string
				if fdiff.NewName == "/dev/null" {
					name = fdiff.OrigName
				} else {
					name = fdiff.NewName
				}
				fmt.Printf("%-50s    ", name)
				st := fdiff.Stat()
				const w = 30
				total := st.Added + st.Changed + st.Deleted
				if st.Added > 0 {
					st.Added = (st.Added*w)/total + 1
				}
				if st.Changed > 0 {
					st.Changed = (st.Changed*w)/total + 1
				}
				if st.Deleted > 0 {
					st.Deleted = (st.Deleted*w)/total + 1
				}
				fmt.Print(strings.Repeat("+", st.Added), strings.Repeat("Δ", st.Changed), strings.Repeat("-", st.Deleted), "\n")
			}
		}

	case "branches":
		if len(args) != 0 {
			log.Fatal("branches takes no arguments.")
		}

		repo, err := vcs.Open("hg", ".")
		if err != nil {
			log.Fatal(err)
		}

		branches, err := repo.Branches()
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Branches (%d total):\n", len(branches))
		for _, b := range branches {
			fmt.Printf("%s %s\n", b.Head, b.Name)
		}
	}
}

func printCommit(c *vcs.Commit) {
	fmt.Printf("%s\n%s <%s> at %s\n%s\n\n", c.ID, c.Author.Name, c.Author.Email, c.Author.Date, text.Indent(c.Message, "\t"))
}

func printHunk(h *vcs.Hunk) {
	fmt.Printf("L%d-%d b%d-%d\t%s\t%v\n", h.StartLine, h.EndLine, h.StartByte, h.EndByte, h.CommitID, h.Author)
}
<|endoftext|>


<commit_before>package server

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/http"

	"github.com/Sirupsen/logrus"
	"github.com/docker/distribution/health"
	"github.com/docker/distribution/registry/auth"
	"github.com/docker/notary/server/handlers"
	"github.com/docker/notary/tuf/data"
	"github.com/docker/notary/tuf/signed"
	"github.com/docker/notary/utils"
	"github.com/gorilla/mux"
	"github.com/prometheus/client_golang/prometheus"
	"golang.org/x/net/context"
)

func init() {
	data.SetDefaultExpiryTimes(
		map[string]int{
			"timestamp": 14,
		},
	)
}

func prometheusOpts(operation string) prometheus.SummaryOpts {
	return prometheus.SummaryOpts{
		Namespace:   "notary_server",
		Subsystem:   "http",
		ConstLabels: prometheus.Labels{"operation": operation},
	}
}

// Run sets up and starts a TLS server that can be cancelled using the
// given configuration. The context it is passed is the context it should
// use directly for the TLS server, and generate children off for requests
func Run(ctx context.Context, addr string, tlsConfig *tls.Config, trust signed.CryptoService, authMethod string, authOpts interface{}) error {

	tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return err
	}
	var lsnr net.Listener
	lsnr, err = net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		return err
	}

	if tlsConfig != nil {
		logrus.Info("Enabling TLS")
		lsnr = tls.NewListener(lsnr, tlsConfig)
	}

	var ac auth.AccessController
	if authMethod == "token" {
		authOptions, ok := authOpts.(map[string]interface{})
		if !ok {
			return fmt.Errorf("auth.options must be a map[string]interface{}")
		}
		ac, err = auth.GetAccessController(authMethod, authOptions)
		if err != nil {
			return err
		}
	}

	svr := http.Server{
		Addr:    addr,
		Handler: RootHandler(ac, ctx, trust),
	}

	logrus.Info("Starting on ", addr)

	err = svr.Serve(lsnr)

	return err
}

// RootHandler returns the handler that routes all the paths from / for the
// server.
func RootHandler(ac auth.AccessController, ctx context.Context, trust signed.CryptoService) http.Handler {
	hand := utils.RootHandlerFactory(ac, ctx, trust)

	r := mux.NewRouter()
	r.Methods("GET").Path("/v2/").Handler(hand(handlers.MainHandler))
	r.Methods("POST").Path("/v2/{imageName:.*}/_trust/tuf/").Handler(
		prometheus.InstrumentHandlerWithOpts(
			prometheusOpts("UpdateTuf"),
			hand(handlers.AtomicUpdateHandler, "push", "pull")))
	r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/{tufRole:(root|targets|snapshot)}.json").Handler(
		prometheus.InstrumentHandlerWithOpts(
			prometheusOpts("GetRole"),
			hand(handlers.GetHandler, "pull")))
	r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/timestamp.json").Handler(
		prometheus.InstrumentHandlerWithOpts(
			prometheusOpts("GetTimestamp"),
			hand(handlers.GetTimestampHandler, "pull")))
	r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/timestamp.key").Handler(
		prometheus.InstrumentHandlerWithOpts(
			prometheusOpts("GetTimestampKey"),
			hand(handlers.GetTimestampKeyHandler, "push", "pull")))
	r.Methods("DELETE").Path("/v2/{imageName:.*}/_trust/tuf/").Handler(
		prometheus.InstrumentHandlerWithOpts(
			prometheusOpts("DeleteTuf"),
			hand(handlers.DeleteHandler, "push", "pull")))
	r.Methods("GET").Path("/_notary_server/health").Handler(hand(
		func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
			health.StatusHandler(w, r)
			return nil
		}))
	r.Methods("GET").Path("/_notary_server/metrics").Handler(prometheus.Handler())
	r.Methods("GET", "POST", "PUT", "HEAD", "DELETE").Path("/{other:.*}").Handler(hand(utils.NotFoundHandler))

	return r
}
<commit_msg>health check will never be able to get auth token so remove RootHandler wrapper Signed-off-by: David Lawrence <david.lawrence@docker.com> (github: endophage)<commit_after>package server

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/http"

	"github.com/Sirupsen/logrus"
	"github.com/docker/distribution/health"
	"github.com/docker/distribution/registry/auth"
	"github.com/docker/notary/server/handlers"
	"github.com/docker/notary/tuf/data"
	"github.com/docker/notary/tuf/signed"
	"github.com/docker/notary/utils"
	"github.com/gorilla/mux"
	"github.com/prometheus/client_golang/prometheus"
	"golang.org/x/net/context"
)

func init() {
	data.SetDefaultExpiryTimes(
		map[string]int{
			"timestamp": 14,
		},
	)
}

func prometheusOpts(operation string) prometheus.SummaryOpts {
	return prometheus.SummaryOpts{
		Namespace:   "notary_server",
		Subsystem:   "http",
		ConstLabels: prometheus.Labels{"operation": operation},
	}
}

// Run sets up and starts a TLS server that can be cancelled using the
// given configuration. The context it is passed is the context it should
// use directly for the TLS server, and generate children off for requests
func Run(ctx context.Context, addr string, tlsConfig *tls.Config, trust signed.CryptoService, authMethod string, authOpts interface{}) error {

	tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return err
	}
	var lsnr net.Listener
	lsnr, err = net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		return err
	}

	if tlsConfig != nil {
		logrus.Info("Enabling TLS")
		lsnr = tls.NewListener(lsnr, tlsConfig)
	}

	var ac auth.AccessController
	if authMethod == "token" {
		authOptions, ok := authOpts.(map[string]interface{})
		if !ok {
			return fmt.Errorf("auth.options must be a map[string]interface{}")
		}
		ac, err = auth.GetAccessController(authMethod, authOptions)
		if err != nil {
			return err
		}
	}

	svr := http.Server{
		Addr:    addr,
		Handler: RootHandler(ac, ctx, trust),
	}

	logrus.Info("Starting on ", addr)

	err = svr.Serve(lsnr)

	return err
}

// RootHandler returns the handler that routes all the paths from / for the
// server.
func RootHandler(ac auth.AccessController, ctx context.Context, trust signed.CryptoService) http.Handler {
	hand := utils.RootHandlerFactory(ac, ctx, trust)

	r := mux.NewRouter()
	r.Methods("GET").Path("/v2/").Handler(hand(handlers.MainHandler))
	r.Methods("POST").Path("/v2/{imageName:.*}/_trust/tuf/").Handler(
		prometheus.InstrumentHandlerWithOpts(
			prometheusOpts("UpdateTuf"),
			hand(handlers.AtomicUpdateHandler, "push", "pull")))
	r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/{tufRole:(root|targets|snapshot)}.json").Handler(
		prometheus.InstrumentHandlerWithOpts(
			prometheusOpts("GetRole"),
			hand(handlers.GetHandler, "pull")))
	r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/timestamp.json").Handler(
		prometheus.InstrumentHandlerWithOpts(
			prometheusOpts("GetTimestamp"),
			hand(handlers.GetTimestampHandler, "pull")))
	r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/timestamp.key").Handler(
		prometheus.InstrumentHandlerWithOpts(
			prometheusOpts("GetTimestampKey"),
			hand(handlers.GetTimestampKeyHandler, "push", "pull")))
	r.Methods("DELETE").Path("/v2/{imageName:.*}/_trust/tuf/").Handler(
		prometheus.InstrumentHandlerWithOpts(
			prometheusOpts("DeleteTuf"),
			hand(handlers.DeleteHandler, "push", "pull")))

	r.Methods("GET").Path("/_notary_server/health").HandlerFunc(health.StatusHandler)
	r.Methods("GET").Path("/_notary_server/metrics").Handler(prometheus.Handler())
	r.Methods("GET", "POST", "PUT", "HEAD", "DELETE").Path("/{other:.*}").Handler(hand(utils.NotFoundHandler))

	return r
}
<|endoftext|>


<commit_before>// Copyright 2015 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 (
	"flag"
	"fmt"
	"os"
	"sort"
	"strings"

	"golang.org/x/build/buildlet"
	"golang.org/x/build/dashboard"
)

func vmTypes() (s []string) {
	for k := range dashboard.Builders {
		s = append(s, k)
	}
	sort.Strings(s)
	return
}

func create(args []string) error {
	fs := flag.NewFlagSet("create", flag.ContinueOnError)

	fs.Usage = func() {
		fmt.Fprintln(os.Stderr, "create usage: gomote create [create-opts] <type>")
		fs.PrintDefaults()
		fmt.Fprintln(os.Stderr, "\nValid types:")
		for _, t := range vmTypes() {
			fmt.Fprintf(os.Stderr, "  * %s\n", t)
		}
		os.Exit(1)
	}
	// TODO(bradfitz): restore this option, and send it to the coordinator:
	// For now, comment it out so it's not misleading.
	// var timeout time.Duration
	// fs.DurationVar(&timeout, "timeout", 60*time.Minute, "how long the VM will live before being deleted.")

	fs.Parse(args)
	if fs.NArg() != 1 {
		fs.Usage()
	}
	builderType := fs.Arg(0)
	_, ok := dashboard.Builders[builderType]
	if !ok {
		var valid []string
		var prefixMatch []string
		for k := range dashboard.Builders {
			valid = append(valid, k)
			if strings.HasPrefix(k, builderType) {
				prefixMatch = append(prefixMatch, k)
			}
		}
		if len(prefixMatch) == 1 {
			builderType = prefixMatch[0]
		} else {
			sort.Strings(valid)
			return fmt.Errorf("Invalid builder type %q. Valid options include: %q", builderType, valid)
		}
	}

	cc, err := buildlet.NewCoordinatorClientFromFlags()
	if err != nil {
		return fmt.Errorf("failed to create coordinator client: %v", err)
	}
	client, err := cc.CreateBuildlet(builderType)
	if err != nil {
		return fmt.Errorf("failed to create buildlet: %v", err)
	}
	fmt.Println(client.RemoteName())
	return nil
}
<commit_msg>cmd/gomote: get list of buildlet types from server<commit_after>// Copyright 2015 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 (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"sort"
	"strings"

	"golang.org/x/build/buildlet"
)

type builderType struct {
	Name      string
	IsReverse bool
	ExpectNum int
}

func builders() (bt []builderType) {
	type builderInfo struct {
		HostType string
	}
	type hostInfo struct {
		IsReverse      bool
		ExpectNum      int
		ContainerImage string
		VMImage        string
	}
	// resj is the response JSON from the builders.
	var resj struct {
		Builders map[string]builderInfo
		Hosts    map[string]hostInfo
	}
	res, err := http.Get("https://farmer.golang.org/builders?mode=json")
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		log.Fatalf("fetching builder types: %s", res.Status)
	}
	if err := json.NewDecoder(res.Body).Decode(&resj); err != nil {
		log.Fatalf("decoding builder types: %v", err)
	}
	for b, bi := range resj.Builders {
		if strings.HasPrefix(b, "misc-compile") {
			continue
		}
		hi, ok := resj.Hosts[bi.HostType]
		if !ok {
			continue
		}
		if !hi.IsReverse && hi.ContainerImage == "" && hi.VMImage == "" {
			continue
		}
		bt = append(bt, builderType{
			Name:      b,
			IsReverse: hi.IsReverse,
			ExpectNum: hi.ExpectNum,
		})
	}
	sort.Slice(bt, func(i, j int) bool {
		return bt[i].Name < bt[j].Name
	})
	return
}

func create(args []string) error {
	fs := flag.NewFlagSet("create", flag.ContinueOnError)

	fs.Usage = func() {
		fmt.Fprintln(os.Stderr, "create usage: gomote create [create-opts] <type>")
		fs.PrintDefaults()
		fmt.Fprintln(os.Stderr, "\nValid types:")
		for _, bt := range builders() {
			var warn string
			if bt.IsReverse {
				if bt.ExpectNum > 0 {
					warn = fmt.Sprintf("   [limited capacity: %d machines]", bt.ExpectNum)
				} else {
					warn = "   [limited capacity]"
				}
			}
			fmt.Fprintf(os.Stderr, "  * %s%s\n", bt.Name, warn)
		}
		os.Exit(1)
	}
	// TODO(bradfitz): restore this option, and send it to the coordinator:
	// For now, comment it out so it's not misleading.
	// var timeout time.Duration
	// fs.DurationVar(&timeout, "timeout", 60*time.Minute, "how long the VM will live before being deleted.")

	fs.Parse(args)
	if fs.NArg() != 1 {
		fs.Usage()
	}
	builderType := fs.Arg(0)

	cc, err := buildlet.NewCoordinatorClientFromFlags()
	if err != nil {
		return fmt.Errorf("failed to create coordinator client: %v", err)
	}
	client, err := cc.CreateBuildlet(builderType)
	if err != nil {
		return fmt.Errorf("failed to create buildlet: %v", err)
	}
	fmt.Println(client.RemoteName())
	return nil
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/cenk/backoff"
	"github.com/gorilla/mux"

	"github.com/jtblin/kube2iam"
	"github.com/jtblin/kube2iam/iam"
	"github.com/jtblin/kube2iam/k8s"
	"github.com/jtblin/kube2iam/store"
)

const (
	defaultAppPort         = "8181"
	defaultIAMRoleKey      = "iam.amazonaws.com/role"
	defaultMaxElapsedTime  = 2 * time.Second
	defaultMaxInterval     = 1 * time.Second
	defaultMetadataAddress = "169.254.169.254"
	defaultNamespaceKey    = "iam.amazonaws.com/allowed-roles"
)

// Server encapsulates all of the parameters necessary for starting up
// the server. These can either be set via command line or directly.
type Server struct {
	APIServer               string
	APIToken                string
	AppPort                 string
	BaseRoleARN             string
	DefaultIAMRole          string
	IAMRoleKey              string
	MetadataAddress         string
	HostInterface           string
	HostIP                  string
	NamespaceKey            string
	AddIPTablesRule         bool
	AutoDiscoverBaseArn     bool
	AutoDiscoverDefaultRole bool
	Debug                   bool
	Insecure                bool
	NamespaceRestriction    bool
	Verbose                 bool
	Version                 bool
	iam                     *iam.Client
	k8s                     *k8s.Client
	store                   *store.Store
	BackoffMaxElapsedTime   time.Duration
	BackoffMaxInterval      time.Duration
}

type appHandler func(*log.Entry, http.ResponseWriter, *http.Request)

type responseWriter struct {
	http.ResponseWriter
	statusCode int
}

func (rw *responseWriter) WriteHeader(code int) {
	rw.statusCode = code
	rw.ResponseWriter.WriteHeader(code)
}

func newResponseWriter(w http.ResponseWriter) *responseWriter {
	return &responseWriter{w, http.StatusOK}
}

// ServeHTTP implements the net/http server Handler interface
// and recovers from panics.
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	logger := log.WithFields(log.Fields{
		"req.method": r.Method,
		"req.path":   r.URL.Path,
		"req.remote": parseRemoteAddr(r.RemoteAddr),
	})
	start := time.Now()
	defer func() {
		var err error
		if rec := recover(); rec != nil {
			switch t := rec.(type) {
			case string:
				err = errors.New(t)
			case error:
				err = t
			default:
				err = errors.New("Unknown error")
			}
			logger.WithField("res.status", http.StatusInternalServerError).
				Errorf("PANIC error processing request: %+v", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
	}()
	rw := newResponseWriter(w)
	fn(logger, rw, r)
	latency := time.Since(start)
	logger.WithFields(log.Fields{"res.duration": latency.Nanoseconds(), "res.status": rw.statusCode}).
		Info("Handling request")
}

func parseRemoteAddr(addr string) string {
	n := strings.IndexByte(addr, ':')
	if n <= 1 {
		return ""
	}
	hostname := addr[0:n]
	if net.ParseIP(hostname) == nil {
		return ""
	}
	return hostname
}

func (s *Server) getRole(IP string) (string, error) {
	var role string
	var err error
	operation := func() error {
		role, err = s.store.Get(IP)
		return err
	}

	expBackoff := backoff.NewExponentialBackOff()
	expBackoff.MaxInterval = s.BackoffMaxInterval
	expBackoff.MaxElapsedTime = s.BackoffMaxElapsedTime

	err = backoff.Retry(operation, expBackoff)
	if err != nil {
		return "", err
	}

	return role, nil
}

// HealthResponse represents a response for the health check.
type HealthResponse struct {
	HostIP     string `json:"hostIP"`
	InstanceID string `json:"instanceId"`
}

func (s *Server) healthHandler(logger *log.Entry, w http.ResponseWriter, r *http.Request) {
	resp, err := http.Get(fmt.Sprintf("http://%s/latest/meta-data/instance-id", s.MetadataAddress))
	if err != nil {
		log.Errorf("Error getting instance id %+v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if resp.StatusCode != 200 {
		msg := fmt.Sprintf("Error getting instance id, got status: %+s", resp.Status)
		log.Error(msg)
		http.Error(w, msg, http.StatusInternalServerError)
		return
	}
	defer resp.Body.Close()
	instanceID, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Errorf("Error reading response body %+v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	health := &HealthResponse{InstanceID: string(instanceID), HostIP: s.HostIP}
	w.Header().Add("Content-Type", "application/json")
	if err := json.NewEncoder(w).Encode(health); err != nil {
		log.Errorf("Error sending json %+v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func (s *Server) debugStoreHandler(logger *log.Entry, w http.ResponseWriter, r *http.Request) {
	output := make(map[string]interface{})

	output["rolesByIP"] = s.store.DumpRolesByIP()
	output["rolesByNamespace"] = s.store.DumpRolesByNamespace()
	output["namespaceByIP"] = s.store.DumpNamespaceByIP()

	o, err := json.Marshal(output)
	if err != nil {
		log.Errorf("Error converting debug map to json: %+v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	write(logger, w, string(o))
}

func (s *Server) securityCredentialsHandler(logger *log.Entry, w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Server", "EC2ws")
	remoteIP := parseRemoteAddr(r.RemoteAddr)
	role, err := s.getRole(remoteIP)
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}
	roleARN := s.iam.RoleARN(role)
	// If a base ARN has been supplied and this is not cross-account then
	// return a simple role-name, otherwise return the full ARN
	if s.iam.BaseARN != "" && strings.HasPrefix(roleARN, s.iam.BaseARN) {
		write(logger, w, strings.TrimPrefix(roleARN, s.iam.baseARN))
		return
	}
	write(logger, w, roleARN)
}

func (s *Server) roleHandler(logger *log.Entry, w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Server", "EC2ws")
	remoteIP := parseRemoteAddr(r.RemoteAddr)

	podRole, err := s.getRole(remoteIP)
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}

	podRoleARN := s.iam.RoleARN(podRole)
	isAllowed, namespace := s.store.CheckNamespaceRestriction(podRoleARN, remoteIP)

	roleLogger := logger.WithFields(log.Fields{
		"pod.iam.role": podRole,
		"ns.name":      namespace,
	})

	if !isAllowed {
		roleLogger.Warn("Rejected due to namespace restrictions")
		http.Error(w, fmt.Sprintf("Role requested %s not valid for namespace of pod at %s with namespace %s", podRole, remoteIP, namespace), http.StatusNotFound)
		return
	}

	wantedRole := mux.Vars(r)["role"]
	wantedRoleARN := s.iam.RoleARN(wantedRole)

	if wantedRoleARN != podRoleARN {
		roleLogger.WithField("params.iam.role", wantedRole).
			Error("Invalid role: does not match annotated role")
		http.Error(w, fmt.Sprintf("Invalid role %s", wantedRole), http.StatusForbidden)
		return
	}

	credentials, err := s.iam.AssumeRole(wantedRoleARN, remoteIP)
	if err != nil {
		roleLogger.Errorf("Error assuming role %+v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if err := json.NewEncoder(w).Encode(credentials); err != nil {
		roleLogger.Errorf("Error sending json %+v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func (s *Server) reverseProxyHandler(logger *log.Entry, w http.ResponseWriter, r *http.Request) {
	proxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: "http", Host: s.MetadataAddress})
	proxy.ServeHTTP(w, r)
	logger.WithField("metadata.url", s.MetadataAddress).Debug("Proxy ec2 metadata request")
}

func write(logger *log.Entry, w http.ResponseWriter, s string) {
	if _, err := w.Write([]byte(s)); err != nil {
		logger.Errorf("Error writing response: %+v", err)
	}
}

// Run runs the specified Server.
func (s *Server) Run(host, token string, insecure bool) error {
	k, err := k8s.NewClient(host, token, insecure)
	if err != nil {
		return err
	}
	s.k8s = k
	s.iam = iam.NewClient(s.BaseRoleARN)
	model := store.NewStore(s.IAMRoleKey, s.DefaultIAMRole, s.NamespaceRestriction, s.NamespaceKey, s.iam)
	s.store = model
	s.k8s.WatchForPods(kube2iam.NewPodHandler(model))
	s.k8s.WatchForNamespaces(kube2iam.NewNamespaceHandler(model))
	r := mux.NewRouter()
	if s.Debug {
		// This is a potential security risk if enabled in some clusters, hence the flag
		r.Handle("/debug/store", appHandler(s.debugStoreHandler))
	}
	r.Handle("/{version}/meta-data/iam/security-credentials/", appHandler(s.securityCredentialsHandler))
	r.Handle("/{version}/meta-data/iam/security-credentials/{role:.*}", appHandler(s.roleHandler))
	r.Handle("/healthz", appHandler(s.healthHandler))
	r.Handle("/{path:.*}", appHandler(s.reverseProxyHandler))

	log.Infof("Listening on port %s", s.AppPort)
	if err := http.ListenAndServe(":"+s.AppPort, r); err != nil {
		log.Fatalf("Error creating http server: %+v", err)
	}
	return nil
}

// NewServer will create a new Server with default values.
func NewServer() *Server {
	return &Server{
		AppPort:               defaultAppPort,
		BackoffMaxElapsedTime: defaultMaxElapsedTime,
		IAMRoleKey:            defaultIAMRoleKey,
		BackoffMaxInterval:    defaultMaxInterval,
		MetadataAddress:       defaultMetadataAddress,
		NamespaceKey:          defaultNamespaceKey,
	}
}
<commit_msg>Fix #85 - do not log requests for health check endpoint<commit_after>package server

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/cenk/backoff"
	"github.com/gorilla/mux"

	"github.com/jtblin/kube2iam"
	"github.com/jtblin/kube2iam/iam"
	"github.com/jtblin/kube2iam/k8s"
	"github.com/jtblin/kube2iam/store"
)

const (
	defaultAppPort         = "8181"
	defaultIAMRoleKey      = "iam.amazonaws.com/role"
	defaultMaxElapsedTime  = 2 * time.Second
	defaultMaxInterval     = 1 * time.Second
	defaultMetadataAddress = "169.254.169.254"
	defaultNamespaceKey    = "iam.amazonaws.com/allowed-roles"
)

// Server encapsulates all of the parameters necessary for starting up
// the server. These can either be set via command line or directly.
type Server struct {
	APIServer               string
	APIToken                string
	AppPort                 string
	BaseRoleARN             string
	DefaultIAMRole          string
	IAMRoleKey              string
	MetadataAddress         string
	HostInterface           string
	HostIP                  string
	NamespaceKey            string
	AddIPTablesRule         bool
	AutoDiscoverBaseArn     bool
	AutoDiscoverDefaultRole bool
	Debug                   bool
	Insecure                bool
	NamespaceRestriction    bool
	Verbose                 bool
	Version                 bool
	iam                     *iam.Client
	k8s                     *k8s.Client
	store                   *store.Store
	BackoffMaxElapsedTime   time.Duration
	BackoffMaxInterval      time.Duration
}

type appHandler func(*log.Entry, http.ResponseWriter, *http.Request)

type responseWriter struct {
	http.ResponseWriter
	statusCode int
}

func (rw *responseWriter) WriteHeader(code int) {
	rw.statusCode = code
	rw.ResponseWriter.WriteHeader(code)
}

func newResponseWriter(w http.ResponseWriter) *responseWriter {
	return &responseWriter{w, http.StatusOK}
}

// ServeHTTP implements the net/http server Handler interface
// and recovers from panics.
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	logger := log.WithFields(log.Fields{
		"req.method": r.Method,
		"req.path":   r.URL.Path,
		"req.remote": parseRemoteAddr(r.RemoteAddr),
	})
	start := time.Now()
	defer func() {
		var err error
		if rec := recover(); rec != nil {
			switch t := rec.(type) {
			case string:
				err = errors.New(t)
			case error:
				err = t
			default:
				err = errors.New("Unknown error")
			}
			logger.WithField("res.status", http.StatusInternalServerError).
				Errorf("PANIC error processing request: %+v", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
	}()
	rw := newResponseWriter(w)
	fn(logger, rw, r)
	if r.URL.Path != "/healthz" {
		latency := time.Since(start)
		logger.WithFields(log.Fields{"res.duration": latency.Nanoseconds(), "res.status": rw.statusCode}).
			Info("Handling request")
	}
}

func parseRemoteAddr(addr string) string {
	n := strings.IndexByte(addr, ':')
	if n <= 1 {
		return ""
	}
	hostname := addr[0:n]
	if net.ParseIP(hostname) == nil {
		return ""
	}
	return hostname
}

func (s *Server) getRole(IP string) (string, error) {
	var role string
	var err error
	operation := func() error {
		role, err = s.store.Get(IP)
		return err
	}

	expBackoff := backoff.NewExponentialBackOff()
	expBackoff.MaxInterval = s.BackoffMaxInterval
	expBackoff.MaxElapsedTime = s.BackoffMaxElapsedTime

	err = backoff.Retry(operation, expBackoff)
	if err != nil {
		return "", err
	}

	return role, nil
}

// HealthResponse represents a response for the health check.
type HealthResponse struct {
	HostIP     string `json:"hostIP"`
	InstanceID string `json:"instanceId"`
}

func (s *Server) healthHandler(logger *log.Entry, w http.ResponseWriter, r *http.Request) {
	resp, err := http.Get(fmt.Sprintf("http://%s/latest/meta-data/instance-id", s.MetadataAddress))
	if err != nil {
		log.Errorf("Error getting instance id %+v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if resp.StatusCode != 200 {
		msg := fmt.Sprintf("Error getting instance id, got status: %+s", resp.Status)
		log.Error(msg)
		http.Error(w, msg, http.StatusInternalServerError)
		return
	}
	defer resp.Body.Close()
	instanceID, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Errorf("Error reading response body %+v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	health := &HealthResponse{InstanceID: string(instanceID), HostIP: s.HostIP}
	w.Header().Add("Content-Type", "application/json")
	if err := json.NewEncoder(w).Encode(health); err != nil {
		log.Errorf("Error sending json %+v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func (s *Server) debugStoreHandler(logger *log.Entry, w http.ResponseWriter, r *http.Request) {
	output := make(map[string]interface{})

	output["rolesByIP"] = s.store.DumpRolesByIP()
	output["rolesByNamespace"] = s.store.DumpRolesByNamespace()
	output["namespaceByIP"] = s.store.DumpNamespaceByIP()

	o, err := json.Marshal(output)
	if err != nil {
		log.Errorf("Error converting debug map to json: %+v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	write(logger, w, string(o))
}

func (s *Server) securityCredentialsHandler(logger *log.Entry, w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Server", "EC2ws")
	remoteIP := parseRemoteAddr(r.RemoteAddr)
	role, err := s.getRole(remoteIP)
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}
	roleARN := s.iam.RoleARN(role)
	// If a base ARN has been supplied and this is not cross-account then
	// return a simple role-name, otherwise return the full ARN
	if s.iam.BaseARN != "" && strings.HasPrefix(roleARN, s.iam.BaseARN) {
		write(logger, w, strings.TrimPrefix(roleARN, s.iam.baseARN))
		return
	}
	write(logger, w, roleARN)
}

func (s *Server) roleHandler(logger *log.Entry, w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Server", "EC2ws")
	remoteIP := parseRemoteAddr(r.RemoteAddr)

	podRole, err := s.getRole(remoteIP)
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}

	podRoleARN := s.iam.RoleARN(podRole)
	isAllowed, namespace := s.store.CheckNamespaceRestriction(podRoleARN, remoteIP)

	roleLogger := logger.WithFields(log.Fields{
		"pod.iam.role": podRole,
		"ns.name":      namespace,
	})

	if !isAllowed {
		roleLogger.Warn("Rejected due to namespace restrictions")
		http.Error(w, fmt.Sprintf("Role requested %s not valid for namespace of pod at %s with namespace %s", podRole, remoteIP, namespace), http.StatusNotFound)
		return
	}

	wantedRole := mux.Vars(r)["role"]
	wantedRoleARN := s.iam.RoleARN(wantedRole)

	if wantedRoleARN != podRoleARN {
		roleLogger.WithField("params.iam.role", wantedRole).
			Error("Invalid role: does not match annotated role")
		http.Error(w, fmt.Sprintf("Invalid role %s", wantedRole), http.StatusForbidden)
		return
	}

	credentials, err := s.iam.AssumeRole(wantedRoleARN, remoteIP)
	if err != nil {
		roleLogger.Errorf("Error assuming role %+v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if err := json.NewEncoder(w).Encode(credentials); err != nil {
		roleLogger.Errorf("Error sending json %+v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func (s *Server) reverseProxyHandler(logger *log.Entry, w http.ResponseWriter, r *http.Request) {
	proxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: "http", Host: s.MetadataAddress})
	proxy.ServeHTTP(w, r)
	logger.WithField("metadata.url", s.MetadataAddress).Debug("Proxy ec2 metadata request")
}

func write(logger *log.Entry, w http.ResponseWriter, s string) {
	if _, err := w.Write([]byte(s)); err != nil {
		logger.Errorf("Error writing response: %+v", err)
	}
}

// Run runs the specified Server.
func (s *Server) Run(host, token string, insecure bool) error {
	k, err := k8s.NewClient(host, token, insecure)
	if err != nil {
		return err
	}
	s.k8s = k
	s.iam = iam.NewClient(s.BaseRoleARN)
	model := store.NewStore(s.IAMRoleKey, s.DefaultIAMRole, s.NamespaceRestriction, s.NamespaceKey, s.iam)
	s.store = model
	s.k8s.WatchForPods(kube2iam.NewPodHandler(model))
	s.k8s.WatchForNamespaces(kube2iam.NewNamespaceHandler(model))
	r := mux.NewRouter()
	if s.Debug {
		// This is a potential security risk if enabled in some clusters, hence the flag
		r.Handle("/debug/store", appHandler(s.debugStoreHandler))
	}
	r.Handle("/{version}/meta-data/iam/security-credentials/", appHandler(s.securityCredentialsHandler))
	r.Handle("/{version}/meta-data/iam/security-credentials/{role:.*}", appHandler(s.roleHandler))
	r.Handle("/healthz", appHandler(s.healthHandler))
	r.Handle("/{path:.*}", appHandler(s.reverseProxyHandler))

	log.Infof("Listening on port %s", s.AppPort)
	if err := http.ListenAndServe(":"+s.AppPort, r); err != nil {
		log.Fatalf("Error creating http server: %+v", err)
	}
	return nil
}

// NewServer will create a new Server with default values.
func NewServer() *Server {
	return &Server{
		AppPort:               defaultAppPort,
		BackoffMaxElapsedTime: defaultMaxElapsedTime,
		IAMRoleKey:            defaultIAMRoleKey,
		BackoffMaxInterval:    defaultMaxInterval,
		MetadataAddress:       defaultMetadataAddress,
		NamespaceKey:          defaultNamespaceKey,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"os"
	"os/exec"

	"github.com/hairyhenderson/gomplate"
	"github.com/hairyhenderson/gomplate/env"
	"github.com/hairyhenderson/gomplate/version"
	"github.com/spf13/cobra"
)

var (
	printVer bool
	verbose  bool
	opts     gomplate.Config
)

func validateOpts(cmd *cobra.Command, args []string) error {
	if cmd.Flag("in").Changed && cmd.Flag("file").Changed {
		return errors.New("--in and --file may not be used together")
	}

	if len(opts.InputFiles) != len(opts.OutputFiles) {
		return fmt.Errorf("Must provide same number of --out (%d) as --file (%d) options", len(opts.OutputFiles), len(opts.InputFiles))
	}

	if cmd.Flag("input-dir").Changed && (cmd.Flag("in").Changed || cmd.Flag("file").Changed) {
		return errors.New("--input-dir can not be used together with --in or --file")
	}

	if cmd.Flag("output-dir").Changed {
		if cmd.Flag("out").Changed {
			return errors.New("--output-dir can not be used together with --out")
		}
		if !cmd.Flag("input-dir").Changed {
			return errors.New("--input-dir must be set when --output-dir is set")
		}
	}
	return nil
}

func printVersion(name string) {
	fmt.Printf("%s version %s\n", name, version.Version)
}

// postRunExec - if templating succeeds, the command following a '--' will be executed
func postRunExec(cmd *cobra.Command, args []string) error {
	if len(args) > 0 {
		name := args[0]
		args = args[1:]
		// nolint: gosec
		c := exec.Command(name, args...)
		c.Stdin = os.Stdin
		c.Stderr = os.Stderr
		c.Stdout = os.Stdout
		return c.Run()
	}
	return nil
}

// optionalExecArgs - implements cobra.PositionalArgs. Allows extra args following
// a '--', but not otherwise.
func optionalExecArgs(cmd *cobra.Command, args []string) error {
	if cmd.ArgsLenAtDash() == 0 {
		return nil
	}
	return cobra.NoArgs(cmd, args)
}

func newGomplateCmd() *cobra.Command {
	rootCmd := &cobra.Command{
		Use:     "gomplate",
		Short:   "Process text files with Go templates",
		PreRunE: validateOpts,
		RunE: func(cmd *cobra.Command, args []string) error {
			if printVer {
				printVersion(cmd.Name())
				return nil
			}
			if verbose {
				fmt.Fprintf(os.Stderr, "%s version %s, build %s (%v)\nconfig is:\n%s\n\n",
					cmd.Name(), version.Version, version.GitCommit, version.BuildDate,
					&opts)
			}

			err := gomplate.RunTemplates(&opts)
			cmd.SilenceErrors = true
			cmd.SilenceUsage = true
			if verbose {
				fmt.Fprintf(os.Stderr, "rendered %d template(s) with %d error(s) in %v\n",
					gomplate.Metrics.TemplatesProcessed, gomplate.Metrics.Errors, gomplate.Metrics.TotalRenderDuration)
			}
			return err
		},
		PostRunE: postRunExec,
		Args:     optionalExecArgs,
	}
	return rootCmd
}

func initFlags(command *cobra.Command) {
	command.Flags().SortFlags = false

	command.Flags().StringArrayVarP(&opts.DataSources, "datasource", "d", nil, "`datasource` in alias=URL form. Specify multiple times to add multiple sources.")
	command.Flags().StringArrayVarP(&opts.DataSourceHeaders, "datasource-header", "H", nil, "HTTP `header` field in 'alias=Name: value' form to be provided on HTTP-based data sources. Multiples can be set.")

	command.Flags().StringArrayVarP(&opts.InputFiles, "file", "f", []string{"-"}, "Template `file` to process. Omit to use standard input, or use --in or --input-dir")
	command.Flags().StringVarP(&opts.Input, "in", "i", "", "Template `string` to process (alternative to --file and --input-dir)")
	command.Flags().StringVar(&opts.InputDir, "input-dir", "", "`directory` which is examined recursively for templates (alternative to --file and --in)")

	command.Flags().StringArrayVar(&opts.ExcludeGlob, "exclude", []string{}, "glob of files to not parse")

	command.Flags().StringArrayVarP(&opts.OutputFiles, "out", "o", []string{"-"}, "output `file` name. Omit to use standard output.")
	command.Flags().StringVar(&opts.OutputDir, "output-dir", ".", "`directory` to store the processed templates. Only used for --input-dir")
	command.Flags().StringVar(&opts.OutMode, "chmod", "", "set the mode for output file(s). Omit to inherit from input file(s)")

	ldDefault := env.Getenv("GOMPLATE_LEFT_DELIM", "{{")
	rdDefault := env.Getenv("GOMPLATE_RIGHT_DELIM", "}}")
	command.Flags().StringVar(&opts.LDelim, "left-delim", ldDefault, "override the default left-`delimiter` [$GOMPLATE_LEFT_DELIM]")
	command.Flags().StringVar(&opts.RDelim, "right-delim", rdDefault, "override the default right-`delimiter` [$GOMPLATE_RIGHT_DELIM]")

	command.Flags().BoolVarP(&verbose, "verbose", "V", false, "output extra information about what gomplate is doing")

	command.Flags().BoolVarP(&printVer, "version", "v", false, "print the version")
}

func main() {
	command := newGomplateCmd()
	initFlags(command)
	if err := command.Execute(); err != nil {
		// nolint: errcheck
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<commit_msg>Don't complain about fmt.Fprintln with os.Stderr<commit_after>package main

import (
	"errors"
	"fmt"
	"os"
	"os/exec"

	"github.com/hairyhenderson/gomplate"
	"github.com/hairyhenderson/gomplate/env"
	"github.com/hairyhenderson/gomplate/version"
	"github.com/spf13/cobra"
)

var (
	printVer bool
	verbose  bool
	opts     gomplate.Config
)

func validateOpts(cmd *cobra.Command, args []string) error {
	if cmd.Flag("in").Changed && cmd.Flag("file").Changed {
		return errors.New("--in and --file may not be used together")
	}

	if len(opts.InputFiles) != len(opts.OutputFiles) {
		return fmt.Errorf("Must provide same number of --out (%d) as --file (%d) options", len(opts.OutputFiles), len(opts.InputFiles))
	}

	if cmd.Flag("input-dir").Changed && (cmd.Flag("in").Changed || cmd.Flag("file").Changed) {
		return errors.New("--input-dir can not be used together with --in or --file")
	}

	if cmd.Flag("output-dir").Changed {
		if cmd.Flag("out").Changed {
			return errors.New("--output-dir can not be used together with --out")
		}
		if !cmd.Flag("input-dir").Changed {
			return errors.New("--input-dir must be set when --output-dir is set")
		}
	}
	return nil
}

func printVersion(name string) {
	fmt.Printf("%s version %s\n", name, version.Version)
}

// postRunExec - if templating succeeds, the command following a '--' will be executed
func postRunExec(cmd *cobra.Command, args []string) error {
	if len(args) > 0 {
		name := args[0]
		args = args[1:]
		// nolint: gosec
		c := exec.Command(name, args...)
		c.Stdin = os.Stdin
		c.Stderr = os.Stderr
		c.Stdout = os.Stdout
		return c.Run()
	}
	return nil
}

// optionalExecArgs - implements cobra.PositionalArgs. Allows extra args following
// a '--', but not otherwise.
func optionalExecArgs(cmd *cobra.Command, args []string) error {
	if cmd.ArgsLenAtDash() == 0 {
		return nil
	}
	return cobra.NoArgs(cmd, args)
}

func newGomplateCmd() *cobra.Command {
	rootCmd := &cobra.Command{
		Use:     "gomplate",
		Short:   "Process text files with Go templates",
		PreRunE: validateOpts,
		RunE: func(cmd *cobra.Command, args []string) error {
			if printVer {
				printVersion(cmd.Name())
				return nil
			}
			if verbose {
				// nolint: errcheck
				fmt.Fprintf(os.Stderr, "%s version %s, build %s (%v)\nconfig is:\n%s\n\n",
					cmd.Name(), version.Version, version.GitCommit, version.BuildDate,
					&opts)
			}

			err := gomplate.RunTemplates(&opts)
			cmd.SilenceErrors = true
			cmd.SilenceUsage = true
			if verbose {
				// nolint: errcheck
				fmt.Fprintf(os.Stderr, "rendered %d template(s) with %d error(s) in %v\n",
					gomplate.Metrics.TemplatesProcessed, gomplate.Metrics.Errors, gomplate.Metrics.TotalRenderDuration)
			}
			return err
		},
		PostRunE: postRunExec,
		Args:     optionalExecArgs,
	}
	return rootCmd
}

func initFlags(command *cobra.Command) {
	command.Flags().SortFlags = false

	command.Flags().StringArrayVarP(&opts.DataSources, "datasource", "d", nil, "`datasource` in alias=URL form. Specify multiple times to add multiple sources.")
	command.Flags().StringArrayVarP(&opts.DataSourceHeaders, "datasource-header", "H", nil, "HTTP `header` field in 'alias=Name: value' form to be provided on HTTP-based data sources. Multiples can be set.")

	command.Flags().StringArrayVarP(&opts.InputFiles, "file", "f", []string{"-"}, "Template `file` to process. Omit to use standard input, or use --in or --input-dir")
	command.Flags().StringVarP(&opts.Input, "in", "i", "", "Template `string` to process (alternative to --file and --input-dir)")
	command.Flags().StringVar(&opts.InputDir, "input-dir", "", "`directory` which is examined recursively for templates (alternative to --file and --in)")

	command.Flags().StringArrayVar(&opts.ExcludeGlob, "exclude", []string{}, "glob of files to not parse")

	command.Flags().StringArrayVarP(&opts.OutputFiles, "out", "o", []string{"-"}, "output `file` name. Omit to use standard output.")
	command.Flags().StringVar(&opts.OutputDir, "output-dir", ".", "`directory` to store the processed templates. Only used for --input-dir")
	command.Flags().StringVar(&opts.OutMode, "chmod", "", "set the mode for output file(s). Omit to inherit from input file(s)")

	ldDefault := env.Getenv("GOMPLATE_LEFT_DELIM", "{{")
	rdDefault := env.Getenv("GOMPLATE_RIGHT_DELIM", "}}")
	command.Flags().StringVar(&opts.LDelim, "left-delim", ldDefault, "override the default left-`delimiter` [$GOMPLATE_LEFT_DELIM]")
	command.Flags().StringVar(&opts.RDelim, "right-delim", rdDefault, "override the default right-`delimiter` [$GOMPLATE_RIGHT_DELIM]")

	command.Flags().BoolVarP(&verbose, "verbose", "V", false, "output extra information about what gomplate is doing")

	command.Flags().BoolVarP(&printVer, "version", "v", false, "print the version")
}

func main() {
	command := newGomplateCmd()
	initFlags(command)
	if err := command.Execute(); err != nil {
		// nolint: errcheck
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package vsphere

import (
	"github.com/Sirupsen/logrus"
	"github.com/emc-advanced-dev/pkg/errors"
	"github.com/emc-advanced-dev/unik/pkg/providers/common"
	"github.com/emc-advanced-dev/unik/pkg/types"
	unikutil "github.com/emc-advanced-dev/unik/pkg/util"
	"github.com/layer-x/layerx-commons/lxhttpclient"
	"time"
)

func (p *VsphereProvider) RunInstance(params types.RunInstanceParams) (_ *types.Instance, err error) {
	logrus.WithFields(logrus.Fields{
		"image-id": params.ImageId,
		"mounts":   params.MntPointsToVolumeIds,
		"env":      params.Env,
	}).Infof("running instance %s", params.Name)

	if _, err := p.GetInstance(params.Name); err == nil {
		return nil, errors.New("instance with name "+params.Name+" already exists. virtualbox provider requires unique names for instances", nil)
	}

	image, err := p.GetImage(params.ImageId)
	if err != nil {
		return nil, errors.New("getting image", err)
	}

	if err := common.VerifyMntsInput(p, image, params.MntPointsToVolumeIds); err != nil {
		return nil, errors.New("invalid mapping for volume", err)
	}

	instanceDir := getInstanceDatastoreDir(params.Name)

	portsUsed := []int{}

	c := p.getClient()

	defer func() {
		if err != nil {
			if params.NoCleanup {
				logrus.Warnf("because --no-cleanup flag was provided, not cleaning up failed instance %s001", params.Name)
				return
			}
			logrus.WithError(err).Warnf("error encountered, ensuring vm and disks are destroyed")
			c.PowerOffVm(params.Name)
			for _, portUsed := range portsUsed {
				c.DetachDisk(params.Name, portUsed, image.RunSpec.StorageDriver)
			}
			c.DestroyVm(params.Name)
			c.Rmdir(instanceDir)
		}
	}()

	logrus.Debugf("creating vsphere vm")

	//if not set, use default
	if params.InstanceMemory <= 0 {
		params.InstanceMemory = image.RunSpec.DefaultInstanceMemory
	}

	if err := c.CreateVm(params.Name, params.InstanceMemory, image.RunSpec.VsphereNetworkType, p.config.NetworkLabel); err != nil {
		return nil, errors.New("creating vm", err)
	}

	logrus.Debugf("powering on vm to assign mac addr")
	if err := c.PowerOnVm(params.Name); err != nil {
		return nil, errors.New("failed to power on vm to assign mac addr", err)
	}

	vm, err := c.GetVm(params.Name)
	if err != nil {
		return nil, errors.New("failed to retrieve vm info after create", err)
	}

	macAddr := ""
	if vm.Config.Hardware.Device != nil {
		for _, device := range vm.Config.Hardware.Device {
			if len(device.MacAddress) > 0 {
				macAddr = device.MacAddress
				break
			}
		}
	}
	if macAddr == "" {
		logrus.WithFields(logrus.Fields{"vm": vm}).Warnf("vm found, cannot identify mac addr")
		return nil, errors.New("could not find mac addr on vm", nil)
	}
	if err := c.PowerOffVm(params.Name); err != nil {
		return nil, errors.New("failed to power off vm after retrieving mac addr", err)
	}

	logrus.Debugf("copying base boot vmdk to instance dir")
	instanceBootImagePath := instanceDir + "/boot.vmdk"
	if err := c.CopyVmdk(getImageDatastorePath(image.Name), instanceBootImagePath); err != nil {
		return nil, errors.New("copying base boot image", err)
	}
	if err := c.AttachDisk(params.Name, instanceBootImagePath, 0, image.RunSpec.StorageDriver); err != nil {
		return nil, errors.New("attaching boot vol to instance", err)
	}

	for mntPoint, volumeId := range params.MntPointsToVolumeIds {
		volume, err := p.GetVolume(volumeId)
		if err != nil {
			return nil, errors.New("getting volume", err)
		}
		controllerPort, err := common.GetControllerPortForMnt(image, mntPoint)
		if err != nil {
			return nil, errors.New("getting controller port for mnt point", err)
		}
		if err := c.AttachDisk(params.Name, getVolumeDatastorePath(volume.Name), controllerPort, image.RunSpec.StorageDriver); err != nil {
			return nil, errors.New("attaching disk to vm", err)
		}
		portsUsed = append(portsUsed, controllerPort)
	}

	instanceListenerIp, err := common.GetInstanceListenerIp(instanceListenerPrefix, timeout)
	if err != nil {
		return nil, errors.New("failed to retrieve instance listener ip. is unik instance listener running?", err)
	}

	logrus.Debugf("sending env to listener")
	if _, _, err := lxhttpclient.Post(instanceListenerIp+":3000", "/set_instance_env?mac_address="+macAddr, nil, params.Env); err != nil {
		return nil, errors.New("sending instance env to listener", err)
	}

	logrus.Debugf("powering on vm")
	if err := c.PowerOnVm(params.Name); err != nil {
		return nil, errors.New("powering on vm", err)
	}

	var instanceIp string
	instanceId := vm.Config.UUID

	if err := unikutil.Retry(5, time.Duration(2000*time.Millisecond), func() error {
		logrus.Debugf("getting instance ip")
		instanceIp, err = common.GetInstanceIp(instanceListenerIp, 3000, macAddr)
		if err != nil {
			return err
		}
		return nil
	}); err != nil {
		logrus.Warnf("failed to retrieve ip for instance %s. instance may be running but has not responded to udp broadcast", instanceId)
	}

	instance := &types.Instance{
		Id:             instanceId,
		Name:           params.Name,
		State:          types.InstanceState_Pending,
		IpAddress:      instanceIp,
		Infrastructure: types.Infrastructure_VSPHERE,
		ImageId:        image.Id,
		Created:        time.Now(),
	}

	if err := p.state.ModifyInstances(func(instances map[string]*types.Instance) error {
		instances[instance.Id] = instance
		return nil
	}); err != nil {
		return nil, errors.New("modifying instance map in state", err)
	}
	if err := p.state.Save(); err != nil {
		return nil, errors.New("saving instance volume map to state", err)
	}

	logrus.WithField("instance", instance).Infof("instance created successfully")

	return instance, nil
}
<commit_msg>Fixing an issue when running an instance with vCenter<commit_after>package vsphere

import (
	"github.com/Sirupsen/logrus"
	"github.com/emc-advanced-dev/pkg/errors"
	"github.com/emc-advanced-dev/unik/pkg/providers/common"
	"github.com/emc-advanced-dev/unik/pkg/types"
	unikutil "github.com/emc-advanced-dev/unik/pkg/util"
	"github.com/layer-x/layerx-commons/lxhttpclient"
	"strings"
	"time"
)

func (p *VsphereProvider) RunInstance(params types.RunInstanceParams) (_ *types.Instance, err error) {
	logrus.WithFields(logrus.Fields{
		"image-id": params.ImageId,
		"mounts":   params.MntPointsToVolumeIds,
		"env":      params.Env,
	}).Infof("running instance %s", params.Name)

	if _, err := p.GetInstance(params.Name); err == nil {
		return nil, errors.New("instance with name "+params.Name+" already exists. virtualbox provider requires unique names for instances", nil)
	}

	image, err := p.GetImage(params.ImageId)
	if err != nil {
		return nil, errors.New("getting image", err)
	}

	if err := common.VerifyMntsInput(p, image, params.MntPointsToVolumeIds); err != nil {
		return nil, errors.New("invalid mapping for volume", err)
	}

	instanceDir := getInstanceDatastoreDir(params.Name)

	portsUsed := []int{}

	c := p.getClient()

	defer func() {
		if err != nil {
			if params.NoCleanup {
				logrus.Warnf("because --no-cleanup flag was provided, not cleaning up failed instance %s001", params.Name)
				return
			}
			logrus.WithError(err).Warnf("error encountered, ensuring vm and disks are destroyed")
			c.PowerOffVm(params.Name)
			for _, portUsed := range portsUsed {
				c.DetachDisk(params.Name, portUsed, image.RunSpec.StorageDriver)
			}
			c.DestroyVm(params.Name)
			c.Rmdir(instanceDir)
		}
	}()

	logrus.Debugf("creating vsphere vm")

	//if not set, use default
	if params.InstanceMemory <= 0 {
		params.InstanceMemory = image.RunSpec.DefaultInstanceMemory
	}

	if err := c.CreateVm(params.Name, params.InstanceMemory, image.RunSpec.VsphereNetworkType, p.config.NetworkLabel); err != nil {
		return nil, errors.New("creating vm", err)
	}

	logrus.Debugf("powering on vm to assign mac addr")
	if err := c.PowerOnVm(params.Name); err != nil {
		return nil, errors.New("failed to power on vm to assign mac addr", err)
	}

	vm, err := c.GetVm(params.Name)
	if err != nil {
		return nil, errors.New("failed to retrieve vm info after create", err)
	}

	macAddr := ""
	if vm.Config.Hardware.Device != nil {
		for _, device := range vm.Config.Hardware.Device {
			if len(device.MacAddress) > 0 {
				macAddr = device.MacAddress
				break
			}
		}
	}
	if macAddr == "" {
		logrus.WithFields(logrus.Fields{"vm": vm}).Warnf("vm found, cannot identify mac addr")
		return nil, errors.New("could not find mac addr on vm", nil)
	}
	if err := c.PowerOffVm(params.Name); err != nil {
		return nil, errors.New("failed to power off vm after retrieving mac addr", err)
	}

	logrus.Debugf("copying base boot vmdk to instance dir")
	instanceBootImagePath := instanceDir + "/boot.vmdk"
	if err := c.CopyFile(getImageDatastorePath(image.Name), instanceBootImagePath); err != nil {
		return nil, errors.New("copying base boot.vmdk", err)
	}
	if err := c.CopyFile(strings.TrimSuffix(getImageDatastorePath(image.Name), ".vmdk") + "-flat.vmdk", strings.TrimSuffix(instanceBootImagePath, ".vmdk") + "-flat.vmdk"); err != nil {
		return nil, errors.New("copying base boot-flat.vmdk", err)
	}
	if err := c.AttachDisk(params.Name, instanceBootImagePath, 0, image.RunSpec.StorageDriver); err != nil {
		return nil, errors.New("attaching boot vol to instance", err)
	}

	for mntPoint, volumeId := range params.MntPointsToVolumeIds {
		volume, err := p.GetVolume(volumeId)
		if err != nil {
			return nil, errors.New("getting volume", err)
		}
		controllerPort, err := common.GetControllerPortForMnt(image, mntPoint)
		if err != nil {
			return nil, errors.New("getting controller port for mnt point", err)
		}
		if err := c.AttachDisk(params.Name, getVolumeDatastorePath(volume.Name), controllerPort, image.RunSpec.StorageDriver); err != nil {
			return nil, errors.New("attaching disk to vm", err)
		}
		portsUsed = append(portsUsed, controllerPort)
	}

	instanceListenerIp, err := common.GetInstanceListenerIp(instanceListenerPrefix, timeout)
	if err != nil {
		return nil, errors.New("failed to retrieve instance listener ip. is unik instance listener running?", err)
	}

	logrus.Debugf("sending env to listener")
	if _, _, err := lxhttpclient.Post(instanceListenerIp+":3000", "/set_instance_env?mac_address="+macAddr, nil, params.Env); err != nil {
		return nil, errors.New("sending instance env to listener", err)
	}

	logrus.Debugf("powering on vm")
	if err := c.PowerOnVm(params.Name); err != nil {
		return nil, errors.New("powering on vm", err)
	}

	var instanceIp string
	instanceId := vm.Config.UUID

	if err := unikutil.Retry(5, time.Duration(2000*time.Millisecond), func() error {
		logrus.Debugf("getting instance ip")
		instanceIp, err = common.GetInstanceIp(instanceListenerIp, 3000, macAddr)
		if err != nil {
			return err
		}
		return nil
	}); err != nil {
		logrus.Warnf("failed to retrieve ip for instance %s. instance may be running but has not responded to udp broadcast", instanceId)
	}

	instance := &types.Instance{
		Id:             instanceId,
		Name:           params.Name,
		State:          types.InstanceState_Pending,
		IpAddress:      instanceIp,
		Infrastructure: types.Infrastructure_VSPHERE,
		ImageId:        image.Id,
		Created:        time.Now(),
	}

	if err := p.state.ModifyInstances(func(instances map[string]*types.Instance) error {
		instances[instance.Id] = instance
		return nil
	}); err != nil {
		return nil, errors.New("modifying instance map in state", err)
	}
	if err := p.state.Save(); err != nil {
		return nil, errors.New("saving instance volume map to state", err)
	}

	logrus.WithField("instance", instance).Infof("instance created successfully")

	return instance, nil
}
<|endoftext|>


<commit_before>/*
i-sudoku is an interactive command-line sudoku tool
*/

package main

import (
	"fmt"
	"github.com/jkomoros/sudoku"
	"github.com/nsf/termbox-go"
	"log"
	"strings"
	"unicode/utf8"
)

const (
	GRID_INVALID    = " INVALID "
	GRID_VALID      = "  VALID  "
	GRID_SOLVED     = "  SOLVED  "
	GRID_NOT_SOLVED = " UNSOLVED "
	FAST_MODE_ON    = "  FAST MODE ON  "
	FAST_MODE_OFF   = "  FAST MODE OFF "
)

//A debug override; if true will print a color palette to the screen, wait for
//a keypress, and then quit. Useful for seeing what different colors are
//available to use.
const DRAW_PALETTE = false

func main() {

	//TODO: should be possible to run it and pass in a puzzle to use.

	if err := termbox.Init(); err != nil {
		log.Fatal("Termbox initialization failed:", err)
	}
	defer termbox.Close()

	termbox.SetOutputMode(termbox.Output256)

	model := newModel()

	width, _ := termbox.Size()
	model.outputWidth = width

	if DRAW_PALETTE {
		drawColorPalette()
		//Wait until something happens, generally a key is pressed.
		termbox.PollEvent()
		return
	}

	draw(model)

mainloop:
	for {
		evt := termbox.PollEvent()
		switch evt.Type {
		case termbox.EventKey:
			model.state.handleInput(model, evt)
		}
		draw(model)
		if model.exitNow {
			break mainloop
		}
		model.EndOfEventLoop()
	}
}

func clearScreen() {
	width, height := termbox.Size()
	for x := 0; x < width; x++ {
		for y := 0; y < height; y++ {
			termbox.SetCell(x, y, ' ', termbox.ColorDefault, termbox.ColorDefault)
		}
	}
}

func drawColorPalette() {
	clearScreen()
	x := 0
	y := 0

	for i := 0x00; i <= 0xFF; i++ {
		numToPrint := "  " + fmt.Sprintf("%02X", i) + "  "
		for _, ch := range numToPrint {
			termbox.SetCell(x, y, ch, termbox.ColorBlack, termbox.Attribute(i))
			x++
		}
		//Fit 8 print outs on a line before creating a new one
		if i%8 == 0 {
			x = 0
			y++
		}
	}

	termbox.Flush()
}

func drawGrid(y int, model *mainModel) (endY int) {
	var x int
	grid := model.grid
	selectedTop, selectedLeft, selectedHeight, selectedWidth := model.Selected().DiagramExtents()
	for _, line := range strings.Split(grid.Diagram(true), "\n") {
		x = 0
		//The first number in range will be byte offset, but for some items like the bullet, it's two bytes.
		//But what we care about is that each item is a character.
		for _, ch := range line {

			defaultColor := termbox.ColorGreen

			numberRune, _ := utf8.DecodeRuneInString(sudoku.DIAGRAM_NUMBER)
			lockedRune, _ := utf8.DecodeRuneInString(sudoku.DIAGRAM_LOCKED)

			if ch == numberRune {
				defaultColor = 0x12
			} else if ch == lockedRune {
				defaultColor = 0x35
			} else if runeIsNum(ch) {
				defaultColor = termbox.ColorWhite | termbox.AttrBold
			}

			backgroundColor := termbox.ColorDefault

			if x >= selectedTop && x < (selectedTop+selectedHeight) && y >= selectedLeft && y < (selectedLeft+selectedWidth) {
				//We're on the selected cell
				backgroundColor = 0xf0
			}

			termbox.SetCell(x, y, ch, defaultColor, backgroundColor)
			x++
		}
		y++
	}
	//The last loop added one extra to y.
	y--
	return y
}

func drawToggleLine(y int, model *mainModel) (newY int) {
	x := 0
	grid := model.grid
	solvedMsg := GRID_NOT_SOLVED
	fg := termbox.ColorBlue
	bg := termbox.ColorBlack
	if grid.Solved() {
		solvedMsg = GRID_SOLVED
		fg, bg = bg, fg
	}

	for _, ch := range solvedMsg {
		termbox.SetCell(x, y, ch, fg, bg)
		x++
	}

	//don't reset x; this next message should go to the right.
	validMsg := GRID_VALID
	fg = termbox.ColorBlue
	bg = termbox.ColorBlack
	if grid.Invalid() {
		validMsg = GRID_INVALID
		fg, bg = bg, fg
	}
	for _, ch := range validMsg {
		termbox.SetCell(x, y, ch, fg, bg)
		x++
	}

	fastModeMsg := FAST_MODE_OFF
	fg = termbox.ColorBlue
	bg = termbox.ColorBlack
	if model.fastMode {
		fastModeMsg = FAST_MODE_ON
		fg, bg = bg, fg
	}
	for _, ch := range fastModeMsg {
		termbox.SetCell(x, y, ch, fg, bg)
		x++
	}
	return y
}

func drawStatusLine(y int, model *mainModel) (newY int) {
	x := 0
	var fg termbox.Attribute
	underlined := false
	for _, ch := range ">>> " + model.StatusLine() {
		//The ( and ) are non-printing control characters
		if ch == '{' {
			underlined = true
			continue
		} else if ch == '}' {
			underlined = false
			continue
		}
		fg = termbox.ColorBlack
		if underlined {
			fg = fg | termbox.AttrUnderline | termbox.AttrBold
		}

		termbox.SetCell(x, y, ch, fg, termbox.ColorWhite)
		x++
	}

	width, _ := termbox.Size()

	for x < width {
		termbox.SetCell(x, y, ' ', fg, termbox.ColorWhite)
		x++
	}
	return y
}

func drawConsole(y int, model *mainModel) (newY int) {

	x := 0

	underlined := false

	splitMessage := strings.Split(model.consoleMessage, "\n")

	for _, line := range splitMessage {

		x = 0
		for _, ch := range line {
			if ch == '{' {
				underlined = true
				continue
			} else if ch == '}' {
				underlined = false
				continue
			}
			fg := termbox.Attribute(0xf6)
			if underlined {
				fg = termbox.Attribute(0xFC) | termbox.AttrBold
			}
			termbox.SetCell(x, y, ch, fg, termbox.ColorBlack)
			x++
		}
		y++
	}
	//y is one too many
	y--
	return y
}

func draw(model *mainModel) {

	clearScreen()

	y := 0

	y = drawGrid(y, model)
	y++
	y = drawToggleLine(y, model)
	y++
	y = drawStatusLine(y, model)
	y++
	y = drawConsole(y, model)

	termbox.Flush()
}
<commit_msg>Add a legend along the top of the grid only. (Next step is along left, too). Part of implementing #167.<commit_after>/*
i-sudoku is an interactive command-line sudoku tool
*/

package main

import (
	"fmt"
	"github.com/jkomoros/sudoku"
	"github.com/nsf/termbox-go"
	"log"
	"strconv"
	"strings"
	"unicode/utf8"
)

const (
	GRID_INVALID    = " INVALID "
	GRID_VALID      = "  VALID  "
	GRID_SOLVED     = "  SOLVED  "
	GRID_NOT_SOLVED = " UNSOLVED "
	FAST_MODE_ON    = "  FAST MODE ON  "
	FAST_MODE_OFF   = "  FAST MODE OFF "
)

//A debug override; if true will print a color palette to the screen, wait for
//a keypress, and then quit. Useful for seeing what different colors are
//available to use.
const DRAW_PALETTE = false

func main() {

	//TODO: should be possible to run it and pass in a puzzle to use.

	if err := termbox.Init(); err != nil {
		log.Fatal("Termbox initialization failed:", err)
	}
	defer termbox.Close()

	termbox.SetOutputMode(termbox.Output256)

	model := newModel()

	width, _ := termbox.Size()
	model.outputWidth = width

	if DRAW_PALETTE {
		drawColorPalette()
		//Wait until something happens, generally a key is pressed.
		termbox.PollEvent()
		return
	}

	draw(model)

mainloop:
	for {
		evt := termbox.PollEvent()
		switch evt.Type {
		case termbox.EventKey:
			model.state.handleInput(model, evt)
		}
		draw(model)
		if model.exitNow {
			break mainloop
		}
		model.EndOfEventLoop()
	}
}

func clearScreen() {
	width, height := termbox.Size()
	for x := 0; x < width; x++ {
		for y := 0; y < height; y++ {
			termbox.SetCell(x, y, ' ', termbox.ColorDefault, termbox.ColorDefault)
		}
	}
}

func drawColorPalette() {
	clearScreen()
	x := 0
	y := 0

	for i := 0x00; i <= 0xFF; i++ {
		numToPrint := "  " + fmt.Sprintf("%02X", i) + "  "
		for _, ch := range numToPrint {
			termbox.SetCell(x, y, ch, termbox.ColorBlack, termbox.Attribute(i))
			x++
		}
		//Fit 8 print outs on a line before creating a new one
		if i%8 == 0 {
			x = 0
			y++
		}
	}

	termbox.Flush()
}

func drawGrid(y int, model *mainModel) (endY int) {
	var x int
	grid := model.grid

	lines := strings.Split(grid.Diagram(true), "\n")

	fg := termbox.ColorBlack
	bg := termbox.ColorGreen

	for i := 0; i < sudoku.DIM; i++ {

		cellLeft, _, _, _ := grid.Cell(0, i).DiagramExtents()
		//Pad until we get to the start of this cell area
		for x < cellLeft {
			termbox.SetCell(x, y, '|', fg, bg)
			x++
		}
		for _, ch := range " " + strconv.Itoa(i) + " " {
			termbox.SetCell(x, y, ch, fg, bg)
			x++
		}
	}

	y++

	//TODO: I'm pretty sure top/left are reversed
	selectedTop, selectedLeft, selectedHeight, selectedWidth := model.Selected().DiagramExtents()
	//Correct the selected coordinate for the offset of the grid from the top.
	selectedLeft += y
	for _, line := range lines {
		x = 0
		//The first number in range will be byte offset, but for some items like the bullet, it's two bytes.
		//But what we care about is that each item is a character.
		for _, ch := range line {

			defaultColor := termbox.ColorGreen

			numberRune, _ := utf8.DecodeRuneInString(sudoku.DIAGRAM_NUMBER)
			lockedRune, _ := utf8.DecodeRuneInString(sudoku.DIAGRAM_LOCKED)

			if ch == numberRune {
				defaultColor = 0x12
			} else if ch == lockedRune {
				defaultColor = 0x35
			} else if runeIsNum(ch) {
				defaultColor = termbox.ColorWhite | termbox.AttrBold
			}

			backgroundColor := termbox.ColorDefault

			if x >= selectedTop && x < (selectedTop+selectedHeight) && y >= selectedLeft && y < (selectedLeft+selectedWidth) {
				//We're on the selected cell
				backgroundColor = 0xf0
			}

			termbox.SetCell(x, y, ch, defaultColor, backgroundColor)
			x++
		}
		y++
	}
	//The last loop added one extra to y.
	y--
	return y
}

func drawToggleLine(y int, model *mainModel) (newY int) {
	x := 0
	grid := model.grid
	solvedMsg := GRID_NOT_SOLVED
	fg := termbox.ColorBlue
	bg := termbox.ColorBlack
	if grid.Solved() {
		solvedMsg = GRID_SOLVED
		fg, bg = bg, fg
	}

	for _, ch := range solvedMsg {
		termbox.SetCell(x, y, ch, fg, bg)
		x++
	}

	//don't reset x; this next message should go to the right.
	validMsg := GRID_VALID
	fg = termbox.ColorBlue
	bg = termbox.ColorBlack
	if grid.Invalid() {
		validMsg = GRID_INVALID
		fg, bg = bg, fg
	}
	for _, ch := range validMsg {
		termbox.SetCell(x, y, ch, fg, bg)
		x++
	}

	fastModeMsg := FAST_MODE_OFF
	fg = termbox.ColorBlue
	bg = termbox.ColorBlack
	if model.fastMode {
		fastModeMsg = FAST_MODE_ON
		fg, bg = bg, fg
	}
	for _, ch := range fastModeMsg {
		termbox.SetCell(x, y, ch, fg, bg)
		x++
	}
	return y
}

func drawStatusLine(y int, model *mainModel) (newY int) {
	x := 0
	var fg termbox.Attribute
	underlined := false
	for _, ch := range ">>> " + model.StatusLine() {
		//The ( and ) are non-printing control characters
		if ch == '{' {
			underlined = true
			continue
		} else if ch == '}' {
			underlined = false
			continue
		}
		fg = termbox.ColorBlack
		if underlined {
			fg = fg | termbox.AttrUnderline | termbox.AttrBold
		}

		termbox.SetCell(x, y, ch, fg, termbox.ColorWhite)
		x++
	}

	width, _ := termbox.Size()

	for x < width {
		termbox.SetCell(x, y, ' ', fg, termbox.ColorWhite)
		x++
	}
	return y
}

func drawConsole(y int, model *mainModel) (newY int) {

	x := 0

	underlined := false

	splitMessage := strings.Split(model.consoleMessage, "\n")

	for _, line := range splitMessage {

		x = 0
		for _, ch := range line {
			if ch == '{' {
				underlined = true
				continue
			} else if ch == '}' {
				underlined = false
				continue
			}
			fg := termbox.Attribute(0xf6)
			if underlined {
				fg = termbox.Attribute(0xFC) | termbox.AttrBold
			}
			termbox.SetCell(x, y, ch, fg, termbox.ColorBlack)
			x++
		}
		y++
	}
	//y is one too many
	y--
	return y
}

func draw(model *mainModel) {

	clearScreen()

	y := 0

	y = drawGrid(y, model)
	y++
	y = drawToggleLine(y, model)
	y++
	y = drawStatusLine(y, model)
	y++
	y = drawConsole(y, model)

	termbox.Flush()
}
<|endoftext|>


<commit_before>// Copyright 2020 The gVisor 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 fifo provides the implementation of data-link layer endpoints that
// wrap another endpoint and queues all outbound packets and asynchronously
// dispatches them to the lower endpoint.
package fifo

import (
	"gvisor.dev/gvisor/pkg/sleep"
	"gvisor.dev/gvisor/pkg/sync"
	"gvisor.dev/gvisor/pkg/tcpip"
	"gvisor.dev/gvisor/pkg/tcpip/buffer"
	"gvisor.dev/gvisor/pkg/tcpip/header"
	"gvisor.dev/gvisor/pkg/tcpip/stack"
)

// endpoint represents a LinkEndpoint which implements a FIFO queue for all
// outgoing packets. endpoint can have 1 or more underlying queueDispatchers.
// All outgoing packets are consistenly hashed to a single underlying queue
// using the PacketBuffer.Hash if set, otherwise all packets are queued to the
// first queue to avoid reordering in case of missing hash.
type endpoint struct {
	dispatcher  stack.NetworkDispatcher
	lower       stack.LinkEndpoint
	wg          sync.WaitGroup
	dispatchers []*queueDispatcher
}

// queueDispatcher is responsible for dispatching all outbound packets in its
// queue. It will also smartly batch packets when possible and write them
// through the lower LinkEndpoint.
type queueDispatcher struct {
	lower          stack.LinkEndpoint
	q              *packetBufferQueue
	newPacketWaker sleep.Waker
	closeWaker     sleep.Waker
}

// New creates a new fifo link endpoint with the n queues with maximum
// capacity of queueLen.
func New(lower stack.LinkEndpoint, n int, queueLen int) stack.LinkEndpoint {
	e := &endpoint{
		lower: lower,
	}
	// Create the required dispatchers
	for i := 0; i < n; i++ {
		qd := &queueDispatcher{
			q:     &packetBufferQueue{limit: queueLen},
			lower: lower,
		}
		e.dispatchers = append(e.dispatchers, qd)
		e.wg.Add(1)
		go func() {
			defer e.wg.Done()
			qd.dispatchLoop()
		}()
	}
	return e
}

func (q *queueDispatcher) dispatchLoop() {
	const newPacketWakerID = 1
	const closeWakerID = 2
	s := sleep.Sleeper{}
	s.AddWaker(&q.newPacketWaker, newPacketWakerID)
	s.AddWaker(&q.closeWaker, closeWakerID)
	defer s.Done()

	const batchSize = 32
	var batch stack.PacketBufferList
	for {
		id, ok := s.Fetch(true)
		if ok && id == closeWakerID {
			return
		}
		for pkt := q.q.dequeue(); pkt != nil; pkt = q.q.dequeue() {
			batch.PushBack(pkt)
			if batch.Len() < batchSize && !q.q.empty() {
				continue
			}
			// We pass a protocol of zero here because each packet carries its
			// NetworkProtocol.
			q.lower.WritePackets(nil /* route */, nil /* gso */, batch, 0 /* protocol */)
			for pkt := batch.Front(); pkt != nil; pkt = pkt.Next() {
				pkt.EgressRoute.Release()
				batch.Remove(pkt)
			}
			batch.Reset()
		}
	}
}

// DeliverNetworkPacket implements stack.NetworkDispatcher.DeliverNetworkPacket.
func (e *endpoint) DeliverNetworkPacket(remote, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
	e.dispatcher.DeliverNetworkPacket(remote, local, protocol, pkt)
}

// DeliverOutboundPacket implements stack.NetworkDispatcher.DeliverOutboundPacket.
func (e *endpoint) DeliverOutboundPacket(remote, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
	e.dispatcher.DeliverOutboundPacket(remote, local, protocol, pkt)
}

// Attach implements stack.LinkEndpoint.Attach.
func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) {
	e.dispatcher = dispatcher
	e.lower.Attach(e)
}

// IsAttached implements stack.LinkEndpoint.IsAttached.
func (e *endpoint) IsAttached() bool {
	return e.dispatcher != nil
}

// MTU implements stack.LinkEndpoint.MTU.
func (e *endpoint) MTU() uint32 {
	return e.lower.MTU()
}

// Capabilities implements stack.LinkEndpoint.Capabilities.
func (e *endpoint) Capabilities() stack.LinkEndpointCapabilities {
	return e.lower.Capabilities()
}

// MaxHeaderLength implements stack.LinkEndpoint.MaxHeaderLength.
func (e *endpoint) MaxHeaderLength() uint16 {
	return e.lower.MaxHeaderLength()
}

// LinkAddress implements stack.LinkEndpoint.LinkAddress.
func (e *endpoint) LinkAddress() tcpip.LinkAddress {
	return e.lower.LinkAddress()
}

// GSOMaxSize returns the maximum GSO packet size.
func (e *endpoint) GSOMaxSize() uint32 {
	if gso, ok := e.lower.(stack.GSOEndpoint); ok {
		return gso.GSOMaxSize()
	}
	return 0
}

// WritePacket implements stack.LinkEndpoint.WritePacket.
func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) *tcpip.Error {
	// WritePacket caller's do not set the following fields in PacketBuffer
	// so we populate them here.
	newRoute := r.Clone()
	pkt.EgressRoute = &newRoute
	pkt.GSOOptions = gso
	pkt.NetworkProtocolNumber = protocol
	d := e.dispatchers[int(pkt.Hash)%len(e.dispatchers)]
	if !d.q.enqueue(pkt) {
		return tcpip.ErrNoBufferSpace
	}
	d.newPacketWaker.Assert()
	return nil
}

// WritePackets implements stack.LinkEndpoint.WritePackets.
//
// Being a batch API, each packet in pkts should have the following fields
// populated:
//   - pkt.EgressRoute
//   - pkt.GSOOptions
//   - pkt.NetworkProtocolNumber
func (e *endpoint) WritePackets(_ *stack.Route, _ *stack.GSO, pkts stack.PacketBufferList, _ tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
	enqueued := 0
	for pkt := pkts.Front(); pkt != nil; {
		d := e.dispatchers[int(pkt.Hash)%len(e.dispatchers)]
		nxt := pkt.Next()
		// Since qdisc can hold onto a packet for long we should Clone
		// the route here to ensure it doesn't get released while the
		// packet is still in our queue.
		newRoute := pkt.EgressRoute.Clone()
		pkt.EgressRoute = &newRoute
		if !d.q.enqueue(pkt) {
			if enqueued > 0 {
				d.newPacketWaker.Assert()
			}
			return enqueued, tcpip.ErrNoBufferSpace
		}
		pkt = nxt
		enqueued++
		d.newPacketWaker.Assert()
	}
	return enqueued, nil
}

// WriteRawPacket implements stack.LinkEndpoint.WriteRawPacket.
func (e *endpoint) WriteRawPacket(vv buffer.VectorisedView) *tcpip.Error {
	// TODO(gvisor.dev/issue/3267/): Queue these packets as well once
	// WriteRawPacket takes PacketBuffer instead of VectorisedView.
	return e.lower.WriteRawPacket(vv)
}

// Wait implements stack.LinkEndpoint.Wait.
func (e *endpoint) Wait() {
	e.lower.Wait()

	// The linkEP is gone. Teardown the outbound dispatcher goroutines.
	for i := range e.dispatchers {
		e.dispatchers[i].closeWaker.Assert()
	}

	e.wg.Wait()
}

// ARPHardwareType implements stack.LinkEndpoint.ARPHardwareType
func (e *endpoint) ARPHardwareType() header.ARPHardwareType {
	return e.lower.ARPHardwareType()
}

// AddHeader implements stack.LinkEndpoint.AddHeader.
func (e *endpoint) AddHeader(local, remote tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
	e.lower.AddHeader(local, remote, protocol, pkt)
}
<commit_msg>Fix-up issue comment.<commit_after>// Copyright 2020 The gVisor 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 fifo provides the implementation of data-link layer endpoints that
// wrap another endpoint and queues all outbound packets and asynchronously
// dispatches them to the lower endpoint.
package fifo

import (
	"gvisor.dev/gvisor/pkg/sleep"
	"gvisor.dev/gvisor/pkg/sync"
	"gvisor.dev/gvisor/pkg/tcpip"
	"gvisor.dev/gvisor/pkg/tcpip/buffer"
	"gvisor.dev/gvisor/pkg/tcpip/header"
	"gvisor.dev/gvisor/pkg/tcpip/stack"
)

// endpoint represents a LinkEndpoint which implements a FIFO queue for all
// outgoing packets. endpoint can have 1 or more underlying queueDispatchers.
// All outgoing packets are consistenly hashed to a single underlying queue
// using the PacketBuffer.Hash if set, otherwise all packets are queued to the
// first queue to avoid reordering in case of missing hash.
type endpoint struct {
	dispatcher  stack.NetworkDispatcher
	lower       stack.LinkEndpoint
	wg          sync.WaitGroup
	dispatchers []*queueDispatcher
}

// queueDispatcher is responsible for dispatching all outbound packets in its
// queue. It will also smartly batch packets when possible and write them
// through the lower LinkEndpoint.
type queueDispatcher struct {
	lower          stack.LinkEndpoint
	q              *packetBufferQueue
	newPacketWaker sleep.Waker
	closeWaker     sleep.Waker
}

// New creates a new fifo link endpoint with the n queues with maximum
// capacity of queueLen.
func New(lower stack.LinkEndpoint, n int, queueLen int) stack.LinkEndpoint {
	e := &endpoint{
		lower: lower,
	}
	// Create the required dispatchers
	for i := 0; i < n; i++ {
		qd := &queueDispatcher{
			q:     &packetBufferQueue{limit: queueLen},
			lower: lower,
		}
		e.dispatchers = append(e.dispatchers, qd)
		e.wg.Add(1)
		go func() {
			defer e.wg.Done()
			qd.dispatchLoop()
		}()
	}
	return e
}

func (q *queueDispatcher) dispatchLoop() {
	const newPacketWakerID = 1
	const closeWakerID = 2
	s := sleep.Sleeper{}
	s.AddWaker(&q.newPacketWaker, newPacketWakerID)
	s.AddWaker(&q.closeWaker, closeWakerID)
	defer s.Done()

	const batchSize = 32
	var batch stack.PacketBufferList
	for {
		id, ok := s.Fetch(true)
		if ok && id == closeWakerID {
			return
		}
		for pkt := q.q.dequeue(); pkt != nil; pkt = q.q.dequeue() {
			batch.PushBack(pkt)
			if batch.Len() < batchSize && !q.q.empty() {
				continue
			}
			// We pass a protocol of zero here because each packet carries its
			// NetworkProtocol.
			q.lower.WritePackets(nil /* route */, nil /* gso */, batch, 0 /* protocol */)
			for pkt := batch.Front(); pkt != nil; pkt = pkt.Next() {
				pkt.EgressRoute.Release()
				batch.Remove(pkt)
			}
			batch.Reset()
		}
	}
}

// DeliverNetworkPacket implements stack.NetworkDispatcher.DeliverNetworkPacket.
func (e *endpoint) DeliverNetworkPacket(remote, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
	e.dispatcher.DeliverNetworkPacket(remote, local, protocol, pkt)
}

// DeliverOutboundPacket implements stack.NetworkDispatcher.DeliverOutboundPacket.
func (e *endpoint) DeliverOutboundPacket(remote, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
	e.dispatcher.DeliverOutboundPacket(remote, local, protocol, pkt)
}

// Attach implements stack.LinkEndpoint.Attach.
func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) {
	e.dispatcher = dispatcher
	e.lower.Attach(e)
}

// IsAttached implements stack.LinkEndpoint.IsAttached.
func (e *endpoint) IsAttached() bool {
	return e.dispatcher != nil
}

// MTU implements stack.LinkEndpoint.MTU.
func (e *endpoint) MTU() uint32 {
	return e.lower.MTU()
}

// Capabilities implements stack.LinkEndpoint.Capabilities.
func (e *endpoint) Capabilities() stack.LinkEndpointCapabilities {
	return e.lower.Capabilities()
}

// MaxHeaderLength implements stack.LinkEndpoint.MaxHeaderLength.
func (e *endpoint) MaxHeaderLength() uint16 {
	return e.lower.MaxHeaderLength()
}

// LinkAddress implements stack.LinkEndpoint.LinkAddress.
func (e *endpoint) LinkAddress() tcpip.LinkAddress {
	return e.lower.LinkAddress()
}

// GSOMaxSize returns the maximum GSO packet size.
func (e *endpoint) GSOMaxSize() uint32 {
	if gso, ok := e.lower.(stack.GSOEndpoint); ok {
		return gso.GSOMaxSize()
	}
	return 0
}

// WritePacket implements stack.LinkEndpoint.WritePacket.
func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) *tcpip.Error {
	// WritePacket caller's do not set the following fields in PacketBuffer
	// so we populate them here.
	newRoute := r.Clone()
	pkt.EgressRoute = &newRoute
	pkt.GSOOptions = gso
	pkt.NetworkProtocolNumber = protocol
	d := e.dispatchers[int(pkt.Hash)%len(e.dispatchers)]
	if !d.q.enqueue(pkt) {
		return tcpip.ErrNoBufferSpace
	}
	d.newPacketWaker.Assert()
	return nil
}

// WritePackets implements stack.LinkEndpoint.WritePackets.
//
// Being a batch API, each packet in pkts should have the following fields
// populated:
//   - pkt.EgressRoute
//   - pkt.GSOOptions
//   - pkt.NetworkProtocolNumber
func (e *endpoint) WritePackets(_ *stack.Route, _ *stack.GSO, pkts stack.PacketBufferList, _ tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
	enqueued := 0
	for pkt := pkts.Front(); pkt != nil; {
		d := e.dispatchers[int(pkt.Hash)%len(e.dispatchers)]
		nxt := pkt.Next()
		// Since qdisc can hold onto a packet for long we should Clone
		// the route here to ensure it doesn't get released while the
		// packet is still in our queue.
		newRoute := pkt.EgressRoute.Clone()
		pkt.EgressRoute = &newRoute
		if !d.q.enqueue(pkt) {
			if enqueued > 0 {
				d.newPacketWaker.Assert()
			}
			return enqueued, tcpip.ErrNoBufferSpace
		}
		pkt = nxt
		enqueued++
		d.newPacketWaker.Assert()
	}
	return enqueued, nil
}

// WriteRawPacket implements stack.LinkEndpoint.WriteRawPacket.
func (e *endpoint) WriteRawPacket(vv buffer.VectorisedView) *tcpip.Error {
	// TODO(gvisor.dev/issue/3267): Queue these packets as well once
	// WriteRawPacket takes PacketBuffer instead of VectorisedView.
	return e.lower.WriteRawPacket(vv)
}

// Wait implements stack.LinkEndpoint.Wait.
func (e *endpoint) Wait() {
	e.lower.Wait()

	// The linkEP is gone. Teardown the outbound dispatcher goroutines.
	for i := range e.dispatchers {
		e.dispatchers[i].closeWaker.Assert()
	}

	e.wg.Wait()
}

// ARPHardwareType implements stack.LinkEndpoint.ARPHardwareType
func (e *endpoint) ARPHardwareType() header.ARPHardwareType {
	return e.lower.ARPHardwareType()
}

// AddHeader implements stack.LinkEndpoint.AddHeader.
func (e *endpoint) AddHeader(local, remote tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
	e.lower.AddHeader(local, remote, protocol, pkt)
}
<|endoftext|>


<commit_before>/*
i-sudoku is an interactive command-line sudoku tool
*/

package main

import (
	"github.com/jkomoros/sudoku"
	"github.com/nsf/termbox-go"
	"log"
	"strconv"
	"strings"
	"unicode/utf8"
)

type mainModel struct {
	grid         *sudoku.Grid
	Selected     *sudoku.Cell
	marksToInput []int
}

func main() {
	if err := termbox.Init(); err != nil {
		log.Fatal("Termbox initialization failed:", err)
	}
	defer termbox.Close()

	model := newModel()

	draw(model)

mainloop:
	for {
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			switch ev.Key {
			case termbox.KeyEsc, termbox.KeyCtrlC:
				break mainloop
			case termbox.KeyArrowDown:
				model.MoveSelectionDown()
			case termbox.KeyArrowLeft:
				model.MoveSelectionLeft()
			case termbox.KeyArrowRight:
				model.MoveSelectionRight()
			case termbox.KeyArrowUp:
				model.MoveSelectionUp()
			}
			switch ev.Ch {
			case 'q':
				break mainloop
			case 'n':
				//TODO: since this is a destructive action, require a confirmation
				model.NewGrid()
			//TODO: do this in a more general way related to DIM
			case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
				//TODO: this is a seriously gross way of converting a rune to a string.
				num, err := strconv.Atoi(strings.Replace(strconv.QuoteRuneToASCII(ev.Ch), "'", "", -1))
				if err != nil {
					panic(err)
				}
				model.ModeInputNumber(num)
			}
		}
		draw(model)
	}
}

func newModel() *mainModel {
	model := &mainModel{}
	model.EnsureSelected()
	return model
}

func (m *mainModel) ModeInputEsc() (quit bool) {
	if m.marksToInput != nil {
		m.ModeCancelMarkMode()
		return false
	}
	return true
}

func (m *mainModel) ModeEnterMarkMode() {
	m.marksToInput = make([]int, 0)
}

func (m *mainModel) ModeCommitMarkMode() {
	for _, num := range m.marksToInput {
		m.ToggleSelectedMark(num)
	}
	m.marksToInput = nil
}

func (m *mainModel) ModeCancelMarkMode() {
	m.marksToInput = nil
}

func (m *mainModel) ModeInputNumber(num int) {
	if m.marksToInput == nil {
		m.SetSelectedNumber(num)
	} else {
		m.marksToInput = append(m.marksToInput, num)
	}
}

func (m *mainModel) EnsureSelected() {
	m.EnsureGrid()
	//Ensures that at least one cell is selected.
	if m.Selected == nil {
		m.Selected = m.grid.Cell(0, 0)
	}
}

func (m *mainModel) MoveSelectionLeft() {
	m.EnsureSelected()
	r := m.Selected.Row()
	c := m.Selected.Col()
	c--
	if c < 0 {
		c = 0
	}
	m.Selected = m.grid.Cell(r, c)
}

func (m *mainModel) MoveSelectionRight() {
	m.EnsureSelected()
	r := m.Selected.Row()
	c := m.Selected.Col()
	c++
	if c >= sudoku.DIM {
		c = sudoku.DIM - 1
	}
	m.Selected = m.grid.Cell(r, c)
}

func (m *mainModel) MoveSelectionUp() {
	m.EnsureSelected()
	r := m.Selected.Row()
	c := m.Selected.Col()
	r--
	if r < 0 {
		r = 0
	}
	m.Selected = m.grid.Cell(r, c)
}

func (m *mainModel) MoveSelectionDown() {
	m.EnsureSelected()
	r := m.Selected.Row()
	c := m.Selected.Col()
	r++
	if r >= sudoku.DIM {
		r = sudoku.DIM - 1
	}
	m.Selected = m.grid.Cell(r, c)
}

func (m *mainModel) EnsureGrid() {
	if m.grid == nil {
		m.NewGrid()
	}
}

func (m *mainModel) NewGrid() {
	m.grid = sudoku.GenerateGrid(nil)
	m.grid.LockFilledCells()
}

func (m *mainModel) SetSelectedNumber(num int) {
	m.EnsureSelected()
	if m.Selected.Locked() {
		return
	}
	m.Selected.SetNumber(num)
}

func (m *mainModel) ToggleSelectedMark(num int) {
	m.EnsureSelected()
	if m.Selected.Locked() {
		return
	}
	m.Selected.SetMark(num, !m.Selected.Mark(num))
}

func draw(model *mainModel) {
	drawGrid(model)
	termbox.Flush()
}

func drawGrid(model *mainModel) {

	//TODO: have a mode line after the grid for if the grid is invalid, if it's solved.

	//TODO: draw which marks are ready to commit.

	grid := model.grid

	selectedTop, selectedLeft, selectedHeight, selectedWidth := model.Selected.DiagramExtents()

	for y, line := range strings.Split(grid.Diagram(true), "\n") {
		x := 0
		//The first number in range will be byte offset, but for some items like the bullet, it's two bytes.
		//But what we care about is that each item is a character.
		for _, ch := range line {

			defaultColor := termbox.ColorGreen

			numberRune, _ := utf8.DecodeRuneInString(sudoku.DIAGRAM_NUMBER)
			lockedRune, _ := utf8.DecodeRuneInString(sudoku.DIAGRAM_LOCKED)

			if ch == numberRune {
				defaultColor = termbox.ColorBlue
			} else if ch == lockedRune {
				defaultColor = termbox.ColorRed
			}

			backgroundColor := termbox.ColorDefault

			if x >= selectedTop && x < (selectedTop+selectedHeight) && y >= selectedLeft && y < (selectedLeft+selectedWidth) {
				//We're on the selected cell
				backgroundColor = termbox.ColorWhite
			}

			termbox.SetCell(x, y, ch, defaultColor, backgroundColor)
			x++
		}
	}
}
<commit_msg>Use model.ModeInputEsc in main event loop.<commit_after>/*
i-sudoku is an interactive command-line sudoku tool
*/

package main

import (
	"github.com/jkomoros/sudoku"
	"github.com/nsf/termbox-go"
	"log"
	"strconv"
	"strings"
	"unicode/utf8"
)

type mainModel struct {
	grid         *sudoku.Grid
	Selected     *sudoku.Cell
	marksToInput []int
}

func main() {
	if err := termbox.Init(); err != nil {
		log.Fatal("Termbox initialization failed:", err)
	}
	defer termbox.Close()

	model := newModel()

	draw(model)

mainloop:
	for {
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			switch ev.Key {
			case termbox.KeyEsc:
				if model.ModeInputEsc() {
					break mainloop
				}
			case termbox.KeyCtrlC:
				break mainloop
			case termbox.KeyArrowDown:
				model.MoveSelectionDown()
			case termbox.KeyArrowLeft:
				model.MoveSelectionLeft()
			case termbox.KeyArrowRight:
				model.MoveSelectionRight()
			case termbox.KeyArrowUp:
				model.MoveSelectionUp()
			}
			switch ev.Ch {
			case 'q':
				break mainloop
			case 'n':
				//TODO: since this is a destructive action, require a confirmation
				model.NewGrid()
			//TODO: do this in a more general way related to DIM
			case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
				//TODO: this is a seriously gross way of converting a rune to a string.
				num, err := strconv.Atoi(strings.Replace(strconv.QuoteRuneToASCII(ev.Ch), "'", "", -1))
				if err != nil {
					panic(err)
				}
				model.ModeInputNumber(num)
			}
		}
		draw(model)
	}
}

func newModel() *mainModel {
	model := &mainModel{}
	model.EnsureSelected()
	return model
}

func (m *mainModel) ModeInputEsc() (quit bool) {
	if m.marksToInput != nil {
		m.ModeCancelMarkMode()
		return false
	}
	return true
}

func (m *mainModel) ModeEnterMarkMode() {
	m.marksToInput = make([]int, 0)
}

func (m *mainModel) ModeCommitMarkMode() {
	for _, num := range m.marksToInput {
		m.ToggleSelectedMark(num)
	}
	m.marksToInput = nil
}

func (m *mainModel) ModeCancelMarkMode() {
	m.marksToInput = nil
}

func (m *mainModel) ModeInputNumber(num int) {
	if m.marksToInput == nil {
		m.SetSelectedNumber(num)
	} else {
		m.marksToInput = append(m.marksToInput, num)
	}
}

func (m *mainModel) EnsureSelected() {
	m.EnsureGrid()
	//Ensures that at least one cell is selected.
	if m.Selected == nil {
		m.Selected = m.grid.Cell(0, 0)
	}
}

func (m *mainModel) MoveSelectionLeft() {
	m.EnsureSelected()
	r := m.Selected.Row()
	c := m.Selected.Col()
	c--
	if c < 0 {
		c = 0
	}
	m.Selected = m.grid.Cell(r, c)
}

func (m *mainModel) MoveSelectionRight() {
	m.EnsureSelected()
	r := m.Selected.Row()
	c := m.Selected.Col()
	c++
	if c >= sudoku.DIM {
		c = sudoku.DIM - 1
	}
	m.Selected = m.grid.Cell(r, c)
}

func (m *mainModel) MoveSelectionUp() {
	m.EnsureSelected()
	r := m.Selected.Row()
	c := m.Selected.Col()
	r--
	if r < 0 {
		r = 0
	}
	m.Selected = m.grid.Cell(r, c)
}

func (m *mainModel) MoveSelectionDown() {
	m.EnsureSelected()
	r := m.Selected.Row()
	c := m.Selected.Col()
	r++
	if r >= sudoku.DIM {
		r = sudoku.DIM - 1
	}
	m.Selected = m.grid.Cell(r, c)
}

func (m *mainModel) EnsureGrid() {
	if m.grid == nil {
		m.NewGrid()
	}
}

func (m *mainModel) NewGrid() {
	m.grid = sudoku.GenerateGrid(nil)
	m.grid.LockFilledCells()
}

func (m *mainModel) SetSelectedNumber(num int) {
	m.EnsureSelected()
	if m.Selected.Locked() {
		return
	}
	m.Selected.SetNumber(num)
}

func (m *mainModel) ToggleSelectedMark(num int) {
	m.EnsureSelected()
	if m.Selected.Locked() {
		return
	}
	m.Selected.SetMark(num, !m.Selected.Mark(num))
}

func draw(model *mainModel) {
	drawGrid(model)
	termbox.Flush()
}

func drawGrid(model *mainModel) {

	//TODO: have a mode line after the grid for if the grid is invalid, if it's solved.

	//TODO: draw which marks are ready to commit.

	grid := model.grid

	selectedTop, selectedLeft, selectedHeight, selectedWidth := model.Selected.DiagramExtents()

	for y, line := range strings.Split(grid.Diagram(true), "\n") {
		x := 0
		//The first number in range will be byte offset, but for some items like the bullet, it's two bytes.
		//But what we care about is that each item is a character.
		for _, ch := range line {

			defaultColor := termbox.ColorGreen

			numberRune, _ := utf8.DecodeRuneInString(sudoku.DIAGRAM_NUMBER)
			lockedRune, _ := utf8.DecodeRuneInString(sudoku.DIAGRAM_LOCKED)

			if ch == numberRune {
				defaultColor = termbox.ColorBlue
			} else if ch == lockedRune {
				defaultColor = termbox.ColorRed
			}

			backgroundColor := termbox.ColorDefault

			if x >= selectedTop && x < (selectedTop+selectedHeight) && y >= selectedLeft && y < (selectedLeft+selectedWidth) {
				//We're on the selected cell
				backgroundColor = termbox.ColorWhite
			}

			termbox.SetCell(x, y, ch, defaultColor, backgroundColor)
			x++
		}
	}
}
<|endoftext|>