<commit_before>package driver

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"syscall"
	"time"

	"github.com/hashicorp/go-plugin"
	"github.com/hashicorp/go-version"
	"github.com/hashicorp/nomad/client/allocdir"
	"github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/client/driver/executor"
	cstructs "github.com/hashicorp/nomad/client/driver/structs"
	"github.com/hashicorp/nomad/client/fingerprint"
	"github.com/hashicorp/nomad/helper/discover"
	"github.com/hashicorp/nomad/nomad/structs"
	"github.com/mitchellh/mapstructure"
)

var (
	reRktVersion  = regexp.MustCompile(`rkt [vV]ersion[:]? (\d[.\d]+)`)
	reAppcVersion = regexp.MustCompile(`appc [vV]ersion[:]? (\d[.\d]+)`)
)

const (
	// minRktVersion is the earliest supported version of rkt. rkt added support
	// for CPU and memory isolators in 0.14.0. We cannot support an earlier
	// version to maintain an uniform interface across all drivers
	minRktVersion = "0.14.0"

	// bytesToMB is the conversion from bytes to megabytes.
	bytesToMB = 1024 * 1024
)

// RktDriver is a driver for running images via Rkt
// We attempt to chose sane defaults for now, with more configuration available
// planned in the future
type RktDriver struct {
	DriverContext
	fingerprint.StaticFingerprinter
}

type RktDriverConfig struct {
	ImageName string   `mapstructure:"image"`
	Args      []string `mapstructure:"args"`
}

// rktHandle is returned from Start/Open as a handle to the PID
type rktHandle struct {
	pluginClient *plugin.Client
	executorPid  int
	executor     executor.Executor
	allocDir     *allocdir.AllocDir
	logger       *log.Logger
	killTimeout  time.Duration
	waitCh       chan *cstructs.WaitResult
	doneCh       chan struct{}
}

// rktPID is a struct to map the pid running the process to the vm image on
// disk
type rktPID struct {
	PluginConfig *PluginReattachConfig
	AllocDir     *allocdir.AllocDir
	ExecutorPid  int
	KillTimeout  time.Duration
}

// NewRktDriver is used to create a new exec driver
func NewRktDriver(ctx *DriverContext) Driver {
	return &RktDriver{DriverContext: *ctx}
}

func (d *RktDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) {
	// Only enable if we are root when running on non-windows systems.
	if runtime.GOOS != "windows" && syscall.Geteuid() != 0 {
		d.logger.Printf("[DEBUG] driver.rkt: must run as root user, disabling")
		return false, nil
	}

	outBytes, err := exec.Command("rkt", "version").Output()
	if err != nil {
		return false, nil
	}
	out := strings.TrimSpace(string(outBytes))

	rktMatches := reRktVersion.FindStringSubmatch(out)
	appcMatches := reAppcVersion.FindStringSubmatch(out)
	if len(rktMatches) != 2 || len(appcMatches) != 2 {
		return false, fmt.Errorf("Unable to parse Rkt version string: %#v", rktMatches)
	}

	node.Attributes["driver.rkt"] = "1"
	node.Attributes["driver.rkt.version"] = rktMatches[1]
	node.Attributes["driver.rkt.appc.version"] = appcMatches[1]

	minVersion, _ := version.NewVersion(minRktVersion)
	currentVersion, _ := version.NewVersion(node.Attributes["driver.rkt.version"])
	if currentVersion.LessThan(minVersion) {
		// Do not allow rkt < 0.14.0
		d.logger.Printf("[WARN] driver.rkt: please upgrade rkt to a version >= %s", minVersion)
		node.Attributes["driver.rkt"] = "0"
	}
	return true, nil
}

// Run an existing Rkt image.
func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) {
	var driverConfig RktDriverConfig
	if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil {
		return nil, err
	}
	// Validate that the config is valid.
	img := driverConfig.ImageName
	if img == "" {
		return nil, fmt.Errorf("Missing ACI image for rkt")
	}

	// Get the tasks local directory.
	taskName := d.DriverContext.taskName
	taskDir, ok := ctx.AllocDir.TaskDirs[taskName]
	if !ok {
		return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName)
	}

	// Build the command.
	var cmdArgs []string

	// Add the given trust prefix
	trustPrefix, trustCmd := task.Config["trust_prefix"]
	insecure := false
	if trustCmd {
		var outBuf, errBuf bytes.Buffer
		cmd := exec.Command("rkt", "trust", "--skip-fingerprint-review=true", fmt.Sprintf("--prefix=%s", trustPrefix))
		cmd.Stdout = &outBuf
		cmd.Stderr = &errBuf
		if err := cmd.Run(); err != nil {
			return nil, fmt.Errorf("Error running rkt trust: %s\n\nOutput: %s\n\nError: %s",
				err, outBuf.String(), errBuf.String())
		}
		d.logger.Printf("[DEBUG] driver.rkt: added trust prefix: %q", trustPrefix)
	} else {
		// Disble signature verification if the trust command was not run.
		insecure = true
	}

	local, ok := ctx.AllocDir.TaskDirs[task.Name]
	if !ok {
		return nil, fmt.Errorf("Failed to find task local directory: %v", task.Name)
	}

	cmdArgs = append(cmdArgs, "run")
	cmdArgs = append(cmdArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s", task.Name, local))
	cmdArgs = append(cmdArgs, fmt.Sprintf("--mount=volume=%s,target=%s", task.Name, ctx.AllocDir.SharedDir))
	cmdArgs = append(cmdArgs, img)
	if insecure == true {
		cmdArgs = append(cmdArgs, "--insecure-options=all")
	}

	// Inject enviornment variables
	for k, v := range d.taskEnv.EnvMap() {
		cmdArgs = append(cmdArgs, fmt.Sprintf("--set-env=%v=%v", k, v))
	}

	// Check if the user has overriden the exec command.
	if execCmd, ok := task.Config["command"]; ok {
		cmdArgs = append(cmdArgs, fmt.Sprintf("--exec=%v", execCmd))
	}

	if task.Resources.MemoryMB == 0 {
		return nil, fmt.Errorf("Memory limit cannot be zero")
	}
	if task.Resources.CPU == 0 {
		return nil, fmt.Errorf("CPU limit cannot be zero")
	}

	// Add memory isolator
	cmdArgs = append(cmdArgs, fmt.Sprintf("--memory=%vM", int64(task.Resources.MemoryMB)*bytesToMB))

	// Add CPU isolator
	cmdArgs = append(cmdArgs, fmt.Sprintf("--cpu=%vm", int64(task.Resources.CPU)))

	// Add user passed arguments.
	if len(driverConfig.Args) != 0 {
		parsed := d.taskEnv.ParseAndReplace(driverConfig.Args)

		// Need to start arguments with "--"
		if len(parsed) > 0 {
			cmdArgs = append(cmdArgs, "--")
		}

		for _, arg := range parsed {
			cmdArgs = append(cmdArgs, fmt.Sprintf("%v", arg))
		}
	}

	bin, err := discover.NomadExecutable()
	if err != nil {
		return nil, fmt.Errorf("unable to find the nomad binary: %v", err)
	}

	pluginLogFile := filepath.Join(taskDir, fmt.Sprintf("%s-executor.out", task.Name))
	pluginConfig := &plugin.ClientConfig{
		Cmd: exec.Command(bin, "executor", pluginLogFile),
	}

	exec, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput, d.config)
	if err != nil {
		return nil, err
	}
	executorCtx := &executor.ExecutorContext{
		TaskEnv:          d.taskEnv,
		AllocDir:         ctx.AllocDir,
		TaskName:         task.Name,
		TaskResources:    task.Resources,
		UnprivilegedUser: false,
		LogConfig:        task.LogConfig,
	}

	ps, err := exec.LaunchCmd(&executor.ExecCommand{Cmd: "rkt", Args: cmdArgs}, executorCtx)
	if err != nil {
		pluginClient.Kill()
		return nil, fmt.Errorf("error starting process via the plugin: %v", err)
	}

	d.logger.Printf("[DEBUG] driver.rkt: started ACI %q with: %v", img, cmdArgs)
	h := &rktHandle{
		pluginClient: pluginClient,
		executor:     exec,
		executorPid:  ps.Pid,
		logger:       d.logger,
		killTimeout:  d.DriverContext.KillTimeout(task),
		doneCh:       make(chan struct{}),
		waitCh:       make(chan *cstructs.WaitResult, 1),
	}
	go h.run()
	return h, nil
}

func (d *RktDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) {
	// Parse the handle
	pidBytes := []byte(strings.TrimPrefix(handleID, "Rkt:"))
	qpid := &rktPID{}
	if err := json.Unmarshal(pidBytes, qpid); err != nil {
		return nil, fmt.Errorf("failed to parse Rkt handle '%s': %v", handleID, err)
	}

	pluginConfig := &plugin.ClientConfig{
		Reattach: qpid.PluginConfig.PluginConfig(),
	}
	executor, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput, d.config)
	if err != nil {
		d.logger.Println("[ERROR] driver.rkt: error connecting to plugin so destroying plugin pid and user pid")
		if e := destroyPlugin(qpid.PluginConfig.Pid, qpid.ExecutorPid); e != nil {
			d.logger.Printf("[ERROR] driver.rkt: error destroying plugin and executor pid: %v", e)
		}
		return nil, fmt.Errorf("error connecting to plugin: %v", err)
	}

	// Return a driver handle
	h := &rktHandle{
		pluginClient: pluginClient,
		executorPid:  qpid.ExecutorPid,
		allocDir:     qpid.AllocDir,
		executor:     executor,
		logger:       d.logger,
		killTimeout:  qpid.KillTimeout,
		doneCh:       make(chan struct{}),
		waitCh:       make(chan *cstructs.WaitResult, 1),
	}

	go h.run()
	return h, nil
}

func (h *rktHandle) ID() string {
	// Return a handle to the PID
	pid := &rktPID{
		PluginConfig: NewPluginReattachConfig(h.pluginClient.ReattachConfig()),
		KillTimeout:  h.killTimeout,
		ExecutorPid:  h.executorPid,
		AllocDir:     h.allocDir,
	}
	data, err := json.Marshal(pid)
	if err != nil {
		h.logger.Printf("[ERR] driver.rkt: failed to marshal rkt PID to JSON: %s", err)
	}
	return fmt.Sprintf("Rkt:%s", string(data))
}

func (h *rktHandle) WaitCh() chan *cstructs.WaitResult {
	return h.waitCh
}

func (h *rktHandle) Update(task *structs.Task) error {
	// Store the updated kill timeout.
	h.killTimeout = task.KillTimeout
	h.executor.UpdateLogConfig(task.LogConfig)

	// Update is not possible
	return nil
}

// Kill is used to terminate the task. We send an Interrupt
// and then provide a 5 second grace period before doing a Kill.
func (h *rktHandle) Kill() error {
	h.executor.ShutDown()
	select {
	case <-h.doneCh:
		return nil
	case <-time.After(h.killTimeout):
		return h.executor.Exit()
	}
}

func (h *rktHandle) run() {
	ps, err := h.executor.Wait()
	close(h.doneCh)
	if ps.ExitCode == 0 && err != nil {
		if e := killProcess(h.executorPid); e != nil {
			h.logger.Printf("[ERROR] driver.rkt: error killing user process: %v", e)
		}
		if e := h.allocDir.UnmountAll(); e != nil {
			h.logger.Printf("[ERROR] driver.rkt: unmounting dev,proc and alloc dirs failed: %v", e)
		}
	}
	h.waitCh <- &cstructs.WaitResult{ExitCode: ps.ExitCode, Signal: 0, Err: err}
	close(h.waitCh)
	h.pluginClient.Kill()
}
<commit_msg>Run rkt interactively<commit_after>package driver

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"syscall"
	"time"

	"github.com/hashicorp/go-plugin"
	"github.com/hashicorp/go-version"
	"github.com/hashicorp/nomad/client/allocdir"
	"github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/client/driver/executor"
	cstructs "github.com/hashicorp/nomad/client/driver/structs"
	"github.com/hashicorp/nomad/client/fingerprint"
	"github.com/hashicorp/nomad/helper/discover"
	"github.com/hashicorp/nomad/nomad/structs"
	"github.com/mitchellh/mapstructure"
)

var (
	reRktVersion  = regexp.MustCompile(`rkt [vV]ersion[:]? (\d[.\d]+)`)
	reAppcVersion = regexp.MustCompile(`appc [vV]ersion[:]? (\d[.\d]+)`)
)

const (
	// minRktVersion is the earliest supported version of rkt. rkt added support
	// for CPU and memory isolators in 0.14.0. We cannot support an earlier
	// version to maintain an uniform interface across all drivers
	minRktVersion = "0.14.0"

	// bytesToMB is the conversion from bytes to megabytes.
	bytesToMB = 1024 * 1024
)

// RktDriver is a driver for running images via Rkt
// We attempt to chose sane defaults for now, with more configuration available
// planned in the future
type RktDriver struct {
	DriverContext
	fingerprint.StaticFingerprinter
}

type RktDriverConfig struct {
	ImageName string   `mapstructure:"image"`
	Args      []string `mapstructure:"args"`
}

// rktHandle is returned from Start/Open as a handle to the PID
type rktHandle struct {
	pluginClient *plugin.Client
	executorPid  int
	executor     executor.Executor
	allocDir     *allocdir.AllocDir
	logger       *log.Logger
	killTimeout  time.Duration
	waitCh       chan *cstructs.WaitResult
	doneCh       chan struct{}
}

// rktPID is a struct to map the pid running the process to the vm image on
// disk
type rktPID struct {
	PluginConfig *PluginReattachConfig
	AllocDir     *allocdir.AllocDir
	ExecutorPid  int
	KillTimeout  time.Duration
}

// NewRktDriver is used to create a new exec driver
func NewRktDriver(ctx *DriverContext) Driver {
	return &RktDriver{DriverContext: *ctx}
}

