<commit_before>package paperless

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"unicode"

	"github.com/kopoli/go-util"
)

// Environment is the run environment for each command. It is supplied as part
// of Status for a Link when it is Run or Validated.
type Environment struct {

	// Constants are variables that are defined before a command chain is run
	Constants map[string]string

	// Tempfiles are constants that house a name of a temporary file. The
	// files are created before the chain is run and they are removed at
	// the end.
	TempFiles []string

	// The directory where the commands are run. This is a created
	// temporary directory
	RootDir string

	// AllowedCommands contain the commands that are allowed. If this is
	// nil, all commands are allowed.
	AllowedCommands map[string]bool

	initialized bool
}

func (e *Environment) initEnv() (err error) {
	if e.initialized {
		return
	}

	if e.Constants == nil {
		return util.E.New("field Constants not initialized")
	}

	e.RootDir, err = ioutil.TempDir("", "chain")
	if err != nil {
		return util.E.Annotate(err, "rootdir creation failed")
	}

	var fp *os.File
	for _, name := range e.TempFiles {
		fp, err = ioutil.TempFile(e.RootDir, "tmp")
		if err != nil {
			err = util.E.Annotate(err, "tempfile creation failed")
			e.initialized = true
			e2 := e.deinitEnv()
			if e2 != nil {
				err = util.E.Annotate(err, "temproot removal failed:", e2)
				e.initialized = false
			}
			return
		}
		e.Constants[name] = fp.Name()
		fp.Close()
	}

	e.initialized = true
	return
}

func (e *Environment) deinitEnv() (err error) {
	if !e.initialized {
		return
	}

	if !strings.HasPrefix(e.RootDir, os.TempDir()) || e.RootDir == os.TempDir() {
		err = util.E.New("Temporary directory path is corrupted: %s", e.RootDir)
		return
	}

	err = os.RemoveAll(e.RootDir)
	if err != nil {
		err = util.E.Annotate(err, "tempdir removal failed:")
	}

	// Clear the temporary file and directory names
	e.RootDir = ""
	for _, n := range e.TempFiles {
		e.Constants[n] = ""
	}

	e.initialized = false
	return
}

func (e *Environment) validate() (err error) {
	if len(e.RootDir) == 0 {
		return util.E.New("the RootDir must be defined")
	}
	info, err := os.Stat(e.RootDir)
	if err != nil || info.Mode()&os.ModeDir == 0 {
		return util.E.Annotate(err, "file ", e.RootDir, " is not a proper directory")
	}

	return
}

// Status is the runtime status of the command chain
type Status struct {
	// The log output will be written to this
	Log io.Writer

	Environment
}

type Link interface {
	Validate(*Environment) error
	Run(*Status) error
}

type CmdChain struct {

	//TODO remove this (this should come from outside)
	Environment

	Links []Link
}

func (c *CmdChain) Validate(e *Environment) (err error) {
	for _, l := range c.Links {
		err = l.Validate(e)
		if err != nil {
			return
		}
	}
	return
}

func (c *CmdChain) Run(s *Status) (err error) {
	err = c.Validate(&s.Environment)
	if err != nil {
		return
	}

	for i := range c.Links {
		err = c.Links[i].Run(s)
		if err != nil {
			return
		}
	}

	return
}

func RunCmdChain(c *CmdChain, s *Status) (err error) {
	err = s.Environment.initEnv()
	if err != nil {
		return
	}

	err = c.Run(s)
	e2 := s.Environment.deinitEnv()
	if e2 != nil {
		err = util.E.Annotate(err, "cmdchain deinit failed: ", e2)
	}

	return
}

////////////////////////////////////////////////////////////

type Cmd struct {
	Cmd []string
}

// NewCmd creates a new Cmd from given command string
func NewCmd(cmdstr string) (c *Cmd, err error) {
	command := splitWsQuote(cmdstr)

	if len(command) == 0 {
		return nil, util.E.New("A command could not be parsed from:", cmdstr)
	}

	c = &Cmd{command}

	_, err = exec.LookPath(c.Cmd[0])
	if err != nil {
		return nil, util.E.Annotate(err, "Command", c.Cmd[0], "could not be found")

	}

	return
}

// Validate makes sure the command is proper and can be run
func (c *Cmd) Validate(e *Environment) (err error) {
	if len(c.Cmd) == 0 {
		return util.E.New("command string must be non-empty")
	}

	if e.AllowedCommands != nil {
		if _, ok := e.AllowedCommands[c.Cmd[0]]; ok != true {
			return util.E.New("command is not allowed")
		}
	}

	_, err = exec.LookPath(c.Cmd[0])
	if err != nil {
		return
	}

	err = e.validate()

	for idx, a := range c.Cmd {
		consts := parseConsts(a)
		if len(consts) > 0 {
			for _, co := range consts {
				if _, ok := e.Constants[co]; !ok {
					return util.E.New("constant \"%s\" not defined", co)
				}
			}
		}

		// Output redirection to a file
		if a == ">" && (idx == len(c.Cmd)-1 || c.Cmd[idx+1] == "") {
			return util.E.New("The output redirection requires a string")
		}
	}

	return
}

func (c *Cmd) Run(s *Status) (err error) {
	err = c.Validate(&s.Environment)
	if err != nil {
		return
	}

	var args []string
	for i := range c.Cmd {
		args = append(args, expandConsts(c.Cmd[i], s.Constants))
	}

	if s.Log != nil {
		fmt.Fprintln(s.Log, "Running command:", strings.Join(args, " "))
	}

	var output io.Writer = s.Log

	redirout, pos := getRedirectFile(">", args)
	if redirout != "" {
		var fp *os.File
		redirout = PathAbs(s.RootDir, redirout)
		fp, err = os.OpenFile(redirout, os.O_WRONLY | os.O_CREATE, 0666)
		if err != nil {
			err = util.E.Annotate(err, "Could not open file",redirout,"for redirection")
			return
		}
		defer fp.Close()
		output = fp

		// Remove the redirection and the file argument from the command
		args = append(args[:pos], args[pos+2:]...)
	}

	cmd := exec.Command(args[0], args[1:]...)
	cmd.Dir = s.RootDir
	cmd.Stdout = output
	cmd.Stderr = s.Log

	return cmd.Run()
}

////////////////////////////////////////////////////////////

var (
	constRe         = regexp.MustCompile(`\$(\w+)`)
	tmpfileConstRe  = regexp.MustCompile(`\$(tmp\w+)`)
	commentRe       = regexp.MustCompile(`#.*$`)
	preWhitespaceRe = regexp.MustCompile(`^\s+`)
)

// parseConsts parses the constants from a string. Returns a list of constant names
func parseConsts(s string) (ret []string) {
	ret = []string{}

	matches := constRe.FindAllStringSubmatch(s, -1)
	if matches == nil {
		return
	}
	for _, m := range matches {
		ret = append(ret, m[1])
	}

	return
}

func expandConsts(s string, constants map[string]string) string {
	return constRe.ReplaceAllStringFunc(s, func(match string) string {
		cs := parseConsts(match)
		if len(cs) != 1 {
			panic("Invalid Regexp parsing")
		}

		ret, ok := constants[cs[0]]
		if !ok {
			ret = ""
		}

		return ret
	})
}

// Gets the string after the given redir string. If not found, returns empty
// string.
func getRedirectFile(redir string, args []string) (file string, pos int) {
	for i := range args {
		if args[i] == redir {
			if i+1 < len(args) {
				file = args[i+1]
				pos = i
			}
			return
		}
	}
	return
}

// NewCmdChainScript creates a CmdChain from a script where each command is on a separate line. The following syntax elements are supported:
//
// - Empty lines are filtered out.
//
// - Comments start with # and end with EOL.
//
// - Constants are strings that begin with $ and they can be set before running the cmdchain.
//
// - Temporary files are strings that start with $tmp and they are automatically created before running the cmdchain and removed afterwards.
func NewCmdChainScript(script string) (c *CmdChain, err error) {
	c = &CmdChain{}
	c.Constants = make(map[string]string)

	for _, line := range strings.Split(script, "\n") {
		line = commentRe.ReplaceAllString(line, "")
		line = preWhitespaceRe.ReplaceAllString(line, "")

		if len(line) == 0 {
			continue
		}

		constants := parseConsts(line)
		for _, co := range constants {
			c.Constants[co] = ""
			if tmpfileConstRe.MatchString("$" + co) {
				c.TempFiles = append(c.TempFiles, co)
			}
		}

		var cmd *Cmd
		cmd, err = NewCmd(line)
		if err != nil {
			return nil, util.E.Annotate(err, "improper command")
		}

		c.Links = append(c.Links, cmd)
	}

	e := c.Environment
	e.RootDir = "/"

	err = c.Validate(&e)
	if err != nil {
		return nil, util.E.Annotate(err, "invalid command chain")
	}

	return
}

// splitWsQuote splits a string by whitespace, but takes doublequotes into
// account
func splitWsQuote(s string) []string {

	quote := rune(0)

	return strings.FieldsFunc(s, func(r rune) bool {
		switch {
		case r == quote:
			quote = rune(0)
			return true
		case quote != rune(0):
			return false
		case unicode.In(r, unicode.Quotation_Mark):
			quote = r
			return true
		default:
			return unicode.IsSpace(r)
		}
	})
}
<commit_msg>cmdchain: make more visible what command is run<commit_after>package paperless

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"unicode"

	"github.com/kopoli/go-util"
)

// Environment is the run environment for each command. It is supplied as part
// of Status for a Link when it is Run or Validated.
type Environment struct {

	// Constants are variables that are defined before a command chain is run
	Constants map[string]string

	// Tempfiles are constants that house a name of a temporary file. The
	// files are created before the chain is run and they are removed at
	// the end.
	TempFiles []string

	// The directory where the commands are run. This is a created
	// temporary directory
	RootDir string

	// AllowedCommands contain the commands that are allowed. If this is
	// nil, all commands are allowed.
	AllowedCommands map[string]bool

	initialized bool
}

func (e *Environment) initEnv() (err error) {
	if e.initialized {
		return
	}

	if e.Constants == nil {
		return util.E.New("field Constants not initialized")
	}

	e.RootDir, err = ioutil.TempDir("", "chain")
	if err != nil {
		return util.E.Annotate(err, "rootdir creation failed")
	}

	var fp *os.File
	for _, name := range e.TempFiles {
		fp, err = ioutil.TempFile(e.RootDir, "tmp")
		if err != nil {
			err = util.E.Annotate(err, "tempfile creation failed")
			e.initialized = true
			e2 := e.deinitEnv()
			if e2 != nil {
				err = util.E.Annotate(err, "temproot removal failed:", e2)
				e.initialized = false
			}
			return
		}
		e.Constants[name] = fp.Name()
		fp.Close()
	}

	e.initialized = true
	return
}

func (e *Environment) deinitEnv() (err error) {
	if !e.initialized {
		return
	}

	if !strings.HasPrefix(e.RootDir, os.TempDir()) || e.RootDir == os.TempDir() {
		err = util.E.New("Temporary directory path is corrupted: %s", e.RootDir)
		return
	}

	err = os.RemoveAll(e.RootDir)
	if err != nil {
		err = util.E.Annotate(err, "tempdir removal failed:")
	}

	// Clear the temporary file and directory names
	e.RootDir = ""
	for _, n := range e.TempFiles {
		e.Constants[n] = ""
	}

	e.initialized = false
	return
}

func (e *Environment) validate() (err error) {
	if len(e.RootDir) == 0 {
		return util.E.New("the RootDir must be defined")
	}
	info, err := os.Stat(e.RootDir)
	if err != nil || info.Mode()&os.ModeDir == 0 {
		return util.E.Annotate(err, "file ", e.RootDir, " is not a proper directory")
	}

	return
}

// Status is the runtime status of the command chain
type Status struct {
	// The log output will be written to this
	Log io.Writer

	Environment
}

type Link interface {
	Validate(*Environment) error
	Run(*Status) error
}

type CmdChain struct {

	//TODO remove this (this should come from outside)
	Environment

	Links []Link
}

func (c *CmdChain) Validate(e *Environment) (err error) {
	for _, l := range c.Links {
		err = l.Validate(e)
		if err != nil {
			return
		}
	}
	return
}

func (c *CmdChain) Run(s *Status) (err error) {
	err = c.Validate(&s.Environment)
	if err != nil {
		return
	}

	for i := range c.Links {
		err = c.Links[i].Run(s)
		if err != nil {
			return
		}
	}

	return
}

func RunCmdChain(c *CmdChain, s *Status) (err error) {
	err = s.Environment.initEnv()
	if err != nil {
		return
	}

	err = c.Run(s)
	e2 := s.Environment.deinitEnv()
	if e2 != nil {
		err = util.E.Annotate(err, "cmdchain deinit failed: ", e2)
	}

	return
}

////////////////////////////////////////////////////////////

type Cmd struct {
	Cmd []string
}

