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

package virtcontainers

import (
	"fmt"
	"os"
	"path/filepath"
	"syscall"

	"github.com/containers/virtcontainers/pkg/hyperstart"
)

var defaultSockPathTemplates = []string{"/tmp/hyper-pod-%s.sock", "/tmp/tty-pod%s.sock"}
var defaultChannelTemplate = "sh.hyper.channel.%d"
var defaultDeviceIDTemplate = "channel%d"
var defaultIDTemplate = "charch%d"
var defaultSharedDir = "/tmp/hyper/shared/pods/"
var defaultPauseBinDir = "/usr/bin/"
var mountTag = "hyperShared"
var rootfsDir = "rootfs"
var pauseBinName = "pause"
var pauseContainerName = "pause-container"

const (
	unixSocket = "unix"
)

// HyperConfig is a structure storing information needed for
// hyperstart agent initialization.
type HyperConfig struct {
	SockCtlName  string
	SockTtyName  string
	Volumes      []Volume
	Sockets      []Socket
	PauseBinPath string
}

func (c *HyperConfig) validate(pod Pod) bool {
	if len(c.Sockets) == 0 {
		virtLog.Infof("No sockets from configuration")

		podSocketPaths := []string{
			fmt.Sprintf(defaultSockPathTemplates[0], pod.id),
			fmt.Sprintf(defaultSockPathTemplates[1], pod.id),
		}

		c.SockCtlName = podSocketPaths[0]
		c.SockTtyName = podSocketPaths[1]

		for i := 0; i < len(podSocketPaths); i++ {
			s := Socket{
				DeviceID: fmt.Sprintf(defaultDeviceIDTemplate, i),
				ID:       fmt.Sprintf(defaultIDTemplate, i),
				HostPath: podSocketPaths[i],
				Name:     fmt.Sprintf(defaultChannelTemplate, i),
			}
			c.Sockets = append(c.Sockets, s)
		}
	}

	if len(c.Sockets) != 2 {
		return false
	}

	if c.PauseBinPath == "" {
		c.PauseBinPath = filepath.Join(defaultPauseBinDir, pauseBinName)
	}

	virtLog.Infof("Hyperstart config %v", c)

	return true
}

// hyper is the Agent interface implementation for hyperstart.
type hyper struct {
	config HyperConfig
	proxy  proxy
}

type hyperstartProxyCmd struct {
	cmd     string
	message interface{}
	token   string
}

func (h *hyper) buildHyperContainerProcess(cmd Cmd) (*hyperstart.Process, error) {
	var envVars []hyperstart.EnvironmentVar

	for _, e := range cmd.Envs {
		envVar := hyperstart.EnvironmentVar{
			Env:   e.Var,
			Value: e.Value,
		}

		envVars = append(envVars, envVar)
	}

	process := &hyperstart.Process{
		User:             cmd.User,
		Group:            cmd.PrimaryGroup,
		AdditionalGroups: cmd.SupplementaryGroups,
		Terminal:         cmd.Interactive,
		Args:             cmd.Args,
		Envs:             envVars,
		Workdir:          cmd.WorkDir,
	}

	return process, nil
}

func (h *hyper) buildNetworkInterfacesAndRoutes(pod Pod) ([]hyperstart.NetworkIface, []hyperstart.Route, error) {
	networkNS, err := pod.storage.fetchPodNetwork(pod.id)
	if err != nil {
		return []hyperstart.NetworkIface{}, []hyperstart.Route{}, err
	}

	if networkNS.NetNsPath == "" {
		return []hyperstart.NetworkIface{}, []hyperstart.Route{}, nil
	}

	netIfaces, err := getIfacesFromNetNs(networkNS.NetNsPath)
	if err != nil {
		return []hyperstart.NetworkIface{}, []hyperstart.Route{}, err
	}

	var ifaces []hyperstart.NetworkIface
	var routes []hyperstart.Route
	for _, endpoint := range networkNS.Endpoints {
		netIface, err := getNetIfaceByName(endpoint.NetPair.VirtIface.Name, netIfaces)
		if err != nil {
			return []hyperstart.NetworkIface{}, []hyperstart.Route{}, err
		}

		var ipAddrs []hyperstart.IPAddress
		for _, ipConfig := range endpoint.Properties.IPs {
			// Skip IPv6 because not supported by hyperstart
			if ipConfig.Version == "6" || ipConfig.Address.IP.To4() == nil {
				continue
			}

			netMask, _ := ipConfig.Address.Mask.Size()

			ipAddr := hyperstart.IPAddress{
				IPAddress: ipConfig.Address.IP.String(),
				NetMask:   fmt.Sprintf("%d", netMask),
			}

			ipAddrs = append(ipAddrs, ipAddr)
		}

		iface := hyperstart.NetworkIface{
			NewDevice:   endpoint.NetPair.VirtIface.Name,
			IPAddresses: ipAddrs,
			MTU:         fmt.Sprintf("%d", netIface.MTU),
			MACAddr:     endpoint.NetPair.TAPIface.HardAddr,
		}

		ifaces = append(ifaces, iface)

		for _, r := range endpoint.Properties.Routes {
			// Skip IPv6 because not supported by hyperstart
			if r.Dst.IP.To4() == nil {
				continue
			}

			gateway := r.GW.String()
			if gateway == "<nil>" {
				gateway = ""
			}

			route := hyperstart.Route{
				Dest:    r.Dst.String(),
				Gateway: gateway,
				Device:  endpoint.NetPair.VirtIface.Name,
			}

			routes = append(routes, route)
		}
	}

	return ifaces, routes, nil
}

func (h *hyper) copyPauseBinary(podID string) error {
	pauseDir := filepath.Join(defaultSharedDir, podID, pauseContainerName, rootfsDir)

	if err := os.MkdirAll(pauseDir, dirMode); err != nil {
		return err
	}

	pausePath := filepath.Join(pauseDir, pauseBinName)

	return fileCopy(h.config.PauseBinPath, pausePath)
}

func (h *hyper) removePauseBinary(podID string) error {
	pauseDir := filepath.Join(defaultSharedDir, podID, pauseContainerName)

	return os.RemoveAll(pauseDir)
}

func (h *hyper) bindMountContainerRootfs(podID, cID, cRootFs string, readonly bool) error {
	rootfsDest := filepath.Join(defaultSharedDir, podID, cID, rootfsDir)

	return bindMount(cRootFs, rootfsDest, readonly)
}

func (h *hyper) bindUnmountContainerRootfs(podID, cID string) error {
	rootfsDest := filepath.Join(defaultSharedDir, podID, cID, rootfsDir)
	syscall.Unmount(rootfsDest, 0)

	return nil
}

func (h *hyper) bindUnmountAllRootfs(pod Pod) {
	for _, c := range pod.containers {
		h.bindUnmountContainerRootfs(pod.id, c.id)
	}
}

// init is the agent initialization implementation for hyperstart.
func (h *hyper) init(pod *Pod, config interface{}) (err error) {
	switch c := config.(type) {
	case HyperConfig:
		if c.validate(*pod) == false {
			return fmt.Errorf("Invalid configuration")
		}
		h.config = c
	default:
		return fmt.Errorf("Invalid config type")
	}

	// Override pod agent configuration
	pod.config.AgentConfig = h.config

	for _, volume := range h.config.Volumes {
		err := pod.hypervisor.addDevice(volume, fsDev)
		if err != nil {
			return err
		}
	}

	for _, socket := range h.config.Sockets {
		err := pod.hypervisor.addDevice(socket, serialPortDev)
		if err != nil {
			return err
		}
	}

	// Adding the hyper shared volume.
	// This volume contains all bind mounted container bundles.
	sharedVolume := Volume{
		MountTag: mountTag,
		HostPath: filepath.Join(defaultSharedDir, pod.id),
	}

	if err := os.MkdirAll(sharedVolume.HostPath, dirMode); err != nil {
		return err
	}

	if err := pod.hypervisor.addDevice(sharedVolume, fsDev); err != nil {
		return err
	}

	h.proxy = pod.proxy

	return nil
}

// exec is the agent command execution implementation for hyperstart.
func (h *hyper) exec(pod *Pod, c Container, process Process, cmd Cmd) error {
	hyperProcess, err := h.buildHyperContainerProcess(cmd)
	if err != nil {
		return err
	}

	execCommand := hyperstart.ExecCommand{
		Container: c.id,
		Process:   *hyperProcess,
	}

	proxyCmd := hyperstartProxyCmd{
		cmd:     hyperstart.ExecCmd,
		message: execCommand,
		token:   process.Token,
	}

	if _, err := h.proxy.sendCmd(proxyCmd); err != nil {
		return err
	}

	return nil
}

// startPod is the agent Pod starting implementation for hyperstart.
func (h *hyper) startPod(pod Pod) error {
	ifaces, routes, err := h.buildNetworkInterfacesAndRoutes(pod)
	if err != nil {
		return err
	}

	hyperPod := hyperstart.Pod{
		Hostname:   pod.id,
		Containers: []hyperstart.Container{},
		Interfaces: ifaces,
		Routes:     routes,
		ShareDir:   mountTag,
	}

	proxyCmd := hyperstartProxyCmd{
		cmd:     hyperstart.StartPod,
		message: hyperPod,
	}

	if _, err := h.proxy.sendCmd(proxyCmd); err != nil {
		return err
	}

	if err := h.startPauseContainer(pod.id); err != nil {
		return err
	}

	for _, c := range pod.containers {
		if err := h.startOneContainer(pod, *c); err != nil {
			return err
		}
	}

	return nil
}

// stopPod is the agent Pod stopping implementation for hyperstart.
func (h *hyper) stopPod(pod Pod) error {
	for _, c := range pod.containers {
		state, err := pod.storage.fetchContainerState(pod.id, c.id)
		if err != nil {
			return err
		}

		if state.State != StateRunning {
			continue
		}

		if err := h.killOneContainer(c.id, syscall.SIGTERM, true); err != nil {
			return err
		}

		if err := h.stopOneContainer(pod.id, c.id); err != nil {
			return err
		}
	}

	if err := h.stopPauseContainer(pod.id); err != nil {
		return err
	}

	return nil
}

// startPauseContainer starts a specific container running the pause binary provided.
func (h *hyper) startPauseContainer(podID string) error {
	cmd := Cmd{
		Args:        []string{fmt.Sprintf("./%s", pauseBinName)},
		Envs:        []EnvVar{},
		WorkDir:     "/",
		Interactive: false,
	}

	process, err := h.buildHyperContainerProcess(cmd)
	if err != nil {
		return err
	}

	container := hyperstart.Container{
		ID:      pauseContainerName,
		Image:   pauseContainerName,
		Rootfs:  rootfsDir,
		Process: process,
	}

	if err := h.copyPauseBinary(podID); err != nil {
		return err
	}

	proxyCmd := hyperstartProxyCmd{
		cmd:     hyperstart.NewContainer,
		message: container,
	}

	if _, err := h.proxy.sendCmd(proxyCmd); err != nil {
		return err
	}

	return nil
}

func (h *hyper) startOneContainer(pod Pod, c Container) error {
	process, err := h.buildHyperContainerProcess(c.config.Cmd)
	if err != nil {
		return err
	}

	container := hyperstart.Container{
		ID:      c.id,
		Image:   c.id,
		Rootfs:  rootfsDir,
		Process: process,
	}

	if err := h.bindMountContainerRootfs(pod.id, c.id, c.rootFs, c.config.ReadonlyRootfs); err != nil {
		h.bindUnmountAllRootfs(pod)
		return err
	}

	proxyCmd := hyperstartProxyCmd{
		cmd:     hyperstart.NewContainer,
		message: container,
		token:   c.process.Token,
	}

	if _, err := h.proxy.sendCmd(proxyCmd); err != nil {
		return err
	}

	return nil
}

// createContainer is the agent Container creation implementation for hyperstart.
func (h *hyper) createContainer(pod *Pod, c *Container) error {
	return nil
}