func (d *RktDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) {
	// Only enable if we are root when running on non-windows systems.
	if runtime.GOOS != "windows" && syscall.Geteuid() != 0 {
		d.logger.Printf("[DEBUG] driver.rkt: must run as root user, disabling")
		return false, nil
	}

	outBytes, err := exec.Command("rkt", "version").Output()
	if err != nil {
		return false, nil
	}
	out := strings.TrimSpace(string(outBytes))

	rktMatches := reRktVersion.FindStringSubmatch(out)
	appcMatches := reAppcVersion.FindStringSubmatch(out)
	if len(rktMatches) != 2 || len(appcMatches) != 2 {
		return false, fmt.Errorf("Unable to parse Rkt version string: %#v", rktMatches)
	}

	node.Attributes["driver.rkt"] = "1"
	node.Attributes["driver.rkt.version"] = rktMatches[1]
	node.Attributes["driver.rkt.appc.version"] = appcMatches[1]

	minVersion, _ := version.NewVersion(minRktVersion)
	currentVersion, _ := version.NewVersion(node.Attributes["driver.rkt.version"])
	if currentVersion.LessThan(minVersion) {
		// Do not allow rkt < 0.14.0
		d.logger.Printf("[WARN] driver.rkt: please upgrade rkt to a version >= %s", minVersion)
		node.Attributes["driver.rkt"] = "0"
	}
	return true, nil
}

// Run an existing Rkt image.
func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) {
	var driverConfig RktDriverConfig
	if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil {
		return nil, err
	}
	// Validate that the config is valid.
	img := driverConfig.ImageName
	if img == "" {
		return nil, fmt.Errorf("Missing ACI image for rkt")
	}

	// Get the tasks local directory.
	taskName := d.DriverContext.taskName
	taskDir, ok := ctx.AllocDir.TaskDirs[taskName]
	if !ok {
		return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName)
	}

	// Build the command.
	var cmdArgs []string

	// Add the given trust prefix
	trustPrefix, trustCmd := task.Config["trust_prefix"]
	insecure := false
	if trustCmd {
		var outBuf, errBuf bytes.Buffer
		cmd := exec.Command("rkt", "trust", "--skip-fingerprint-review=true", fmt.Sprintf("--prefix=%s", trustPrefix))
		cmd.Stdout = &outBuf
		cmd.Stderr = &errBuf
		if err := cmd.Run(); err != nil {
			return nil, fmt.Errorf("Error running rkt trust: %s\n\nOutput: %s\n\nError: %s",
				err, outBuf.String(), errBuf.String())
		}
		d.logger.Printf("[DEBUG] driver.rkt: added trust prefix: %q", trustPrefix)
	} else {
		// Disble signature verification if the trust command was not run.
		insecure = true
	}

	local, ok := ctx.AllocDir.TaskDirs[task.Name]
	if !ok {
		return nil, fmt.Errorf("Failed to find task local directory: %v", task.Name)
	}

	cmdArgs = append(cmdArgs, "run", "--interactive")
	cmdArgs = append(cmdArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s", task.Name, local))
	cmdArgs = append(cmdArgs, fmt.Sprintf("--mount=volume=%s,target=%s", task.Name, ctx.AllocDir.SharedDir))
	cmdArgs = append(cmdArgs, img)
	if insecure == true {
		cmdArgs = append(cmdArgs, "--insecure-options=all")
	}

	// Inject enviornment variables
	for k, v := range d.taskEnv.EnvMap() {
		cmdArgs = append(cmdArgs, fmt.Sprintf("--set-env=%v=%v", k, v))
	}

	// Check if the user has overriden the exec command.
	if execCmd, ok := task.Config["command"]; ok {
		cmdArgs = append(cmdArgs, fmt.Sprintf("--exec=%v", execCmd))
	}

	if task.Resources.MemoryMB == 0 {
		return nil, fmt.Errorf("Memory limit cannot be zero")
	}
	if task.Resources.CPU == 0 {
		return nil, fmt.Errorf("CPU limit cannot be zero")
	}

	// Add memory isolator
	cmdArgs = append(cmdArgs, fmt.Sprintf("--memory=%vM", int64(task.Resources.MemoryMB)*bytesToMB))

	// Add CPU isolator
	cmdArgs = append(cmdArgs, fmt.Sprintf("--cpu=%vm", int64(task.Resources.CPU)))

	// Add user passed arguments.
	if len(driverConfig.Args) != 0 {
		parsed := d.taskEnv.ParseAndReplace(driverConfig.Args)

		// Need to start arguments with "--"
		if len(parsed) > 0 {
			cmdArgs = append(cmdArgs, "--")
		}

		for _, arg := range parsed {
			cmdArgs = append(cmdArgs, fmt.Sprintf("%v", arg))
		}
	}

	bin, err := discover.NomadExecutable()
	if err != nil {
		return nil, fmt.Errorf("unable to find the nomad binary: %v", err)
	}

	pluginLogFile := filepath.Join(taskDir, fmt.Sprintf("%s-executor.out", task.Name))
	pluginConfig := &plugin.ClientConfig{
		Cmd: exec.Command(bin, "executor", pluginLogFile),
	}

	exec, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput, d.config)
	if err != nil {
		return nil, err
	}
	executorCtx := &executor.ExecutorContext{
		TaskEnv:          d.taskEnv,
		AllocDir:         ctx.AllocDir,
		TaskName:         task.Name,
		TaskResources:    task.Resources,
		UnprivilegedUser: false,
		LogConfig:        task.LogConfig,
	}

	ps, err := exec.LaunchCmd(&executor.ExecCommand{Cmd: "rkt", Args: cmdArgs}, executorCtx)
	if err != nil {
		pluginClient.Kill()
		return nil, fmt.Errorf("error starting process via the plugin: %v", err)
	}

	d.logger.Printf("[DEBUG] driver.rkt: started ACI %q with: %v", img, cmdArgs)
	h := &rktHandle{
		pluginClient: pluginClient,
		executor:     exec,
		executorPid:  ps.Pid,
		logger:       d.logger,
		killTimeout:  d.DriverContext.KillTimeout(task),
		doneCh:       make(chan struct{}),
		waitCh:       make(chan *cstructs.WaitResult, 1),
	}
	go h.run()
	return h, nil
}

func (d *RktDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) {
	// Parse the handle
	pidBytes := []byte(strings.TrimPrefix(handleID, "Rkt:"))
	qpid := &rktPID{}
	if err := json.Unmarshal(pidBytes, qpid); err != nil {
		return nil, fmt.Errorf("failed to parse Rkt handle '%s': %v", handleID, err)
	}

	pluginConfig := &plugin.ClientConfig{
		Reattach: qpid.PluginConfig.PluginConfig(),
	}
	executor, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput, d.config)
	if err != nil {
		d.logger.Println("[ERROR] driver.rkt: error connecting to plugin so destroying plugin pid and user pid")
		if e := destroyPlugin(qpid.PluginConfig.Pid, qpid.ExecutorPid); e != nil {
			d.logger.Printf("[ERROR] driver.rkt: error destroying plugin and executor pid: %v", e)
		}
		return nil, fmt.Errorf("error connecting to plugin: %v", err)
	}

	// Return a driver handle
	h := &rktHandle{
		pluginClient: pluginClient,
		executorPid:  qpid.ExecutorPid,
		allocDir:     qpid.AllocDir,
		executor:     executor,
		logger:       d.logger,
		killTimeout:  qpid.KillTimeout,
		doneCh:       make(chan struct{}),
		waitCh:       make(chan *cstructs.WaitResult, 1),
	}

	go h.run()
	return h, nil
}

func (h *rktHandle) ID() string {
	// Return a handle to the PID
	pid := &rktPID{
		PluginConfig: NewPluginReattachConfig(h.pluginClient.ReattachConfig()),
		KillTimeout:  h.killTimeout,
		ExecutorPid:  h.executorPid,
		AllocDir:     h.allocDir,
	}
	data, err := json.Marshal(pid)
	if err != nil {
		h.logger.Printf("[ERR] driver.rkt: failed to marshal rkt PID to JSON: %s", err)
	}
	return fmt.Sprintf("Rkt:%s", string(data))
}

func (h *rktHandle) WaitCh() chan *cstructs.WaitResult {
	return h.waitCh
}

func (h *rktHandle) Update(task *structs.Task) error {
	// Store the updated kill timeout.
	h.killTimeout = task.KillTimeout
	h.executor.UpdateLogConfig(task.LogConfig)

	// Update is not possible
	return nil
}

// Kill is used to terminate the task. We send an Interrupt
// and then provide a 5 second grace period before doing a Kill.
func (h *rktHandle) Kill() error {
	h.executor.ShutDown()
	select {
	case <-h.doneCh:
		return nil
	case <-time.After(h.killTimeout):
		return h.executor.Exit()
	}
}

func (h *rktHandle) run() {
	ps, err := h.executor.Wait()
	close(h.doneCh)
	if ps.ExitCode == 0 && err != nil {
		if e := killProcess(h.executorPid); e != nil {
			h.logger.Printf("[ERROR] driver.rkt: error killing user process: %v", e)
		}
		if e := h.allocDir.UnmountAll(); e != nil {
			h.logger.Printf("[ERROR] driver.rkt: unmounting dev,proc and alloc dirs failed: %v", e)
		}
	}
	h.waitCh <- &cstructs.WaitResult{ExitCode: ps.ExitCode, Signal: 0, Err: err}
	close(h.waitCh)
	h.pluginClient.Kill()
}
<|endoftext|>


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

// +build darwin
// +build arm arm64

package app

/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Foundation -framework UIKit -framework GLKit -framework OpenGLES -framework QuartzCore
#include <sys/utsname.h>
#include <stdint.h>
#include <pthread.h>

extern struct utsname sysInfo;

void runApp(void);
void setContext(void* context);
uint64_t threadID();
*/
import "C"
import (
	"log"
	"runtime"
	"sync"
	"unsafe"

	"golang.org/x/mobile/event"
	"golang.org/x/mobile/geom"
	"golang.org/x/mobile/gl"
)

var initThreadID uint64

func init() {
	// Lock the goroutine responsible for initialization to an OS thread.
	// This means the goroutine running main (and calling the run function
	// below) is locked to the OS thread that started the program. This is
	// necessary for the correct delivery of UIKit events to the process.
	//
	// A discussion on this topic:
	// https://groups.google.com/forum/#!msg/golang-nuts/IiWZ2hUuLDA/SNKYYZBelsYJ
	runtime.LockOSThread()
	initThreadID = uint64(C.threadID())
}

func run(cbs []Callbacks) {
	if tid := uint64(C.threadID()); tid != initThreadID {
		log.Fatalf("app.Run called on thread %d, but app.init ran on %d", tid, initThreadID)
	}
	close(mainCalled)
	callbacks = cbs
	C.runApp()
}

// TODO(crawshaw): determine minimum iOS version and remove irrelevant devices.
var machinePPI = map[string]int{
	"i386":      163, // simulator
	"x86_64":    163, // simulator
	"iPod1,1":   163, // iPod Touch gen1
	"iPod2,1":   163, // iPod Touch gen2
	"iPod3,1":   163, // iPod Touch gen3
	"iPod4,1":   326, // iPod Touch gen4
	"iPod5,1":   326, // iPod Touch gen5
	"iPhone1,1": 163, // iPhone
	"iPhone1,2": 163, // iPhone 3G
	"iPhone2,1": 163, // iPhone 3GS
	"iPad1,1":   132, // iPad gen1
	"iPad2,1":   132, // iPad gen2
	"iPad2,2":   132, // iPad gen2 GSM
	"iPad2,3":   132, // iPad gen2 CDMA
	"iPad2,4":   132, // iPad gen2
	"iPad2,5":   163, // iPad Mini gen1
	"iPad2,6":   163, // iPad Mini gen1 AT&T
	"iPad2,7":   163, // iPad Mini gen1 VZ
	"iPad3,1":   264, // iPad gen3
	"iPad3,2":   264, // iPad gen3 VZ
	"iPad3,3":   264, // iPad gen3 AT&T
	"iPad3,4":   264, // iPad gen4
	"iPad3,5":   264, // iPad gen4 AT&T
	"iPad3,6":   264, // iPad gen4 VZ
	"iPad4,1":   264, // iPad Air wifi
	"iPad4,2":   264, // iPad Air LTE
	"iPad4,3":   264, // iPad Air LTE China
	"iPad4,4":   326, // iPad Mini gen2 wifi
	"iPad4,5":   326, // iPad Mini gen2 LTE
	"iPad4,6":   326, // iPad Mini 3
	"iPad4,7":   326, // iPad Mini 3
	"iPhone3,1": 326, // iPhone 4
	"iPhone4,1": 326, // iPhone 4S
	"iPhone5,1": 326, // iPhone 5
	"iPhone5,2": 326, // iPhone 5
	"iPhone5,3": 326, // iPhone 5c
	"iPhone5,4": 326, // iPhone 5c
	"iPhone6,1": 326, // iPhone 5s
	"iPhone6,2": 326, // iPhone 5s
	"iPhone7,1": 401, // iPhone 6 Plus
	"iPhone7,2": 326, // iPhone 6
}

func ppi() int {
	C.uname(&C.sysInfo)
	name := C.GoString(&C.sysInfo.machine[0])
	v, ok := machinePPI[name]
	if !ok {
		log.Printf("unknown machine: %s", name)
		v = 163 // emergency fallback
	}
	return v
}

//export setGeom
func setGeom(width, height int) {
	if geom.PixelsPerPt == 0 {
		geom.PixelsPerPt = float32(ppi()) / 72
	}
	configAlt.Width = geom.Pt(float32(width) / geom.PixelsPerPt)
	configAlt.Height = geom.Pt(float32(height) / geom.PixelsPerPt)
	configSwap(callbacks)
}

var startedgl = false

// touchIDs is the current active touches. The position in the array
// is the ID, the value is the UITouch* pointer value.
//
// It is widely reported that the iPhone can handle up to 5 simultaneous
// touch events, while the iPad can handle 11.
var touchIDs [11]uintptr

var touchEvents struct {
	sync.Mutex
	pending []event.Touch
}