// NewCmd creates a new Cmd from given command string
func NewCmd(cmdstr string) (c *Cmd, err error) {
	command := splitWsQuote(cmdstr)

	if len(command) == 0 {
		return nil, util.E.New("A command could not be parsed from:", cmdstr)
	}

	c = &Cmd{command}

	_, err = exec.LookPath(c.Cmd[0])
	if err != nil {
		return nil, util.E.Annotate(err, "Command", c.Cmd[0], "could not be found")

	}

	return
}

// Validate makes sure the command is proper and can be run
func (c *Cmd) Validate(e *Environment) (err error) {
	if len(c.Cmd) == 0 {
		return util.E.New("command string must be non-empty")
	}

	if e.AllowedCommands != nil {
		if _, ok := e.AllowedCommands[c.Cmd[0]]; ok != true {
			return util.E.New("command is not allowed")
		}
	}

	_, err = exec.LookPath(c.Cmd[0])
	if err != nil {
		return
	}

	err = e.validate()

	for idx, a := range c.Cmd {
		consts := parseConsts(a)
		if len(consts) > 0 {
			for _, co := range consts {
				if _, ok := e.Constants[co]; !ok {
					return util.E.New("constant \"%s\" not defined", co)
				}
			}
		}

		// Output redirection to a file
		if a == ">" && (idx == len(c.Cmd)-1 || c.Cmd[idx+1] == "") {
			return util.E.New("The output redirection requires a string")
		}
	}

	return
}

func (c *Cmd) Run(s *Status) (err error) {
	err = c.Validate(&s.Environment)
	if err != nil {
		return
	}

	var args []string
	for i := range c.Cmd {
		args = append(args, expandConsts(c.Cmd[i], s.Constants))
	}

	if s.Log != nil {
		fmt.Fprintln(s.Log, "# Running command:", strings.Join(args, " "))
	}

	var output io.Writer = s.Log

	redirout, pos := getRedirectFile(">", args)
	if redirout != "" {
		var fp *os.File
		redirout = PathAbs(s.RootDir, redirout)
		fp, err = os.OpenFile(redirout, os.O_WRONLY | os.O_CREATE, 0666)
		if err != nil {
			err = util.E.Annotate(err, "Could not open file",redirout,"for redirection")
			return
		}
		defer fp.Close()
		output = fp

		// Remove the redirection and the file argument from the command
		args = append(args[:pos], args[pos+2:]...)
	}

	cmd := exec.Command(args[0], args[1:]...)
	cmd.Dir = s.RootDir
	cmd.Stdout = output
	cmd.Stderr = s.Log

	return cmd.Run()
}

////////////////////////////////////////////////////////////

var (
	constRe         = regexp.MustCompile(`\$(\w+)`)
	tmpfileConstRe  = regexp.MustCompile(`\$(tmp\w+)`)
	commentRe       = regexp.MustCompile(`#.*$`)
	preWhitespaceRe = regexp.MustCompile(`^\s+`)
)

// parseConsts parses the constants from a string. Returns a list of constant names
func parseConsts(s string) (ret []string) {
	ret = []string{}

	matches := constRe.FindAllStringSubmatch(s, -1)
	if matches == nil {
		return
	}
	for _, m := range matches {
		ret = append(ret, m[1])
	}

	return
}

func expandConsts(s string, constants map[string]string) string {
	return constRe.ReplaceAllStringFunc(s, func(match string) string {
		cs := parseConsts(match)
		if len(cs) != 1 {
			panic("Invalid Regexp parsing")
		}

		ret, ok := constants[cs[0]]
		if !ok {
			ret = ""
		}

		return ret
	})
}

// Gets the string after the given redir string. If not found, returns empty
// string.
func getRedirectFile(redir string, args []string) (file string, pos int) {
	for i := range args {
		if args[i] == redir {
			if i+1 < len(args) {
				file = args[i+1]
				pos = i
			}
			return
		}
	}
	return
}

// NewCmdChainScript creates a CmdChain from a script where each command is on a separate line. The following syntax elements are supported:
//
// - Empty lines are filtered out.
//
// - Comments start with # and end with EOL.
//
// - Constants are strings that begin with $ and they can be set before running the cmdchain.
//
// - Temporary files are strings that start with $tmp and they are automatically created before running the cmdchain and removed afterwards.
func NewCmdChainScript(script string) (c *CmdChain, err error) {
	c = &CmdChain{}
	c.Constants = make(map[string]string)

	for _, line := range strings.Split(script, "\n") {
		line = commentRe.ReplaceAllString(line, "")
		line = preWhitespaceRe.ReplaceAllString(line, "")

		if len(line) == 0 {
			continue
		}

		constants := parseConsts(line)
		for _, co := range constants {
			c.Constants[co] = ""
			if tmpfileConstRe.MatchString("$" + co) {
				c.TempFiles = append(c.TempFiles, co)
			}
		}

		var cmd *Cmd
		cmd, err = NewCmd(line)
		if err != nil {
			return nil, util.E.Annotate(err, "improper command")
		}

		c.Links = append(c.Links, cmd)
	}

	e := c.Environment
	e.RootDir = "/"

	err = c.Validate(&e)
	if err != nil {
		return nil, util.E.Annotate(err, "invalid command chain")
	}

	return
}

// splitWsQuote splits a string by whitespace, but takes doublequotes into
// account
func splitWsQuote(s string) []string {

	quote := rune(0)

	return strings.FieldsFunc(s, func(r rune) bool {
		switch {
		case r == quote:
			quote = rune(0)
			return true
		case quote != rune(0):
			return false
		case unicode.In(r, unicode.Quotation_Mark):
			quote = r
			return true
		default:
			return unicode.IsSpace(r)
		}
	})
}
<|endoftext|>


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

/*
Package runtime contains operations that interact with Go's runtime system,
such as functions to control goroutines. It also includes the low-level type information
used by the reflect package; see reflect's documentation for the programmable
interface to the run-time type system.

Environment Variables

The following environment variables ($name or %name%, depending on the host
operating system) control the run-time behavior of Go programs. The meanings
and use may change from release to release.

The GOGC variable sets the initial garbage collection target percentage.
A collection is triggered when the ratio of freshly allocated data to live data
remaining after the previous collection reaches this percentage. The default
is GOGC=100. Setting GOGC=off disables the garbage collector entirely.
The runtime/debug package's SetGCPercent function allows changing this
percentage at run time. See https://golang.org/pkg/runtime/debug/#SetGCPercent.

The GODEBUG variable controls debugging variables within the runtime.
It is a comma-separated list of name=val pairs setting these named variables:

	allocfreetrace: setting allocfreetrace=1 causes every allocation to be
	profiled and a stack trace printed on each object's allocation and free.

	cgocheck: setting cgocheck=0 disables all checks for packages
	using cgo to incorrectly pass Go pointers to non-Go code.
	Setting cgocheck=1 (the default) enables relatively cheap
	checks that may miss some errors.  Setting cgocheck=2 enables
	expensive checks that should not miss any errors, but will
	cause your program to run slower.

	efence: setting efence=1 causes the allocator to run in a mode
	where each object is allocated on a unique page and addresses are
	never recycled.

	gccheckmark: setting gccheckmark=1 enables verification of the
	garbage collector's concurrent mark phase by performing a
	second mark pass while the world is stopped.  If the second
	pass finds a reachable object that was not found by concurrent
	mark, the garbage collector will panic.

	gcpacertrace: setting gcpacertrace=1 causes the garbage collector to
	print information about the internal state of the concurrent pacer.

	gcshrinkstackoff: setting gcshrinkstackoff=1 disables moving goroutines
	onto smaller stacks. In this mode, a goroutine's stack can only grow.

	gcstackbarrieroff: setting gcstackbarrieroff=1 disables the use of stack barriers
	that allow the garbage collector to avoid repeating a stack scan during the
	mark termination phase.

	gcstackbarrierall: setting gcstackbarrierall=1 installs stack barriers
	in every stack frame, rather than in exponentially-spaced frames.

	gcstoptheworld: setting gcstoptheworld=1 disables concurrent garbage collection,
	making every garbage collection a stop-the-world event. Setting gcstoptheworld=2
	also disables concurrent sweeping after the garbage collection finishes.

	gctrace: setting gctrace=1 causes the garbage collector to emit a single line to standard
	error at each collection, summarizing the amount of memory collected and the
	length of the pause. Setting gctrace=2 emits the same summary but also
	repeats each collection. The format of this line is subject to change.
	Currently, it is:
		gc # @#s #%: #+#+# ms clock, #+#/#/#+# ms cpu, #->#-># MB, # MB goal, # P
	where the fields are as follows:
		gc #        the GC number, incremented at each GC
		@#s         time in seconds since program start
		#%          percentage of time spent in GC since program start
		#+...+#     wall-clock/CPU times for the phases of the GC
		#->#-># MB  heap size at GC start, at GC end, and live heap
		# MB goal   goal heap size
		# P         number of processors used
	The phases are stop-the-world (STW) sweep termination, concurrent
	mark and scan, and STW mark termination. The CPU times
	for mark/scan are broken down in to assist time (GC performed in
	line with allocation), background GC time, and idle GC time.
	If the line ends with "(forced)", this GC was forced by a
	runtime.GC() call and all phases are STW.

	memprofilerate: setting memprofilerate=X will update the value of runtime.MemProfileRate.
	When set to 0 memory profiling is disabled.  Refer to the description of
	MemProfileRate for the default value.

	invalidptr: defaults to invalidptr=1, causing the garbage collector and stack
	copier to crash the program if an invalid pointer value (for example, 1)
	is found in a pointer-typed location. Setting invalidptr=0 disables this check.
	This should only be used as a temporary workaround to diagnose buggy code.
	The real fix is to not store integers in pointer-typed locations.

	sbrk: setting sbrk=1 replaces the memory allocator and garbage collector
	with a trivial allocator that obtains memory from the operating system and
	never reclaims any memory.

	scavenge: scavenge=1 enables debugging mode of heap scavenger.

	scheddetail: setting schedtrace=X and scheddetail=1 causes the scheduler to emit
	detailed multiline info every X milliseconds, describing state of the scheduler,
	processors, threads and goroutines.

	schedtrace: setting schedtrace=X causes the scheduler to emit a single line to standard
	error every X milliseconds, summarizing the scheduler state.

The net and net/http packages also refer to debugging variables in GODEBUG.
See the documentation for those packages for details.

The GOMAXPROCS variable limits the number of operating system threads that
can execute user-level Go code simultaneously. There is no limit to the number of threads
that can be blocked in system calls on behalf of Go code; those do not count against
the GOMAXPROCS limit. This package's GOMAXPROCS function queries and changes
the limit.

The GOTRACEBACK variable controls the amount of output generated when a Go
program fails due to an unrecovered panic or an unexpected runtime condition.
By default, a failure prints a stack trace for the current goroutine,
eliding functions internal to the run-time system, and then exits with exit code 2.
The failure prints stack traces for all goroutines if there is no current goroutine
or the failure is internal to the run-time.
GOTRACEBACK=none omits the goroutine stack traces entirely.
GOTRACEBACK=single (the default) behaves as described above.
GOTRACEBACK=all adds stack traces for all user-created goroutines.
GOTRACEBACK=system is like ``all'' but adds stack frames for run-time functions
and shows goroutines created internally by the run-time.
GOTRACEBACK=crash is like ``system'' but crashes in an operating system-specific
manner instead of exiting. For example, on Unix systems, the crash raises
SIGABRT to trigger a core dump.
For historical reasons, the GOTRACEBACK settings 0, 1, and 2 are synonyms for
none, all, and system, respectively.
The runtime/debug package's SetTraceback function allows increasing the
amount of output at run time, but it cannot reduce the amount below that
specified by the environment variable.
See https://golang.org/pkg/runtime/debug/#SetTraceback.

The GOARCH, GOOS, GOPATH, and GOROOT environment variables complete
the set of Go environment variables. They influence the building of Go programs
(see https://golang.org/cmd/go and https://golang.org/pkg/go/build).
GOARCH, GOOS, and GOROOT are recorded at compile time and made available by
constants or functions in this package, but they do not influence the execution
of the run-time system.
*/
package runtime

import "runtime/internal/sys"

// Caller reports file and line number information about function invocations on
// the calling goroutine's stack. The argument skip is the number of stack frames
// to ascend, with 0 identifying the caller of Caller.  (For historical reasons the
// meaning of skip differs between Caller and Callers.) The return values report the
// program counter, file name, and line number within the file of the corresponding
// call. The boolean ok is false if it was not possible to recover the information.
func Caller(skip int) (pc uintptr, file string, line int, ok bool) {
	// Ask for two PCs: the one we were asked for
	// and what it called, so that we can see if it
	// "called" sigpanic.
	var rpc [2]uintptr
	if callers(1+skip-1, rpc[:]) < 2 {
		return
	}
	f := findfunc(rpc[1])
	if f == nil {
		// TODO(rsc): Probably a bug?
		// The C version said "have retpc at least"
		// but actually returned pc=0.
		ok = true
		return
	}
	pc = rpc[1]
	xpc := pc
	g := findfunc(rpc[0])
	// All architectures turn faults into apparent calls to sigpanic.
	// If we see a call to sigpanic, we do not back up the PC to find
	// the line number of the call instruction, because there is no call.
	if xpc > f.entry && (g == nil || g.entry != funcPC(sigpanic)) {
		xpc--
	}
	file, line32 := funcline(f, xpc)
	line = int(line32)
	ok = true
	return
}