// startContainer is the agent Container starting implementation for hyperstart.
func (h *hyper) startContainer(pod Pod, c Container) error {
	return h.startOneContainer(pod, c)
}

func (h *hyper) stopPauseContainer(podID string) error {
	if err := h.killOneContainer(pauseContainerName, syscall.SIGKILL, true); err != nil {
		return err
	}

	if err := h.removePauseBinary(podID); err != nil {
		return err
	}

	return nil
}

// stopContainer is the agent Container stopping implementation for hyperstart.
func (h *hyper) stopContainer(pod Pod, c Container) error {
	return h.stopOneContainer(pod.id, c.id)
}

func (h *hyper) stopOneContainer(podID, cID string) error {
	removeCommand := hyperstart.RemoveCommand{
		Container: cID,
	}

	proxyCmd := hyperstartProxyCmd{
		cmd:     hyperstart.RemoveContainer,
		message: removeCommand,
	}

	if _, err := h.proxy.sendCmd(proxyCmd); err != nil {
		return err
	}

	if err := h.bindUnmountContainerRootfs(podID, cID); err != nil {
		return err
	}

	return nil
}

// killContainer is the agent process signal implementation for hyperstart.
func (h *hyper) killContainer(pod Pod, c Container, signal syscall.Signal, all bool) error {
	return h.killOneContainer(c.id, signal, all)
}