//export sendTouch
func sendTouch(touch uintptr, touchType int, x, y float32) {
	id := -1
	for i, val := range touchIDs {
		if val == touch {
			id = i
			break
		}
	}
	if id == -1 {
		for i, val := range touchIDs {
			if val == 0 {
				touchIDs[i] = touch
				id = i
				break
			}
		}
		if id == -1 {
			panic("out of touchIDs")
		}
	}

	ty := event.TouchType(touchType)
	if ty == event.TouchEnd {
		touchIDs[id] = 0
	}

	touchEvents.Lock()
	touchEvents.pending = append(touchEvents.pending, event.Touch{
		ID:   event.TouchSequenceID(id),
		Type: ty,
		Loc: geom.Point{
			X: geom.Pt(x / geom.PixelsPerPt),
			Y: geom.Pt(y / geom.PixelsPerPt),
		},
	})
	touchEvents.Unlock()
}

//export drawgl
func drawgl(ctx uintptr) {
	if !startedgl {
		startedgl = true
		go gl.Start(func() {
			C.setContext(unsafe.Pointer(ctx))
		})
		stateStart(cb)
	}

	touchEvents.Lock()
	pending := touchEvents.pending
	touchEvents.pending = nil
	touchEvents.Unlock()
	for _, cb := range callbacks {
		if cb.Touch != nil {
			for _, e := range pending {
				cb.Touch(e)
			}
		}
	}

	// TODO not here?
	gl.ClearColor(0, 0, 0, 1)
	gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
	for _, cb := range callbacks {
		if cb.Draw != nil {
			cb.Draw()
		}
	}
}
<commit_msg>app: fix broken build for armx.<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build darwin
// +build arm arm64

package app

/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Foundation -framework UIKit -framework GLKit -framework OpenGLES -framework QuartzCore
#include <sys/utsname.h>
#include <stdint.h>
#include <pthread.h>

extern struct utsname sysInfo;

void runApp(void);
void setContext(void* context);
uint64_t threadID();
*/
import "C"
import (
	"log"
	"runtime"
	"sync"
	"unsafe"

	"golang.org/x/mobile/event"
	"golang.org/x/mobile/geom"
	"golang.org/x/mobile/gl"
)

var initThreadID uint64

func init() {
	// Lock the goroutine responsible for initialization to an OS thread.
	// This means the goroutine running main (and calling the run function
	// below) is locked to the OS thread that started the program. This is
	// necessary for the correct delivery of UIKit events to the process.
	//
	// A discussion on this topic:
	// https://groups.google.com/forum/#!msg/golang-nuts/IiWZ2hUuLDA/SNKYYZBelsYJ
	runtime.LockOSThread()
	initThreadID = uint64(C.threadID())
}

func run(cbs []Callbacks) {
	if tid := uint64(C.threadID()); tid != initThreadID {
		log.Fatalf("app.Run called on thread %d, but app.init ran on %d", tid, initThreadID)
	}
	close(mainCalled)
	callbacks = cbs
	C.runApp()
}

// TODO(crawshaw): determine minimum iOS version and remove irrelevant devices.
var machinePPI = map[string]int{
	"i386":      163, // simulator
	"x86_64":    163, // simulator
	"iPod1,1":   163, // iPod Touch gen1
	"iPod2,1":   163, // iPod Touch gen2
	"iPod3,1":   163, // iPod Touch gen3
	"iPod4,1":   326, // iPod Touch gen4
	"iPod5,1":   326, // iPod Touch gen5
	"iPhone1,1": 163, // iPhone
	"iPhone1,2": 163, // iPhone 3G
	"iPhone2,1": 163, // iPhone 3GS
	"iPad1,1":   132, // iPad gen1
	"iPad2,1":   132, // iPad gen2
	"iPad2,2":   132, // iPad gen2 GSM
	"iPad2,3":   132, // iPad gen2 CDMA
	"iPad2,4":   132, // iPad gen2
	"iPad2,5":   163, // iPad Mini gen1
	"iPad2,6":   163, // iPad Mini gen1 AT&T
	"iPad2,7":   163, // iPad Mini gen1 VZ
	"iPad3,1":   264, // iPad gen3
	"iPad3,2":   264, // iPad gen3 VZ
	"iPad3,3":   264, // iPad gen3 AT&T
	"iPad3,4":   264, // iPad gen4
	"iPad3,5":   264, // iPad gen4 AT&T
	"iPad3,6":   264, // iPad gen4 VZ
	"iPad4,1":   264, // iPad Air wifi
	"iPad4,2":   264, // iPad Air LTE
	"iPad4,3":   264, // iPad Air LTE China
	"iPad4,4":   326, // iPad Mini gen2 wifi
	"iPad4,5":   326, // iPad Mini gen2 LTE
	"iPad4,6":   326, // iPad Mini 3
	"iPad4,7":   326, // iPad Mini 3
	"iPhone3,1": 326, // iPhone 4
	"iPhone4,1": 326, // iPhone 4S
	"iPhone5,1": 326, // iPhone 5
	"iPhone5,2": 326, // iPhone 5
	"iPhone5,3": 326, // iPhone 5c
	"iPhone5,4": 326, // iPhone 5c
	"iPhone6,1": 326, // iPhone 5s
	"iPhone6,2": 326, // iPhone 5s
	"iPhone7,1": 401, // iPhone 6 Plus
	"iPhone7,2": 326, // iPhone 6
}

func ppi() int {
	C.uname(&C.sysInfo)
	name := C.GoString(&C.sysInfo.machine[0])
	v, ok := machinePPI[name]
	if !ok {
		log.Printf("unknown machine: %s", name)
		v = 163 // emergency fallback
	}
	return v
}

//export setGeom
func setGeom(width, height int) {
	if geom.PixelsPerPt == 0 {
		geom.PixelsPerPt = float32(ppi()) / 72
	}
	configAlt.Width = geom.Pt(float32(width) / geom.PixelsPerPt)
	configAlt.Height = geom.Pt(float32(height) / geom.PixelsPerPt)
	configSwap(callbacks)
}

var startedgl = false

// touchIDs is the current active touches. The position in the array
// is the ID, the value is the UITouch* pointer value.
//
// It is widely reported that the iPhone can handle up to 5 simultaneous
// touch events, while the iPad can handle 11.
var touchIDs [11]uintptr

var touchEvents struct {
	sync.Mutex
	pending []event.Touch
}

//export sendTouch
func sendTouch(touch uintptr, touchType int, x, y float32) {
	id := -1
	for i, val := range touchIDs {
		if val == touch {
			id = i
			break
		}
	}
	if id == -1 {
		for i, val := range touchIDs {
			if val == 0 {
				touchIDs[i] = touch
				id = i
				break
			}
		}
		if id == -1 {
			panic("out of touchIDs")
		}
	}

	ty := event.TouchType(touchType)
	if ty == event.TouchEnd {
		touchIDs[id] = 0
	}

	touchEvents.Lock()
	touchEvents.pending = append(touchEvents.pending, event.Touch{
		ID:   event.TouchSequenceID(id),
		Type: ty,
		Loc: geom.Point{
			X: geom.Pt(x / geom.PixelsPerPt),
			Y: geom.Pt(y / geom.PixelsPerPt),
		},
	})
	touchEvents.Unlock()
}

//export drawgl
func drawgl(ctx uintptr) {
	if !startedgl {
		startedgl = true
		go gl.Start(func() {
			C.setContext(unsafe.Pointer(ctx))
		})
		stateStart(callbacks)
	}

	touchEvents.Lock()
	pending := touchEvents.pending
	touchEvents.pending = nil
	touchEvents.Unlock()
	for _, cb := range callbacks {
		if cb.Touch != nil {
			for _, e := range pending {
				cb.Touch(e)
			}
		}
	}

	// TODO not here?
	gl.ClearColor(0, 0, 0, 1)
	gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
	for _, cb := range callbacks {
		if cb.Draw != nil {
			cb.Draw()
		}
	}
}
<|endoftext|>


<commit_before>// +build appengine

package log

import (
	"io"
)

func isatty(w io.Writer) bool {
	return false
}
<commit_msg>Add colored logging for GAE dev server<commit_after>// +build appengine

package log

import (
	"io"

	"gnd.la/internal"
)