// Callers fills the slice pc with the return program counters of function invocations
// on the calling goroutine's stack. The argument skip is the number of stack frames
// to skip before recording in pc, with 0 identifying the frame for Callers itself and
// 1 identifying the caller of Callers.
// It returns the number of entries written to pc.
//
// Note that since each slice entry pc[i] is a return program counter,
// looking up the file and line for pc[i] (for example, using (*Func).FileLine)
// will normally return the file and line number of the instruction immediately
// following the call.
// To easily look up file/line information for the call sequence, use Frames.
func Callers(skip int, pc []uintptr) int {
	// runtime.callers uses pc.array==nil as a signal
	// to print a stack trace. Pick off 0-length pc here
	// so that we don't let a nil pc slice get to it.
	if len(pc) == 0 {
		return 0
	}
	return callers(skip, pc)
}

// GOROOT returns the root of the Go tree.
// It uses the GOROOT environment variable, if set,
// or else the root used during the Go build.
func GOROOT() string {
	s := gogetenv("GOROOT")
	if s != "" {
		return s
	}
	return sys.DefaultGoroot
}

// Version returns the Go tree's version string.
// It is either the commit hash and date at the time of the build or,
// when possible, a release tag like "go1.3".
func Version() string {
	return sys.TheVersion
}

// GOOS is the running program's operating system target:
// one of darwin, freebsd, linux, and so on.
const GOOS string = sys.GOOS

// GOARCH is the running program's architecture target:
// 386, amd64, arm, or s390x.
const GOARCH string = sys.GOARCH
<commit_msg>runtime: document heap scavenger memory summary<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package runtime contains operations that interact with Go's runtime system,
such as functions to control goroutines. It also includes the low-level type information
used by the reflect package; see reflect's documentation for the programmable
interface to the run-time type system.

Environment Variables

The following environment variables ($name or %name%, depending on the host
operating system) control the run-time behavior of Go programs. The meanings
and use may change from release to release.

The GOGC variable sets the initial garbage collection target percentage.
A collection is triggered when the ratio of freshly allocated data to live data
remaining after the previous collection reaches this percentage. The default
is GOGC=100. Setting GOGC=off disables the garbage collector entirely.
The runtime/debug package's SetGCPercent function allows changing this
percentage at run time. See https://golang.org/pkg/runtime/debug/#SetGCPercent.

The GODEBUG variable controls debugging variables within the runtime.
It is a comma-separated list of name=val pairs setting these named variables:

	allocfreetrace: setting allocfreetrace=1 causes every allocation to be
	profiled and a stack trace printed on each object's allocation and free.

	cgocheck: setting cgocheck=0 disables all checks for packages
	using cgo to incorrectly pass Go pointers to non-Go code.
	Setting cgocheck=1 (the default) enables relatively cheap
	checks that may miss some errors.  Setting cgocheck=2 enables
	expensive checks that should not miss any errors, but will
	cause your program to run slower.

	efence: setting efence=1 causes the allocator to run in a mode
	where each object is allocated on a unique page and addresses are
	never recycled.

	gccheckmark: setting gccheckmark=1 enables verification of the
	garbage collector's concurrent mark phase by performing a
	second mark pass while the world is stopped.  If the second
	pass finds a reachable object that was not found by concurrent
	mark, the garbage collector will panic.

	gcpacertrace: setting gcpacertrace=1 causes the garbage collector to
	print information about the internal state of the concurrent pacer.

	gcshrinkstackoff: setting gcshrinkstackoff=1 disables moving goroutines
	onto smaller stacks. In this mode, a goroutine's stack can only grow.

	gcstackbarrieroff: setting gcstackbarrieroff=1 disables the use of stack barriers
	that allow the garbage collector to avoid repeating a stack scan during the
	mark termination phase.

	gcstackbarrierall: setting gcstackbarrierall=1 installs stack barriers
	in every stack frame, rather than in exponentially-spaced frames.

	gcstoptheworld: setting gcstoptheworld=1 disables concurrent garbage collection,
	making every garbage collection a stop-the-world event. Setting gcstoptheworld=2
	also disables concurrent sweeping after the garbage collection finishes.

	gctrace: setting gctrace=1 causes the garbage collector to emit a single line to standard
	error at each collection, summarizing the amount of memory collected and the
	length of the pause. Setting gctrace=2 emits the same summary but also
	repeats each collection. The format of this line is subject to change.
	Currently, it is:
		gc # @#s #%: #+#+# ms clock, #+#/#/#+# ms cpu, #->#-># MB, # MB goal, # P
	where the fields are as follows:
		gc #        the GC number, incremented at each GC
		@#s         time in seconds since program start
		#%          percentage of time spent in GC since program start
		#+...+#     wall-clock/CPU times for the phases of the GC
		#->#-># MB  heap size at GC start, at GC end, and live heap
		# MB goal   goal heap size
		# P         number of processors used
	The phases are stop-the-world (STW) sweep termination, concurrent
	mark and scan, and STW mark termination. The CPU times
	for mark/scan are broken down in to assist time (GC performed in
	line with allocation), background GC time, and idle GC time.
	If the line ends with "(forced)", this GC was forced by a
	runtime.GC() call and all phases are STW.

	Setting gctrace to any value > 0 also causes the garbage collector
	to emit a summary when memory is released back to the system.
	This process of returning memory to the system is called scavenging.
	The format of this summary is subject to change.
	Currently it is:
		scvg#: # MB released  printed only if non-zero
		scvg#: inuse: # idle: # sys: # released: # consumed: # (MB)
	where the fields are as follows:
		scvg#        the scavenge cycle number, incremented at each scavenge
		inuse: #     MB used or partially used spans
		idle: #      MB spans pending scavenging
		sys: #       MB mapped from the system
		released: #  MB released to the system
		consumed: #  MB allocated from the system

	memprofilerate: setting memprofilerate=X will update the value of runtime.MemProfileRate.
	When set to 0 memory profiling is disabled.  Refer to the description of
	MemProfileRate for the default value.

	invalidptr: defaults to invalidptr=1, causing the garbage collector and stack
	copier to crash the program if an invalid pointer value (for example, 1)
	is found in a pointer-typed location. Setting invalidptr=0 disables this check.
	This should only be used as a temporary workaround to diagnose buggy code.
	The real fix is to not store integers in pointer-typed locations.

	sbrk: setting sbrk=1 replaces the memory allocator and garbage collector
	with a trivial allocator that obtains memory from the operating system and
	never reclaims any memory.

	scavenge: scavenge=1 enables debugging mode of heap scavenger.

	scheddetail: setting schedtrace=X and scheddetail=1 causes the scheduler to emit
	detailed multiline info every X milliseconds, describing state of the scheduler,
	processors, threads and goroutines.

	schedtrace: setting schedtrace=X causes the scheduler to emit a single line to standard
	error every X milliseconds, summarizing the scheduler state.

The net and net/http packages also refer to debugging variables in GODEBUG.
See the documentation for those packages for details.

The GOMAXPROCS variable limits the number of operating system threads that
can execute user-level Go code simultaneously. There is no limit to the number of threads
that can be blocked in system calls on behalf of Go code; those do not count against
the GOMAXPROCS limit. This package's GOMAXPROCS function queries and changes
the limit.

The GOTRACEBACK variable controls the amount of output generated when a Go
program fails due to an unrecovered panic or an unexpected runtime condition.
By default, a failure prints a stack trace for the current goroutine,
eliding functions internal to the run-time system, and then exits with exit code 2.
The failure prints stack traces for all goroutines if there is no current goroutine
or the failure is internal to the run-time.
GOTRACEBACK=none omits the goroutine stack traces entirely.
GOTRACEBACK=single (the default) behaves as described above.
GOTRACEBACK=all adds stack traces for all user-created goroutines.
GOTRACEBACK=system is like ``all'' but adds stack frames for run-time functions
and shows goroutines created internally by the run-time.
GOTRACEBACK=crash is like ``system'' but crashes in an operating system-specific
manner instead of exiting. For example, on Unix systems, the crash raises
SIGABRT to trigger a core dump.
For historical reasons, the GOTRACEBACK settings 0, 1, and 2 are synonyms for
none, all, and system, respectively.
The runtime/debug package's SetTraceback function allows increasing the
amount of output at run time, but it cannot reduce the amount below that
specified by the environment variable.
See https://golang.org/pkg/runtime/debug/#SetTraceback.

The GOARCH, GOOS, GOPATH, and GOROOT environment variables complete
the set of Go environment variables. They influence the building of Go programs
(see https://golang.org/cmd/go and https://golang.org/pkg/go/build).
GOARCH, GOOS, and GOROOT are recorded at compile time and made available by
constants or functions in this package, but they do not influence the execution
of the run-time system.
*/
package runtime

import "runtime/internal/sys"

// Caller reports file and line number information about function invocations on
// the calling goroutine's stack. The argument skip is the number of stack frames
// to ascend, with 0 identifying the caller of Caller.  (For historical reasons the
// meaning of skip differs between Caller and Callers.) The return values report the
// program counter, file name, and line number within the file of the corresponding
// call. The boolean ok is false if it was not possible to recover the information.
func Caller(skip int) (pc uintptr, file string, line int, ok bool) {
	// Ask for two PCs: the one we were asked for
	// and what it called, so that we can see if it
	// "called" sigpanic.
	var rpc [2]uintptr
	if callers(1+skip-1, rpc[:]) < 2 {
		return
	}
	f := findfunc(rpc[1])
	if f == nil {
		// TODO(rsc): Probably a bug?
		// The C version said "have retpc at least"
		// but actually returned pc=0.
		ok = true
		return
	}
	pc = rpc[1]
	xpc := pc
	g := findfunc(rpc[0])
	// All architectures turn faults into apparent calls to sigpanic.
	// If we see a call to sigpanic, we do not back up the PC to find
	// the line number of the call instruction, because there is no call.
	if xpc > f.entry && (g == nil || g.entry != funcPC(sigpanic)) {
		xpc--
	}
	file, line32 := funcline(f, xpc)
	line = int(line32)
	ok = true
	return
}

// Callers fills the slice pc with the return program counters of function invocations
// on the calling goroutine's stack. The argument skip is the number of stack frames
// to skip before recording in pc, with 0 identifying the frame for Callers itself and
// 1 identifying the caller of Callers.
// It returns the number of entries written to pc.
//
// Note that since each slice entry pc[i] is a return program counter,
// looking up the file and line for pc[i] (for example, using (*Func).FileLine)
// will normally return the file and line number of the instruction immediately
// following the call.
// To easily look up file/line information for the call sequence, use Frames.
func Callers(skip int, pc []uintptr) int {
	// runtime.callers uses pc.array==nil as a signal
	// to print a stack trace. Pick off 0-length pc here
	// so that we don't let a nil pc slice get to it.
	if len(pc) == 0 {
		return 0
	}
	return callers(skip, pc)
}

// GOROOT returns the root of the Go tree.
// It uses the GOROOT environment variable, if set,
// or else the root used during the Go build.
func GOROOT() string {
	s := gogetenv("GOROOT")
	if s != "" {
		return s
	}
	return sys.DefaultGoroot
}

// Version returns the Go tree's version string.
// It is either the commit hash and date at the time of the build or,
// when possible, a release tag like "go1.3".
func Version() string {
	return sys.TheVersion
}

// GOOS is the running program's operating system target:
// one of darwin, freebsd, linux, and so on.
const GOOS string = sys.GOOS

// GOARCH is the running program's architecture target:
// 386, amd64, arm, or s390x.
const GOARCH string = sys.GOARCH
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"sort"
)

const (
	VERSION = "0.0.1"
)

// sort interface-typed arrays by first-class functions
type ByFn struct{
	elems []interface{}
	comp func(a, b interface{}) bool
}
func (c ByFn) Len() int { return len(c.elems)  }
func (c ByFn) Less(i, j int) bool { return c.comp(c.elems[i], c.elems[j]) }
func (c ByFn) Swap(i, j int) { c.elems[i], c.elems[j] = c.elems[j], c.elems[i] }

// generic api requests
func apiReq(meth string, url string) interface{} {
	client := &http.Client{}
	req, err := http.NewRequest(meth, url, nil)
	req.SetBasicAuth("x", os.Getenv("HEROKU_API_KEY"))
	req.Header.Add("User-Agent", fmt.Sprintf("hk/%s", VERSION))
	req.Header.Add("Accept", "application/json")
	res, err := client.Do(req)
		if err != nil {
		panic(err)
	}
	if (res.StatusCode == 401) {
		error("Unauthorized")
	}
	if (res.StatusCode != 200) {
		error("Unexpected error")
	}
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}
	var data interface{}
	err = json.Unmarshal(body, &data)
	if err != nil {
		panic(err)
	}
	return data
}