func (h *hyper) killOneContainer(cID string, signal syscall.Signal, all bool) error {
	killCmd := hyperstart.KillCommand{
		Container:    cID,
		Signal:       signal,
		AllProcesses: all,
	}

	proxyCmd := hyperstartProxyCmd{
		cmd:     hyperstart.KillContainer,
		message: killCmd,
	}

	if _, err := h.proxy.sendCmd(proxyCmd); err != nil {
		return err
	}

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

package virtcontainers

import (
	"fmt"
	"os"
	"path/filepath"
	"syscall"

	"github.com/containers/virtcontainers/pkg/hyperstart"
)

var defaultSockPathTemplates = []string{"/tmp/hyper-pod-%s.sock", "/tmp/tty-pod%s.sock"}
var defaultChannelTemplate = "sh.hyper.channel.%d"
var defaultDeviceIDTemplate = "channel%d"
var defaultIDTemplate = "charch%d"
var defaultSharedDir = "/tmp/hyper/shared/pods/"
var defaultPauseBinDir = "/usr/bin/"
var mountTag = "hyperShared"
var rootfsDir = "rootfs"
var pauseBinName = "pause"
var pauseContainerName = "pause-container"

const (
	unixSocket = "unix"
)

// HyperConfig is a structure storing information needed for
// hyperstart agent initialization.
type HyperConfig struct {
	SockCtlName  string
	SockTtyName  string
	Volumes      []Volume
	Sockets      []Socket
	PauseBinPath string
}

func (c *HyperConfig) validate(pod Pod) bool {
	if len(c.Sockets) == 0 {
		virtLog.Infof("No sockets from configuration")

		podSocketPaths := []string{
			fmt.Sprintf(defaultSockPathTemplates[0], pod.id),
			fmt.Sprintf(defaultSockPathTemplates[1], pod.id),
		}

		c.SockCtlName = podSocketPaths[0]
		c.SockTtyName = podSocketPaths[1]

		for i := 0; i < len(podSocketPaths); i++ {
			s := Socket{
				DeviceID: fmt.Sprintf(defaultDeviceIDTemplate, i),
				ID:       fmt.Sprintf(defaultIDTemplate, i),
				HostPath: podSocketPaths[i],
				Name:     fmt.Sprintf(defaultChannelTemplate, i),
			}
			c.Sockets = append(c.Sockets, s)
		}
	}

	if len(c.Sockets) != 2 {
		return false
	}

	if c.PauseBinPath == "" {
		c.PauseBinPath = filepath.Join(defaultPauseBinDir, pauseBinName)
	}

	virtLog.Infof("Hyperstart config %v", c)

	return true
}

// hyper is the Agent interface implementation for hyperstart.
type hyper struct {
	config HyperConfig
	proxy  proxy
}

type hyperstartProxyCmd struct {
	cmd     string
	message interface{}
	token   string
}

func (h *hyper) buildHyperContainerProcess(cmd Cmd) (*hyperstart.Process, error) {
	var envVars []hyperstart.EnvironmentVar

	for _, e := range cmd.Envs {
		envVar := hyperstart.EnvironmentVar{
			Env:   e.Var,
			Value: e.Value,
		}

		envVars = append(envVars, envVar)
	}

	process := &hyperstart.Process{
		Terminal: cmd.Interactive,
		Args:     cmd.Args,
		Envs:     envVars,
		Workdir:  cmd.WorkDir,
	}

	return process, nil
}

func (h *hyper) buildNetworkInterfacesAndRoutes(pod Pod) ([]hyperstart.NetworkIface, []hyperstart.Route, error) {
	networkNS, err := pod.storage.fetchPodNetwork(pod.id)
	if err != nil {
		return []hyperstart.NetworkIface{}, []hyperstart.Route{}, err
	}

	if networkNS.NetNsPath == "" {
		return []hyperstart.NetworkIface{}, []hyperstart.Route{}, nil
	}

	netIfaces, err := getIfacesFromNetNs(networkNS.NetNsPath)
	if err != nil {
		return []hyperstart.NetworkIface{}, []hyperstart.Route{}, err
	}

	var ifaces []hyperstart.NetworkIface
	var routes []hyperstart.Route
	for _, endpoint := range networkNS.Endpoints {
		netIface, err := getNetIfaceByName(endpoint.NetPair.VirtIface.Name, netIfaces)
		if err != nil {
			return []hyperstart.NetworkIface{}, []hyperstart.Route{}, err
		}

		var ipAddrs []hyperstart.IPAddress
		for _, ipConfig := range endpoint.Properties.IPs {
			// Skip IPv6 because not supported by hyperstart
			if ipConfig.Version == "6" || ipConfig.Address.IP.To4() == nil {
				continue
			}

			netMask, _ := ipConfig.Address.Mask.Size()

			ipAddr := hyperstart.IPAddress{
				IPAddress: ipConfig.Address.IP.String(),
				NetMask:   fmt.Sprintf("%d", netMask),
			}

			ipAddrs = append(ipAddrs, ipAddr)
		}

		iface := hyperstart.NetworkIface{
			NewDevice:   endpoint.NetPair.VirtIface.Name,
			IPAddresses: ipAddrs,
			MTU:         fmt.Sprintf("%d", netIface.MTU),
			MACAddr:     endpoint.NetPair.TAPIface.HardAddr,
		}

		ifaces = append(ifaces, iface)

		for _, r := range endpoint.Properties.Routes {
			// Skip IPv6 because not supported by hyperstart
			if r.Dst.IP.To4() == nil {
				continue
			}

			gateway := r.GW.String()
			if gateway == "<nil>" {
				gateway = ""
			}

			route := hyperstart.Route{
				Dest:    r.Dst.String(),
				Gateway: gateway,
				Device:  endpoint.NetPair.VirtIface.Name,
			}

			routes = append(routes, route)
		}
	}

	return ifaces, routes, nil
}

func (h *hyper) copyPauseBinary(podID string) error {
	pauseDir := filepath.Join(defaultSharedDir, podID, pauseContainerName, rootfsDir)

	if err := os.MkdirAll(pauseDir, dirMode); err != nil {
		return err
	}

	pausePath := filepath.Join(pauseDir, pauseBinName)

	return fileCopy(h.config.PauseBinPath, pausePath)
}

func (h *hyper) removePauseBinary(podID string) error {
	pauseDir := filepath.Join(defaultSharedDir, podID, pauseContainerName)

	return os.RemoveAll(pauseDir)
}

func (h *hyper) bindMountContainerRootfs(podID, cID, cRootFs string, readonly bool) error {
	rootfsDest := filepath.Join(defaultSharedDir, podID, cID, rootfsDir)

	return bindMount(cRootFs, rootfsDest, readonly)
}

func (h *hyper) bindUnmountContainerRootfs(podID, cID string) error {
	rootfsDest := filepath.Join(defaultSharedDir, podID, cID, rootfsDir)
	syscall.Unmount(rootfsDest, 0)

	return nil
}

func (h *hyper) bindUnmountAllRootfs(pod Pod) {
	for _, c := range pod.containers {
		h.bindUnmountContainerRootfs(pod.id, c.id)
	}
}

// init is the agent initialization implementation for hyperstart.
func (h *hyper) init(pod *Pod, config interface{}) (err error) {
	switch c := config.(type) {
	case HyperConfig:
		if c.validate(*pod) == false {
			return fmt.Errorf("Invalid configuration")
		}
		h.config = c
	default:
		return fmt.Errorf("Invalid config type")
	}

	// Override pod agent configuration
	pod.config.AgentConfig = h.config

	for _, volume := range h.config.Volumes {
		err := pod.hypervisor.addDevice(volume, fsDev)
		if err != nil {
			return err
		}
	}

	for _, socket := range h.config.Sockets {
		err := pod.hypervisor.addDevice(socket, serialPortDev)
		if err != nil {
			return err
		}
	}

	// Adding the hyper shared volume.
	// This volume contains all bind mounted container bundles.
	sharedVolume := Volume{
		MountTag: mountTag,
		HostPath: filepath.Join(defaultSharedDir, pod.id),
	}

	if err := os.MkdirAll(sharedVolume.HostPath, dirMode); err != nil {
		return err
	}

	if err := pod.hypervisor.addDevice(sharedVolume, fsDev); err != nil {
		return err
	}

	h.proxy = pod.proxy

	return nil
}

// exec is the agent command execution implementation for hyperstart.
func (h *hyper) exec(pod *Pod, c Container, process Process, cmd Cmd) error {
	hyperProcess, err := h.buildHyperContainerProcess(cmd)
	if err != nil {
		return err
	}

	execCommand := hyperstart.ExecCommand{
		Container: c.id,
		Process:   *hyperProcess,
	}

	proxyCmd := hyperstartProxyCmd{
		cmd:     hyperstart.ExecCmd,
		message: execCommand,
		token:   process.Token,
	}

	if _, err := h.proxy.sendCmd(proxyCmd); err != nil {
		return err
	}

	return nil
}

// startPod is the agent Pod starting implementation for hyperstart.
func (h *hyper) startPod(pod Pod) error {
	ifaces, routes, err := h.buildNetworkInterfacesAndRoutes(pod)
	if err != nil {
		return err
	}

	hyperPod := hyperstart.Pod{
		Hostname:   pod.id,
		Containers: []hyperstart.Container{},
		Interfaces: ifaces,
		Routes:     routes,
		ShareDir:   mountTag,
	}

	proxyCmd := hyperstartProxyCmd{
		cmd:     hyperstart.StartPod,
		message: hyperPod,
	}

	if _, err := h.proxy.sendCmd(proxyCmd); err != nil {
		return err
	}

	if err := h.startPauseContainer(pod.id); err != nil {
		return err
	}

	for _, c := range pod.containers {
		if err := h.startOneContainer(pod, *c); err != nil {
			return err
		}
	}

	return nil
}

// stopPod is the agent Pod stopping implementation for hyperstart.
func (h *hyper) stopPod(pod Pod) error {
	for _, c := range pod.containers {
		state, err := pod.storage.fetchContainerState(pod.id, c.id)
		if err != nil {
			return err
		}

		if state.State != StateRunning {
			continue
		}

		if err := h.killOneContainer(c.id, syscall.SIGTERM, true); err != nil {
			return err
		}

		if err := h.stopOneContainer(pod.id, c.id); err != nil {
			return err
		}
	}

	if err := h.stopPauseContainer(pod.id); err != nil {
		return err
	}

	return nil
}

// startPauseContainer starts a specific container running the pause binary provided.
func (h *hyper) startPauseContainer(podID string) error {
	cmd := Cmd{
		Args:        []string{fmt.Sprintf("./%s", pauseBinName)},
		Envs:        []EnvVar{},
		WorkDir:     "/",
		Interactive: false,
	}

	process, err := h.buildHyperContainerProcess(cmd)
	if err != nil {
		return err
	}

	container := hyperstart.Container{
		ID:      pauseContainerName,
		Image:   pauseContainerName,
		Rootfs:  rootfsDir,
		Process: process,
	}

	if err := h.copyPauseBinary(podID); err != nil {
		return err
	}

	proxyCmd := hyperstartProxyCmd{
		cmd:     hyperstart.NewContainer,
		message: container,
	}

	if _, err := h.proxy.sendCmd(proxyCmd); err != nil {
		return err
	}

	return nil
}

func (h *hyper) startOneContainer(pod Pod, c Container) error {
	process, err := h.buildHyperContainerProcess(c.config.Cmd)
	if err != nil {
		return err
	}

	container := hyperstart.Container{
		ID:      c.id,
		Image:   c.id,
		Rootfs:  rootfsDir,
		Process: process,
	}

	if err := h.bindMountContainerRootfs(pod.id, c.id, c.rootFs, c.config.ReadonlyRootfs); err != nil {
		h.bindUnmountAllRootfs(pod)
		return err
	}

	proxyCmd := hyperstartProxyCmd{
		cmd:     hyperstart.NewContainer,
		message: container,
		token:   c.process.Token,
	}

	if _, err := h.proxy.sendCmd(proxyCmd); err != nil {
		return err
	}

	return nil
}

// createContainer is the agent Container creation implementation for hyperstart.
func (h *hyper) createContainer(pod *Pod, c *Container) error {
	return nil
}

// startContainer is the agent Container starting implementation for hyperstart.
func (h *hyper) startContainer(pod Pod, c Container) error {
	return h.startOneContainer(pod, c)
}

func (h *hyper) stopPauseContainer(podID string) error {
	if err := h.killOneContainer(pauseContainerName, syscall.SIGKILL, true); err != nil {
		return err
	}

	if err := h.removePauseBinary(podID); err != nil {
		return err
	}

	return nil
}

// stopContainer is the agent Container stopping implementation for hyperstart.
func (h *hyper) stopContainer(pod Pod, c Container) error {
	return h.stopOneContainer(pod.id, c.id)
}

func (h *hyper) stopOneContainer(podID, cID string) error {
	removeCommand := hyperstart.RemoveCommand{
		Container: cID,
	}

	proxyCmd := hyperstartProxyCmd{
		cmd:     hyperstart.RemoveContainer,
		message: removeCommand,
	}

	if _, err := h.proxy.sendCmd(proxyCmd); err != nil {
		return err
	}

	if err := h.bindUnmountContainerRootfs(podID, cID); err != nil {
		return err
	}

	return nil
}

// killContainer is the agent process signal implementation for hyperstart.
func (h *hyper) killContainer(pod Pod, c Container, signal syscall.Signal, all bool) error {
	return h.killOneContainer(c.id, signal, all)
}

func (h *hyper) killOneContainer(cID string, signal syscall.Signal, all bool) error {
	killCmd := hyperstart.KillCommand{
		Container:    cID,
		Signal:       signal,
		AllProcesses: all,
	}

	proxyCmd := hyperstartProxyCmd{
		cmd:     hyperstart.KillContainer,
		message: killCmd,
	}

	if _, err := h.proxy.sendCmd(proxyCmd); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package natural

import "github.com/xconstruct/stark/proto"

const defaultRegularText = `
- example: I started to play music
  fields:
    action: event/new
    subject: [I]
    verb: [play]
    object: [music]
    status: started

- example: I finished to play music
  fields:
    action: event/new
    subject: [I]
    verb: [play]
    object: [music]
    status: ended

- example: I drink coffee
  fields:
    action: event/new
    subject: I
    verb: [drink]
    object: [coffee]
    status: singular

- example: When did I last visit Big City
  fields:
    action: location/last
    address: [Big City]

- example: When did I last drink coffee
  fields:
    action: event/last
    subject: [I]
    verb: [drink]
    object: [coffee]

- example: Push this long text to phone
  fields:
    action: push/text
    text: [this long text]
    device: [phone]

- example: Remind me in 10m to make coffee
  fields:
    action: schedule/duration
    duration: [10m]
    text: [make coffee]

- example: Remind me to make coffee in 10m
  fields:
    action: schedule/duration
    duration: [10m]
    text: [make coffee]

- example: Remind me in 10m
  fields:
    action: schedule/duration
    duration: [10m]

- example: What is 3 + 5
  fields:
    action: know/query
    query: [3 + 5]

- example: Create a geofence named home at 221b Baker Street
  fields:
    action: location/fence/create
    name: [home]
    address: [221b Baker Street]

- example: Create a geofence at friends house
  fields:
    action: location/fence/create
    address: [friends house]
`

var defaultRegular RegularSchemata

func ParseRegular(text string) (proto.Message, bool) {
	if defaultRegular == nil {
		var err error
		defaultRegular, err = LoadRegularSchemata(defaultRegularText)
		if err != nil {
			panic(err)
		}
	}

	return defaultRegular.Parse(text)
}
<commit_msg>Natural: fix knowledge query rule.<commit_after>package natural

import "github.com/xconstruct/stark/proto"

const defaultRegularText = `
- example: I started to play music
  fields:
    action: event/new
    subject: [I]
    verb: [play]
    object: [music]
    status: started

- example: I finished to play music
  fields:
    action: event/new
    subject: [I]
    verb: [play]
    object: [music]
    status: ended

- example: I drink coffee
  fields:
    action: event/new
    subject: I
    verb: [drink]
    object: [coffee]
    status: singular

- example: When did I last visit Big City
  fields:
    action: location/last
    address: [Big City]

- example: When did I last drink coffee
  fields:
    action: event/last
    subject: [I]
    verb: [drink]
    object: [coffee]

- example: Push this long text to phone
  fields:
    action: push/text
    text: [this long text]
    device: [phone]

- example: Remind me in 10m to make coffee
  fields:
    action: schedule/duration
    duration: [10m]
    text: [make coffee]

- example: Remind me to make coffee in 10m
  fields:
    action: schedule/duration
    duration: [10m]
    text: [make coffee]

- example: Remind me in 10m
  fields:
    action: schedule/duration
    duration: [10m]

- example: What is 3 + 5
  fields:
    action: knowledge/query
    text: [3 + 5]

- example: Create a geofence named home at 221b Baker Street
  fields:
    action: location/fence/create
    name: [home]
    address: [221b Baker Street]

- example: Create a geofence at friends house
  fields:
    action: location/fence/create
    address: [friends house]

- example: What is the birth day of Tuomas Holopainen?
  fields:
    action: knowledge/query
    text: [birth day of Tuomas Holopainen]
`

var defaultRegular RegularSchemata

func ParseRegular(text string) (proto.Message, bool) {
	if defaultRegular == nil {
		var err error
		defaultRegular, err = LoadRegularSchemata(defaultRegularText)
		if err != nil {
			panic(err)
		}
	}

	return defaultRegular.Parse(text)
}
<|endoftext|>


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

package azure

import (
	. "launchpad.net/gocheck"
	"launchpad.net/juju-core/environs/config"
	"sync"
)

type EnvironSuite struct {
	ProviderSuite
}

var _ = Suite(new(EnvironSuite))

func makeEnviron(c *C) *azureEnviron {
	attrs := makeAzureConfigMap(c)
	cfg, err := config.New(attrs)
	c.Assert(err, IsNil)
	ecfg, err := azureEnvironProvider{}.newConfig(cfg)
	c.Assert(err, IsNil)
	return &azureEnviron{
		name: "env",
		ecfg: ecfg,
	}
}

// exerciseLockingFunction verifies that a function obeys a given lock.
//
// Use this as a building block in your own tests for proper locking.
// Parameters are the lock that you expect your function to block on; the
// function that you want to test for proper locking on that lock; and a
// marker callback whose effect you can observe.
//
// There needs to be a testable interaction between your "function" and your
// "marker," such that you can see from the result of "function" which of the
// two executed first.  So the "marker" must make a change that your "function"
// can observe, and the "function" must return a result that you can look at
// later to see whether it ran before or after the "marker."  You will get that
// result back as the return value.
//
// If the function that you're actually testing does not return anything that
// you can check for the marker effect, then pass a wrapper function that first
// invokes the function whose locking you want to test, and then records a
// result that shows whether the marker has run.
//
// After calling this function, you should test that:
// 1. The lock has been released.
// 2. Your "marker" function was completed before "function" executed.
//
// The "marker" will be executed while holding "lock."
//
// The return value of "function" must be suitable for transmission through a
// channel.
func exerciseLockingFunction(lock sync.Locker, function func() interface{}, marker func()) interface{} {
	// Here's the scenario this test aims for:
	// 1. We grab the lock.
	// 2. Goroutine invokes "function."
	// 3. Since the lock is not available, goroutine should block there.
	// 4. Execute "marker," while "function" is still blocked.
	// 5. Release the lock.
	// 6. Now execution of "function" can complete.
	//
	// If "function" blocks on "lock," then you should see the effect of
	// "marker" already present in the result.  If it doesn't, then
	// "function" will execute straight through.  In single-thread
	// execution you'll see a result that predates execution of the marker,
	// or in multi-thread execution the result will be indeterminate.
	result := make(chan interface{})
	proceed := make(chan bool)

	lock.Lock()
	go func() {
		proceed <- true
		result <- function()
	}()

	// Wait for the goroutine to start.  It should get stuck on "function."
	<-proceed

	// TODO: In Go 1.1, call runtime.GoSched a few times to give a
	// misbehaved "function" plenty of rope to hang itself.

	// Run the marker, while still holding the lock.  If "function" is
	// well-behaved, it's still waiting for us to do this.
	marker()
	// And now, finally, allow "function" to complete.
	lock.Unlock()

	return <-result
}

func (EnvironSuite) TestGetSnapshot(c *C) {
	original := azureEnviron{name: "this-env", ecfg: new(azureEnvironConfig)}
	snapshot := original.getSnapshot()

	// The snapshot is identical to the original.
	c.Check(*snapshot, DeepEquals, original)

	// However, they are distinct objects.
	c.Check(snapshot, Not(Equals), &original)

	// It's a shallow copy; they still share pointers.
	c.Check(snapshot.ecfg, Equals, original.ecfg)

	// Neither object is locked at the end of the copy.
	c.Check(original.Mutex, Equals, sync.Mutex{})
	c.Check(snapshot.Mutex, Equals, sync.Mutex{})
}

func (EnvironSuite) TestGetSnapshotLocksEnviron(c *C) {
	original := azureEnviron{name: "old-name"}
	snapshot := exerciseLockingFunction(
		&original,
		func() interface{} { return original.getSnapshot() },
		func() { original.name = "new-name" })

	c.Check(original.Mutex, Equals, sync.Mutex{})
	c.Check(snapshot.(*azureEnviron).name, Equals, "new-name")
}

func (EnvironSuite) TestName(c *C) {
	env := azureEnviron{name: "foo"}
	c.Check(env.Name(), Equals, env.name)
}

func (EnvironSuite) TestConfigReturnsConfig(c *C) {
	cfg := new(config.Config)
	ecfg := azureEnvironConfig{Config: cfg}
	env := azureEnviron{ecfg: &ecfg}
	c.Check(env.Config(), Equals, cfg)
}

func (EnvironSuite) TestConfigLocksEnviron(c *C) {
	env := azureEnviron{name: "env", ecfg: new(azureEnvironConfig)}
	newConfig := new(config.Config)
	config := exerciseLockingFunction(
		&env,
		func() interface{} { return env.Config() },
		func() { env.ecfg = &azureEnvironConfig{Config: newConfig} })
	c.Check(env.Mutex, Equals, sync.Mutex{})
	c.Check(config, Equals, newConfig)
}

// TODO: Temporarily deactivating this code.  Passing certificate in-memory
// may require gwacl change.
/*
func (EnvironSuite) TestGetManagementAPI(c *C) {
	env := makeEnviron(c)
	management, err := env.getManagementAPI()
	c.Assert(err, IsNil)
	c.Check(management, NotNil)
}
*/

func (EnvironSuite) TestGetStorageContext(c *C) {
	env := makeEnviron(c)
	storage, err := env.getStorageContext()
	c.Assert(err, IsNil)
	c.Assert(storage, NotNil)
	c.Check(storage.Account, Equals, env.ecfg.StorageAccountName())
	c.Check(storage.Key, Equals, env.ecfg.StorageAccountKey())
}
<commit_msg>Test helper: test a function's locking behaviour.<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package azure

import (
	. "launchpad.net/gocheck"
	"launchpad.net/juju-core/environs/config"
	"sync"
)

type EnvironSuite struct {
	ProviderSuite
}

var _ = Suite(new(EnvironSuite))

func makeEnviron(c *C) *azureEnviron {
	attrs := makeAzureConfigMap(c)
	cfg, err := config.New(attrs)
	c.Assert(err, IsNil)
	ecfg, err := azureEnvironProvider{}.newConfig(cfg)
	c.Assert(err, IsNil)
	return &azureEnviron{
		name: "env",
		ecfg: ecfg,
	}
}

// testLockingFunction verifies that a function obeys a given lock.
//
// Use this as a building block in your own tests for proper locking.
// Parameters are a gocheck object to run assertions on; the lock that you
// expect your function to block on; and the function that you want to test
// for proper locking on that lock.
func testLockingFunction(c *C, lock *sync.Mutex, function func()) {
	// We record two events that must happen in the right order.
	// Buffer the channel so that we don't get hung up during attempts
	// to push the events in.
	events := make(chan string, 2)
	// Synchronization channel, to make sure that the function starts
	// trying to run at the point where we're going to make it block.
	proceed := make(chan bool)

	goroutine := func() {
		proceed <- true
		function()
		events <- "function completed"
	}

	lock.Lock()
	go goroutine()
	// Kick off the goroutine.  It should block in "function()."
	<-proceed

	// TODO: In Go 1.1, call runtime.GoSched a few times to give a
	// misbehaved "function" plenty of rope to hang itself.

	events <- "releasing lock"
	lock.Unlock()

	// Now that we've released the lock, the function is unblocked.  Read
	// the 2 events.  (This will wait until the function has completed.)
	firstEvent := <-events
	secondEvent := <-events
	c.Check(firstEvent, Equals, "releasing lock")
	c.Check(secondEvent, Equals, "function completed")

	// Check that the function has released the lock.
	c.Check(*lock, Equals, sync.Mutex{})
}

func (EnvironSuite) TestGetSnapshot(c *C) {
	original := azureEnviron{name: "this-env", ecfg: new(azureEnvironConfig)}
	snapshot := original.getSnapshot()

	// The snapshot is identical to the original.
	c.Check(*snapshot, DeepEquals, original)

	// However, they are distinct objects.
	c.Check(snapshot, Not(Equals), &original)

	// It's a shallow copy; they still share pointers.
	c.Check(snapshot.ecfg, Equals, original.ecfg)

	// Neither object is locked at the end of the copy.
	c.Check(original.Mutex, Equals, sync.Mutex{})
	c.Check(snapshot.Mutex, Equals, sync.Mutex{})
}

func (EnvironSuite) TestGetSnapshotLocksEnviron(c *C) {
	original := azureEnviron{}
	testLockingFunction(c, &original.Mutex, func() { original.getSnapshot() })
}

func (EnvironSuite) TestName(c *C) {
	env := azureEnviron{name: "foo"}
	c.Check(env.Name(), Equals, env.name)
}

func (EnvironSuite) TestConfigReturnsConfig(c *C) {
	cfg := new(config.Config)
	ecfg := azureEnvironConfig{Config: cfg}
	env := azureEnviron{ecfg: &ecfg}
	c.Check(env.Config(), Equals, cfg)
}

func (EnvironSuite) TestConfigLocksEnviron(c *C) {
	env := azureEnviron{name: "env", ecfg: new(azureEnvironConfig)}
	testLockingFunction(c, &env.Mutex, func() { env.Config() })
}

// TODO: Temporarily deactivating this code.  Passing certificate in-memory
// may require gwacl change.
/*
func (EnvironSuite) TestGetManagementAPI(c *C) {
	env := makeEnviron(c)
	management, err := env.getManagementAPI()
	c.Assert(err, IsNil)
	c.Check(management, NotNil)
}
*/

func (EnvironSuite) TestGetStorageContext(c *C) {
	env := makeEnviron(c)
	storage, err := env.getStorageContext()
	c.Assert(err, IsNil)
	c.Assert(storage, NotNil)
	c.Check(storage.Account, Equals, env.ecfg.StorageAccountName())
	c.Check(storage.Key, Equals, env.ecfg.StorageAccountKey())
}
<|endoftext|>


<commit_before>// This package implements a provisioner for Packer that executes
// shell scripts within the remote machine.
package shell

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	"strings"
	"time"

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

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

	// If true, the script contains binary and line endings will not be
	// converted from Windows to Unix-style.
	Binary bool

	// An inline script to execute. Multiple strings are all executed
	// in the context of a single shell.
	Inline []string

	// The shebang value used when running inline scripts.
	InlineShebang string `mapstructure:"inline_shebang"`

	// The local path of the shell script to upload and execute.
	Script string

	// An array of multiple scripts to run.
	Scripts []string

	// An array of environment variables that will be injected before
	// your command(s) are executed.
	Vars []string `mapstructure:"environment_vars"`

	// The remote folder where the local shell script will be uploaded to.
	// This should be set to a pre-existing directory, it defaults to /tmp
	RemoteFolder string `mapstructure:"remote_folder"`

	// The remote file name of the local shell script.
	// This defaults to script_nnn.sh
	RemoteFile string `mapstructure:"remote_file"`

	// The remote path where the local shell script will be uploaded to.
	// This should be set to a writable file that is in a pre-existing directory.
	// This defaults to remote_folder/remote_file
	RemotePath string `mapstructure:"remote_path"`

	// The command used to execute the script. The '{{ .Path }}' variable
	// should be used to specify where the script goes, {{ .Vars }}
	// can be used to inject the environment_vars into the environment.
	ExecuteCommand string `mapstructure:"execute_command"`

	// The timeout for retrying to start the process. Until this timeout
	// is reached, if the provisioner can't start a process, it retries.
	// This can be set high to allow for reboots.
	RawStartRetryTimeout string `mapstructure:"start_retry_timeout"`

	// Whether to clean scripts up
	SkipClean bool `mapstructure:"skip_clean"`

	ExpectDisconnect *bool `mapstructure:"expect_disconnect"`

	startRetryTimeout time.Duration
	ctx               interpolate.Context
}

type Provisioner struct {
	config Config
}

type ExecuteCommandTemplate struct {
	Vars string
	Path string
}

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

	if p.config.ExecuteCommand == "" {
		p.config.ExecuteCommand = "chmod +x {{.Path}}; {{.Vars}} {{.Path}}"
	}

	if p.config.ExpectDisconnect == nil {
		t := true
		p.config.ExpectDisconnect = &t
	}

	if p.config.Inline != nil && len(p.config.Inline) == 0 {
		p.config.Inline = nil
	}

	if p.config.InlineShebang == "" {
		p.config.InlineShebang = "/bin/sh -e"
	}

	if p.config.RawStartRetryTimeout == "" {
		p.config.RawStartRetryTimeout = "5m"
	}

	if p.config.RemoteFolder == "" {
		p.config.RemoteFolder = "/tmp"
	}

	if p.config.RemoteFile == "" {
		p.config.RemoteFile = fmt.Sprintf("script_%d.sh", rand.Intn(9999))
	}

	if p.config.RemotePath == "" {
		p.config.RemotePath = fmt.Sprintf(
			"%s/%s", p.config.RemoteFolder, p.config.RemoteFile)
	}

	if p.config.Scripts == nil {
		p.config.Scripts = make([]string, 0)
	}

	if p.config.Vars == nil {
		p.config.Vars = make([]string, 0)
	}

	var errs *packer.MultiError
	if p.config.Script != "" && len(p.config.Scripts) > 0 {
		errs = packer.MultiErrorAppend(errs,
			errors.New("Only one of script or scripts can be specified."))
	}

	if p.config.Script != "" {
		p.config.Scripts = []string{p.config.Script}
	}

	if len(p.config.Scripts) == 0 && p.config.Inline == nil {
		errs = packer.MultiErrorAppend(errs,
			errors.New("Either a script file or inline script must be specified."))
	} else if len(p.config.Scripts) > 0 && p.config.Inline != nil {
		errs = packer.MultiErrorAppend(errs,
			errors.New("Only a script file or an inline script can be specified, not both."))
	}

	for _, path := range p.config.Scripts {
		if _, err := os.Stat(path); err != nil {
			errs = packer.MultiErrorAppend(errs,
				fmt.Errorf("Bad script '%s': %s", path, err))
		}
	}

	// Do a check for bad environment variables, such as '=foo', 'foobar'
	for idx, kv := range p.config.Vars {
		vs := strings.SplitN(kv, "=", 2)
		if len(vs) != 2 || vs[0] == "" {
			errs = packer.MultiErrorAppend(errs,
				fmt.Errorf("Environment variable not in format 'key=value': %s", kv))
		} else {
			// Replace single quotes so they parse
			vs[1] = strings.Replace(vs[1], "'", `'"'"'`, -1)

			// Single quote env var values
			p.config.Vars[idx] = fmt.Sprintf("%s='%s'", vs[0], vs[1])
		}
	}

	if p.config.RawStartRetryTimeout != "" {
		p.config.startRetryTimeout, err = time.ParseDuration(p.config.RawStartRetryTimeout)
		if err != nil {
			errs = packer.MultiErrorAppend(
				errs, fmt.Errorf("Failed parsing start_retry_timeout: %s", err))
		}
	}

	if errs != nil && len(errs.Errors) > 0 {
		return errs
	}

	return nil
}