func isatty(w io.Writer) bool {
	if internal.InAppEngineDevServer() {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package cli_test

import (
	"fmt"
	"github.com/qadium/plumber/cli"
	"io/ioutil"
	"os"
	"os/user"
	"testing"
	"runtime"
)

const testPlumberDir = "foo"

const testBootstrapDir = "boot"

const testKubeSubdir = "k8s"

// mock for cli context (used for testing)
// uses temp directories
func NewTestContext(t *testing.T) (*cli.Context, string) {
	// use the current user to store the plumb data
	usr, err := user.Current()
	if err != nil {
		t.Errorf("Got an error getting current user: '%v'", err)
	}

	tempDir, err := ioutil.TempDir(usr.HomeDir, "plumberTest")
	if err != nil {
		t.Errorf("Got an error constructing context: '%v'", err)
	}

	d := &cli.Context{
		fmt.Sprintf("%s/%s", tempDir, testPlumberDir),
		testKubeSubdir,
		"",
		"test-version",
		"manager",
		fmt.Sprintf("%s/%s", tempDir, testBootstrapDir),
		"plumber_test",
		"docker",
		"docker0",
		"DOCKER_HOST",
		"true",
		"true",
	}
	return d, tempDir
}

func cleanTestDir(t *testing.T, tempDir string) {
	if err := os.RemoveAll(tempDir); err != nil {
		t.Errorf("Had an issue removing the temp directory, '%v'", err)
	}
}

func TestPipelinePath(t *testing.T) {
	ctx, tempDir := NewTestContext(t)
	defer cleanTestDir(t, tempDir)

	expectedPath := fmt.Sprintf("%s/foobar", ctx.PipeDir)

	path := ctx.PipelinePath("foobar")
	if expectedPath != path {
		t.Error("PipelinePath: did not return expected path.")
	}
}

func TestGetPipeline(t *testing.T) {
	ctx, tempDir := NewTestContext(t)
	defer cleanTestDir(t, tempDir)

	expectedPath := fmt.Sprintf("%s/mypipe", ctx.PipeDir)
	// first, check that we fail with a "no such directory"
	path, err := ctx.GetPipeline("mypipe")
	if err == nil || err.Error() != fmt.Sprintf("stat %s: no such file or directory", expectedPath) {
		t.Errorf("We expected to fail with an error with no such directory, but got '%v' instead", err)
	}
	// make the expected directory and delete it after this test
	if err := os.MkdirAll(expectedPath, 0755); err != nil {
		t.Errorf("Encountered error making test directory '%s': '%v'", expectedPath, err)
	}
	defer os.RemoveAll(expectedPath)

	path, _ = ctx.GetPipeline("mypipe")
	if path != expectedPath {
		t.Error("GetPipeline: did not return expected path.")
	}
}

func TestKubernetesPath(t *testing.T) {
	ctx, tempDir := NewTestContext(t)
	defer cleanTestDir(t, tempDir)

	expectedPath := fmt.Sprintf("%s/barbaz/k8s", ctx.PipeDir)

	path := ctx.KubernetesPath("barbaz")
	if expectedPath != path {
		t.Error("KubernetesPath: did not return expected path.")
	}
}

func TestDefaultContext(t *testing.T) {
	usr, err := user.Current()
	if err != nil {
		t.Errorf("DefaultContext: Got an error getting current user: '%v'", err)
	}

	ctx, err := cli.NewDefaultContext()
	if err != nil {
		t.Errorf("DefaultContext: Got error '%v'", err)
	}

	if ctx.PipeDir != fmt.Sprintf("%s/.plumber", usr.HomeDir) ||
		ctx.KubeSubdir != "k8s" || ctx.ManagerImage != "manager" ||
		ctx.BootstrapDir != fmt.Sprintf("%s/.plumber-bootstrap", usr.HomeDir) ||
		ctx.ImageRepo != "plumber" || ctx.DockerCmd != "docker" ||
		ctx.DockerIface != "docker0" || ctx.DockerHostEnv != "DOCKER_HOST" ||
		ctx.GcloudCmd != "gcloud" || ctx.KubectlCmd != "kubectl" {
		t.Errorf("DefaultContext: '%v' was not expected.", ctx)
	}
}

func TestGetManagerImage(t *testing.T) {
	ctx, tempDir := NewTestContext(t)
	defer cleanTestDir(t, tempDir)

	imageName := ctx.GetManagerImage()
	if imageName != "plumber_test/manager" {
		t.Error("GetManagerImage: did not return expected image name.")
	}
}

func TestGetImage(t *testing.T) {
	ctx, tempDir := NewTestContext(t)
	defer cleanTestDir(t, tempDir)

	imageName := ctx.GetImage("whatnot")
	if imageName != "plumber_test/whatnot" {
		t.Error("GetImage: did not return expected image name.")
	}
}

func TestGetImageWithNoImageRepo(t *testing.T) {
	ctx, tempDir := NewTestContext(t)
	ctx.ImageRepo = ""
	defer cleanTestDir(t, tempDir)

	imageName := ctx.GetImage("IAmReallyHere")
	if imageName != "IAmReallyHere" {
		t.Error("GetImageWithNoImageRepo: did not return expected image name.")
	}
}

func TestGetDockerHostFail(t *testing.T) {
	ctx, tempDir := NewTestContext(t)
	defer cleanTestDir(t, tempDir)
	ctx.DockerHostEnv = "I_AM_AN_ENV_THAT_DOESNT_EXIST_***"
	ctx.DockerIface = "reallyYouHaveAnIfaceWithThisName?"

	hostIp, err := ctx.GetDockerHost()
	if hostIp != "" || err == nil {
		t.Errorf("GetDockerHostFail: did not fail as expected")
	}
	if err.Error() != "no such network interface" {
		t.Errorf("GetDockerHostFail: got an unexpected error '%v'", err)
	}
}

func TestGetDockerHostWithDockerHostEnv(t *testing.T) {
	ctx, tempDir := NewTestContext(t)
	defer cleanTestDir(t, tempDir)
	if err := os.Setenv("PLUMBER_TEST_ENV", "http://127.0.0.1"); err != nil {
		t.Errorf("GetDockerHostWithDockerHostEnv: did not set test env variable, '%v'.", err)
	}
	defer os.Unsetenv("PLUMBER_TEST_ENV")
	ctx.DockerHostEnv = "PLUMBER_TEST_ENV"

	hostIp, err := ctx.GetDockerHost()
	if err != nil {
		t.Errorf("GetDockerHostWithDockerHostEnv: got unexpected error '%v'.", err)
	}
	if hostIp != "127.0.0.1" {
		t.Errorf("GetDockerHostWithDockerHostEnv: did not get expected IP. Got '%s' instead.", hostIp)
	}
}

func TestGetDockerHostWithDockerIface(t *testing.T) {
	ctx, tempDir := NewTestContext(t)
	defer cleanTestDir(t, tempDir)
	ctx.DockerHostEnv = "I_AM_AN_ENV_THAT_DOESNT_EXIST_***"
	if runtime.GOOS == "darwin" {
		ctx.DockerIface = "lo0"
	} else if runtime.GOOS == "linux" {
		ctx.DockerIface = "lo"
	} else {
		t.Skipf("GetDockerHostWithDockerIface: skipping test for this os '%s'", runtime.GOOS)
	}

	hostIp, err := ctx.GetDockerHost()
	if err != nil {
		t.Errorf("GetDockerHostWithDockerIface: got unexpected error '%v'.", err)
	}
	if hostIp != "127.0.0.1" {
		t.Errorf("GetDockerHostWithDockerIface: did not get expected IP.")
	}
}
<commit_msg>updated utils_test<commit_after>package cli_test

import (
	"fmt"
	"github.com/qadium/plumber/cli"
	"io/ioutil"
	"os"
	"os/user"
	"testing"
	"runtime"
	"strings"
)

const testPlumberDir = "foo"

const testBootstrapDir = "boot"

const testKubeSubdir = "k8s"

// mock for cli context (used for testing)
// uses temp directories
func NewTestContext(t *testing.T) (*cli.Context, string) {
	// use the current user to store the plumb data
	usr, err := user.Current()
	if err != nil {
		t.Errorf("Got an error getting current user: '%v'", err)
	}

	tempDir, err := ioutil.TempDir(usr.HomeDir, "plumberTest")
	if err != nil {
		t.Errorf("Got an error constructing context: '%v'", err)
	}

	d := &cli.Context{
		fmt.Sprintf("%s/%s", tempDir, testPlumberDir),
		testKubeSubdir,
		"",
		"test-version",
		"manager",
		fmt.Sprintf("%s/%s", tempDir, testBootstrapDir),
		"plumber_test",
		"docker",
		"docker0",
		"DOCKER_HOST",
		"true",
		"true",
	}
	return d, tempDir
}

func cleanTestDir(t *testing.T, tempDir string) {
	if err := os.RemoveAll(tempDir); err != nil {
		t.Errorf("Had an issue removing the temp directory, '%v'", err)
	}
}

func TestPipelinePath(t *testing.T) {
	ctx, tempDir := NewTestContext(t)
	defer cleanTestDir(t, tempDir)

	expectedPath := fmt.Sprintf("%s/foobar", ctx.PipeDir)

	path := ctx.PipelinePath("foobar")
	if expectedPath != path {
		t.Error("PipelinePath: did not return expected path.")
	}
}

func TestGetPipeline(t *testing.T) {
	ctx, tempDir := NewTestContext(t)
	defer cleanTestDir(t, tempDir)

	expectedPath := fmt.Sprintf("%s/mypipe", ctx.PipeDir)
	// first, check that we fail with a "no such directory"
	path, err := ctx.GetPipeline("mypipe")
	if err == nil || err.Error() != fmt.Sprintf("stat %s: no such file or directory", expectedPath) {
		t.Errorf("We expected to fail with an error with no such directory, but got '%v' instead", err)
	}
	// make the expected directory and delete it after this test
	if err := os.MkdirAll(expectedPath, 0755); err != nil {
		t.Errorf("Encountered error making test directory '%s': '%v'", expectedPath, err)
	}
	defer os.RemoveAll(expectedPath)

	path, _ = ctx.GetPipeline("mypipe")
	if path != expectedPath {
		t.Error("GetPipeline: did not return expected path.")
	}
}

func TestKubernetesPath(t *testing.T) {
	ctx, tempDir := NewTestContext(t)
	defer cleanTestDir(t, tempDir)

	expectedPath := fmt.Sprintf("%s/barbaz/k8s", ctx.PipeDir)

	path := ctx.KubernetesPath("barbaz")
	if expectedPath != path {
		t.Error("KubernetesPath: did not return expected path.")
	}
}

func TestDefaultContext(t *testing.T) {
	usr, err := user.Current()
	if err != nil {
		t.Errorf("DefaultContext: Got an error getting current user: '%v'", err)
	}

	ctx, err := cli.NewDefaultContext()
	if err != nil {
		t.Errorf("DefaultContext: Got error '%v'", err)
	}

	if ctx.PipeDir != fmt.Sprintf("%s/.plumber", usr.HomeDir) ||
		ctx.KubeSubdir != "k8s" || ctx.ManagerImage != "manager" ||
		ctx.BootstrapDir != fmt.Sprintf("%s/.plumber-bootstrap", usr.HomeDir) ||
		ctx.ImageRepo != "plumber" || ctx.DockerCmd != "docker" ||
		ctx.DockerIface != "docker0" || ctx.DockerHostEnv != "DOCKER_HOST" ||
		ctx.GcloudCmd != "gcloud" || ctx.KubectlCmd != "kubectl" {
		t.Errorf("DefaultContext: '%v' was not expected.", ctx)
	}
}

func TestGetManagerImage(t *testing.T) {
	ctx, tempDir := NewTestContext(t)
	defer cleanTestDir(t, tempDir)

	imageName := ctx.GetManagerImage()
	if imageName != "plumber_test/manager" {
		t.Error("GetManagerImage: did not return expected image name.")
	}
}

func TestGetImage(t *testing.T) {
	ctx, tempDir := NewTestContext(t)
	defer cleanTestDir(t, tempDir)

	imageName := ctx.GetImage("whatnot")
	if imageName != "plumber_test/whatnot" {
		t.Error("GetImage: did not return expected image name.")
	}
}

func TestGetImageWithNoImageRepo(t *testing.T) {
	ctx, tempDir := NewTestContext(t)
	ctx.ImageRepo = ""
	defer cleanTestDir(t, tempDir)

	imageName := ctx.GetImage("IAmReallyHere")
	if imageName != "IAmReallyHere" {
		t.Error("GetImageWithNoImageRepo: did not return expected image name.")
	}
}

func TestGetDockerHostFail(t *testing.T) {
	ctx, tempDir := NewTestContext(t)
	defer cleanTestDir(t, tempDir)
	ctx.DockerHostEnv = "I_AM_AN_ENV_THAT_DOESNT_EXIST_***"
	ctx.DockerIface = "reallyYouHaveAnIfaceWithThisName?"

	hostIp, err := ctx.GetDockerHost()
	if hostIp != "" || err == nil {
		t.Errorf("GetDockerHostFail: did not fail as expected")
	}
	if !strings.Contains(err.Error(), "no such network interface") {
		t.Errorf("GetDockerHostFail: got an unexpected error '%v'", err)
	}
}

func TestGetDockerHostWithDockerHostEnv(t *testing.T) {
	ctx, tempDir := NewTestContext(t)
	defer cleanTestDir(t, tempDir)
	if err := os.Setenv("PLUMBER_TEST_ENV", "http://127.0.0.1"); err != nil {
		t.Errorf("GetDockerHostWithDockerHostEnv: did not set test env variable, '%v'.", err)
	}
	defer os.Unsetenv("PLUMBER_TEST_ENV")
	ctx.DockerHostEnv = "PLUMBER_TEST_ENV"

	hostIp, err := ctx.GetDockerHost()
	if err != nil {
		t.Errorf("GetDockerHostWithDockerHostEnv: got unexpected error '%v'.", err)
	}
	if hostIp != "127.0.0.1" {
		t.Errorf("GetDockerHostWithDockerHostEnv: did not get expected IP. Got '%s' instead.", hostIp)
	}
}

func TestGetDockerHostWithDockerIface(t *testing.T) {
	ctx, tempDir := NewTestContext(t)
	defer cleanTestDir(t, tempDir)
	ctx.DockerHostEnv = "I_AM_AN_ENV_THAT_DOESNT_EXIST_***"
	if runtime.GOOS == "darwin" {
		ctx.DockerIface = "lo0"
	} else if runtime.GOOS == "linux" {
		ctx.DockerIface = "lo"
	} else {
		t.Skipf("GetDockerHostWithDockerIface: skipping test for this os '%s'", runtime.GOOS)
	}

	hostIp, err := ctx.GetDockerHost()
	if err != nil {
		t.Errorf("GetDockerHostWithDockerIface: got unexpected error '%v'.", err)
	}
	if hostIp != "127.0.0.1" {
		t.Errorf("GetDockerHostWithDockerIface: did not get expected IP.")
	}
}
<|endoftext|>


<commit_before>// Comparing the speeds of the golang native regex library and rubex.
// The numbers show a dramatic difference, with rubex being nearly 400
// times slower than the native go libraries.  Unfortunately for us,
// the native go libraries have a different regex behavior than rubex,
// so we'll have to hack at it a bit to fit our needs if we decide to use it.
// (which we should, I mean, come on, 400 times faster?  That's mad wins.)

package main

import re "rubex"
import "time"
import "regexp"
import "runtime"
import "os"
import "strconv"

var re1 []Matcher
var re2 []Matcher
const NUM = 100
const NNN = 1000
var STR = "abcdabc"

type Matcher interface {
	MatchString(string) bool
}

type Task struct {
	str string
	m Matcher
	t time.Time
}

var TaskChann chan *Task

func init() {
	re1 = make([]Matcher, NUM)
	re2 = make([]Matcher, NUM)
	for i := 0; i < NUM; i ++ {
		re1[i] = regexp.MustCompile("[a-c]*$")
		re2[i] = re.MustCompile("[a-c]*$")
	}
	TaskChann = make(chan *Task, 100)
	for i := 0; i < 10; i ++ {
		STR += STR
	}
	println("len:", len(STR))
}

func render_pages(name string, marray []Matcher, num_routines, num_renders int) {
	for i := 0; i < num_routines; i++ {
		m := marray[i]
		go func () {
			runtime.LockOSThread()
			for j := 0; j < num_renders; j++ {
				var totalDuration int64 = 0
				for i := 0; i < NNN; i++ {
					t := time.Now()
					m.MatchString(STR)
					totalDuration += time.Since(t).Nanoseconds()
				}
				println(name + "-average: ",  totalDuration/int64(1000*NNN), "us")
			}
		}()
	}
}

func render_pages2(name string, marray []Matcher, num_routines, num_renders int) {
	go func() {
		for i := 0; i < 100000; i ++ {
			t := &Task{str: STR, m: marray[0], t: time.Now()}
			TaskChann <- t
		}
	}()
	for i := 0; i < num_routines; i++ {
		m := marray[i]
		go func () {
			runtime.LockOSThread()
			for j := 0; j < num_renders; j++ {
				var totalDuration int64 = 0
				for i := 0; i < NNN; i++ {
					task := <-TaskChann
					m.MatchString(task.str)
					totalDuration += time.Since(task.t).Nanoseconds()
				}
				println(name + "-average: ",  totalDuration/int64(1000*NNN), "us")
			}
		}()
	}
}

func main() {
	cpu, _ := strconv.Atoi(os.Args[1])
	lib := os.Args[2]
	println("using CPUs:", cpu)
	runtime.GOMAXPROCS(cpu)
	num_routines := 2
	num_renders := 20

	if lib == "rubex" {
		render_pages2("rubex", re2, num_routines, num_renders)
	} else {
		render_pages2("regexp", re1, num_routines, num_renders)
	}

	d, _ := time.ParseDuration("5s")
	for i := 0; i < 100; i ++ {
		println("goroutine:", runtime.NumGoroutine())
		time.Sleep(d)

	}
	println ("Done")
}

<commit_msg>more updates<commit_after>// Comparing the speeds of the golang native regex library and rubex.
// The numbers show a dramatic difference, with rubex being nearly 400
// times slower than the native go libraries.  Unfortunately for us,
// the native go libraries have a different regex behavior than rubex,
// so we'll have to hack at it a bit to fit our needs if we decide to use it.
// (which we should, I mean, come on, 400 times faster?  That's mad wins.)

package main

import re "rubex"
import "time"
import "regexp"
import "runtime"
import "os"
import "strconv"
import "sync"

var mu sync.Mutex
var count = 0
var re1 []Matcher
var re2 []Matcher
const NUM = 100
const NNN = 1000
const CCC = 100000
var STR = "abcdabc"

type Matcher interface {
	MatchString(string) bool
}

type Task struct {
	str string
	m Matcher
	t time.Time
}

var TaskChann chan *Task

func init() {
	re1 = make([]Matcher, NUM)
	re2 = make([]Matcher, NUM)
	for i := 0; i < NUM; i ++ {
		re1[i] = regexp.MustCompile("[a-c]*$")
		re2[i] = re.MustCompile("[a-c]*$")
	}
	TaskChann = make(chan *Task, 100)
	for i := 0; i < 10; i ++ {
		STR += STR
	}
	println("len:", len(STR))
}

func render_pages(name string, marray []Matcher, num_routines, num_renders int) {
	for i := 0; i < num_routines; i++ {
		m := marray[i]
		go func () {
			runtime.LockOSThread()
			for j := 0; j < num_renders; j++ {
				var totalDuration int64 = 0
				for i := 0; i < NNN; i++ {
					t := time.Now()
					mu.Lock()
					if count > CCC {
						mu.Unlock()
						return
					}
					count += 1
					m.MatchString(STR)
					mu.Unlock()
					totalDuration += time.Since(t).Nanoseconds()
				}
				println(name + "-average: ",  totalDuration/int64(1000*NNN), "us")
			}
		}()
	}
}

func render_pages2(name string, marray []Matcher, num_routines, num_renders int) {
	go func() {
		for i := 0; i < CCC; i ++ {
			t := &Task{str: STR, m: marray[0], t: time.Now()}
			TaskChann <- t
		}
	}()
	for i := 0; i < num_routines; i++ {
		m := marray[i]
		go func () {
			runtime.LockOSThread()
			for j := 0; j < num_renders; j++ {
				var totalDuration int64 = 0
				for i := 0; i < NNN; i++ {
					task := <-TaskChann
					m.MatchString(task.str)
					totalDuration += time.Since(task.t).Nanoseconds()
				}
				println(name + "-average: ",  totalDuration/int64(1000*NNN), "us")
			}
		}()
	}
}

func main() {
	cpu, _ := strconv.Atoi(os.Args[1])
	lib := os.Args[2]
	method := os.Args[3]
	println("using CPUs:", cpu)
	runtime.GOMAXPROCS(cpu)
	num_routines := 6
	num_renders := 20

	if method == "chan" {
	if lib == "rubex" {
		render_pages2("rubex", re2, num_routines, num_renders)
	} else {
		render_pages2("regexp", re1, num_routines, num_renders)
	}
	} else {
	if lib == "rubex" {
		render_pages("rubex", re2, num_routines, num_renders)
	} else {
		render_pages("regexp", re1, num_routines, num_renders)
	}

	}
	d, _ := time.ParseDuration("5s")
	for i := 0; i < 100; i ++ {
		println("goroutine:", runtime.NumGoroutine())
		time.Sleep(d)

	}
	println ("Done")
}

<|endoftext|>


<commit_before>package terraform

import (
	"fmt"
	"log"
	"strings"

	"github.com/hashicorp/terraform/config"
)

// EvalCompareDiff is an EvalNode implementation that compares two diffs
// and errors if the diffs are not equal.
type EvalCompareDiff struct {
	Info     *InstanceInfo
	One, Two **InstanceDiff
}

// TODO: test
func (n *EvalCompareDiff) Eval(ctx EvalContext) (interface{}, error) {
	one, two := *n.One, *n.Two

	// If either are nil, let them be empty
	if one == nil {
		one = new(InstanceDiff)
		one.init()
	}
	if two == nil {
		two = new(InstanceDiff)
		two.init()
	}
	oneId := one.Attributes["id"]
	twoId := two.Attributes["id"]
	delete(one.Attributes, "id")
	delete(two.Attributes, "id")
	defer func() {
		if oneId != nil {
			one.Attributes["id"] = oneId
		}
		if twoId != nil {
			two.Attributes["id"] = twoId
		}
	}()

	if same, reason := one.Same(two); !same {
		log.Printf("[ERROR] %s: diffs didn't match", n.Info.Id)
		log.Printf("[ERROR] %s: reason: %s", n.Info.Id, reason)
		log.Printf("[ERROR] %s: diff one: %#v", n.Info.Id, one)
		log.Printf("[ERROR] %s: diff two: %#v", n.Info.Id, two)
		return nil, fmt.Errorf(
			"%s: diffs didn't match during apply. This is a bug with "+
				"Terraform and should be reported as a GitHub Issue.\n"+
				"\n"+
				"Please include the following information in your report:\n"+
				"\n"+
				"    Terraform Version: %s\n"+
				"    Resource ID: %s\n"+
				"    Mismatch reason: %s\n"+
				"    Diff One (usually from plan): %#v\n"+
				"    Diff Two (usually from apply): %#v\n"+
				"\n"+
				"Also include as much context as you can about your config, state, "+
				"and the steps you performed to trigger this error.\n",
			n.Info.Id, Version, n.Info.Id, reason, one, two)
	}

	return nil, nil
}

// EvalDiff is an EvalNode implementation that does a refresh for
// a resource.
type EvalDiff struct {
	Info        *InstanceInfo
	Config      **ResourceConfig
	Provider    *ResourceProvider
	Diff        **InstanceDiff
	State       **InstanceState
	OutputDiff  **InstanceDiff
	OutputState **InstanceState

	// Resource is needed to fetch the ignore_changes list so we can
	// filter user-requested ignored attributes from the diff.
	Resource *config.Resource
}

// TODO: test
func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
	state := *n.State
	config := *n.Config
	provider := *n.Provider

	// Call pre-diff hook
	err := ctx.Hook(func(h Hook) (HookAction, error) {
		return h.PreDiff(n.Info, state)
	})
	if err != nil {
		return nil, err
	}

	// The state for the diff must never be nil
	diffState := state
	if diffState == nil {
		diffState = new(InstanceState)
	}
	diffState.init()

	// Diff!
	diff, err := provider.Diff(n.Info, diffState, config)
	if err != nil {
		return nil, err
	}
	if diff == nil {
		diff = new(InstanceDiff)
	}

	// Preserve the DestroyTainted flag
	if n.Diff != nil {
		diff.DestroyTainted = (*n.Diff).DestroyTainted
	}

	// Require a destroy if there is an ID and it requires new.
	if diff.RequiresNew() && state != nil && state.ID != "" {
		diff.Destroy = true
	}

	// If we're creating a new resource, compute its ID
	if diff.RequiresNew() || state == nil || state.ID == "" {
		var oldID string
		if state != nil {
			oldID = state.Attributes["id"]
		}

		// Add diff to compute new ID
		diff.init()
		diff.Attributes["id"] = &ResourceAttrDiff{
			Old:         oldID,
			NewComputed: true,
			RequiresNew: true,
			Type:        DiffAttrOutput,
		}
	}

	if err := n.processIgnoreChanges(diff); err != nil {
		return nil, err
	}

	// Call post-refresh hook
	err = ctx.Hook(func(h Hook) (HookAction, error) {
		return h.PostDiff(n.Info, diff)
	})
	if err != nil {
		return nil, err
	}

	// Update our output
	*n.OutputDiff = diff

	// Update the state if we care
	if n.OutputState != nil {
		*n.OutputState = state

		// Merge our state so that the state is updated with our plan
		if !diff.Empty() && n.OutputState != nil {
			*n.OutputState = state.MergeDiff(diff)
		}
	}

	return nil, nil
}

func (n *EvalDiff) processIgnoreChanges(diff *InstanceDiff) error {
	if diff == nil || n.Resource == nil || n.Resource.Id() == "" {
		return nil
	}
	ignoreChanges := n.Resource.Lifecycle.IgnoreChanges

	if len(ignoreChanges) == 0 {
		return nil
	}

	changeType := diff.ChangeType()

	// If we're just creating the resource, we shouldn't alter the
	// Diff at all
	if changeType == DiffCreate {
		return nil
	}

	ignorableAttrKeys := make(map[string]bool)
	for _, ignoredKey := range ignoreChanges {
		for k := range diff.Attributes {
			if strings.HasPrefix(k, ignoredKey) {
				ignorableAttrKeys[k] = true
			}
		}
	}

	// If we are replacing the resource, then we expect there to be a bunch of
	// extraneous attribute diffs we need to filter out for the other
	// non-requires-new attributes going from "" -> "configval" or "" ->
	// "<computed>". Filtering these out allows us to see if we might be able to
	// skip this diff altogether.
	if changeType == DiffDestroyCreate {
		for k, v := range diff.Attributes {
			if v.Empty() || v.NewComputed {
				ignorableAttrKeys[k] = true
			}
		}

		// Here we emulate the implementation of diff.RequiresNew() with one small
		// tweak, we ignore the "id" attribute diff that gets added by EvalDiff,
		// since that was added in reaction to RequiresNew being true.
		requiresNewAfterIgnores := false
		for k, v := range diff.Attributes {
			if k == "id" {
				continue
			}
			if _, ok := ignorableAttrKeys[k]; ok {
				continue
			}
			if v.RequiresNew == true {
				requiresNewAfterIgnores = true
			}
		}

		// If we still require resource replacement after ignores, we
		// can't touch the diff, as all of the attributes will be
		// required to process the replacement.
		if requiresNewAfterIgnores {
			return nil
		}

		// Here we undo the two reactions to RequireNew in EvalDiff - the "id"
		// attribute diff and the Destroy boolean field
		log.Printf("[DEBUG] Removing 'id' diff and setting Destroy to false " +
			"because after ignore_changes, this diff no longer requires replacement")
		delete(diff.Attributes, "id")
		diff.Destroy = false
	}

	// If we didn't hit any of our early exit conditions, we can filter the diff.
	for k := range ignorableAttrKeys {
		log.Printf("[DEBUG] [EvalIgnoreChanges] %s - Ignoring diff attribute: %s",
			n.Resource.Id(), k)
		delete(diff.Attributes, k)
	}

	return nil
}

// EvalDiffDestroy is an EvalNode implementation that returns a plain
// destroy diff.
type EvalDiffDestroy struct {
	Info   *InstanceInfo
	State  **InstanceState
	Output **InstanceDiff
}

// TODO: test
func (n *EvalDiffDestroy) Eval(ctx EvalContext) (interface{}, error) {
	state := *n.State

	// If there is no state or we don't have an ID, we're already destroyed
	if state == nil || state.ID == "" {
		return nil, nil
	}

	// Call pre-diff hook
	err := ctx.Hook(func(h Hook) (HookAction, error) {
		return h.PreDiff(n.Info, state)
	})
	if err != nil {
		return nil, err
	}

	// The diff
	diff := &InstanceDiff{Destroy: true}

	// Call post-diff hook
	err = ctx.Hook(func(h Hook) (HookAction, error) {
		return h.PostDiff(n.Info, diff)
	})
	if err != nil {
		return nil, err
	}

	// Update our output
	*n.Output = diff

	return nil, nil
}

// EvalDiffDestroyModule is an EvalNode implementation that writes the diff to
// the full diff.
type EvalDiffDestroyModule struct {
	Path []string
}

// TODO: test
func (n *EvalDiffDestroyModule) Eval(ctx EvalContext) (interface{}, error) {
	diff, lock := ctx.Diff()

	// Acquire the lock so that we can do this safely concurrently
	lock.Lock()
	defer lock.Unlock()

	// Write the diff
	modDiff := diff.ModuleByPath(n.Path)
	if modDiff == nil {
		modDiff = diff.AddModule(n.Path)
	}
	modDiff.Destroy = true

	return nil, nil
}

// EvalFilterDiff is an EvalNode implementation that filters the diff
// according to some filter.
type EvalFilterDiff struct {
	// Input and output
	Diff   **InstanceDiff
	Output **InstanceDiff

	// Destroy, if true, will only include a destroy diff if it is set.
	Destroy bool
}

func (n *EvalFilterDiff) Eval(ctx EvalContext) (interface{}, error) {
	if *n.Diff == nil {
		return nil, nil
	}

	input := *n.Diff
	result := new(InstanceDiff)

	if n.Destroy {
		if input.Destroy || input.RequiresNew() {
			result.Destroy = true
		}
	}

	if n.Output != nil {
		*n.Output = result
	}

	return nil, nil
}

// EvalReadDiff is an EvalNode implementation that writes the diff to
// the full diff.
type EvalReadDiff struct {
	Name string
	Diff **InstanceDiff
}

func (n *EvalReadDiff) Eval(ctx EvalContext) (interface{}, error) {
	diff, lock := ctx.Diff()

	// Acquire the lock so that we can do this safely concurrently
	lock.Lock()
	defer lock.Unlock()

	// Write the diff
	modDiff := diff.ModuleByPath(ctx.Path())
	if modDiff == nil {
		return nil, nil
	}

	*n.Diff = modDiff.Resources[n.Name]

	return nil, nil
}

// EvalWriteDiff is an EvalNode implementation that writes the diff to
// the full diff.
type EvalWriteDiff struct {
	Name string
	Diff **InstanceDiff
}

// TODO: test
func (n *EvalWriteDiff) Eval(ctx EvalContext) (interface{}, error) {
	diff, lock := ctx.Diff()

	// The diff to write, if its empty it should write nil
	var diffVal *InstanceDiff
	if n.Diff != nil {
		diffVal = *n.Diff
	}
	if diffVal.Empty() {
		diffVal = nil
	}

	// Acquire the lock so that we can do this safely concurrently
	lock.Lock()
	defer lock.Unlock()

	// Write the diff
	modDiff := diff.ModuleByPath(ctx.Path())
	if modDiff == nil {
		modDiff = diff.AddModule(ctx.Path())
	}
	if diffVal != nil {
		modDiff.Resources[n.Name] = diffVal
	} else {
		delete(modDiff.Resources, n.Name)
	}

	return nil, nil
}
<commit_msg>Tainted resource not recreated if ignore_changes used on any attributes.<commit_after>package terraform

import (
	"fmt"
	"log"
	"strings"

	"github.com/hashicorp/terraform/config"
)

// EvalCompareDiff is an EvalNode implementation that compares two diffs
// and errors if the diffs are not equal.
type EvalCompareDiff struct {
	Info     *InstanceInfo
	One, Two **InstanceDiff
}

// TODO: test
func (n *EvalCompareDiff) Eval(ctx EvalContext) (interface{}, error) {
	one, two := *n.One, *n.Two

	// If either are nil, let them be empty
	if one == nil {
		one = new(InstanceDiff)
		one.init()
	}
	if two == nil {
		two = new(InstanceDiff)
		two.init()
	}
	oneId := one.Attributes["id"]
	twoId := two.Attributes["id"]
	delete(one.Attributes, "id")
	delete(two.Attributes, "id")
	defer func() {
		if oneId != nil {
			one.Attributes["id"] = oneId
		}
		if twoId != nil {
			two.Attributes["id"] = twoId
		}
	}()

	if same, reason := one.Same(two); !same {
		log.Printf("[ERROR] %s: diffs didn't match", n.Info.Id)
		log.Printf("[ERROR] %s: reason: %s", n.Info.Id, reason)
		log.Printf("[ERROR] %s: diff one: %#v", n.Info.Id, one)
		log.Printf("[ERROR] %s: diff two: %#v", n.Info.Id, two)
		return nil, fmt.Errorf(
			"%s: diffs didn't match during apply. This is a bug with "+
				"Terraform and should be reported as a GitHub Issue.\n"+
				"\n"+
				"Please include the following information in your report:\n"+
				"\n"+
				"    Terraform Version: %s\n"+
				"    Resource ID: %s\n"+
				"    Mismatch reason: %s\n"+
				"    Diff One (usually from plan): %#v\n"+
				"    Diff Two (usually from apply): %#v\n"+
				"\n"+
				"Also include as much context as you can about your config, state, "+
				"and the steps you performed to trigger this error.\n",
			n.Info.Id, Version, n.Info.Id, reason, one, two)
	}

	return nil, nil
}

// EvalDiff is an EvalNode implementation that does a refresh for
// a resource.
type EvalDiff struct {
	Info        *InstanceInfo
	Config      **ResourceConfig
	Provider    *ResourceProvider
	Diff        **InstanceDiff
	State       **InstanceState
	OutputDiff  **InstanceDiff
	OutputState **InstanceState

	// Resource is needed to fetch the ignore_changes list so we can
	// filter user-requested ignored attributes from the diff.
	Resource *config.Resource
}

// TODO: test
func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
	state := *n.State
	config := *n.Config
	provider := *n.Provider

	// Call pre-diff hook
	err := ctx.Hook(func(h Hook) (HookAction, error) {
		return h.PreDiff(n.Info, state)
	})
	if err != nil {
		return nil, err
	}

	// The state for the diff must never be nil
	diffState := state
	if diffState == nil {
		diffState = new(InstanceState)
	}
	diffState.init()

	// Diff!
	diff, err := provider.Diff(n.Info, diffState, config)
	if err != nil {
		return nil, err
	}
	if diff == nil {
		diff = new(InstanceDiff)
	}

	// Preserve the DestroyTainted flag
	if n.Diff != nil {
		diff.DestroyTainted = (*n.Diff).DestroyTainted
	}

	// Require a destroy if there is an ID and it requires new.
	if diff.RequiresNew() && state != nil && state.ID != "" {
		diff.Destroy = true
	}

	// If we're creating a new resource, compute its ID
	if diff.RequiresNew() || state == nil || state.ID == "" {
		var oldID string
		if state != nil {
			oldID = state.Attributes["id"]
		}

		// Add diff to compute new ID
		diff.init()
		diff.Attributes["id"] = &ResourceAttrDiff{
			Old:         oldID,
			NewComputed: true,
			RequiresNew: true,
			Type:        DiffAttrOutput,
		}
	}

	if err := n.processIgnoreChanges(diff); err != nil {
		return nil, err
	}

	// Call post-refresh hook
	err = ctx.Hook(func(h Hook) (HookAction, error) {
		return h.PostDiff(n.Info, diff)
	})
	if err != nil {
		return nil, err
	}

	// Update our output
	*n.OutputDiff = diff

	// Update the state if we care
	if n.OutputState != nil {
		*n.OutputState = state

		// Merge our state so that the state is updated with our plan
		if !diff.Empty() && n.OutputState != nil {
			*n.OutputState = state.MergeDiff(diff)
		}
	}

	return nil, nil
}