// error formatting
func error(msg string) {
	fmt.Fprintf(os.Stderr, "Error: %s.\n", msg)
	os.Exit(1)
}

func unrecArg(arg string) {
	error(fmt.Sprintf("Unrecognized argument '%s'", arg))
}

func unrecCmd(cmd string) {
	error(fmt.Sprintf("'%s' is not an hk command. See 'hk help'", cmd))
}

// commands
func envHelp() {
	fmt.Printf("Usage: hk env -a <app>\n\n")
	fmt.Printf("Show all config vars.")
	os.Exit(0)
}

func env() {
	if (len(os.Args) != 4) || (os.Args[2] != "-a") {
		error("Invalid usage. See hk help env")
	}
	appName := os.Args[3]
	data := apiReq("GET", fmt.Sprintf("https://api.heroku.com/apps/%s/config_vars", appName))
	config := data.(map[string]interface{})
	for k, v := range config {
		fmt.Printf("%s=%v\n", k, v)
	}
	os.Exit(0)
}

func getHelp() {
	fmt.Printf("Usage: hk get -a <app> <key>\n\n")
	fmt.Printf("Get the value of a config var.\n")
	os.Exit(0)
}

func get() {
	if (len(os.Args) != 5) || (os.Args[2] != "-a") {
		error("Invalid usage. See hk help get")
	}
	appName := os.Args[3]
	key := os.Args[4]
	data := apiReq("GET", fmt.Sprintf("https://api.heroku.com/apps/%s/config_vars", appName))
	config := data.(map[string]string)
	value, found := config[key]
	if !found {
		error(fmt.Sprintf("No such key as '%s'", key))
	}
	fmt.Println(value)
	os.Exit(0)
}

func listHelp() {
	fmt.Printf("Usage: hk list\n\n")
	fmt.Printf("List accessible apps.\n")
	os.Exit(0)
}

func list() {
	if len(os.Args) != 2 {
		unrecArg(os.Args[2])
	}
	data := apiReq("GET", "https://api.heroku.com/apps")
	apps := data.([]interface{})
	for i := range apps {
		app := apps[i].(map[string]interface{})
		fmt.Printf("%s\n", app["name"])
	}
	os.Exit(0)
}

func psHelp() {
	fmt.Printf("Usage: hk ps -a <app>\n\n")
	fmt.Printf("List apps processes.\n")
	os.Exit(0)
}

func ps() {
	if (len(os.Args) != 4) || (os.Args[2] != "-a") {
		error("Invalid usage. See hk help ps")
	}
	appName := os.Args[3]
	data := apiReq("GET", fmt.Sprintf("https://api.heroku.com/apps/%s/ps", appName))
	processes := data.([]interface{})
	sort.Sort(ByFn{
		processes,
		func(a, b interface{}) bool {
			p1 := a.(map[string]interface{})["process"].(string)
			p2 := b.(map[string]interface{})["process"].(string)
		  return p1 < p2
	  }})
	for i := range processes {
		process := processes[i].(map[string]interface{})
		fmt.Printf("%v\n", process["process"])
	}
	os.Exit(0)
}

func versionHelp() {
	fmt.Printf("Usage: hk version\n\n")
	fmt.Printf("Show hk client version.\n")
	os.Exit(0)
}

func version() {
	if len(os.Args) != 2 {
	  unrecArg(os.Args[2])
  }
	fmt.Printf("%s\n", VERSION)
	os.Exit(0)
}

func help() {
	if len(os.Args) <= 2 {
		usage()
	} else {
		cmd := os.Args[2]
		switch cmd {
	  case "env":
		  envHelp()
	  case "get":
			getHelp()
		case "list":
		  listHelp()
	  case "ps":
		  psHelp()
		case "version":
			versionHelp()
		}
		unrecCmd(cmd)
	}
}

// top-level usage
func usage() {
	fmt.Printf("Usage: hk <command> [-a <app>] [command-specific-options]\n\n")
	fmt.Printf("Supported hk commands are:\n")
	fmt.Printf("  addons          List add-ons\n")
	fmt.Printf("  addons-add      Add an add-on\n")
	fmt.Printf("  addons-open     Open an add-on page\n")
	fmt.Printf("  addons-remove   Remove an add-on \n")
	fmt.Printf("  create          Create an app\n")
	fmt.Printf("  destroy         Destroy an app\n")
	fmt.Printf("  env             List config vars\n")
	fmt.Printf("  get             Get config var\n")
	fmt.Printf("  help            Show this help\n")
	fmt.Printf("  info            Show app info\n")
	fmt.Printf("  list            List apps\n")
	fmt.Printf("  login           Log in\n")
	fmt.Printf("  logout          Log out\n")
	fmt.Printf("  logs            Show logs\n")
	fmt.Printf("  open            Open app\n")
	fmt.Printf("  pg              List databases\n")
	fmt.Printf("  pg-info         Show database info\n")
	fmt.Printf("  pg-promote      Promote a database\n")
	fmt.Printf("  ps-psql         Open a psql database shell\n")
	fmt.Printf("  pg-wait         Await a database\n")
	fmt.Printf("  ps              List processes\n")
	fmt.Printf("  release         Show release info\n")
	fmt.Printf("  releases        List releases\n")
	fmt.Printf("  rename          Rename an app\n")
	fmt.Printf("  restart         Restart processes\n")
	fmt.Printf("  rollback        Rollback to a previous release\n")
	fmt.Printf("  run             Run a process\n")
	fmt.Printf("  set             Set config var\n")
	fmt.Printf("  scale           Scale processes\n")
	fmt.Printf("  run             Run a process\n")
	fmt.Printf("  stop            Stop a process\n")
	fmt.Printf("  token           Show auth token\n")
	fmt.Printf("  unset           Unset config vars\n")
	fmt.Printf("  version         Display version\n\n")
	fmt.Printf("See 'hk help <command>' for more information on a specific command.\n")
	os.Exit(0)
}

// entry point
func main() {
	if len(os.Args) <= 1 {
		usage()
	} else {
		cmd := os.Args[1]
		switch cmd {
		case "env":
			env()
		case "get":
			get()
		case "help":
			help()
		case "list":
			list()
		case "ps":
			ps()
		case "version":
			version()
		}
		unrecCmd(cmd)
	}
}
<commit_msg>more usable ps<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"sort"
)

const (
	VERSION = "0.0.1"
)

// sort interface-typed arrays by first-class functions
type ByFn struct{
	elems []interface{}
	comp func(a, b interface{}) bool
}
func (c ByFn) Len() int { return len(c.elems)  }
func (c ByFn) Less(i, j int) bool { return c.comp(c.elems[i], c.elems[j]) }
func (c ByFn) Swap(i, j int) { c.elems[i], c.elems[j] = c.elems[j], c.elems[i] }

// generic api requests
func apiReq(meth string, url string) interface{} {
	client := &http.Client{}
	req, err := http.NewRequest(meth, url, nil)
	req.SetBasicAuth("x", os.Getenv("HEROKU_API_KEY"))
	req.Header.Add("User-Agent", fmt.Sprintf("hk/%s", VERSION))
	req.Header.Add("Accept", "application/json")
	res, err := client.Do(req)
		if err != nil {
		panic(err)
	}
	if (res.StatusCode == 401) {
		error("Unauthorized")
	}
	if (res.StatusCode != 200) {
		error("Unexpected error")
	}
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}
	var data interface{}
	err = json.Unmarshal(body, &data)
	if err != nil {
		panic(err)
	}
	return data
}

// error formatting
func error(msg string) {
	fmt.Fprintf(os.Stderr, "Error: %s.\n", msg)
	os.Exit(1)
}

func unrecArg(arg string) {
	error(fmt.Sprintf("Unrecognized argument '%s'", arg))
}

func unrecCmd(cmd string) {
	error(fmt.Sprintf("'%s' is not an hk command. See 'hk help'", cmd))
}

// commands
func envHelp() {
	fmt.Printf("Usage: hk env -a <app>\n\n")
	fmt.Printf("Show all config vars.")
	os.Exit(0)
}

func env() {
	if (len(os.Args) != 4) || (os.Args[2] != "-a") {
		error("Invalid usage. See hk help env")
	}
	appName := os.Args[3]
	data := apiReq("GET", fmt.Sprintf("https://api.heroku.com/apps/%s/config_vars", appName))
	config := data.(map[string]interface{})
	for k, v := range config {
		fmt.Printf("%s=%v\n", k, v)
	}
	os.Exit(0)
}

func getHelp() {
	fmt.Printf("Usage: hk get -a <app> <key>\n\n")
	fmt.Printf("Get the value of a config var.\n")
	os.Exit(0)
}

func get() {
	if (len(os.Args) != 5) || (os.Args[2] != "-a") {
		error("Invalid usage. See hk help get")
	}
	appName := os.Args[3]
	key := os.Args[4]
	data := apiReq("GET", fmt.Sprintf("https://api.heroku.com/apps/%s/config_vars", appName))
	config := data.(map[string]string)
	value, found := config[key]
	if !found {
		error(fmt.Sprintf("No such key as '%s'", key))
	}
	fmt.Println(value)
	os.Exit(0)
}

func listHelp() {
	fmt.Printf("Usage: hk list\n\n")
	fmt.Printf("List accessible apps.\n")
	os.Exit(0)
}

func list() {
	if len(os.Args) != 2 {
		unrecArg(os.Args[2])
	}
	data := apiReq("GET", "https://api.heroku.com/apps")
	apps := data.([]interface{})
	for i := range apps {
		app := apps[i].(map[string]interface{})
		fmt.Printf("%s\n", app["name"])
	}
	os.Exit(0)
}

func psHelp() {
	fmt.Printf("Usage: hk ps -a <app>\n\n")
	fmt.Printf("List apps processes.\n")
	os.Exit(0)
}

func ps() {
	if (len(os.Args) != 4) || (os.Args[2] != "-a") {
		error("Invalid usage. See hk help ps")
	}
	appName := os.Args[3]
	data := apiReq("GET", fmt.Sprintf("https://api.heroku.com/apps/%s/ps", appName))
	processes := data.([]interface{})
	sort.Sort(ByFn{
		processes,
		func(a, b interface{}) bool {
			p1 := a.(map[string]interface{})["process"].(string)
			p2 := b.(map[string]interface{})["process"].(string)
		  return p1 < p2
	  }})
	fmt.Printf("Process           State       Command\n")
	fmt.Printf("----------------  ----------  ------------------------\n")
	for i := range processes {
		process := processes[i].(map[string]interface{})
		fmt.Printf("%-16s  %-10s  %s\n", process["process"], process["state"], process["command"])
	}
	os.Exit(0)
}

func versionHelp() {
	fmt.Printf("Usage: hk version\n\n")
	fmt.Printf("Show hk client version.\n")
	os.Exit(0)
}

func version() {
	if len(os.Args) != 2 {
	  unrecArg(os.Args[2])
  }
	fmt.Printf("%s\n", VERSION)
	os.Exit(0)
}

func help() {
	if len(os.Args) <= 2 {
		usage()
	} else {
		cmd := os.Args[2]
		switch cmd {
	  case "env":
		  envHelp()
	  case "get":
			getHelp()
		case "list":
		  listHelp()
	  case "ps":
		  psHelp()
		case "version":
			versionHelp()
		}
		unrecCmd(cmd)
	}
}

// top-level usage
func usage() {
	fmt.Printf("Usage: hk <command> [-a <app>] [command-specific-options]\n\n")
	fmt.Printf("Supported hk commands are:\n")
	fmt.Printf("  addons          List add-ons\n")
	fmt.Printf("  addons-add      Add an add-on\n")
	fmt.Printf("  addons-open     Open an add-on page\n")
	fmt.Printf("  addons-remove   Remove an add-on \n")
	fmt.Printf("  create          Create an app\n")
	fmt.Printf("  destroy         Destroy an app\n")
	fmt.Printf("  env             List config vars\n")
	fmt.Printf("  get             Get config var\n")
	fmt.Printf("  help            Show this help\n")
	fmt.Printf("  info            Show app info\n")
	fmt.Printf("  list            List apps\n")
	fmt.Printf("  login           Log in\n")
	fmt.Printf("  logout          Log out\n")
	fmt.Printf("  logs            Show logs\n")
	fmt.Printf("  open            Open app\n")
	fmt.Printf("  pg              List databases\n")
	fmt.Printf("  pg-info         Show database info\n")
	fmt.Printf("  pg-promote      Promote a database\n")
	fmt.Printf("  ps-psql         Open a psql database shell\n")
	fmt.Printf("  pg-wait         Await a database\n")
	fmt.Printf("  ps              List processes\n")
	fmt.Printf("  release         Show release info\n")
	fmt.Printf("  releases        List releases\n")
	fmt.Printf("  rename          Rename an app\n")
	fmt.Printf("  restart         Restart processes\n")
	fmt.Printf("  rollback        Rollback to a previous release\n")
	fmt.Printf("  run             Run a process\n")
	fmt.Printf("  set             Set config var\n")
	fmt.Printf("  scale           Scale processes\n")
	fmt.Printf("  run             Run a process\n")
	fmt.Printf("  stop            Stop a process\n")
	fmt.Printf("  token           Show auth token\n")
	fmt.Printf("  unset           Unset config vars\n")
	fmt.Printf("  version         Display version\n\n")
	fmt.Printf("See 'hk help <command>' for more information on a specific command.\n")
	os.Exit(0)
}