func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
	scripts := make([]string, len(p.config.Scripts))
	copy(scripts, p.config.Scripts)

	// If we have an inline script, then turn that into a temporary
	// shell script and use that.
	if p.config.Inline != nil {
		tf, err := ioutil.TempFile("", "packer-shell")
		if err != nil {
			return fmt.Errorf("Error preparing shell script: %s", err)
		}
		defer os.Remove(tf.Name())

		// Set the path to the temporary file
		scripts = append(scripts, tf.Name())

		// Write our contents to it
		writer := bufio.NewWriter(tf)
		writer.WriteString(fmt.Sprintf("#!%s\n", p.config.InlineShebang))
		for _, command := range p.config.Inline {
			if _, err := writer.WriteString(command + "\n"); err != nil {
				return fmt.Errorf("Error preparing shell script: %s", err)
			}
		}

		if err := writer.Flush(); err != nil {
			return fmt.Errorf("Error preparing shell script: %s", err)
		}

		tf.Close()
	}

	// Build our variables up by adding in the build name and builder type
	envVars := make([]string, len(p.config.Vars)+2)
	envVars[0] = fmt.Sprintf("PACKER_BUILD_NAME='%s'", p.config.PackerBuildName)
	envVars[1] = fmt.Sprintf("PACKER_BUILDER_TYPE='%s'", p.config.PackerBuilderType)

	copy(envVars[2:], p.config.Vars)
	httpAddr := common.GetHTTPAddr()
	if httpAddr != "" {
		envVars = append(envVars, fmt.Sprintf("PACKER_HTTP_ADDR=%s", common.GetHTTPAddr()))
	}

	for _, path := range scripts {
		ui.Say(fmt.Sprintf("Provisioning with shell script: %s", path))

		log.Printf("Opening %s for reading", path)
		f, err := os.Open(path)
		if err != nil {
			return fmt.Errorf("Error opening shell script: %s", err)
		}
		defer f.Close()

		// Flatten the environment variables
		flattendVars := strings.Join(envVars, " ")

		// Compile the command
		p.config.ctx.Data = &ExecuteCommandTemplate{
			Vars: flattendVars,
			Path: p.config.RemotePath,
		}
		command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
		if err != nil {
			return fmt.Errorf("Error processing command: %s", err)
		}

		// Upload the file and run the command. Do this in the context of
		// a single retryable function so that we don't end up with
		// the case that the upload succeeded, a restart is initiated,
		// and then the command is executed but the file doesn't exist
		// any longer.
		var cmd *packer.RemoteCmd
		err = p.retryable(func() error {
			if _, err := f.Seek(0, 0); err != nil {
				return err
			}

			var r io.Reader = f
			if !p.config.Binary {
				r = &UnixReader{Reader: r}
			}

			if err := comm.Upload(p.config.RemotePath, r, nil); err != nil {
				return fmt.Errorf("Error uploading script: %s", err)
			}

			cmd = &packer.RemoteCmd{
				Command: fmt.Sprintf("chmod 0755 %s", p.config.RemotePath),
			}
			if err := comm.Start(cmd); err != nil {
				return fmt.Errorf(
					"Error chmodding script file to 0755 in remote "+
						"machine: %s", err)
			}
			cmd.Wait()

			cmd = &packer.RemoteCmd{Command: command}
			return cmd.StartWithUi(comm, ui)
		})

		if err != nil {
			return err
		}

		// If the exit code indicates a remote disconnect, fail unless
		// we were expecting it.
		if cmd.ExitStatus == packer.CmdDisconnect {
			if !*p.config.ExpectDisconnect {
				return fmt.Errorf("Script disconnected unexpectedly.")
			}
		} else if cmd.ExitStatus != 0 {
			return fmt.Errorf("Script exited with non-zero exit status: %d", cmd.ExitStatus)
		}

		if !p.config.SkipClean {

			// Delete the temporary file we created. We retry this a few times
			// since if the above rebooted we have to wait until the reboot
			// completes.
			err = p.retryable(func() error {
				cmd = &packer.RemoteCmd{
					Command: fmt.Sprintf("rm -f %s", p.config.RemotePath),
				}
				if err := comm.Start(cmd); err != nil {
					return fmt.Errorf(
						"Error removing temporary script at %s: %s",
						p.config.RemotePath, err)
				}
				cmd.Wait()
				// treat disconnects as retryable by returning an error
				if cmd.ExitStatus == packer.CmdDisconnect {
					return fmt.Errorf("Disconnect while removing temporary script.")
				}
				return nil
			})
			if err != nil {
				return err
			}

			if cmd.ExitStatus != 0 {
				return fmt.Errorf(
					"Error removing temporary script at %s!",
					p.config.RemotePath)
			}
		}
	}

	return nil
}