func (n *EvalDiff) processIgnoreChanges(diff *InstanceDiff) error {
	if diff == nil || n.Resource == nil || n.Resource.Id() == "" {
		return nil
	}
	ignoreChanges := n.Resource.Lifecycle.IgnoreChanges

	if len(ignoreChanges) == 0 {
		return nil
	}

	changeType := diff.ChangeType()

	// If we're just creating the resource, we shouldn't alter the
	// Diff at all
	if changeType == DiffCreate {
		return nil
	}

	// If the resource has been tainted we shouldn't alter the Diff
	if diff.DestroyTainted {
		return nil
	}

	ignorableAttrKeys := make(map[string]bool)
	for _, ignoredKey := range ignoreChanges {
		for k := range diff.Attributes {
			if strings.HasPrefix(k, ignoredKey) {
				ignorableAttrKeys[k] = true
			}
		}
	}

	// If we are replacing the resource, then we expect there to be a bunch of
	// extraneous attribute diffs we need to filter out for the other
	// non-requires-new attributes going from "" -> "configval" or "" ->
	// "<computed>". Filtering these out allows us to see if we might be able to
	// skip this diff altogether.
	if changeType == DiffDestroyCreate {
		for k, v := range diff.Attributes {
			if v.Empty() || v.NewComputed {
				ignorableAttrKeys[k] = true
			}
		}

		// Here we emulate the implementation of diff.RequiresNew() with one small
		// tweak, we ignore the "id" attribute diff that gets added by EvalDiff,
		// since that was added in reaction to RequiresNew being true.
		requiresNewAfterIgnores := false
		for k, v := range diff.Attributes {
			if k == "id" {
				continue
			}
			if _, ok := ignorableAttrKeys[k]; ok {
				continue
			}
			if v.RequiresNew == true {
				requiresNewAfterIgnores = true
			}
		}

		// If we still require resource replacement after ignores, we
		// can't touch the diff, as all of the attributes will be
		// required to process the replacement.
		if requiresNewAfterIgnores {
			return nil
		}

		// Here we undo the two reactions to RequireNew in EvalDiff - the "id"
		// attribute diff and the Destroy boolean field
		log.Printf("[DEBUG] Removing 'id' diff and setting Destroy to false " +
			"because after ignore_changes, this diff no longer requires replacement")
		delete(diff.Attributes, "id")
		diff.Destroy = false
	}

	// If we didn't hit any of our early exit conditions, we can filter the diff.
	for k := range ignorableAttrKeys {
		log.Printf("[DEBUG] [EvalIgnoreChanges] %s - Ignoring diff attribute: %s",
			n.Resource.Id(), k)
		delete(diff.Attributes, k)
	}

	return nil
}