// entry point
func main() {
	if len(os.Args) <= 1 {
		usage()
	} else {
		cmd := os.Args[1]
		switch cmd {
		case "env":
			env()
		case "get":
			get()
		case "help":
			help()
		case "list":
			list()
		case "ps":
			ps()
		case "version":
			version()
		}
		unrecCmd(cmd)
	}
}
<|endoftext|>


<commit_before>package file

import (
	"archive/tar"
	"context"
	"os"
	"time"

	"github.com/containerd/continuity/fs"
	"github.com/docker/docker/pkg/archive"
	"github.com/docker/docker/pkg/chrootarchive"
	copy "github.com/tonistiigi/fsutil/copy"
)

func unpack(ctx context.Context, srcRoot string, src string, destRoot string, dest string, ch copy.Chowner, tm *time.Time) (bool, error) {
	src, err := fs.RootPath(srcRoot, src)
	if err != nil {
		return false, err
	}
	if !isArchivePath(src) {
		return false, nil
	}

	dest, err = fs.RootPath(destRoot, dest)
	if err != nil {
		return false, err
	}
	if err := copy.MkdirAll(dest, 0755, ch, tm); err != nil {
		return false, err
	}

	file, err := os.Open(src)
	if err != nil {
		return false, err
	}
	defer file.Close()

	return true, chrootarchive.Untar(file, dest, nil)
}

func isArchivePath(path string) bool {
	fi, err := os.Lstat(path)
	if err != nil {
		return false
	}
	if fi.Mode()&os.ModeType != 0 {
		return false
	}
	file, err := os.Open(path)
	if err != nil {
		return false
	}
	defer file.Close()
	rdr, err := archive.DecompressStream(file)
	if err != nil {
		return false
	}
	r := tar.NewReader(rdr)
	_, err = r.Next()
	return err == nil
}
<commit_msg>Close readclosers returned by DecompressStream<commit_after>package file

import (
	"archive/tar"
	"context"
	"os"
	"time"

	"github.com/containerd/continuity/fs"
	"github.com/docker/docker/pkg/archive"
	"github.com/docker/docker/pkg/chrootarchive"
	copy "github.com/tonistiigi/fsutil/copy"
)

func unpack(ctx context.Context, srcRoot string, src string, destRoot string, dest string, ch copy.Chowner, tm *time.Time) (bool, error) {
	src, err := fs.RootPath(srcRoot, src)
	if err != nil {
		return false, err
	}
	if !isArchivePath(src) {
		return false, nil
	}

	dest, err = fs.RootPath(destRoot, dest)
	if err != nil {
		return false, err
	}
	if err := copy.MkdirAll(dest, 0755, ch, tm); err != nil {
		return false, err
	}

	file, err := os.Open(src)
	if err != nil {
		return false, err
	}
	defer file.Close()

	return true, chrootarchive.Untar(file, dest, nil)
}

func isArchivePath(path string) bool {
	fi, err := os.Lstat(path)
	if err != nil {
		return false
	}
	if fi.Mode()&os.ModeType != 0 {
		return false
	}
	file, err := os.Open(path)
	if err != nil {
		return false
	}
	defer file.Close()
	rdr, err := archive.DecompressStream(file)
	if err != nil {
		return false
	}
	defer rdr.Close()
	r := tar.NewReader(rdr)
	_, err = r.Next()
	return err == nil
}
<|endoftext|>


<commit_before>package cc1100

import (
	"log"
)

const (
	verbose            = false
	writeUsingTransfer = false
)

func (r *Radio) ReadRegister(addr byte) (byte, error) {
	buf := []byte{READ_MODE | addr, 0xFF}
	err := r.device.Transfer(buf)
	if err != nil {
		return 0, err
	}
	return buf[1], nil
}

func (r *Radio) ReadBurst(addr byte, n int) ([]byte, error) {
	reg := addr & 0x3F
	if 0x30 <= reg && reg <= 0x3D {
		log.Panicf("burst access for status register %X is not available\n", reg)
	}
	buf := make([]byte, n+1)
	buf[0] = READ_MODE | BURST_MODE | addr
	err := r.device.Transfer(buf)
	return buf[1:], err
}

func (r *Radio) writeData(data []byte) error {
	if writeUsingTransfer {
		return r.device.Transfer(data)
	} else {
		return r.device.Write(data)
	}
}

func (r *Radio) WriteRegister(addr byte, value byte) error {
	return r.writeData([]byte{addr, value})
}

func (r *Radio) WriteBurst(addr byte, data []byte) error {
	return r.writeData(append([]byte{BURST_MODE | addr}, data...))
}

func (r *Radio) WriteEach(data []byte) error {
	n := len(data)
	if n%2 != 0 {
		panic("odd data length")
	}
	for i := 0; i < n; i += 2 {
		err := r.WriteRegister(data[i], data[i+1])
		if err != nil {
			return err
		}
	}
	return nil
}

func (r *Radio) Strobe(cmd byte) (byte, error) {
	if verbose && cmd != SNOP {
		log.Printf("issuing %s command\n", strobeName(cmd))
	}
	buf := []byte{cmd}
	err := r.device.Transfer(buf)
	if err != nil {
		return 0, err
	}
	return buf[0], nil
}

func (r *Radio) Reset() error {
	_, err := r.Strobe(SRES)
	return err
}

func (r *Radio) Version() (uint16, error) {
	p, err := r.ReadRegister(PARTNUM)
	if err != nil {
		return 0, err
	}
	v, err := r.ReadRegister(VERSION)
	if err != nil {
		return 0, err
	}
	return uint16(p)<<8 | uint16(v), nil
}
<commit_msg>Use microsecond-resolution logging in verbose mode<commit_after>package cc1100

import (
	"log"
)

const (
	verbose            = false
	writeUsingTransfer = false
)

func init() {
	if verbose {
		log.SetFlags(log.Ltime | log.Lmicroseconds | log.LUTC)
	}
}

func (r *Radio) ReadRegister(addr byte) (byte, error) {
	buf := []byte{READ_MODE | addr, 0xFF}
	err := r.device.Transfer(buf)
	if err != nil {
		return 0, err
	}
	return buf[1], nil
}

func (r *Radio) ReadBurst(addr byte, n int) ([]byte, error) {
	reg := addr & 0x3F
	if 0x30 <= reg && reg <= 0x3D {
		log.Panicf("burst access for status register %X is not available\n", reg)
	}
	buf := make([]byte, n+1)
	buf[0] = READ_MODE | BURST_MODE | addr
	err := r.device.Transfer(buf)
	return buf[1:], err
}

func (r *Radio) writeData(data []byte) error {
	if writeUsingTransfer {
		return r.device.Transfer(data)
	} else {
		return r.device.Write(data)
	}
}

func (r *Radio) WriteRegister(addr byte, value byte) error {
	return r.writeData([]byte{addr, value})
}

func (r *Radio) WriteBurst(addr byte, data []byte) error {
	return r.writeData(append([]byte{BURST_MODE | addr}, data...))
}

func (r *Radio) WriteEach(data []byte) error {
	n := len(data)
	if n%2 != 0 {
		panic("odd data length")
	}
	for i := 0; i < n; i += 2 {
		err := r.WriteRegister(data[i], data[i+1])
		if err != nil {
			return err
		}
	}
	return nil
}

func (r *Radio) Strobe(cmd byte) (byte, error) {
	if verbose && cmd != SNOP {
		log.Printf("issuing %s command\n", strobeName(cmd))
	}
	buf := []byte{cmd}
	err := r.device.Transfer(buf)
	if err != nil {
		return 0, err
	}
	return buf[0], nil
}

func (r *Radio) Reset() error {
	_, err := r.Strobe(SRES)
	return err
}