func (p *Provisioner) Cancel() {
	// Just hard quit. It isn't a big deal if what we're doing keeps
	// running on the other side.
	os.Exit(0)
}

// retryable will retry the given function over and over until a
// non-error is returned.
func (p *Provisioner) retryable(f func() error) error {
	startTimeout := time.After(p.config.startRetryTimeout)
	for {
		var err error
		if err = f(); err == nil {
			return nil
		}

		// Create an error and log it
		err = fmt.Errorf("Retryable error: %s", err)
		log.Printf(err.Error())

		// Check if we timed out, otherwise we retry. It is safe to
		// retry since the only error case above is if the command
		// failed to START.
		select {
		case <-startTimeout:
			return err
		default:
			time.Sleep(2 * time.Second)
		}
	}
}
<commit_msg>provisioner/shell: Align gen of flattened env vars with powershell/win-shell<commit_after>// This package implements a provisioner for Packer that executes
// shell scripts within the remote machine.
package shell

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	"sort"
	"strings"
	"time"

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

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

	// If true, the script contains binary and line endings will not be
	// converted from Windows to Unix-style.
	Binary bool

	// An inline script to execute. Multiple strings are all executed
	// in the context of a single shell.
	Inline []string

	// The shebang value used when running inline scripts.
	InlineShebang string `mapstructure:"inline_shebang"`

	// The local path of the shell script to upload and execute.
	Script string

	// An array of multiple scripts to run.
	Scripts []string

	// An array of environment variables that will be injected before
	// your command(s) are executed.
	Vars []string `mapstructure:"environment_vars"`

	// The remote folder where the local shell script will be uploaded to.
	// This should be set to a pre-existing directory, it defaults to /tmp
	RemoteFolder string `mapstructure:"remote_folder"`

	// The remote file name of the local shell script.
	// This defaults to script_nnn.sh
	RemoteFile string `mapstructure:"remote_file"`

	// The remote path where the local shell script will be uploaded to.
	// This should be set to a writable file that is in a pre-existing directory.
	// This defaults to remote_folder/remote_file
	RemotePath string `mapstructure:"remote_path"`

	// The command used to execute the script. The '{{ .Path }}' variable
	// should be used to specify where the script goes, {{ .Vars }}
	// can be used to inject the environment_vars into the environment.
	ExecuteCommand string `mapstructure:"execute_command"`

	// The timeout for retrying to start the process. Until this timeout
	// is reached, if the provisioner can't start a process, it retries.
	// This can be set high to allow for reboots.
	RawStartRetryTimeout string `mapstructure:"start_retry_timeout"`

	// Whether to clean scripts up
	SkipClean bool `mapstructure:"skip_clean"`

	ExpectDisconnect *bool `mapstructure:"expect_disconnect"`

	startRetryTimeout time.Duration
	ctx               interpolate.Context
}

type Provisioner struct {
	config Config
}

type ExecuteCommandTemplate struct {
	Vars string
	Path string
}

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

	if p.config.ExecuteCommand == "" {
		p.config.ExecuteCommand = "chmod +x {{.Path}}; {{.Vars}} {{.Path}}"
	}

	if p.config.ExpectDisconnect == nil {
		t := true
		p.config.ExpectDisconnect = &t
	}

	if p.config.Inline != nil && len(p.config.Inline) == 0 {
		p.config.Inline = nil
	}

	if p.config.InlineShebang == "" {
		p.config.InlineShebang = "/bin/sh -e"
	}

	if p.config.RawStartRetryTimeout == "" {
		p.config.RawStartRetryTimeout = "5m"
	}

	if p.config.RemoteFolder == "" {
		p.config.RemoteFolder = "/tmp"
	}

	if p.config.RemoteFile == "" {
		p.config.RemoteFile = fmt.Sprintf("script_%d.sh", rand.Intn(9999))
	}

	if p.config.RemotePath == "" {
		p.config.RemotePath = fmt.Sprintf(
			"%s/%s", p.config.RemoteFolder, p.config.RemoteFile)
	}

	if p.config.Scripts == nil {
		p.config.Scripts = make([]string, 0)
	}

	if p.config.Vars == nil {
		p.config.Vars = make([]string, 0)
	}

	var errs *packer.MultiError
	if p.config.Script != "" && len(p.config.Scripts) > 0 {
		errs = packer.MultiErrorAppend(errs,
			errors.New("Only one of script or scripts can be specified."))
	}

	if p.config.Script != "" {
		p.config.Scripts = []string{p.config.Script}
	}

	if len(p.config.Scripts) == 0 && p.config.Inline == nil {
		errs = packer.MultiErrorAppend(errs,
			errors.New("Either a script file or inline script must be specified."))
	} else if len(p.config.Scripts) > 0 && p.config.Inline != nil {
		errs = packer.MultiErrorAppend(errs,
			errors.New("Only a script file or an inline script can be specified, not both."))
	}

	for _, path := range p.config.Scripts {
		if _, err := os.Stat(path); err != nil {
			errs = packer.MultiErrorAppend(errs,
				fmt.Errorf("Bad script '%s': %s", path, err))
		}
	}

	// Do a check for bad environment variables, such as '=foo', 'foobar'
	for _, kv := range p.config.Vars {
		vs := strings.SplitN(kv, "=", 2)
		if len(vs) != 2 || vs[0] == "" {
			errs = packer.MultiErrorAppend(errs,
				fmt.Errorf("Environment variable not in format 'key=value': %s", kv))
		}
	}

	if p.config.RawStartRetryTimeout != "" {
		p.config.startRetryTimeout, err = time.ParseDuration(p.config.RawStartRetryTimeout)
		if err != nil {
			errs = packer.MultiErrorAppend(
				errs, fmt.Errorf("Failed parsing start_retry_timeout: %s", err))
		}
	}

	if errs != nil && len(errs.Errors) > 0 {
		return errs
	}

	return nil
}

func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
	scripts := make([]string, len(p.config.Scripts))
	copy(scripts, p.config.Scripts)

	// If we have an inline script, then turn that into a temporary
	// shell script and use that.
	if p.config.Inline != nil {
		tf, err := ioutil.TempFile("", "packer-shell")
		if err != nil {
			return fmt.Errorf("Error preparing shell script: %s", err)
		}
		defer os.Remove(tf.Name())

		// Set the path to the temporary file
		scripts = append(scripts, tf.Name())

		// Write our contents to it
		writer := bufio.NewWriter(tf)
		writer.WriteString(fmt.Sprintf("#!%s\n", p.config.InlineShebang))
		for _, command := range p.config.Inline {
			if _, err := writer.WriteString(command + "\n"); err != nil {
				return fmt.Errorf("Error preparing shell script: %s", err)
			}
		}

		if err := writer.Flush(); err != nil {
			return fmt.Errorf("Error preparing shell script: %s", err)
		}

		tf.Close()
	}

	// Create environment variables to set before executing the command
	flattenedEnvVars := p.createFlattenedEnvVars()

	for _, path := range scripts {
		ui.Say(fmt.Sprintf("Provisioning with shell script: %s", path))

		log.Printf("Opening %s for reading", path)
		f, err := os.Open(path)
		if err != nil {
			return fmt.Errorf("Error opening shell script: %s", err)
		}
		defer f.Close()

		// Compile the command
		p.config.ctx.Data = &ExecuteCommandTemplate{
			Vars: flattenedEnvVars,
			Path: p.config.RemotePath,
		}
		command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
		if err != nil {
			return fmt.Errorf("Error processing command: %s", err)
		}

		// Upload the file and run the command. Do this in the context of
		// a single retryable function so that we don't end up with
		// the case that the upload succeeded, a restart is initiated,
		// and then the command is executed but the file doesn't exist
		// any longer.
		var cmd *packer.RemoteCmd
		err = p.retryable(func() error {
			if _, err := f.Seek(0, 0); err != nil {
				return err
			}

			var r io.Reader = f
			if !p.config.Binary {
				r = &UnixReader{Reader: r}
			}

			if err := comm.Upload(p.config.RemotePath, r, nil); err != nil {
				return fmt.Errorf("Error uploading script: %s", err)
			}

			cmd = &packer.RemoteCmd{
				Command: fmt.Sprintf("chmod 0755 %s", p.config.RemotePath),
			}
			if err := comm.Start(cmd); err != nil {
				return fmt.Errorf(
					"Error chmodding script file to 0755 in remote "+
						"machine: %s", err)
			}
			cmd.Wait()

			cmd = &packer.RemoteCmd{Command: command}
			return cmd.StartWithUi(comm, ui)
		})

		if err != nil {
			return err
		}

		// If the exit code indicates a remote disconnect, fail unless
		// we were expecting it.
		if cmd.ExitStatus == packer.CmdDisconnect {
			if !*p.config.ExpectDisconnect {
				return fmt.Errorf("Script disconnected unexpectedly.")
			}
		} else if cmd.ExitStatus != 0 {
			return fmt.Errorf("Script exited with non-zero exit status: %d", cmd.ExitStatus)
		}

		if !p.config.SkipClean {

			// Delete the temporary file we created. We retry this a few times
			// since if the above rebooted we have to wait until the reboot
			// completes.
			err = p.retryable(func() error {
				cmd = &packer.RemoteCmd{
					Command: fmt.Sprintf("rm -f %s", p.config.RemotePath),
				}
				if err := comm.Start(cmd); err != nil {
					return fmt.Errorf(
						"Error removing temporary script at %s: %s",
						p.config.RemotePath, err)
				}
				cmd.Wait()
				// treat disconnects as retryable by returning an error
				if cmd.ExitStatus == packer.CmdDisconnect {
					return fmt.Errorf("Disconnect while removing temporary script.")
				}
				return nil
			})
			if err != nil {
				return err
			}

			if cmd.ExitStatus != 0 {
				return fmt.Errorf(
					"Error removing temporary script at %s!",
					p.config.RemotePath)
			}
		}
	}

	return nil
}