// EvalDiffDestroy is an EvalNode implementation that returns a plain
// destroy diff.
type EvalDiffDestroy struct {
	Info   *InstanceInfo
	State  **InstanceState
	Output **InstanceDiff
}

// TODO: test
func (n *EvalDiffDestroy) Eval(ctx EvalContext) (interface{}, error) {
	state := *n.State

	// If there is no state or we don't have an ID, we're already destroyed
	if state == nil || state.ID == "" {
		return nil, nil
	}

	// Call pre-diff hook
	err := ctx.Hook(func(h Hook) (HookAction, error) {
		return h.PreDiff(n.Info, state)
	})
	if err != nil {
		return nil, err
	}

	// The diff
	diff := &InstanceDiff{Destroy: true}

	// Call post-diff hook
	err = ctx.Hook(func(h Hook) (HookAction, error) {
		return h.PostDiff(n.Info, diff)
	})
	if err != nil {
		return nil, err
	}

	// Update our output
	*n.Output = diff

	return nil, nil
}

// EvalDiffDestroyModule is an EvalNode implementation that writes the diff to
// the full diff.
type EvalDiffDestroyModule struct {
	Path []string
}

// TODO: test
func (n *EvalDiffDestroyModule) Eval(ctx EvalContext) (interface{}, error) {
	diff, lock := ctx.Diff()

	// Acquire the lock so that we can do this safely concurrently
	lock.Lock()
	defer lock.Unlock()

	// Write the diff
	modDiff := diff.ModuleByPath(n.Path)
	if modDiff == nil {
		modDiff = diff.AddModule(n.Path)
	}
	modDiff.Destroy = true

	return nil, nil
}

// EvalFilterDiff is an EvalNode implementation that filters the diff
// according to some filter.
type EvalFilterDiff struct {
	// Input and output
	Diff   **InstanceDiff
	Output **InstanceDiff

	// Destroy, if true, will only include a destroy diff if it is set.
	Destroy bool
}

func (n *EvalFilterDiff) Eval(ctx EvalContext) (interface{}, error) {
	if *n.Diff == nil {
		return nil, nil
	}

	input := *n.Diff
	result := new(InstanceDiff)

	if n.Destroy {
		if input.Destroy || input.RequiresNew() {
			result.Destroy = true
		}
	}

	if n.Output != nil {
		*n.Output = result
	}

	return nil, nil
}

// EvalReadDiff is an EvalNode implementation that writes the diff to
// the full diff.
type EvalReadDiff struct {
	Name string
	Diff **InstanceDiff
}

func (n *EvalReadDiff) Eval(ctx EvalContext) (interface{}, error) {
	diff, lock := ctx.Diff()

	// Acquire the lock so that we can do this safely concurrently
	lock.Lock()
	defer lock.Unlock()

	// Write the diff
	modDiff := diff.ModuleByPath(ctx.Path())
	if modDiff == nil {
		return nil, nil
	}

	*n.Diff = modDiff.Resources[n.Name]

	return nil, nil
}

// EvalWriteDiff is an EvalNode implementation that writes the diff to
// the full diff.
type EvalWriteDiff struct {
	Name string
	Diff **InstanceDiff
}

// TODO: test
func (n *EvalWriteDiff) Eval(ctx EvalContext) (interface{}, error) {
	diff, lock := ctx.Diff()

	// The diff to write, if its empty it should write nil
	var diffVal *InstanceDiff
	if n.Diff != nil {
		diffVal = *n.Diff
	}
	if diffVal.Empty() {
		diffVal = nil
	}

	// Acquire the lock so that we can do this safely concurrently
	lock.Lock()
	defer lock.Unlock()

	// Write the diff
	modDiff := diff.ModuleByPath(ctx.Path())
	if modDiff == nil {
		modDiff = diff.AddModule(ctx.Path())
	}
	if diffVal != nil {
		modDiff.Resources[n.Name] = diffVal
	} else {
		delete(modDiff.Resources, n.Name)
	}

	return nil, nil
}
<|endoftext|>


<commit_before>package acsengine

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
	"testing"

	"github.com/Azure/acs-engine/pkg/api"
	"github.com/Azure/acs-engine/pkg/api/v20160930"
)

const (
	TestDataDir = "./testdata"
)

func TestExpected(t *testing.T) {
	// iterate the test data directory
	apiModelTestFiles := &[]APIModelTestFile{}
	if e := IterateTestFilesDirectory(TestDataDir, apiModelTestFiles); e != nil {
		t.Error(e.Error())
		return
	}

	for _, tuple := range *apiModelTestFiles {
		containerService, version, err := api.LoadContainerServiceFromFile(tuple.APIModelFilename)
		if err != nil {
			t.Errorf("Loading file %s got error: %s", tuple.APIModelFilename, err.Error())
			continue
		}

		if version == v20160930.APIVersion {
			// v20160930 need certificate profile to match expected template
			containerService.Properties.CertificateProfile = &api.CertificateProfile{}
			addV20160930CertificateProfile(containerService.Properties.CertificateProfile)
		}

		isClassicMode := false
		if strings.Contains(tuple.APIModelFilename, "_classicmode") {
			isClassicMode = true
		}

		// test the output container service 3 times:
		// 1. first time tests loaded containerService
		// 2. second time tests generated containerService
		// 3. third time tests the generated containerService from the generated containerService
		templateGenerator, e3 := InitializeTemplateGenerator(isClassicMode)
		if e3 != nil {
			t.Error(e3.Error())
			continue
		}

		armTemplate, params, certsGenerated, err := templateGenerator.GenerateTemplate(containerService)
		if err != nil {
			t.Error(fmt.Errorf("error in file %s: %s", tuple.APIModelFilename, err.Error()))
			continue
		}

		ppArmTemplate, e1 := PrettyPrintArmTemplate(armTemplate)
		if e1 != nil {
			t.Error(armTemplate)
			t.Error(fmt.Errorf("error in file %s: %s", tuple.APIModelFilename, e1.Error()))
			break
		}

		ppParams, e2 := PrettyPrintJSON(params)
		if e2 != nil {
			t.Error(fmt.Errorf("error in file %s: %s", tuple.APIModelFilename, e2.Error()))
			continue
		}

		if certsGenerated == true {
			t.Errorf("cert generation unexpected for %s", containerService.Properties.OrchestratorProfile.OrchestratorType)
		}

		for i := 0; i < 3; i++ {
			armTemplate, params, certsGenerated, err := templateGenerator.GenerateTemplate(containerService)
			if err != nil {
				t.Error(fmt.Errorf("error in file %s: %s", tuple.APIModelFilename, err.Error()))
				continue
			}
			ppArmTemplateNew, e1 := PrettyPrintArmTemplate(armTemplate)
			if e1 != nil {
				t.Error(armTemplate)
				t.Error(fmt.Errorf("error in file %s: %s", tuple.APIModelFilename, e1.Error()))
				break
			}

			ppParamsNew, e2 := PrettyPrintJSON(params)
			if e2 != nil {
				t.Error(fmt.Errorf("error in file %s: %s", tuple.APIModelFilename, e2.Error()))
				continue
			}

			if certsGenerated == true {
				t.Errorf("cert generation unexpected for %s", containerService.Properties.OrchestratorProfile.OrchestratorType)
			}

			if !bytes.Equal([]byte(ppArmTemplateNew), []byte(ppArmTemplate)) {
				diffstr, differr := tuple.WriteArmTemplateErrFilename([]byte(ppArmTemplateNew))
				if differr != nil {
					diffstr += differr.Error()
				}
				t.Errorf("generated output different from expected for model %s: '%s'", tuple.APIModelFilename, diffstr)
			}

			if !bytes.Equal([]byte(ppParamsNew), []byte(ppParams)) {
				diffstr, differr := tuple.WriteArmTemplateParamsErrFilename([]byte(ppParamsNew))
				if differr != nil {
					diffstr += differr.Error()
				}
				t.Errorf("generated parameters different from expected for model %s: '%s'", tuple.APIModelFilename, diffstr)
			}

			b, err := api.SerializeContainerService(containerService, version)
			if err != nil {
				t.Error(err)
			}
			containerService, version, err = api.DeserializeContainerService(b)
			if err != nil {
				t.Error(err)
			}
			if version == v20160930.APIVersion {
				// v20160930 need certificate profile to match expected template
				containerService.Properties.CertificateProfile = &api.CertificateProfile{}
				addV20160930CertificateProfile(containerService.Properties.CertificateProfile)
			}
		}
	}
}

// APIModelTestFile holds the test file name and knows how to find the expected files
type APIModelTestFile struct {
	APIModelFilename string
}

// WriteArmTemplateErrFilename writes out an error file to sit parallel for comparison
func (a *APIModelTestFile) WriteArmTemplateErrFilename(contents []byte) (string, error) {
	filename := fmt.Sprintf("%s_expected.err", a.APIModelFilename)
	if err := ioutil.WriteFile(filename, contents, 0600); err != nil {
		return "", err
	}
	return fmt.Sprintf("%s written for diff", filename), nil
}