func (r *Radio) Version() (uint16, error) {
	p, err := r.ReadRegister(PARTNUM)
	if err != nil {
		return 0, err
	}
	v, err := r.ReadRegister(VERSION)
	if err != nil {
		return 0, err
	}
	return uint16(p)<<8 | uint16(v), nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Tigera, Inc. All rights reserved.

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

package intdataplane

import (
	log "github.com/Sirupsen/logrus"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/projectcalico/felix/go/felix/ipsets"
	"github.com/projectcalico/felix/go/felix/iptables"
	"github.com/projectcalico/felix/go/felix/proto"
	"github.com/projectcalico/felix/go/felix/rules"
	"github.com/projectcalico/felix/go/felix/set"
	"reflect"
)

type mockTable struct {
	currentChains  map[string]*iptables.Chain
	expectedChains map[string]*iptables.Chain
}

func newMockTable() *mockTable {
	return &mockTable{
		currentChains:  map[string]*iptables.Chain{},
		expectedChains: map[string]*iptables.Chain{},
	}
}

func logChains(message string, chains []*iptables.Chain) {
	if chains == nil {
		log.Debug(message, " with nil chains")
	} else {
		log.WithField("chains", chains).Debug(message)
		for _, chain := range chains {
			log.WithField("chain", *chain).Debug("")
		}
	}
}

func (t *mockTable) UpdateChains(chains []*iptables.Chain) {
	logChains("UpdateChains", chains)
	for _, chain := range chains {
		t.currentChains[chain.Name] = chain
	}
}

func (t *mockTable) RemoveChains(chains []*iptables.Chain) {
	logChains("RemoveChains", chains)
	for _, chain := range chains {
		_, prs := t.currentChains[chain.Name]
		Expect(prs).To(BeTrue())
		delete(t.currentChains, chain.Name)
	}
}

func (t *mockTable) checkChains(expected []*iptables.Chain) {
	t.expectedChains = map[string]*iptables.Chain{}
	for _, chain := range expected {
		t.expectedChains[chain.Name] = chain
	}
	t.checkChainsSameAsBefore()
}

func (t *mockTable) checkChainsSameAsBefore() {
	Expect(reflect.DeepEqual(t.currentChains, t.expectedChains)).To(BeTrue())
}

var wlDispatchEmpty = []*iptables.Chain{
	&iptables.Chain{
		Name: "cali-to-wl-dispatch",
		Rules: []iptables.Rule{
			{
				Match:   iptables.Match(),
				Action:  iptables.DropAction{},
				Comment: "Unknown interface",
			},
		},
	},
	&iptables.Chain{
		Name: "cali-from-wl-dispatch",
		Rules: []iptables.Rule{
			{
				Match:   iptables.Match(),
				Action:  iptables.DropAction{},
				Comment: "Unknown interface",
			},
		},
	},
}

var hostDispatchEmpty = []*iptables.Chain{
	&iptables.Chain{
		Name:  "cali-to-host-endpoint",
		Rules: []iptables.Rule{},
	},
	&iptables.Chain{
		Name:  "cali-from-host-endpoint",
		Rules: []iptables.Rule{},
	},
}

func hostDispatchForIface(ifaceName string) []*iptables.Chain {
	return []*iptables.Chain{
		&iptables.Chain{
			Name: "calith-" + ifaceName,
			Rules: []iptables.Rule{
				{
					Match:  iptables.Match(),
					Action: iptables.JumpAction{Target: "cali-failsafe-out"},
				},
				{
					Match:  iptables.Match(),
					Action: iptables.ClearMarkAction{Mark: 8},
				},
				{
					Match:   iptables.Match(),
					Action:  iptables.DropAction{},
					Comment: "Drop if no profiles matched",
				},
			},
		},
		&iptables.Chain{
			Name: "califh-" + ifaceName,
			Rules: []iptables.Rule{
				{
					Match:  iptables.Match(),
					Action: iptables.JumpAction{Target: "cali-failsafe-in"},
				},
				{
					Match:  iptables.Match(),
					Action: iptables.ClearMarkAction{Mark: 8},
				},
				{
					Match:   iptables.Match(),
					Action:  iptables.DropAction{},
					Comment: "Drop if no profiles matched",
				},
			},
		},
		&iptables.Chain{
			Name: "cali-to-host-endpoint",
			Rules: []iptables.Rule{
				{
					Match:  iptables.Match().OutInterface(ifaceName),
					Action: iptables.GotoAction{Target: "calith-" + ifaceName},
				},
			},
		},
		&iptables.Chain{
			Name: "cali-from-host-endpoint",
			Rules: []iptables.Rule{
				{
					Match:  iptables.Match().InInterface(ifaceName),
					Action: iptables.GotoAction{Target: "califh-" + ifaceName},
				},
			},
		},
	}
}

var _ = Describe("EndpointManager test", func() {

	var epMgr *endpointManager
	var filterTable *mockTable

	rrConfigNormal := rules.Config{
		IPIPEnabled:          true,
		IPIPTunnelAddress:    nil,
		IPSetConfigV4:        ipsets.NewIPVersionConfig(ipsets.IPFamilyV4, "cali", nil, nil),
		IPSetConfigV6:        ipsets.NewIPVersionConfig(ipsets.IPFamilyV6, "cali", nil, nil),
		IptablesMarkAccept:   0x8,
		IptablesMarkNextTier: 0x10,
	}

	for ip_version := range []uint8{4, 6} {
		BeforeEach(func() {
			renderer := rules.NewRenderer(rrConfigNormal)
			filterTable = newMockTable()
			epMgr = newEndpointManager(
				filterTable,
				renderer,
				nil,
				uint8(ip_version),
				[]string{"cali"},
				nil,
			)
		})

		It("should be constructable", func() {
			Expect(epMgr).ToNot(BeNil())
		})

		It("should process host endpoints", func() {

			log.Info("TEST: Define a host endpoint for a named interface")
			epMgr.OnUpdate(&proto.HostEndpointUpdate{
				Id: &proto.HostEndpointID{
					EndpointId: "endpoint-id-11",
				},
				Endpoint: &proto.HostEndpoint{
					Name:              "eth0",
					ProfileIds:        []string{},
					Tiers:             []*proto.TierInfo{},
					ExpectedIpv4Addrs: []string{},
					ExpectedIpv6Addrs: []string{},
				},
			})
			epMgr.CompleteDeferredWork()
			filterTable.checkChains(append(wlDispatchEmpty, hostDispatchEmpty...))

			log.Info("TEST: Signal that that interface exists")
			epMgr.OnUpdate(&ifaceUpdate{
				Name:  "eth0",
				State: "up",
			})
			addrs := set.New()
			epMgr.OnUpdate(&ifaceAddrsUpdate{
				Name:  "eth0",
				Addrs: addrs,
			})
			epMgr.CompleteDeferredWork()
			filterTable.checkChains(append(wlDispatchEmpty, hostDispatchForIface("eth0")...))

			log.Info("TEST: Add an address to the interface")
			addrs.Add("10.0.240.10")
			epMgr.OnUpdate(&ifaceAddrsUpdate{
				Name:  "eth0",
				Addrs: addrs,
			})
			epMgr.CompleteDeferredWork()
			filterTable.checkChainsSameAsBefore()

			log.Info("TEST: Change host endpoint to expect that address instead of a named interface")
			epMgr.OnUpdate(&proto.HostEndpointUpdate{
				Id: &proto.HostEndpointID{
					EndpointId: "endpoint-id-11",
				},
				Endpoint: &proto.HostEndpoint{
					ProfileIds:        []string{},
					Tiers:             []*proto.TierInfo{},
					ExpectedIpv4Addrs: []string{"10.0.240.10"},
					ExpectedIpv6Addrs: []string{},
				},
			})
			epMgr.CompleteDeferredWork()
			filterTable.checkChainsSameAsBefore()

			log.Info("TEST: Signal another host endpoint that also matches the IP address")
			epMgr.OnUpdate(&proto.HostEndpointUpdate{
				Id: &proto.HostEndpointID{
					EndpointId: "other-endpoint-id-55",
				},
				Endpoint: &proto.HostEndpoint{
					ProfileIds:        []string{},
					Tiers:             []*proto.TierInfo{},
					ExpectedIpv4Addrs: []string{"8.8.8.8", "10.0.240.10"},
					ExpectedIpv6Addrs: []string{},
				},
			})
			epMgr.CompleteDeferredWork()
			filterTable.checkChainsSameAsBefore()

			log.Info("TEST: Remove that other host endpoint again")
			epMgr.OnUpdate(&proto.HostEndpointRemove{
				Id: &proto.HostEndpointID{
					EndpointId: "other-endpoint-id-55",
				},
			})
			epMgr.CompleteDeferredWork()
			filterTable.checkChainsSameAsBefore()

			log.Info("TEST: Change host endpoint to expect a different address")
			epMgr.OnUpdate(&proto.HostEndpointUpdate{
				Id: &proto.HostEndpointID{
					EndpointId: "endpoint-id-11",
				},
				Endpoint: &proto.HostEndpoint{
					ProfileIds:        []string{},
					Tiers:             []*proto.TierInfo{},
					ExpectedIpv4Addrs: []string{"10.0.240.11"},
					ExpectedIpv6Addrs: []string{},
				},
			})
			epMgr.CompleteDeferredWork()
			filterTable.checkChains(append(wlDispatchEmpty, hostDispatchEmpty...))

			log.Info("TEST: Change host endpoint to be for an interface that doesn't exist yet")
			epMgr.OnUpdate(&proto.HostEndpointUpdate{
				Id: &proto.HostEndpointID{
					EndpointId: "endpoint-id-11",
				},
				Endpoint: &proto.HostEndpoint{
					Name:              "eth1",
					ProfileIds:        []string{},
					Tiers:             []*proto.TierInfo{},
					ExpectedIpv4Addrs: []string{},
					ExpectedIpv6Addrs: []string{},
				},
			})
			epMgr.CompleteDeferredWork()
			filterTable.checkChainsSameAsBefore()

			log.Info("TEST: Signal that interface")
			epMgr.OnUpdate(&ifaceUpdate{
				Name:  "eth1",
				State: "up",
			})
			addrs = set.New()
			epMgr.OnUpdate(&ifaceAddrsUpdate{
				Name:  "eth1",
				Addrs: addrs,
			})
			epMgr.CompleteDeferredWork()
			filterTable.checkChains(append(wlDispatchEmpty, hostDispatchForIface("eth1")...))
		})

		It("should process a workload endpoint update", func() {
		})
	}
})
<commit_msg>Rework host endpoint tests into better ginkgo style<commit_after>// Copyright (c) 2017 Tigera, Inc. All rights reserved.

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

package intdataplane

import (
	log "github.com/Sirupsen/logrus"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/projectcalico/felix/go/felix/ipsets"
	"github.com/projectcalico/felix/go/felix/iptables"
	"github.com/projectcalico/felix/go/felix/proto"
	"github.com/projectcalico/felix/go/felix/rules"
	"github.com/projectcalico/felix/go/felix/set"
	"reflect"
)

type mockTable struct {
	currentChains  map[string]*iptables.Chain
	expectedChains map[string]*iptables.Chain
}

func newMockTable() *mockTable {
	return &mockTable{
		currentChains:  map[string]*iptables.Chain{},
		expectedChains: map[string]*iptables.Chain{},
	}
}

func logChains(message string, chains []*iptables.Chain) {
	if chains == nil {
		log.Debug(message, " with nil chains")
	} else {
		log.WithField("chains", chains).Debug(message)
		for _, chain := range chains {
			log.WithField("chain", *chain).Debug("")
		}
	}
}

func (t *mockTable) UpdateChains(chains []*iptables.Chain) {
	logChains("UpdateChains", chains)
	for _, chain := range chains {
		t.currentChains[chain.Name] = chain
	}
}

func (t *mockTable) RemoveChains(chains []*iptables.Chain) {
	logChains("RemoveChains", chains)
	for _, chain := range chains {
		_, prs := t.currentChains[chain.Name]
		Expect(prs).To(BeTrue())
		delete(t.currentChains, chain.Name)
	}
}

func (t *mockTable) checkChains(expected []*iptables.Chain) {
	t.expectedChains = map[string]*iptables.Chain{}
	for _, chain := range expected {
		t.expectedChains[chain.Name] = chain
	}
	t.checkChainsSameAsBefore()
}

func (t *mockTable) checkChainsSameAsBefore() {
	Expect(reflect.DeepEqual(t.currentChains, t.expectedChains)).To(BeTrue())
}

var wlDispatchEmpty = []*iptables.Chain{
	&iptables.Chain{
		Name: "cali-to-wl-dispatch",
		Rules: []iptables.Rule{
			{
				Match:   iptables.Match(),
				Action:  iptables.DropAction{},
				Comment: "Unknown interface",
			},
		},
	},
	&iptables.Chain{
		Name: "cali-from-wl-dispatch",
		Rules: []iptables.Rule{
			{
				Match:   iptables.Match(),
				Action:  iptables.DropAction{},
				Comment: "Unknown interface",
			},
		},
	},
}

var hostDispatchEmpty = []*iptables.Chain{
	&iptables.Chain{
		Name:  "cali-to-host-endpoint",
		Rules: []iptables.Rule{},
	},
	&iptables.Chain{
		Name:  "cali-from-host-endpoint",
		Rules: []iptables.Rule{},
	},
}

func hostDispatchForIface(ifaceName string) []*iptables.Chain {
	return []*iptables.Chain{
		&iptables.Chain{
			Name: "calith-" + ifaceName,
			Rules: []iptables.Rule{
				{
					Match:  iptables.Match(),
					Action: iptables.JumpAction{Target: "cali-failsafe-out"},
				},
				{
					Match:  iptables.Match(),
					Action: iptables.ClearMarkAction{Mark: 8},
				},
				{
					Match:   iptables.Match(),
					Action:  iptables.DropAction{},
					Comment: "Drop if no profiles matched",
				},
			},
		},
		&iptables.Chain{
			Name: "califh-" + ifaceName,
			Rules: []iptables.Rule{
				{
					Match:  iptables.Match(),
					Action: iptables.JumpAction{Target: "cali-failsafe-in"},
				},
				{
					Match:  iptables.Match(),
					Action: iptables.ClearMarkAction{Mark: 8},
				},
				{
					Match:   iptables.Match(),
					Action:  iptables.DropAction{},
					Comment: "Drop if no profiles matched",
				},
			},
		},
		&iptables.Chain{
			Name: "cali-to-host-endpoint",
			Rules: []iptables.Rule{
				{
					Match:  iptables.Match().OutInterface(ifaceName),
					Action: iptables.GotoAction{Target: "calith-" + ifaceName},
				},
			},
		},
		&iptables.Chain{
			Name: "cali-from-host-endpoint",
			Rules: []iptables.Rule{
				{
					Match:  iptables.Match().InInterface(ifaceName),
					Action: iptables.GotoAction{Target: "califh-" + ifaceName},
				},
			},
		},
	}
}

var _ = FDescribe("EndpointManager testing", func() {
	const (
		ipv4 = "10.0.240.10"
		ipv6 = "2001:db8::10.0.240.10"
	)
	var (
		epMgr          *endpointManager
		filterTable    *mockTable
		rrConfigNormal rules.Config
		ipVersion      int
		eth0Addrs      set.Set
		loAddrs        set.Set
	)

	BeforeEach(func() {
		rrConfigNormal = rules.Config{
			IPIPEnabled:          true,
			IPIPTunnelAddress:    nil,
			IPSetConfigV4:        ipsets.NewIPVersionConfig(ipsets.IPFamilyV4, "cali", nil, nil),
			IPSetConfigV6:        ipsets.NewIPVersionConfig(ipsets.IPFamilyV6, "cali", nil, nil),
			IptablesMarkAccept:   0x8,
			IptablesMarkNextTier: 0x10,
		}
		eth0Addrs = set.New()
		eth0Addrs.Add(ipv4)
		eth0Addrs.Add(ipv6)
		loAddrs = set.New()
		loAddrs.Add("127.0.1.1")
		loAddrs.Add("::1")
	})

	JustBeforeEach(func() {
		renderer := rules.NewRenderer(rrConfigNormal)
		filterTable = newMockTable()
		epMgr = newEndpointManager(
			filterTable,
			renderer,
			nil,
			uint8(ipVersion),
			[]string{"cali"},
			nil,
		)
	})

	for ipVersion = range []uint8{4, 6} {
		It("should be constructable", func() {
			Expect(epMgr).ToNot(BeNil())
		})

		configureHostEp := func(id string, name string, ipv4Addrs []string, ipv6Addrs []string) func() {
			return func() {
				epMgr.OnUpdate(&proto.HostEndpointUpdate{
					Id: &proto.HostEndpointID{
						EndpointId: id,
					},
					Endpoint: &proto.HostEndpoint{
						Name:              name,
						ProfileIds:        []string{},
						Tiers:             []*proto.TierInfo{},
						ExpectedIpv4Addrs: ipv4Addrs,
						ExpectedIpv6Addrs: ipv6Addrs,
					},
				})
				epMgr.CompleteDeferredWork()
			}
		}

		expectChainsFor := func(name string) func() {
			return func() {
				filterTable.checkChains(append(wlDispatchEmpty, hostDispatchForIface(name)...))
			}
		}

		expectEmptyChains := func() func() {
			return func() {
				filterTable.checkChains(append(wlDispatchEmpty, hostDispatchEmpty...))
			}
		}

		removeHostEp := func(id string) func() {
			return func() {
				epMgr.OnUpdate(&proto.HostEndpointRemove{
					Id: &proto.HostEndpointID{
						EndpointId: id,
					},
				})
				epMgr.CompleteDeferredWork()
			}
		}

		Context("with host interfaces eth0, lo", func() {
			JustBeforeEach(func() {
				epMgr.OnUpdate(&ifaceUpdate{
					Name:  "eth0",
					State: "up",
				})
				epMgr.OnUpdate(&ifaceAddrsUpdate{
					Name:  "eth0",
					Addrs: eth0Addrs,
				})
				epMgr.OnUpdate(&ifaceUpdate{
					Name:  "lo",
					State: "up",
				})
				epMgr.OnUpdate(&ifaceAddrsUpdate{
					Name:  "lo",
					Addrs: loAddrs,
				})
				epMgr.CompleteDeferredWork()
			})

			It("should have empty dispatch chains", func() {
				filterTable.checkChains(append(wlDispatchEmpty, hostDispatchEmpty...))
			})

			Describe("with host endpoint matching eth0", func() {
				JustBeforeEach(configureHostEp("id1", "eth0", []string{}, []string{}))
				It("should have expected chains", expectChainsFor("eth0"))

				Context("with another host ep that matches the IPv4 address", func() {
					JustBeforeEach(configureHostEp("id2", "", []string{ipv4}, []string{}))
					It("should have expected chains", expectChainsFor("eth0"))

					Context("with the first host ep removed", func() {
						JustBeforeEach(removeHostEp("id1"))
						It("should have expected chains", expectChainsFor("eth0"))

						Context("with both host eps removed", func() {
							JustBeforeEach(removeHostEp("id2"))
							It("should have empty dispatch chains", expectEmptyChains())
						})
					})
				})
			})

			Describe("with host endpoint matching non-existent interface", func() {
				JustBeforeEach(configureHostEp("id3", "eth1", []string{}, []string{}))
				It("should have empty dispatch chains", expectEmptyChains())
			})

			Describe("with host endpoint matching IPv4 address", func() {
				JustBeforeEach(configureHostEp("id4", "", []string{ipv4}, []string{}))
				It("should have expected chains", expectChainsFor("eth0"))
			})

			Describe("with host endpoint matching IPv6 address", func() {
				JustBeforeEach(configureHostEp("id5", "", []string{}, []string{ipv6}))
				It("should have expected chains", expectChainsFor("eth0"))
			})

			Describe("with host endpoint matching IPv4 address and correct interface name", func() {
				JustBeforeEach(configureHostEp("id3", "eth0", []string{ipv4}, []string{}))
				It("should have expected chains", expectChainsFor("eth0"))
			})

			Describe("with host endpoint matching IPv6 address and correct interface name", func() {
				JustBeforeEach(configureHostEp("id3", "eth0", []string{}, []string{ipv6}))
				It("should have expected chains", expectChainsFor("eth0"))
			})

			Describe("with host endpoint matching IPv4 address and wrong interface name", func() {
				JustBeforeEach(configureHostEp("id3", "eth1", []string{ipv4}, []string{}))
				It("should have empty dispatch chains", expectEmptyChains())
			})

			Describe("with host endpoint matching IPv6 address and wrong interface name", func() {
				JustBeforeEach(configureHostEp("id3", "eth1", []string{}, []string{ipv6}))
				It("should have empty dispatch chains", expectEmptyChains())
			})

			Describe("with host endpoint with unmatched IPv4 address", func() {
				JustBeforeEach(configureHostEp("id4", "", []string{"8.8.8.8"}, []string{}))
				It("should have empty dispatch chains", expectEmptyChains())
			})

			Describe("with host endpoint with unmatched IPv6 address", func() {
				JustBeforeEach(configureHostEp("id5", "", []string{}, []string{"fe08::2"}))
				It("should have empty dispatch chains", expectEmptyChains())
			})

		})

		Context("with host endpoint configured before interface signaled", func() {
			JustBeforeEach(configureHostEp("id3", "eth0", []string{}, []string{}))
			It("should have empty dispatch chains", expectEmptyChains())

			Context("with interface signaled", func() {
				JustBeforeEach(func() {
					epMgr.OnUpdate(&ifaceUpdate{
						Name:  "eth0",
						State: "up",
					})
					epMgr.OnUpdate(&ifaceAddrsUpdate{
						Name:  "eth0",
						Addrs: eth0Addrs,
					})
					epMgr.CompleteDeferredWork()
				})
				It("should have expected chains", expectChainsFor("eth0"))
			})
		})

		It("should process a workload endpoint update", func() {
		})
	}
})
<|endoftext|>


<commit_before>// 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 awskms

import (
	"bytes"
	"errors"
	"os"
	// ignore-placeholder1
	// ignore-placeholder2
	"testing"

	"flag"
	// context is used to cancel outstanding requests
	// TEST_SRCDIR to read the roots.pem
	"github.com/google/tink/go/aead"
	"github.com/google/tink/go/core/registry"
	"github.com/google/tink/go/keyset"
	// ignore-placeholder3
	"github.com/google/tink/go/subtle/random"
	"github.com/google/tink/go/tink"
)

const (
	keyURI  = "aws-kms://arn:aws:kms:us-east-2:235739564943:key/3ee50705-5a82-4f5b-9753-05c4f473922f"
	profile = "tink-user1"
)

var (
	// lint placeholder header, please ignore
	credFile = os.Getenv("TEST_SRCDIR") + "/" + os.Getenv("TEST_WORKSPACE") + "/" + "testdata/credentials_aws.csv"
	// lint placeholder footer, please ignore
)

// lint placeholder header, please ignore
func init() {
	certPath := os.Getenv("TEST_SRCDIR") + "/" + os.Getenv("TEST_WORKSPACE") + "/" + "roots.pem"
	flag.Set("cacerts", certPath)
	os.Setenv("SSL_CERT_FILE", certPath)
}

// lint placeholder footer, please ignore

func setupKMS(t *testing.T) {
	t.Helper()
	g, err := NewAWSClient(keyURI)
	if err != nil {
		t.Fatalf("error setting up aws client: %v", err)
	}
	_, err = g.LoadCredentials(credFile)
	if err != nil {
		t.Fatalf("error loading credentials : %v", err)
	}
	registry.RegisterKMSClient(g)
}

func basicAEADTest(t *testing.T, a tink.AEAD) error {
	t.Helper()
	for i := 0; i < 100; i++ {
		pt := random.GetRandomBytes(20)
		ad := random.GetRandomBytes(20)
		ct, err := a.Encrypt(pt, ad)
		if err != nil {
			return err
		}
		dt, err := a.Decrypt(ct, ad)
		if err != nil {
			return err
		}
		if !bytes.Equal(dt, pt) {
			return errors.New("decrypt not inverse of encrypt")
		}
	}
	return nil
}
func TestBasicAead(t *testing.T) {
	setupKMS(t)
	// ignore-placeholder4
	dek := aead.AES128CTRHMACSHA256KeyTemplate()
	kh, err := keyset.NewHandle(aead.KMSEnvelopeAEADKeyTemplate(keyURI, dek))
	if err != nil {
		t.Fatalf("error getting a new keyset handle: %v", err)
	}
	a, err := awsaead(kh)
	if err != nil {
		t.Fatalf("error getting the primitive: %v", err)
	}
	if err := basicAEADTest(t, a); err != nil {
		t.Errorf("error in basic aead tests: %v", err)
	}
}

// ignore-placeholder5
<commit_msg>add test for aws kms with no additional data<commit_after>// 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 awskms

import (
	"bytes"
	"errors"
	"os"
	// ignore-placeholder1
	// ignore-placeholder2
	"testing"

	"flag"
	// context is used to cancel outstanding requests
	// TEST_SRCDIR to read the roots.pem
	"github.com/google/tink/go/aead"
	"github.com/google/tink/go/core/registry"
	"github.com/google/tink/go/keyset"
	// ignore-placeholder3
	"github.com/google/tink/go/subtle/random"
	"github.com/google/tink/go/tink"
)

const (
	keyURI  = "aws-kms://arn:aws:kms:us-east-2:235739564943:key/3ee50705-5a82-4f5b-9753-05c4f473922f"
	profile = "tink-user1"
)

var (
	// lint placeholder header, please ignore
	credFile = os.Getenv("TEST_SRCDIR") + "/" + os.Getenv("TEST_WORKSPACE") + "/" + "testdata/credentials_aws.csv"
	// lint placeholder footer, please ignore
)

// lint placeholder header, please ignore
func init() {
	certPath := os.Getenv("TEST_SRCDIR") + "/" + os.Getenv("TEST_WORKSPACE") + "/" + "roots.pem"
	flag.Set("cacerts", certPath)
	os.Setenv("SSL_CERT_FILE", certPath)
}

// lint placeholder footer, please ignore

func setupKMS(t *testing.T) {
	t.Helper()
	g, err := NewAWSClient(keyURI)
	if err != nil {
		t.Fatalf("error setting up aws client: %v", err)
	}
	_, err = g.LoadCredentials(credFile)
	if err != nil {
		t.Fatalf("error loading credentials : %v", err)
	}
	registry.RegisterKMSClient(g)
}

func basicAEADTest(t *testing.T, a tink.AEAD) error {
	t.Helper()
	for i := 0; i < 100; i++ {
		pt := random.GetRandomBytes(20)
		ad := random.GetRandomBytes(20)
		ct, err := a.Encrypt(pt, ad)
		if err != nil {
			return err
		}
		dt, err := a.Decrypt(ct, ad)
		if err != nil {
			return err
		}
		if !bytes.Equal(dt, pt) {
			return errors.New("decrypt not inverse of encrypt")
		}
	}
	return nil
}

func TestBasicAead(t *testing.T) {
	setupKMS(t)
	// ignore-placeholder4
	dek := aead.AES128CTRHMACSHA256KeyTemplate()
	kh, err := keyset.NewHandle(aead.KMSEnvelopeAEADKeyTemplate(keyURI, dek))
	if err != nil {
		t.Fatalf("error getting a new keyset handle: %v", err)
	}
	a, err := awsaead(kh)
	if err != nil {
		t.Fatalf("error getting the primitive: %v", err)
	}
	if err := basicAEADTest(t, a); err != nil {
		t.Errorf("error in basic aead tests: %v", err)
	}
}

func TestBasicAeadWithoutAdditionalData(t *testing.T) {
	setupKMS(t)
	// ignore-placeholder4
	dek := aead.AES128CTRHMACSHA256KeyTemplate()
	kh, err := keyset.NewHandle(aead.KMSEnvelopeAEADKeyTemplate(keyURI, dek))
	if err != nil {
		t.Fatalf("error getting a new keyset handle: %v", err)
	}
	a, err := awsaead(kh)
	if err != nil {
		t.Fatalf("error getting the primitive: %v", err)
	}
	for i := 0; i < 100; i++ {
		pt := random.GetRandomBytes(20)
		ct, err := a.Encrypt(pt, nil)
		if err != nil {
			t.Fatalf("error encrypting data: %v", err)
		}
		dt, err := a.Decrypt(ct, nil)
		if err != nil {
			t.Fatalf("erorr decrypting data: %v", err)
		}
		if !bytes.Equal(dt, pt) {
			t.Fatalf("decrypt not inverse of encrypt")
		}
	}
}

// ignore-placeholder5
<|endoftext|>


<commit_before>/*
Copyright 2018 The Vitess 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 srvtopo

import (
	"fmt"

	"golang.org/x/net/context"

	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
	vschemapb "vitess.io/vitess/go/vt/proto/vschema"
	"vitess.io/vitess/go/vt/topo"
)

var (
	// ErrNilUnderlyingServer is returned when attempting to create a new keyspace
	// filtering server if a nil underlying server implementation is provided.
	ErrNilUnderlyingServer = fmt.Errorf("Unable to construct filtering server without an underlying server")

	// ErrTopoServerNotAvailable is returned if a caller tries to access the
	// topo.Server supporting this srvtopo.Server.
	ErrTopoServerNotAvailable = fmt.Errorf("Cannot access underlying topology server when keyspace filtering is enabled")
)

// NewKeyspaceFilteringServer constructs a new server based on the provided
// implementation that prevents the specified keyspaces from being exposed
// to consumers of the new Server.
func NewKeyspaceFilteringServer(underlying Server, selectedKeyspaces []string) (Server, error) {
	if underlying == nil {
		return nil, ErrNilUnderlyingServer
	}

	keyspaces := map[string]bool{}
	for _, ks := range selectedKeyspaces {
		keyspaces[ks] = true
	}

	return keyspaceFilteringServer{
		server:          underlying,
		selectKeyspaces: keyspaces,
	}, nil
}

type keyspaceFilteringServer struct {
	server          Server
	selectKeyspaces map[string]bool
}

func (ksf keyspaceFilteringServer) GetTopoServer() (*topo.Server, error) {
	return nil, ErrTopoServerNotAvailable
}

func (ksf keyspaceFilteringServer) GetSrvKeyspaceNames(
	ctx context.Context,
	cell string,
) ([]string, error) {
	keyspaces, err := ksf.server.GetSrvKeyspaceNames(ctx, cell)
	ret := make([]string, 0, len(keyspaces))
	for _, ks := range keyspaces {
		if ksf.selectKeyspaces[ks] {
			ret = append(ret, ks)
		}
	}
	return ret, err
}

func (ksf keyspaceFilteringServer) GetSrvKeyspace(
	ctx context.Context,
	cell,
	keyspace string,
) (*topodatapb.SrvKeyspace, error) {
	if !ksf.selectKeyspaces[keyspace] {
		return nil, topo.NewError(topo.NoNode, keyspace)
	}

	return ksf.server.GetSrvKeyspace(ctx, cell, keyspace)
}

func (ksf keyspaceFilteringServer) WatchSrvVSchema(
	ctx context.Context,
	cell string,
	callback func(*vschemapb.SrvVSchema, error),
) {
	filteringCallback := func(schema *vschemapb.SrvVSchema, err error) {
		if schema != nil {
			for ks := range schema.Keyspaces {
				if !ksf.selectKeyspaces[ks] {
					delete(schema.Keyspaces, ks)
				}
			}
		}

		callback(schema, err)
	}

	ksf.server.WatchSrvVSchema(ctx, cell, filteringCallback)
}
<commit_msg>Document the error behavior<commit_after>/*
Copyright 2018 The Vitess 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 srvtopo

import (
	"fmt"

	"golang.org/x/net/context"

	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
	vschemapb "vitess.io/vitess/go/vt/proto/vschema"
	"vitess.io/vitess/go/vt/topo"
)

var (
	// ErrNilUnderlyingServer is returned when attempting to create a new keyspace
	// filtering server if a nil underlying server implementation is provided.
	ErrNilUnderlyingServer = fmt.Errorf("Unable to construct filtering server without an underlying server")

	// ErrTopoServerNotAvailable is returned if a caller tries to access the
	// topo.Server supporting this srvtopo.Server.
	ErrTopoServerNotAvailable = fmt.Errorf("Cannot access underlying topology server when keyspace filtering is enabled")
)

// NewKeyspaceFilteringServer constructs a new server based on the provided
// implementation that prevents the specified keyspaces from being exposed
// to consumers of the new Server.
//
// A filtering server will not allow access to the topo.Server to prevent
// updates that may corrupt the global VSchema keyspace.
func NewKeyspaceFilteringServer(underlying Server, selectedKeyspaces []string) (Server, error) {
	if underlying == nil {
		return nil, ErrNilUnderlyingServer
	}

	keyspaces := map[string]bool{}
	for _, ks := range selectedKeyspaces {
		keyspaces[ks] = true
	}

	return keyspaceFilteringServer{
		server:          underlying,
		selectKeyspaces: keyspaces,
	}, nil
}

type keyspaceFilteringServer struct {
	server          Server
	selectKeyspaces map[string]bool
}

// GetTopoServer returns an error; filtering srvtopo.Server consumers may not
// access the underlying topo.Server.
func (ksf keyspaceFilteringServer) GetTopoServer() (*topo.Server, error) {
	return nil, ErrTopoServerNotAvailable
}

func (ksf keyspaceFilteringServer) GetSrvKeyspaceNames(
	ctx context.Context,
	cell string,
) ([]string, error) {
	keyspaces, err := ksf.server.GetSrvKeyspaceNames(ctx, cell)
	ret := make([]string, 0, len(keyspaces))
	for _, ks := range keyspaces {
		if ksf.selectKeyspaces[ks] {
			ret = append(ret, ks)
		}
	}
	return ret, err
}

func (ksf keyspaceFilteringServer) GetSrvKeyspace(
	ctx context.Context,
	cell,
	keyspace string,
) (*topodatapb.SrvKeyspace, error) {
	if !ksf.selectKeyspaces[keyspace] {
		return nil, topo.NewError(topo.NoNode, keyspace)
	}

	return ksf.server.GetSrvKeyspace(ctx, cell, keyspace)
}

func (ksf keyspaceFilteringServer) WatchSrvVSchema(
	ctx context.Context,
	cell string,
	callback func(*vschemapb.SrvVSchema, error),
) {
	filteringCallback := func(schema *vschemapb.SrvVSchema, err error) {
		if schema != nil {
			for ks := range schema.Keyspaces {
				if !ksf.selectKeyspaces[ks] {
					delete(schema.Keyspaces, ks)
				}
			}
		}

		callback(schema, err)
	}

	ksf.server.WatchSrvVSchema(ctx, cell, filteringCallback)
}
<|endoftext|>


<commit_before>package manifest

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"time"

	"github.com/mitchellh/packer/common"
	"github.com/mitchellh/packer/helper/config"
	"github.com/mitchellh/packer/packer"
	"github.com/mitchellh/packer/template/interpolate"
)

type Config struct {
	common.PackerConfig `mapstructure:",squash"`

	Filename string `mapstructure:"filename"`

	ctx interpolate.Context
}

type PostProcessor struct {
	config Config
}

type ManifestFile struct {
	Builds      []Artifact `json:"builds"`
	LastRunUUID string     `json:"last_run_uuid"`
}

func (p *PostProcessor) Configure(raws ...interface{}) error {
	err := config.Decode(&p.config, &config.DecodeOpts{
		Interpolate:        true,
		InterpolateContext: &p.config.ctx,
		InterpolateFilter: &interpolate.RenderFilter{
			Exclude: []string{},
		},
	}, raws...)
	if err != nil {
		return err
	}

	if p.config.Filename == "" {
		p.config.Filename = "packer-manifest.json"
	}

	return nil
}

func (p *PostProcessor) PostProcess(ui packer.Ui, source packer.Artifact) (packer.Artifact, bool, error) {
	artifact := &Artifact{}

	var err error

	// Create the current artifact.
	artifact.ArtifactFiles = source.Files()
	artifact.ArtifactId = source.Id()
	artifact.BuilderType = p.config.PackerBuilderType
	artifact.BuildName = p.config.PackerBuildName
	artifact.BuildTime = time.Now().Unix()
	// Since each post-processor runs in a different process we need a way to
	// coordinate between various post-processors in a single packer run. We do
	// this by setting a UUID per run and tracking this in the manifest file.
	// When we detect that the UUID in the file is the same, we know that we are
	// part of the same run and we simply add our data to the list. If the UUID
	// is different we will check the -force flag and decide whether to truncate
	// the file before we proceed.
	artifact.PackerRunUUID = os.Getenv("PACKER_RUN_UUID")

	// Create a lock file with exclusive access. If this fails we will retry
	// after a delay.
	lockFilename := p.config.Filename + ".lock"
	for i := 0; i < 3; i++ {
		// The file should not be locked for very long so we'll keep this short.
		time.Sleep((time.Duration(i) * 200 * time.Millisecond))
		_, err = os.OpenFile(lockFilename, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
		if err == nil {
			break
		}
		log.Printf("Error locking manifest file for reading and writing. Will sleep and retry. %s", err)
	}
	defer os.Remove(lockFilename)

	// TODO fix error on first run:
	// * Post-processor failed: open packer-manifest.json: no such file or directory
	//
	// Read the current manifest file from disk
	contents := []byte{}
	if contents, err = ioutil.ReadFile(p.config.Filename); err != nil && !os.IsNotExist(err) {
		return source, true, fmt.Errorf("Unable to open %s for reading: %s", p.config.Filename, err)
	}

	// Parse the manifest file JSON, if we have one
	manifestFile := &ManifestFile{}
	if len(contents) > 0 {
		if err = json.Unmarshal(contents, manifestFile); err != nil {
			return source, true, fmt.Errorf("Unable to parse content from %s: %s", p.config.Filename, err)
		}
	}

	// If -force is set and we are not on same run, truncate the file. Otherwise
	// we will continue to add new builds to the existing manifest file.
	if p.config.PackerForce && os.Getenv("PACKER_RUN_UUID") != manifestFile.LastRunUUID {
		manifestFile = &ManifestFile{}
	}

	// Add the current artifact to the manifest file
	manifestFile.Builds = append(manifestFile.Builds, *artifact)
	manifestFile.LastRunUUID = os.Getenv("PACKER_RUN_UUID")

	// Write JSON to disk
	if out, err := json.MarshalIndent(manifestFile, "", "  "); err == nil {
		if err = ioutil.WriteFile(p.config.Filename, out, 0664); err != nil {
			return source, true, fmt.Errorf("Unable to write %s: %s", p.config.Filename, err)
		}
	} else {
		return source, true, fmt.Errorf("Unable to marshal JSON %s", err)
	}

	return source, true, err
}
<commit_msg>Fix build failure when there is no packer-manifest.json file<commit_after>package manifest

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"time"

	"github.com/mitchellh/packer/common"
	"github.com/mitchellh/packer/helper/config"
	"github.com/mitchellh/packer/packer"
	"github.com/mitchellh/packer/template/interpolate"
)

type Config struct {
	common.PackerConfig `mapstructure:",squash"`

	Filename string `mapstructure:"filename"`

	ctx interpolate.Context
}

type PostProcessor struct {
	config Config
}

type ManifestFile struct {
	Builds      []Artifact `json:"builds"`
	LastRunUUID string     `json:"last_run_uuid"`
}

func (p *PostProcessor) Configure(raws ...interface{}) error {
	err := config.Decode(&p.config, &config.DecodeOpts{
		Interpolate:        true,
		InterpolateContext: &p.config.ctx,
		InterpolateFilter: &interpolate.RenderFilter{
			Exclude: []string{},
		},
	}, raws...)
	if err != nil {
		return err
	}

	if p.config.Filename == "" {
		p.config.Filename = "packer-manifest.json"
	}

	return nil
}

func (p *PostProcessor) PostProcess(ui packer.Ui, source packer.Artifact) (packer.Artifact, bool, error) {
	artifact := &Artifact{}

	var err error

	// Create the current artifact.
	artifact.ArtifactFiles = source.Files()
	artifact.ArtifactId = source.Id()
	artifact.BuilderType = p.config.PackerBuilderType
	artifact.BuildName = p.config.PackerBuildName
	artifact.BuildTime = time.Now().Unix()
	// Since each post-processor runs in a different process we need a way to
	// coordinate between various post-processors in a single packer run. We do
	// this by setting a UUID per run and tracking this in the manifest file.
	// When we detect that the UUID in the file is the same, we know that we are
	// part of the same run and we simply add our data to the list. If the UUID
	// is different we will check the -force flag and decide whether to truncate
	// the file before we proceed.
	artifact.PackerRunUUID = os.Getenv("PACKER_RUN_UUID")

	// Create a lock file with exclusive access. If this fails we will retry
	// after a delay.
	lockFilename := p.config.Filename + ".lock"
	for i := 0; i < 3; i++ {
		// The file should not be locked for very long so we'll keep this short.
		time.Sleep((time.Duration(i) * 200 * time.Millisecond))
		_, err = os.OpenFile(lockFilename, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
		if err == nil {
			break
		}
		log.Printf("Error locking manifest file for reading and writing. Will sleep and retry. %s", err)
	}
	defer os.Remove(lockFilename)

	// TODO fix error on first run:
	// * Post-processor failed: open packer-manifest.json: no such file or directory
	//
	// Read the current manifest file from disk
	contents := []byte{}
	if contents, err = ioutil.ReadFile(p.config.Filename); err != nil && !os.IsNotExist(err) {
		return source, true, fmt.Errorf("Unable to open %s for reading: %s", p.config.Filename, err)
	}

	// Parse the manifest file JSON, if we have one
	manifestFile := &ManifestFile{}
	if len(contents) > 0 {
		if err = json.Unmarshal(contents, manifestFile); err != nil {
			return source, true, fmt.Errorf("Unable to parse content from %s: %s", p.config.Filename, err)
		}
	}

	// If -force is set and we are not on same run, truncate the file. Otherwise
	// we will continue to add new builds to the existing manifest file.
	if p.config.PackerForce && os.Getenv("PACKER_RUN_UUID") != manifestFile.LastRunUUID {
		manifestFile = &ManifestFile{}
	}

	// Add the current artifact to the manifest file
	manifestFile.Builds = append(manifestFile.Builds, *artifact)
	manifestFile.LastRunUUID = os.Getenv("PACKER_RUN_UUID")

	// Write JSON to disk
	if out, err := json.MarshalIndent(manifestFile, "", "  "); err == nil {
		if err = ioutil.WriteFile(p.config.Filename, out, 0664); err != nil {
			return source, true, fmt.Errorf("Unable to write %s: %s", p.config.Filename, err)
		}
	} else {
		return source, true, fmt.Errorf("Unable to marshal JSON %s", err)
	}

	return source, true, nil
}
<|endoftext|>