func (p *Provisioner) Cancel() {
	// Just hard quit. It isn't a big deal if what we're doing keeps
	// running on the other side.
	os.Exit(0)
}

// retryable will retry the given function over and over until a
// non-error is returned.
func (p *Provisioner) retryable(f func() error) error {
	startTimeout := time.After(p.config.startRetryTimeout)
	for {
		var err error
		if err = f(); err == nil {
			return nil
		}

		// Create an error and log it
		err = fmt.Errorf("Retryable error: %s", err)
		log.Printf(err.Error())

		// Check if we timed out, otherwise we retry. It is safe to
		// retry since the only error case above is if the command
		// failed to START.
		select {
		case <-startTimeout:
			return err
		default:
			time.Sleep(2 * time.Second)
		}
	}
}

func (p *Provisioner) createFlattenedEnvVars() (flattened string) {
	flattened = ""
	envVars := make(map[string]string)

	// Always available Packer provided env vars
	envVars["PACKER_BUILD_NAME"] = fmt.Sprintf("%s", p.config.PackerBuildName)
	envVars["PACKER_BUILDER_TYPE"] = fmt.Sprintf("%s", p.config.PackerBuilderType)
	httpAddr := common.GetHTTPAddr()
	if httpAddr != "" {
		envVars["PACKER_HTTP_ADDR"] = fmt.Sprintf("%s", httpAddr)
	}

	// Split vars into key/value components
	for _, envVar := range p.config.Vars {
		keyValue := strings.SplitN(envVar, "=", 2)
		// Store pair, replacing any single quotes in value so they parse
		// correctly with required environment variable format
		envVars[keyValue[0]] = strings.Replace(keyValue[1], "'", `'"'"'`, -1)
	}

	// Create a list of env var keys in sorted order
	var keys []string
	for k := range envVars {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	// Re-assemble vars surrounding value with single quotes and flatten
	for _, key := range keys {
		flattened += fmt.Sprintf("%s='%s' ", key, envVars[key])
	}
	return
}
<|endoftext|>


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

package main

import (
	"context"
	"flag"
	"fmt"
	"image"
	"image/color"
	"log"
	"sync"
	"time"

	"github.com/zagrodzki/goscope/dummy"
	"github.com/zagrodzki/goscope/gui"
	"github.com/zagrodzki/goscope/scope"
	"github.com/zagrodzki/goscope/triggers"
	"golang.org/x/exp/shiny/driver"
	"golang.org/x/exp/shiny/screen"
	"golang.org/x/time/rate"
)

var (
	triggerSource    = flag.String("trigger_source", "", "Name of the channel to use as a trigger source")
	triggerThresh    = flag.Float64("trigger_threshold", 0, "Trigger threshold")
	triggerEdge      = flag.String("trigger_edge", "rising", "Trigger edge, rising or falling")
	useChan          = flag.String("channel", "sin", "one of the channels of dummy device: zero,random,sin,triangle,square")
	timeBase         = flag.Duration("timebase", time.Second, "timebase of the displayed waveform")
	perDiv           = flag.Float64("v_per_div", 2, "volts per div")
	screenWidth      = flag.Int("width", 800, "UI width, in pixels")
	screenHeight     = flag.Int("height", 600, "UI height, in pixels")
	refreshRateLimit = flag.Float64("refresh_rate", 25, "maximum refresh rate, in frames per second")
)

type waveform struct {
	tb    scope.Duration
	inter scope.Duration
	tp    map[scope.ChanID]scope.TraceParams

	mu      sync.Mutex
	plot    gui.Plot
	bufPlot gui.Plot
}

func (w *waveform) TimeBase() scope.Duration {
	return w.tb
}

var allColors = []color.RGBA{
	color.RGBA{255, 0, 0, 255},
	color.RGBA{0, 200, 0, 255},
	color.RGBA{0, 0, 255, 255},
	color.RGBA{255, 0, 255, 255},
	color.RGBA{255, 255, 0, 255},
}

func (w *waveform) swapPlot() {
	w.mu.Lock()
	defer w.mu.Unlock()
	w.plot, w.bufPlot = w.bufPlot, w.plot
}

func (w *waveform) keepReading(dataCh <-chan []scope.ChannelData) {
	var buf []scope.ChannelData
	var tbCount = int(w.tb / w.inter)
	chColor := make(map[scope.ChanID]color.RGBA)
	for data := range dataCh {
		if len(data) == 0 {
			continue
		}
		if buf == nil {
			buf = make([]scope.ChannelData, len(data))
			for i, d := range data {
				buf[i].ID = d.ID
				buf[i].Samples = make([]scope.Voltage, 0, 2*tbCount)
				chColor[d.ID] = allColors[i]
			}
		}
		for i, d := range data {
			buf[i].Samples = append(buf[i].Samples, d.Samples...)
		}
		if len(buf[0].Samples) >= tbCount {
			for i := range data {
				buf[i].Samples = buf[i].Samples[:tbCount]
			}

			// full timebase, draw and go to beginning
			w.bufPlot.DrawAll(buf, w.tp, chColor)
			w.swapPlot()
			// truncate the buffers
			for i := range buf {
				buf[i].Samples = buf[i].Samples[:0]
			}
		}
	}
}

func (w *waveform) Reset(inter scope.Duration, d <-chan []scope.ChannelData) {
	w.inter = inter
	go w.keepReading(d)
}

func (w *waveform) Error(error) {}

func (w *waveform) SetTimeBase(d scope.Duration) {
	w.tb = d
}

func (w *waveform) SetChannel(ch scope.ChanID, p scope.TraceParams) {
	if w.tp == nil {
		w.tp = make(map[scope.ChanID]scope.TraceParams)
	}
	w.tp[ch] = p
}

func (w *waveform) Render() *image.RGBA {
	w.mu.Lock()
	defer w.mu.Unlock()
	ret := image.NewRGBA(w.plot.RGBA.Rect)
	copy(ret.Pix, w.plot.RGBA.Pix)
	return ret
}

func main() {
	flag.Parse()

	var edge triggers.RisingEdge
	switch *triggerEdge {
	case "rising":
		edge = triggers.Rising
	case "falling":
		edge = triggers.Falling
	default:
		log.Fatalf("Unknown value %q for flag trigger_edge, expected rising or falling", *triggerEdge)
	}

	dev, _ := dummy.Open(*useChan)

	screenSize := image.Point{*screenWidth, *screenHeight}
	wf := &waveform{
		plot:    gui.NewPlot(screenSize),
		bufPlot: gui.NewPlot(screenSize),
	}
	wf.SetTimeBase(scope.DurationFromNano(*timeBase))

	for _, id := range dev.Channels() {
		wf.SetChannel(id, scope.TraceParams{Zero: 0.5, PerDiv: *perDiv})
	}

	tr := triggers.New(wf)
	tr.Source(scope.ChanID(*triggerSource))
	tr.Edge(edge)
	tr.Level(scope.Voltage(*triggerThresh))

	dev.Attach(tr)
	dev.Start()
	defer dev.Stop()

	driver.Main(func(s screen.Screen) {
		w, err := s.NewWindow(&screen.NewWindowOptions{Width: screenSize.X, Height: screenSize.Y})
		if err != nil {
			log.Fatalf("NewWindow: %v", err)
		}
		defer w.Release()
		stop := make(chan struct{})
		go processEvents(w, stop)

		b, err := s.NewBuffer(screenSize)
		if err != nil {
			log.Fatalf("NewBuffer(): %v", err)
		}
		defer b.Release()
		limiter := rate.NewLimiter(rate.Limit(*refreshRateLimit), 1)
		sometimes := rate.NewLimiter(0.2, 1)
		for {
			select {
			case <-stop:
				return
			default:
			}
			limiter.Wait(context.Background())
			t := time.Now()
			trace := wf.Render()
			if trace == nil {
				continue
			}
			copy(b.RGBA().Pix, trace.Pix)
			w.Upload(image.Point{0, 0}, b, b.Bounds())
			w.Publish()
			if sometimes.Allow() {
				d := time.Since(t)
				fmt.Printf("Rendering 1 frame took %v (%.2ffps)\n", d, float64(time.Second)/float64(d))
			}
		}
	})
}
<commit_msg>More parameters to control the device used.<commit_after>//  Copyright 2016 The goscope Authors
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

package main

import (
	"context"
	"flag"
	"fmt"
	"image"
	"image/color"
	"log"
	"strings"
	"sync"
	"time"

	"github.com/zagrodzki/goscope/dummy"
	"github.com/zagrodzki/goscope/gui"
	"github.com/zagrodzki/goscope/scope"
	"github.com/zagrodzki/goscope/triggers"
	"github.com/zagrodzki/goscope/usb"
	"golang.org/x/exp/shiny/driver"
	"golang.org/x/exp/shiny/screen"
	"golang.org/x/time/rate"
)

var (
	device           = flag.String("device", "", "Device to use, autodetect if empty")
	list             = flag.Bool("list", false, "If set, only list available devices")
	triggerSource    = flag.String("trigger_source", "", "Name of the channel to use as a trigger source")
	triggerThresh    = flag.Float64("trigger_threshold", 0, "Trigger threshold")
	triggerEdge      = flag.String("trigger_edge", "rising", "Trigger edge, rising or falling")
	useChan          = flag.String("channel", "sin", "one of the channels of dummy device: zero,random,sin,triangle,square")
	timeBase         = flag.Duration("timebase", time.Second, "timebase of the displayed waveform")
	perDiv           = flag.Float64("v_per_div", 2, "volts per div")
	screenWidth      = flag.Int("width", 800, "UI width, in pixels")
	screenHeight     = flag.Int("height", 600, "UI height, in pixels")
	refreshRateLimit = flag.Float64("refresh_rate", 25, "maximum refresh rate, in frames per second")
)

type waveform struct {
	tb    scope.Duration
	inter scope.Duration
	tp    map[scope.ChanID]scope.TraceParams

	mu      sync.Mutex
	plot    gui.Plot
	bufPlot gui.Plot
}

func (w *waveform) TimeBase() scope.Duration {
	return w.tb
}

var allColors = []color.RGBA{
	color.RGBA{255, 0, 0, 255},
	color.RGBA{0, 200, 0, 255},
	color.RGBA{0, 0, 255, 255},
	color.RGBA{255, 0, 255, 255},
	color.RGBA{255, 255, 0, 255},
}

func (w *waveform) swapPlot() {
	w.mu.Lock()
	defer w.mu.Unlock()
	w.plot, w.bufPlot = w.bufPlot, w.plot
}

func (w *waveform) keepReading(dataCh <-chan []scope.ChannelData) {
	var buf []scope.ChannelData
	var tbCount = int(w.tb / w.inter)
	chColor := make(map[scope.ChanID]color.RGBA)
	for data := range dataCh {
		if len(data) == 0 {
			continue
		}
		if buf == nil {
			buf = make([]scope.ChannelData, len(data))
			for i, d := range data {
				buf[i].ID = d.ID
				buf[i].Samples = make([]scope.Voltage, 0, 2*tbCount)
				chColor[d.ID] = allColors[i]
			}
		}
		for i, d := range data {
			buf[i].Samples = append(buf[i].Samples, d.Samples...)
		}
		if len(buf[0].Samples) >= tbCount {
			for i := range data {
				buf[i].Samples = buf[i].Samples[:tbCount]
			}

			// full timebase, draw and go to beginning
			w.bufPlot.DrawAll(buf, w.tp, chColor)
			w.swapPlot()
			// truncate the buffers
			for i := range buf {
				buf[i].Samples = buf[i].Samples[:0]
			}
		}
	}
}

func (w *waveform) Reset(inter scope.Duration, d <-chan []scope.ChannelData) {
	w.inter = inter
	go w.keepReading(d)
}

func (w *waveform) Error(error) {}

func (w *waveform) SetTimeBase(d scope.Duration) {
	w.tb = d
}

func (w *waveform) SetChannel(ch scope.ChanID, p scope.TraceParams) {
	if w.tp == nil {
		w.tp = make(map[scope.ChanID]scope.TraceParams)
	}
	w.tp[ch] = p
}

func (w *waveform) Render() *image.RGBA {
	w.mu.Lock()
	defer w.mu.Unlock()
	ret := image.NewRGBA(w.plot.RGBA.Rect)
	copy(ret.Pix, w.plot.RGBA.Pix)
	return ret
}

type system struct {
	name      string
	enumerate func() map[string]string
	open      func(string) (scope.Device, error)
}

var (
	systems = []system{
		{
			name:      "dummy",
			enumerate: dummy.Enumerate,
			open:      dummy.Open,
		},
		{
			name:      "usb",
			enumerate: usb.Enumerate,
			open:      usb.Open,
		},
	}
	systemsByName = make(map[string]int)
)

func main() {
	flag.Parse()

	var edge triggers.RisingEdge
	switch *triggerEdge {
	case "rising":
		edge = triggers.Rising
	case "falling":
		edge = triggers.Falling
	default:
		log.Fatalf("Unknown value %q for flag trigger_edge, expected rising or falling", *triggerEdge)
	}

	var all []string
	for idx, sys := range systems {
		systemsByName[sys.name] = idx
		for id := range sys.enumerate() {
			all = append(all, fmt.Sprintf("%s:%s", sys.name, id))
		}
	}

	if len(all) == 0 {
		log.Fatalf("Did not find any supported devices")
	}
	if *list {
		fmt.Println("Devices found:")
		for _, d := range all {
			fmt.Println(d)
		}
		return
	}
	id := all[0]
	if *device != "" {
		for _, d := range all {
			if d == *device {
				id = d
				break
			}
		}
		if id != *device {
			log.Fatalf("Device %s not detected on the list. Available devices: %v", *device, all)
		}
	} else if len(all) > 1 {
		log.Printf("Multiple devices found: %v", all)
		log.Printf("Using the first device (%s)", id)
	}

	parts := strings.SplitN(id, ":", 2)
	s := systemsByName[parts[0]]
	osc, err := systems[s].open(parts[1])

	if err != nil {
		log.Fatalf("Open: %+v", err)
	}

	screenSize := image.Point{*screenWidth, *screenHeight}
	wf := &waveform{
		plot:    gui.NewPlot(screenSize),
		bufPlot: gui.NewPlot(screenSize),
	}
	wf.SetTimeBase(scope.DurationFromNano(*timeBase))

	for _, id := range osc.Channels() {
		wf.SetChannel(id, scope.TraceParams{Zero: 0.5, PerDiv: *perDiv})
	}

	tr := triggers.New(wf)
	tr.Source(scope.ChanID(*triggerSource))
	tr.Edge(edge)
	tr.Level(scope.Voltage(*triggerThresh))

	osc.Attach(tr)
	osc.Start()
	defer osc.Stop()

	driver.Main(func(s screen.Screen) {
		w, err := s.NewWindow(&screen.NewWindowOptions{Width: screenSize.X, Height: screenSize.Y})
		if err != nil {
			log.Fatalf("NewWindow: %v", err)
		}
		defer w.Release()
		stop := make(chan struct{})
		go processEvents(w, stop)

		b, err := s.NewBuffer(screenSize)
		if err != nil {
			log.Fatalf("NewBuffer(): %v", err)
		}
		defer b.Release()
		limiter := rate.NewLimiter(rate.Limit(*refreshRateLimit), 1)
		sometimes := rate.NewLimiter(0.2, 1)
		for {
			select {
			case <-stop:
				return
			default:
			}
			limiter.Wait(context.Background())
			t := time.Now()
			trace := wf.Render()
			if trace == nil {
				continue
			}
			copy(b.RGBA().Pix, trace.Pix)
			w.Upload(image.Point{0, 0}, b, b.Bounds())
			w.Publish()
			if sometimes.Allow() {
				d := time.Since(t)
				fmt.Printf("Rendering 1 frame took %v (%.2ffps)\n", d, float64(time.Second)/float64(d))
			}
		}
	})
}
<|endoftext|>


<commit_before>package main

import "github.com/fogleman/pt/pt"

// http://graphics.cs.williams.edu/data/meshes/dabrovic-sponza.zip

func main() {
	scene := pt.Scene{}
	material := pt.DiffuseMaterial(pt.HexColor(0xFCFAE1))
	mesh, _ := pt.LoadOBJ("examples/sponza/sponza.obj", material)
	mesh.MoveTo(pt.Vector{}, pt.Vector{0.5, 0, 0.5})
	// fmt.Println(mesh.Box())
	scene.Add(mesh)
	scene.Add(pt.NewSphere(pt.Vector{0, 20, 0}, 3, pt.LightMaterial(pt.Color{1, 1, 1}, 1, pt.NoAttenuation)))
	camera := pt.LookAt(pt.Vector{-10, 2, 0}, pt.Vector{0, 4, 0}, pt.Vector{0, 1, 0}, 45)
	pt.IterativeRender("out%03d.png", 1000, &scene, &camera, 2560/2, 1440/2, 0, 1, 4)
}
<commit_msg>updated directory<commit_after>package main

import "github.com/fogleman/pt/pt"

// http://graphics.cs.williams.edu/data/meshes/dabrovic-sponza.zip

func main() {
	scene := pt.Scene{}
	material := pt.DiffuseMaterial(pt.HexColor(0xFCFAE1))
	mesh, _ := pt.LoadOBJ("examples/dabrovic-sponza/sponza.obj", material)
	mesh.MoveTo(pt.Vector{}, pt.Vector{0.5, 0, 0.5})
	// fmt.Println(mesh.Box())
	scene.Add(mesh)
	scene.Add(pt.NewSphere(pt.Vector{0, 20, 0}, 3, pt.LightMaterial(pt.Color{1, 1, 1}, 1, pt.NoAttenuation)))
	camera := pt.LookAt(pt.Vector{-10, 2, 0}, pt.Vector{0, 4, 0}, pt.Vector{0, 1, 0}, 45)
	pt.IterativeRender("out%03d.png", 1000, &scene, &camera, 2560/2, 1440/2, 0, 1, 4)
}
<|endoftext|>


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

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"os/signal"
	"path"
	"path/filepath"
	"syscall"
	"time"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/strategicpatch"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/kubernetes"
	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/clientcmd"
)