// WriteArmTemplateParamsErrFilename writes out an error file to sit parallel for comparison
func (a *APIModelTestFile) WriteArmTemplateParamsErrFilename(contents []byte) (string, error) {
	filename := fmt.Sprintf("%s_expected_params.err", a.APIModelFilename)
	if err := ioutil.WriteFile(filename, contents, 0600); err != nil {
		return "", err
	}
	return fmt.Sprintf("%s written for diff", filename), nil
}

// IterateTestFilesDirectory iterates the test data directory adding api model files to the test file slice.
func IterateTestFilesDirectory(directory string, APIModelTestFiles *[]APIModelTestFile) error {
	files, err := ioutil.ReadDir(directory)
	if err != nil {
		return err
	}

	for _, file := range files {
		if file.IsDir() {
			if e := IterateTestFilesDirectory(filepath.Join(directory, file.Name()), APIModelTestFiles); e != nil {
				return e
			}
		} else {
			if !strings.Contains(file.Name(), "_expected") && strings.HasSuffix(file.Name(), ".json") {
				tuple := &APIModelTestFile{}
				tuple.APIModelFilename = filepath.Join(directory, file.Name())
				*APIModelTestFiles = append(*APIModelTestFiles, *tuple)
			}
		}
	}
	return nil
}

// addV20160930CertificateProfile add certificate artifacts for test purpose
func addV20160930CertificateProfile(api *api.CertificateProfile) {
	api.CaCertificate = "caCertificate"
	api.APIServerCertificate = "apiServerCertificate"
	api.APIServerPrivateKey = "apiServerPrivateKey"
	api.ClientCertificate = "clientCertificate"
	api.ClientPrivateKey = "clientPrivateKey"
	api.KubeConfigCertificate = "kubeConfigCertificate"
	api.KubeConfigPrivateKey = "kubeConfigPrivateKey"
	api.SetCAPrivateKey("")
}
<commit_msg>updated ppArmTemplate and ppParams variable names with 'expected' and 'generated' prefix<commit_after>package acsengine

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
	"testing"

	"github.com/Azure/acs-engine/pkg/api"
	"github.com/Azure/acs-engine/pkg/api/v20160930"
)

const (
	TestDataDir = "./testdata"
)

func TestExpected(t *testing.T) {
	// iterate the test data directory
	apiModelTestFiles := &[]APIModelTestFile{}
	if e := IterateTestFilesDirectory(TestDataDir, apiModelTestFiles); e != nil {
		t.Error(e.Error())
		return
	}

	for _, tuple := range *apiModelTestFiles {
		containerService, version, err := api.LoadContainerServiceFromFile(tuple.APIModelFilename)
		if err != nil {
			t.Errorf("Loading file %s got error: %s", tuple.APIModelFilename, err.Error())
			continue
		}

		if version == v20160930.APIVersion {
			// v20160930 need certificate profile to match expected template
			containerService.Properties.CertificateProfile = &api.CertificateProfile{}
			addV20160930CertificateProfile(containerService.Properties.CertificateProfile)
		}

		isClassicMode := false
		if strings.Contains(tuple.APIModelFilename, "_classicmode") {
			isClassicMode = true
		}

		// test the output container service 3 times:
		// 1. first time tests loaded containerService
		// 2. second time tests generated containerService
		// 3. third time tests the generated containerService from the generated containerService
		templateGenerator, e3 := InitializeTemplateGenerator(isClassicMode)
		if e3 != nil {
			t.Error(e3.Error())
			continue
		}

		armTemplate, params, certsGenerated, err := templateGenerator.GenerateTemplate(containerService)
		if err != nil {
			t.Error(fmt.Errorf("error in file %s: %s", tuple.APIModelFilename, err.Error()))
			continue
		}

		expectedPpArmTemplate, e1 := PrettyPrintArmTemplate(armTemplate)
		if e1 != nil {
			t.Error(armTemplate)
			t.Error(fmt.Errorf("error in file %s: %s", tuple.APIModelFilename, e1.Error()))
			break
		}

		expectedPpParams, e2 := PrettyPrintJSON(params)
		if e2 != nil {
			t.Error(fmt.Errorf("error in file %s: %s", tuple.APIModelFilename, e2.Error()))
			continue
		}

		if certsGenerated == true {
			t.Errorf("cert generation unexpected for %s", containerService.Properties.OrchestratorProfile.OrchestratorType)
		}

		for i := 0; i < 3; i++ {
			armTemplate, params, certsGenerated, err := templateGenerator.GenerateTemplate(containerService)
			if err != nil {
				t.Error(fmt.Errorf("error in file %s: %s", tuple.APIModelFilename, err.Error()))
				continue
			}
			generatedPpArmTemplate, e1 := PrettyPrintArmTemplate(armTemplate)
			if e1 != nil {
				t.Error(armTemplate)
				t.Error(fmt.Errorf("error in file %s: %s", tuple.APIModelFilename, e1.Error()))
				break
			}

			generatedPpParams, e2 := PrettyPrintJSON(params)
			if e2 != nil {
				t.Error(fmt.Errorf("error in file %s: %s", tuple.APIModelFilename, e2.Error()))
				continue
			}

			if certsGenerated == true {
				t.Errorf("cert generation unexpected for %s", containerService.Properties.OrchestratorProfile.OrchestratorType)
			}

			if !bytes.Equal([]byte(expectedPpArmTemplate), []byte(generatedPpArmTemplate)) {
				diffstr, differr := tuple.WriteArmTemplateErrFilename([]byte(generatedPpArmTemplate))
				if differr != nil {
					diffstr += differr.Error()
				}
				t.Errorf("generated output different from expected for model %s: '%s'", tuple.APIModelFilename, diffstr)
			}

			if !bytes.Equal([]byte(expectedPpParams), []byte(generatedPpParams)) {
				diffstr, differr := tuple.WriteArmTemplateParamsErrFilename([]byte(generatedPpParams))
				if differr != nil {
					diffstr += differr.Error()
				}
				t.Errorf("generated parameters different from expected for model %s: '%s'", tuple.APIModelFilename, diffstr)
			}

			b, err := api.SerializeContainerService(containerService, version)
			if err != nil {
				t.Error(err)
			}
			containerService, version, err = api.DeserializeContainerService(b)
			if err != nil {
				t.Error(err)
			}
			if version == v20160930.APIVersion {
				// v20160930 need certificate profile to match expected template
				containerService.Properties.CertificateProfile = &api.CertificateProfile{}
				addV20160930CertificateProfile(containerService.Properties.CertificateProfile)
			}
		}
	}
}

// APIModelTestFile holds the test file name and knows how to find the expected files
type APIModelTestFile struct {
	APIModelFilename string
}

// WriteArmTemplateErrFilename writes out an error file to sit parallel for comparison
func (a *APIModelTestFile) WriteArmTemplateErrFilename(contents []byte) (string, error) {
	filename := fmt.Sprintf("%s_expected.err", a.APIModelFilename)
	if err := ioutil.WriteFile(filename, contents, 0600); err != nil {
		return "", err
	}
	return fmt.Sprintf("%s written for diff", filename), nil
}

// WriteArmTemplateParamsErrFilename writes out an error file to sit parallel for comparison
func (a *APIModelTestFile) WriteArmTemplateParamsErrFilename(contents []byte) (string, error) {
	filename := fmt.Sprintf("%s_expected_params.err", a.APIModelFilename)
	if err := ioutil.WriteFile(filename, contents, 0600); err != nil {
		return "", err
	}
	return fmt.Sprintf("%s written for diff", filename), nil
}

// IterateTestFilesDirectory iterates the test data directory adding api model files to the test file slice.
func IterateTestFilesDirectory(directory string, APIModelTestFiles *[]APIModelTestFile) error {
	files, err := ioutil.ReadDir(directory)
	if err != nil {
		return err
	}

	for _, file := range files {
		if file.IsDir() {
			if e := IterateTestFilesDirectory(filepath.Join(directory, file.Name()), APIModelTestFiles); e != nil {
				return e
			}
		} else {
			if !strings.Contains(file.Name(), "_expected") && strings.HasSuffix(file.Name(), ".json") {
				tuple := &APIModelTestFile{}
				tuple.APIModelFilename = filepath.Join(directory, file.Name())
				*APIModelTestFiles = append(*APIModelTestFiles, *tuple)
			}
		}
	}
	return nil
}

// addV20160930CertificateProfile add certificate artifacts for test purpose
func addV20160930CertificateProfile(api *api.CertificateProfile) {
	api.CaCertificate = "caCertificate"
	api.APIServerCertificate = "apiServerCertificate"
	api.APIServerPrivateKey = "apiServerPrivateKey"
	api.ClientCertificate = "clientCertificate"
	api.ClientPrivateKey = "clientPrivateKey"
	api.KubeConfigCertificate = "kubeConfigCertificate"
	api.KubeConfigPrivateKey = "kubeConfigPrivateKey"
	api.SetCAPrivateKey("")
}
<|endoftext|>


<commit_before>package manifest

import (
	"bytes"
	"fmt"

	bytes2 "github.com/9elements/converged-security-suite/v2/pkg/bytes"
)

// Firmware is an abstraction of a firmware image, obtained for example via flashrom
type Firmware interface {
	ImageBytes() []byte
	PhysAddrToOffset(physAddr uint64) uint64
	OffsetToPhysAddr(offset uint64) uint64
}

// PSPFirmware contains essential parts of the AMD's PSP firmware internals
type PSPFirmware struct {
	EmbeddedFirmware      EmbeddedFirmwareStructure
	EmbeddedFirmwareRange bytes2.Range

	PSPDirectoryLevel1      *PSPDirectoryTable
	PSPDirectoryLevel1Range bytes2.Range
	PSPDirectoryLevel2      *PSPDirectoryTable
	PSPDirectoryLevel2Range bytes2.Range

	BIOSDirectoryLevel1      *BIOSDirectoryTable
	BIOSDirectoryLevel1Range bytes2.Range
	BIOSDirectoryLevel2      *BIOSDirectoryTable
	BIOSDirectoryLevel2Range bytes2.Range
}

// AMDFirmware represents an instance of firmware that exposes AMD specific
// meatadata and structure.
type AMDFirmware struct {
	// firmware is a reference to a generic firmware interface
	firmware Firmware

	// pspFirmware is a reference to PSPFirmware structure. It is built at
	// construction time and not exported.
	pspFirmware *PSPFirmware
}

// Firmware returns the internal reference to Firmawre interface
func (a *AMDFirmware) Firmware() Firmware {
	return a.firmware
}

// PSPFirmware returns the PSPFirmware reference held by the AMDFirmware object
func (a *AMDFirmware) PSPFirmware() *PSPFirmware {
	return a.pspFirmware
}

// parsePSPFirmware parses input firmware as PSP firmware image and
// collects Embedded firmware, PSP directory and BIOS directory structures
func parsePSPFirmware(firmware Firmware) (*PSPFirmware, error) {
	image := firmware.ImageBytes()

	var result PSPFirmware
	efs, r, err := FindEmbeddedFirmwareStructure(firmware)
	if err != nil {
		return nil, err
	}
	result.EmbeddedFirmware = *efs
	result.EmbeddedFirmwareRange = r

	var pspDirectoryLevel1 *PSPDirectoryTable
	var pspDirectoryLevel1Range bytes2.Range
	if efs.PSPDirectoryTablePointer != 0 && efs.PSPDirectoryTablePointer < uint32(len(image)) {
		var length uint64
		pspDirectoryLevel1, length, err = ParsePSPDirectoryTable(bytes.NewBuffer(image[efs.PSPDirectoryTablePointer:]))
		if err == nil {
			pspDirectoryLevel1Range.Offset = uint64(efs.PSPDirectoryTablePointer)
			pspDirectoryLevel1Range.Length = length
		}
	}
	if pspDirectoryLevel1 == nil {
		pspDirectoryLevel1, pspDirectoryLevel1Range, _ = FindPSPDirectoryTable(image)
	}
	if pspDirectoryLevel1 != nil {
		result.PSPDirectoryLevel1 = pspDirectoryLevel1
		result.PSPDirectoryLevel1Range = pspDirectoryLevel1Range

		for _, entry := range pspDirectoryLevel1.Entries {
			if entry.Type != PSPDirectoryTableLevel2Entry {
				continue
			}
			if entry.LocationOrValue != 0 && entry.LocationOrValue < uint64(len(image)) {
				pspDirectoryLevel2, length, err := ParsePSPDirectoryTable(bytes.NewBuffer(image[entry.LocationOrValue:]))
				if err == nil {
					result.PSPDirectoryLevel2 = pspDirectoryLevel2
					result.PSPDirectoryLevel2Range.Offset = entry.LocationOrValue
					result.PSPDirectoryLevel2Range.Length = length
				}
			}
			break
		}
	}

	var biosDirectoryLevel1 *BIOSDirectoryTable
	var biosDirectoryLevel1Range bytes2.Range

	biosDirectoryOffsets := []uint32{
		efs.BIOSDirectoryTableFamily17hModels00h0FhPointer,
		efs.BIOSDirectoryTableFamily17hModels10h1FhPointer,
		efs.BIOSDirectoryTableFamily17hModels30h3FhPointer,
		efs.BIOSDirectoryTableFamily17hModels60h3FhPointer,
	}
	for _, offset := range biosDirectoryOffsets {
		if offset == 0 {
			continue
		}
		var length uint64
		biosDirectoryLevel1, length, err = ParseBIOSDirectoryTable(bytes.NewBuffer(image[offset:]))
		if err != nil {
			continue
		}
		biosDirectoryLevel1Range.Offset = uint64(offset)
		biosDirectoryLevel1Range.Length = length
		break
	}

	if biosDirectoryLevel1 == nil {
		biosDirectoryLevel1, biosDirectoryLevel1Range, _ = FindBIOSDirectoryTable(image)
	}

	if biosDirectoryLevel1 != nil {
		result.BIOSDirectoryLevel1 = biosDirectoryLevel1
		result.BIOSDirectoryLevel1Range = biosDirectoryLevel1Range

		for _, entry := range biosDirectoryLevel1.Entries {
			if entry.Type != BIOSDirectoryTableLevel2Entry {
				continue
			}
			if entry.SourceAddress != 0 && entry.SourceAddress < uint64(len(image)) {
				biosDirectoryLevel2, length, err := ParseBIOSDirectoryTable(bytes.NewBuffer(image[entry.SourceAddress:]))
				if err == nil {
					result.BIOSDirectoryLevel2 = biosDirectoryLevel2
					result.BIOSDirectoryLevel2Range.Offset = entry.SourceAddress
					result.BIOSDirectoryLevel2Range.Length = length
				}
			}
			break
		}
	}

	return &result, nil
}