const (
	annotation       = "iam.cloud.google.com/service-account"
	initializerName  = "serviceaccounts.cloud.google.com"
	defaultNamespace = "default"
	resyncPeriod     = 30 * time.Second

	secretMountPath    = "/var/run/secrets/gcp/"
	serviceAccountFile = "key.json"
)

type config struct {
	Containers []corev1.Container
	Volumes    []corev1.Volume
}

func main() {
	log.Println("Starting the GCP Service accounts initializer...")

	log.Println("Using in-cluster token discovery")
	clusterConfig, err := rest.InClusterConfig()
	if err != nil {
		log.Printf("failed to use in-cluster token: %+v", err)
		kubecfg := filepath.Join(os.Getenv("HOME"), ".kube", "config")
		log.Printf("Using kubeconfig file at %s", kubecfg)
		clusterConfig, err = clientcmd.BuildConfigFromFlags("", kubecfg)
		if err != nil {
			log.Printf("failed to find kubeconfig file: %+v", err)
			log.Fatal("No authentication is available.")
		}
	}

	clientset, err := kubernetes.NewForConfig(clusterConfig)
	if err != nil {
		log.Fatalf("failed to initialize kubernetes client: %+v", err)
	}

	// Watch uninitialized Pods in all namespaces.
	restClient := clientset.CoreV1().RESTClient()
	watchlist := cache.NewListWatchFromClient(restClient,
		"pods", corev1.NamespaceAll, fields.Everything())

	// Wrap the returned watchlist to workaround the inability to include
	// the `IncludeUninitialized` list option when setting up watch clients.
	includeUninitializedWatchlist := &cache.ListWatch{
		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
			options.IncludeUninitialized = true
			return watchlist.List(options)
		},
		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
			options.IncludeUninitialized = true
			return watchlist.Watch(options)
		},
	}

	_, controller := cache.NewInformer(includeUninitializedWatchlist,
		&corev1.Pod{},
		resyncPeriod,
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				pod, ok := obj.(*corev1.Pod)
				if !ok {
					log.Fatalf("watch returned non-pod object: %T", pod)
				}

				if !needsInitialization(pod) {
					log.Printf("skipping pod/%s", pod.GetName())
					return
				}

				modifiedPod := pod.DeepCopy()
				if !modifyPodSpec(modifiedPod) {
					log.Printf("no injection in pod/%s", pod.GetName())
				}

				removeSelfPendingInitializer(modifiedPod)

				if err := patchPod(pod, modifiedPod, clientset); err != nil {
					log.Printf("error saving pod/%s: %+v", pod.GetName(), err)
				} else {
					log.Printf("initialized pod/%s", pod.GetName())
				}
			},
		},
	)

	stop := make(chan struct{})
	go controller.Run(stop)

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
	<-signalChan

	log.Println("Shutdown signal received, exiting...")
	close(stop)
}

// needsInitialization determines if the pod is required to be initialized
// currently by this initializer.
func needsInitialization(pod *corev1.Pod) bool {
	initializers := pod.ObjectMeta.GetInitializers()
	return initializers != nil &&
		len(initializers.Pending) > 0 &&
		initializers.Pending[0].Name == initializerName
}

// removeSelfPendingInitializer removes the first element from pending
// initializers list of in-memory pod value.
func removeSelfPendingInitializer(pod *corev1.Pod) {
	if pod.ObjectMeta.GetInitializers() == nil {
		return
	}
	pendingInitializers := pod.ObjectMeta.GetInitializers().Pending
	if len(pendingInitializers) == 1 {
		pod.ObjectMeta.Initializers.Pending = nil
	} else if len(pendingInitializers) > 1 {
		pod.ObjectMeta.Initializers.Pending = append(
			pendingInitializers[:0], pendingInitializers[1:]...)
	}
}

// patchPod saves the pod to the API using a strategic 2-way JSON merge patch.
func patchPod(origPod, newPod *corev1.Pod, clientset *kubernetes.Clientset) error {
	origData, err := json.Marshal(origPod)
	if err != nil {
		return fmt.Errorf("failed to marshal original pod: %+v", err)
	}

	newData, err := json.Marshal(newPod)
	if err != nil {
		return fmt.Errorf("failed to marshal modified pod: %+v", err)
	}

	patch, err := strategicpatch.CreateTwoWayMergePatch(origData, newData, corev1.Pod{})
	if err != nil {
		return fmt.Errorf("failed to create 2-way merge patch: %+v", err)
	}

	if _, err = clientset.CoreV1().Pods(origPod.GetNamespace()).Patch(
		origPod.GetName(), types.StrategicMergePatchType, patch); err != nil {
		return fmt.Errorf("failed to patch pod/%s: %+v", origPod.GetName(), err)
	}
	return nil
}

// modifyPodSpec makes modifications to in-memory pod value to inject the
// service account. Returns whether any modifications have been made.
func modifyPodSpec(pod *corev1.Pod) bool {
	if pod == nil || pod.ObjectMeta.Annotations == nil {
		return false
	}
	serviceAccountName, ok := pod.ObjectMeta.Annotations[annotation]
	if !ok {
		return false
	}

	volName := fmt.Sprintf("gcp-%s", serviceAccountName)
	mountPath := path.Join(secretMountPath, serviceAccountName)
	keyPath := path.Join(mountPath, serviceAccountFile)

	pod.Spec.Volumes = append(pod.Spec.Volumes,
		corev1.Volume{
			Name: volName,
			VolumeSource: corev1.VolumeSource{
				Secret: &corev1.SecretVolumeSource{
					SecretName: serviceAccountName,
					Items: []corev1.KeyToPath{{
						Key:  "key.json",
						Path: "key.json",
					}}}}})

	for i, c := range pod.Spec.Containers {
		pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
			corev1.VolumeMount{
				Name:      volName,
				MountPath: mountPath,
				SubPath:   "",
				ReadOnly:  true})

		pod.Spec.Containers[i].Env = append(c.Env, corev1.EnvVar{
			Name:  "GOOGLE_APPLICATION_CREDENTIALS",
			Value: keyPath})
	}

	return true
}
<commit_msg>Use non-typecasted value for %T<commit_after>// Copyright 2017 Google Inc. All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//     http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"os/signal"
	"path"
	"path/filepath"
	"syscall"
	"time"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/strategicpatch"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/kubernetes"
	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/clientcmd"
)

const (
	annotation       = "iam.cloud.google.com/service-account"
	initializerName  = "serviceaccounts.cloud.google.com"
	defaultNamespace = "default"
	resyncPeriod     = 30 * time.Second

	secretMountPath    = "/var/run/secrets/gcp/"
	serviceAccountFile = "key.json"
)

type config struct {
	Containers []corev1.Container
	Volumes    []corev1.Volume
}

func main() {
	log.Println("Starting the GCP Service accounts initializer...")

	log.Println("Using in-cluster token discovery")
	clusterConfig, err := rest.InClusterConfig()
	if err != nil {
		log.Printf("failed to use in-cluster token: %+v", err)
		kubecfg := filepath.Join(os.Getenv("HOME"), ".kube", "config")
		log.Printf("Using kubeconfig file at %s", kubecfg)
		clusterConfig, err = clientcmd.BuildConfigFromFlags("", kubecfg)
		if err != nil {
			log.Printf("failed to find kubeconfig file: %+v", err)
			log.Fatal("No authentication is available.")
		}
	}

	clientset, err := kubernetes.NewForConfig(clusterConfig)
	if err != nil {
		log.Fatalf("failed to initialize kubernetes client: %+v", err)
	}

	// Watch uninitialized Pods in all namespaces.
	restClient := clientset.CoreV1().RESTClient()
	watchlist := cache.NewListWatchFromClient(restClient,
		"pods", corev1.NamespaceAll, fields.Everything())

	// Wrap the returned watchlist to workaround the inability to include
	// the `IncludeUninitialized` list option when setting up watch clients.
	includeUninitializedWatchlist := &cache.ListWatch{
		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
			options.IncludeUninitialized = true
			return watchlist.List(options)
		},
		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
			options.IncludeUninitialized = true
			return watchlist.Watch(options)
		},
	}

	_, controller := cache.NewInformer(includeUninitializedWatchlist,
		&corev1.Pod{},
		resyncPeriod,
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				pod, ok := obj.(*corev1.Pod)
				if !ok {
					log.Fatalf("watch returned non-pod object: %T", obj)
				}

				if !needsInitialization(pod) {
					log.Printf("skipping pod/%s", pod.GetName())
					return
				}

				modifiedPod := pod.DeepCopy()
				if !modifyPodSpec(modifiedPod) {
					log.Printf("no injection in pod/%s", pod.GetName())
				}

				removeSelfPendingInitializer(modifiedPod)

				if err := patchPod(pod, modifiedPod, clientset); err != nil {
					log.Printf("error saving pod/%s: %+v", pod.GetName(), err)
				} else {
					log.Printf("initialized pod/%s", pod.GetName())
				}
			},
		},
	)

	stop := make(chan struct{})
	go controller.Run(stop)

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
	<-signalChan

	log.Println("Shutdown signal received, exiting...")
	close(stop)
}

// needsInitialization determines if the pod is required to be initialized
// currently by this initializer.
func needsInitialization(pod *corev1.Pod) bool {
	initializers := pod.ObjectMeta.GetInitializers()
	return initializers != nil &&
		len(initializers.Pending) > 0 &&
		initializers.Pending[0].Name == initializerName
}

// removeSelfPendingInitializer removes the first element from pending
// initializers list of in-memory pod value.
func removeSelfPendingInitializer(pod *corev1.Pod) {
	if pod.ObjectMeta.GetInitializers() == nil {
		return
	}
	pendingInitializers := pod.ObjectMeta.GetInitializers().Pending
	if len(pendingInitializers) == 1 {
		pod.ObjectMeta.Initializers.Pending = nil
	} else if len(pendingInitializers) > 1 {
		pod.ObjectMeta.Initializers.Pending = append(
			pendingInitializers[:0], pendingInitializers[1:]...)
	}
}

// patchPod saves the pod to the API using a strategic 2-way JSON merge patch.
func patchPod(origPod, newPod *corev1.Pod, clientset *kubernetes.Clientset) error {
	origData, err := json.Marshal(origPod)
	if err != nil {
		return fmt.Errorf("failed to marshal original pod: %+v", err)
	}

	newData, err := json.Marshal(newPod)
	if err != nil {
		return fmt.Errorf("failed to marshal modified pod: %+v", err)
	}

	patch, err := strategicpatch.CreateTwoWayMergePatch(origData, newData, corev1.Pod{})
	if err != nil {
		return fmt.Errorf("failed to create 2-way merge patch: %+v", err)
	}

	if _, err = clientset.CoreV1().Pods(origPod.GetNamespace()).Patch(
		origPod.GetName(), types.StrategicMergePatchType, patch); err != nil {
		return fmt.Errorf("failed to patch pod/%s: %+v", origPod.GetName(), err)
	}
	return nil
}

// modifyPodSpec makes modifications to in-memory pod value to inject the
// service account. Returns whether any modifications have been made.
func modifyPodSpec(pod *corev1.Pod) bool {
	if pod == nil || pod.ObjectMeta.Annotations == nil {
		return false
	}
	serviceAccountName, ok := pod.ObjectMeta.Annotations[annotation]
	if !ok {
		return false
	}

	volName := fmt.Sprintf("gcp-%s", serviceAccountName)
	mountPath := path.Join(secretMountPath, serviceAccountName)
	keyPath := path.Join(mountPath, serviceAccountFile)

	pod.Spec.Volumes = append(pod.Spec.Volumes,
		corev1.Volume{
			Name: volName,
			VolumeSource: corev1.VolumeSource{
				Secret: &corev1.SecretVolumeSource{
					SecretName: serviceAccountName,
					Items: []corev1.KeyToPath{{
						Key:  "key.json",
						Path: "key.json",
					}}}}})

	for i, c := range pod.Spec.Containers {
		pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
			corev1.VolumeMount{
				Name:      volName,
				MountPath: mountPath,
				SubPath:   "",
				ReadOnly:  true})

		pod.Spec.Containers[i].Env = append(c.Env, corev1.EnvVar{
			Name:  "GOOGLE_APPLICATION_CREDENTIALS",
			Value: keyPath})
	}

	return true
}
<|endoftext|>


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

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

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

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

package config

import (
	"bufio"
	"encoding/json"
	"os"
	"strings"
	"testing"

	"k8s.io/minikube/pkg/minikube/out"
)

func TestAddonsList(t *testing.T) {
	t.Run("NonExistingClusterTable", func(t *testing.T) {
		r, w, err := os.Pipe()
		if err != nil {
			t.Fatalf("failed to create pipe: %v", err)
		}
		old := os.Stdout
		defer func() { os.Stdout = old }()
		os.Stdout = w
		printAddonsList(nil)
		if err := w.Close(); err != nil {
			t.Fatalf("failed to close pipe: %v", err)
		}
		buf := bufio.NewScanner(r)
		pipeCount := 0
		got := ""
		// Pull the first 3 lines from stdout
		for i := 0; i < 3; i++ {
			if !buf.Scan() {
				t.Fatalf("failed to read stdout")
			}
			pipeCount += strings.Count(buf.Text(), "|")
			got += buf.Text()
		}
		// The lines we pull should look something like
		// |-----------------------------|-----------------------|
		// |         ADDON NAME          |      MAINTAINER       |
		// |-----------------------------|-----------------------|
		// which has 9 pipes
		expected := 9
		if pipeCount != expected {
			t.Errorf("Expected header to be to have %d pipes; got = %d: %q", expected, pipeCount, got)
		}
	})

	t.Run("NonExistingClusterJSON", func(t *testing.T) {
		type addons struct {
			Ambassador *interface{} `json:"ambassador"`
		}

		b := make([]byte, 534)
		r, w, err := os.Pipe()
		if err != nil {
			t.Fatalf("failed to create pipe: %v", err)
		}
		old := os.Stdout
		defer func() {
			os.Stdout = old
			out.SetOutFile(os.Stdout)
		}()
		os.Stdout = w
		out.SetOutFile(os.Stdout)
		printAddonsJSON(nil)
		if err := w.Close(); err != nil {
			t.Fatalf("failed to close pipe: %v", err)
		}
		if _, err := r.Read(b); err != nil {
			t.Fatalf("failed to read bytes: %v", err)
		}
		got := addons{}
		if err := json.Unmarshal(b, &got); err != nil {
			t.Fatalf("failed to unmarshal output; output: %q; err: %v", string(b), err)
		}
		if got.Ambassador == nil {
			t.Errorf("expected `ambassador` field to not be nil, but was")
		}
	})
}
<commit_msg>Update cmd/minikube/cmd/config/addons_list_test.go<commit_after>/*
Copyright 2021 The Kubernetes Authors All rights reserved.

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

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

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

package config

import (
	"bufio"
	"encoding/json"
	"os"
	"strings"
	"testing"

	"k8s.io/minikube/pkg/minikube/out"
)

func TestAddonsList(t *testing.T) {
	t.Run("NonExistingClusterTable", func(t *testing.T) {
		r, w, err := os.Pipe()
		if err != nil {
			t.Fatalf("failed to create pipe: %v", err)
		}
		old := os.Stdout
		defer func() { os.Stdout = old }()
		os.Stdout = w
		printAddonsList(nil)
		if err := w.Close(); err != nil {
			t.Fatalf("failed to close pipe: %v", err)
		}
		buf := bufio.NewScanner(r)
		pipeCount := 0
		got := ""
		// Pull the first 3 lines from stdout
		for i := 0; i < 3; i++ {
			if !buf.Scan() {
				t.Fatalf("failed to read stdout")
			}
			pipeCount += strings.Count(buf.Text(), "|")
			got += buf.Text()
		}
		// The lines we pull should look something like
		// |-----------------------------|-----------------------|
		// |         ADDON NAME          |      MAINTAINER       |
		// |-----------------------------|-----------------------|
		// which has 9 pipes
		expected := 9
		if pipeCount != expected {
			t.Errorf("Expected header to have %d pipes; got = %d: %q", expected, pipeCount, got)
		}
	})

	t.Run("NonExistingClusterJSON", func(t *testing.T) {
		type addons struct {
			Ambassador *interface{} `json:"ambassador"`
		}

		b := make([]byte, 534)
		r, w, err := os.Pipe()
		if err != nil {
			t.Fatalf("failed to create pipe: %v", err)
		}
		old := os.Stdout
		defer func() {
			os.Stdout = old
			out.SetOutFile(os.Stdout)
		}()
		os.Stdout = w
		out.SetOutFile(os.Stdout)
		printAddonsJSON(nil)
		if err := w.Close(); err != nil {
			t.Fatalf("failed to close pipe: %v", err)
		}
		if _, err := r.Read(b); err != nil {
			t.Fatalf("failed to read bytes: %v", err)
		}
		got := addons{}
		if err := json.Unmarshal(b, &got); err != nil {
			t.Fatalf("failed to unmarshal output; output: %q; err: %v", string(b), err)
		}
		if got.Ambassador == nil {
			t.Errorf("expected `ambassador` field to not be nil, but was")
		}
	})
}
<|endoftext|>