// NewAMDFirmware returns an AMDFirmware structure or an error if internal firmare structures cannot be parsed
func NewAMDFirmware(firmware Firmware) (*AMDFirmware, error) {
	pspFirmware, err := parsePSPFirmware(firmware)
	if err != nil {
		return nil, fmt.Errorf("could not construct AMDFirmware, cannot parse PSP firmware: %w", err)
	}
	return &AMDFirmware{firmware: firmware, pspFirmware: pspFirmware}, nil

}
<commit_msg>Fix AMD firmware parsing slice bounds out of range (#299)<commit_after>package manifest

import (
	"bytes"
	"fmt"

	bytes2 "github.com/9elements/converged-security-suite/v2/pkg/bytes"
)

// Firmware is an abstraction of a firmware image, obtained for example via flashrom
type Firmware interface {
	ImageBytes() []byte
	PhysAddrToOffset(physAddr uint64) uint64
	OffsetToPhysAddr(offset uint64) uint64
}

// PSPFirmware contains essential parts of the AMD's PSP firmware internals
type PSPFirmware struct {
	EmbeddedFirmware      EmbeddedFirmwareStructure
	EmbeddedFirmwareRange bytes2.Range

	PSPDirectoryLevel1      *PSPDirectoryTable
	PSPDirectoryLevel1Range bytes2.Range
	PSPDirectoryLevel2      *PSPDirectoryTable
	PSPDirectoryLevel2Range bytes2.Range

	BIOSDirectoryLevel1      *BIOSDirectoryTable
	BIOSDirectoryLevel1Range bytes2.Range
	BIOSDirectoryLevel2      *BIOSDirectoryTable
	BIOSDirectoryLevel2Range bytes2.Range
}

// AMDFirmware represents an instance of firmware that exposes AMD specific
// meatadata and structure.
type AMDFirmware struct {
	// firmware is a reference to a generic firmware interface
	firmware Firmware

	// pspFirmware is a reference to PSPFirmware structure. It is built at
	// construction time and not exported.
	pspFirmware *PSPFirmware
}

// Firmware returns the internal reference to Firmawre interface
func (a *AMDFirmware) Firmware() Firmware {
	return a.firmware
}

// PSPFirmware returns the PSPFirmware reference held by the AMDFirmware object
func (a *AMDFirmware) PSPFirmware() *PSPFirmware {
	return a.pspFirmware
}

// parsePSPFirmware parses input firmware as PSP firmware image and
// collects Embedded firmware, PSP directory and BIOS directory structures
func parsePSPFirmware(firmware Firmware) (*PSPFirmware, error) {
	image := firmware.ImageBytes()

	var result PSPFirmware
	efs, r, err := FindEmbeddedFirmwareStructure(firmware)
	if err != nil {
		return nil, err
	}
	result.EmbeddedFirmware = *efs
	result.EmbeddedFirmwareRange = r

	var pspDirectoryLevel1 *PSPDirectoryTable
	var pspDirectoryLevel1Range bytes2.Range
	if efs.PSPDirectoryTablePointer != 0 && efs.PSPDirectoryTablePointer < uint32(len(image)) {
		var length uint64
		pspDirectoryLevel1, length, err = ParsePSPDirectoryTable(bytes.NewBuffer(image[efs.PSPDirectoryTablePointer:]))
		if err == nil {
			pspDirectoryLevel1Range.Offset = uint64(efs.PSPDirectoryTablePointer)
			pspDirectoryLevel1Range.Length = length
		}
	}
	if pspDirectoryLevel1 == nil {
		pspDirectoryLevel1, pspDirectoryLevel1Range, _ = FindPSPDirectoryTable(image)
	}
	if pspDirectoryLevel1 != nil {
		result.PSPDirectoryLevel1 = pspDirectoryLevel1
		result.PSPDirectoryLevel1Range = pspDirectoryLevel1Range

		for _, entry := range pspDirectoryLevel1.Entries {
			if entry.Type != PSPDirectoryTableLevel2Entry {
				continue
			}
			if entry.LocationOrValue != 0 && entry.LocationOrValue < uint64(len(image)) {
				pspDirectoryLevel2, length, err := ParsePSPDirectoryTable(bytes.NewBuffer(image[entry.LocationOrValue:]))
				if err == nil {
					result.PSPDirectoryLevel2 = pspDirectoryLevel2
					result.PSPDirectoryLevel2Range.Offset = entry.LocationOrValue
					result.PSPDirectoryLevel2Range.Length = length
				}
			}
			break
		}
	}

	var biosDirectoryLevel1 *BIOSDirectoryTable
	var biosDirectoryLevel1Range bytes2.Range

	biosDirectoryOffsets := []uint32{
		efs.BIOSDirectoryTableFamily17hModels00h0FhPointer,
		efs.BIOSDirectoryTableFamily17hModels10h1FhPointer,
		efs.BIOSDirectoryTableFamily17hModels30h3FhPointer,
		efs.BIOSDirectoryTableFamily17hModels60h3FhPointer,
	}
	for _, offset := range biosDirectoryOffsets {
		if offset == 0 || int(offset) > len(image) {
			continue
		}
		var length uint64
		biosDirectoryLevel1, length, err = ParseBIOSDirectoryTable(bytes.NewBuffer(image[offset:]))
		if err != nil {
			continue
		}
		biosDirectoryLevel1Range.Offset = uint64(offset)
		biosDirectoryLevel1Range.Length = length
		break
	}

	if biosDirectoryLevel1 == nil {
		biosDirectoryLevel1, biosDirectoryLevel1Range, _ = FindBIOSDirectoryTable(image)
	}

	if biosDirectoryLevel1 != nil {
		result.BIOSDirectoryLevel1 = biosDirectoryLevel1
		result.BIOSDirectoryLevel1Range = biosDirectoryLevel1Range

		for _, entry := range biosDirectoryLevel1.Entries {
			if entry.Type != BIOSDirectoryTableLevel2Entry {
				continue
			}
			if entry.SourceAddress != 0 && entry.SourceAddress < uint64(len(image)) {
				biosDirectoryLevel2, length, err := ParseBIOSDirectoryTable(bytes.NewBuffer(image[entry.SourceAddress:]))
				if err == nil {
					result.BIOSDirectoryLevel2 = biosDirectoryLevel2
					result.BIOSDirectoryLevel2Range.Offset = entry.SourceAddress
					result.BIOSDirectoryLevel2Range.Length = length
				}
			}
			break
		}
	}

	return &result, nil
}

// NewAMDFirmware returns an AMDFirmware structure or an error if internal firmare structures cannot be parsed
func NewAMDFirmware(firmware Firmware) (*AMDFirmware, error) {
	pspFirmware, err := parsePSPFirmware(firmware)
	if err != nil {
		return nil, fmt.Errorf("could not construct AMDFirmware, cannot parse PSP firmware: %w", err)
	}
	return &AMDFirmware{firmware: firmware, pspFirmware: pspFirmware}, nil

}
<|endoftext|>


<commit_before>// Copyright 2016 Cisco Systems, Inc.
//
// 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.

// Handlers for namespace updates.  Keeps an index of namespace
// annotations

package controller

import (
	"reflect"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/kubernetes"
	v1 "k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/kubernetes/pkg/controller"

	"github.com/noironetworks/aci-containers/pkg/apicapi"
)

func (cont *AciController) initNamespaceInformerFromClient(
	kubeClient kubernetes.Interface) {

	cont.initNamespaceInformerBase(
		&cache.ListWatch{
			ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
				return kubeClient.CoreV1().Namespaces().List(options)
			},
			WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
				return kubeClient.CoreV1().Namespaces().Watch(options)
			},
		})
}

func (cont *AciController) initNamespaceInformerBase(listWatch *cache.ListWatch) {
	cont.namespaceInformer = cache.NewSharedIndexInformer(
		listWatch,
		&v1.Namespace{},
		controller.NoResyncPeriodFunc(),
		cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
	)
	cont.namespaceInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			cont.namespaceAdded(obj)
		},
		UpdateFunc: func(oldobj interface{}, newobj interface{}) {
			cont.namespaceChanged(oldobj, newobj)
		},
		DeleteFunc: func(obj interface{}) {
			cont.namespaceDeleted(obj)
		},
	})

}

func (cont *AciController) updatePodsForNamespace(ns string) {
	cache.ListAllByNamespace(cont.podInformer.GetIndexer(), ns, labels.Everything(),
		func(podobj interface{}) {
			cont.queuePodUpdate(podobj.(*v1.Pod))
		})
}

func (cont *AciController) writeApicNs(ns *v1.Namespace) {
	aobj := apicapi.NewVmmInjectedNs("Kubernetes",
		cont.config.AciVmmDomain, cont.config.AciVmmController,
		ns.Name)
	cont.apicConn.WriteApicContainer(cont.aciNameForKey("ns", ns.Name),
		apicapi.ApicSlice{aobj})
}

func (cont *AciController) namespaceAdded(obj interface{}) {
	ns := obj.(*v1.Namespace)
	cont.writeApicNs(ns)
	cont.depPods.UpdateNamespace(ns)
	cont.updatePodsForNamespace(ns.ObjectMeta.Name)
}

func (cont *AciController) namespaceChanged(oldobj interface{},
	newobj interface{}) {

	oldns := oldobj.(*v1.Namespace)
	newns := newobj.(*v1.Namespace)

	cont.writeApicNs(newns)

	if !reflect.DeepEqual(oldns.ObjectMeta.Labels, newns.ObjectMeta.Labels) {
		cont.depPods.UpdateNamespace(newns)
	}
	if !reflect.DeepEqual(oldns.ObjectMeta.Annotations,
		newns.ObjectMeta.Annotations) {
		cont.updatePodsForNamespace(newns.ObjectMeta.Name)
	}
}

func (cont *AciController) namespaceDeleted(obj interface{}) {
	ns := obj.(*v1.Namespace)
	cont.apicConn.ClearApicObjects(cont.aciNameForKey("ns", ns.Name))
	cont.depPods.DeleteNamespace(ns)
	cont.updatePodsForNamespace(ns.ObjectMeta.Name)
}
<commit_msg>Update netpol namespace indices on label changes<commit_after>// Copyright 2016 Cisco Systems, Inc.
//
// 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.

// Handlers for namespace updates.  Keeps an index of namespace
// annotations

package controller

import (
	"reflect"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/kubernetes"
	v1 "k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/kubernetes/pkg/controller"

	"github.com/noironetworks/aci-containers/pkg/apicapi"
)

func (cont *AciController) initNamespaceInformerFromClient(
	kubeClient kubernetes.Interface) {

	cont.initNamespaceInformerBase(
		&cache.ListWatch{
			ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
				return kubeClient.CoreV1().Namespaces().List(options)
			},
			WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
				return kubeClient.CoreV1().Namespaces().Watch(options)
			},
		})
}

func (cont *AciController) initNamespaceInformerBase(listWatch *cache.ListWatch) {
	cont.namespaceInformer = cache.NewSharedIndexInformer(
		listWatch,
		&v1.Namespace{},
		controller.NoResyncPeriodFunc(),
		cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
	)
	cont.namespaceInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			cont.namespaceAdded(obj)
		},
		UpdateFunc: func(oldobj interface{}, newobj interface{}) {
			cont.namespaceChanged(oldobj, newobj)
		},
		DeleteFunc: func(obj interface{}) {
			cont.namespaceDeleted(obj)
		},
	})

}

func (cont *AciController) updatePodsForNamespace(ns string) {
	cache.ListAllByNamespace(cont.podInformer.GetIndexer(), ns, labels.Everything(),
		func(podobj interface{}) {
			cont.queuePodUpdate(podobj.(*v1.Pod))
		})
}

func (cont *AciController) writeApicNs(ns *v1.Namespace) {
	aobj := apicapi.NewVmmInjectedNs("Kubernetes",
		cont.config.AciVmmDomain, cont.config.AciVmmController,
		ns.Name)
	cont.apicConn.WriteApicContainer(cont.aciNameForKey("ns", ns.Name),
		apicapi.ApicSlice{aobj})
}

func (cont *AciController) namespaceAdded(obj interface{}) {
	ns := obj.(*v1.Namespace)
	cont.writeApicNs(ns)
	cont.depPods.UpdateNamespace(ns)
	cont.updatePodsForNamespace(ns.ObjectMeta.Name)
}

func (cont *AciController) namespaceChanged(oldobj interface{},
	newobj interface{}) {

	oldns := oldobj.(*v1.Namespace)
	newns := newobj.(*v1.Namespace)

	cont.writeApicNs(newns)

	if !reflect.DeepEqual(oldns.ObjectMeta.Labels, newns.ObjectMeta.Labels) {
		cont.depPods.UpdateNamespace(newns)
		cont.netPolPods.UpdateNamespace(newns)
		cont.netPolIngressPods.UpdateNamespace(newns)
	}
	if !reflect.DeepEqual(oldns.ObjectMeta.Annotations,
		newns.ObjectMeta.Annotations) {
		cont.updatePodsForNamespace(newns.ObjectMeta.Name)
	}
}

func (cont *AciController) namespaceDeleted(obj interface{}) {
	ns := obj.(*v1.Namespace)
	cont.apicConn.ClearApicObjects(cont.aciNameForKey("ns", ns.Name))
	cont.depPods.DeleteNamespace(ns)
	cont.netPolPods.DeleteNamespace(ns)
	cont.netPolIngressPods.DeleteNamespace(ns)
	cont.updatePodsForNamespace(ns.ObjectMeta.Name)
}
<|endoftext|>