<commit_before>package chlib

import (
	"bufio"
	"os"
	"strconv"
	"strings"

	"fmt"

	jww "github.com/spf13/jwalterweatherman"
	"github.com/spf13/pflag"
)

type validationFunc func(string) bool

var reader = bufio.NewReader(os.Stdin)

func Prompt(np *jww.Notepad, prompt string, validator validationFunc) string {
	np.FEEDBACK.Printf("%s: ", prompt)
	ret, _ := reader.ReadString('\n')
	ret = strings.TrimRight(ret, "\r\n")
	for !validator(ret) {
		np.FEEDBACK.Printf("Invalid input\n%s: ", prompt)
		ret, _ = reader.ReadString('\n')
		ret = strings.TrimRight(ret, "\r\n")
	}
	return ret
}

func portsValidateStr(portsStr string) bool {
	if portsStr == "" {
		return true
	}
	for _, portStr := range strings.Split(portsStr, " ") {
		port, err := strconv.Atoi(portStr)
		if err != nil || port <= 0 || port > 65535 {
			return false
		}
	}
	return true
}

func portsValidateInt(ports []int) bool {
	for _, port := range ports {
		if port <= 0 || port > 65535 {
			return false
		}
	}
	return true
}

func labelSliceValidate(labels []string) bool {
	for _, labelStr := range labels {
		label := strings.Split(labelStr, "=")
		if len(label) != 2 || !LabelRegex.MatchString(label[0]) || !LabelRegex.MatchString(label[1]) {
			return false
		}
	}
	return true
}

func labelsValidate(labelsStr string) bool {
	return labelsStr == "" || labelSliceValidate(strings.Split(labelsStr, " "))
}

func envSliceValidate(envVars []string) bool {
	for _, envVarStr := range envVars {
		envVar := strings.Split(envVarStr, "=")
		if len(envVar) != 2 {
			return false
		}
	}
	return true
}

func envVarsValidate(envVarsStr string) bool {
	return envVarsStr == "" || envSliceValidate(strings.Split(envVarsStr, " "))
}

func cpuValidate(cpuStr string) bool {
	if cpuStr == "" {
		return true
	}
	return CpuRegex.MatchString(cpuStr)
}

func memValidate(memStr string) bool {
	if memStr == "" {
		return true
	}
	return MemRegex.MatchString(memStr)
}

func replicasValidate(replicasStr string) bool {
	if replicasStr == "" {
		return true
	}
	ret, err := strconv.Atoi(replicasStr)
	return err == nil && ret > 0
}

func volumesValidate(volumesStr string) bool {
	if volumesStr == "" {
		return true
	}
	for _, v := range strings.Fields(volumesStr) {
		if !VolumeRegex.MatchString(v) {
			return false
		}
		if strings.Count(v, "\"")%2 != 0 {
			return false
		}
	}
	return true
}

func volumesSliceValidate(vols []string) bool {
	for _, v := range vols {
		if !VolumeRegex.MatchString(v) {
			return false
		}
		if strings.Count(v, "\"")%2 != 0 {
			return false
		}
	}
	return true
}

func PromptParams(np *jww.Notepad) (params ConfigureParams) {
	params.Image = Prompt(np, "Enter image", ImageRegex.MatchString)
	if portsStr := Prompt(np, "Enter ports (PORT1 PORT2 ... PORTN)", portsValidateStr); portsStr != "" {
		for _, p := range strings.Split(portsStr, " ") {
			port, _ := strconv.Atoi(p)
			params.Ports = append(params.Ports, port)
		}
	}
	params.Labels = make(map[string]string)
	if labelsStr := Prompt(np, "Enter labels (key1=value1 key2=value2 ... keyN=valueN)", labelsValidate); labelsStr != "" {
		for _, labelStr := range strings.Split(labelsStr, " ") {
			label := strings.Split(labelStr, "=")
			params.Labels[label[0]] = label[1]
		}
	}
	if commands := Prompt(np, "Enter commands (command1 command2 ... commandN)", func(string) bool { return true }); commands != "" {
		params.Command = strings.Split(commands, " ")
	}
	if envVarsStr := Prompt(np, "Enter environment variables (key1=value1 ... keyN=valueN)", envVarsValidate); envVarsStr != "" {
		for _, envVarStr := range strings.Split(envVarsStr, " ") {
			envVar := strings.Split(envVarStr, "=")
			params.Env = append(params.Env, EnvVar{
				Name:  envVar[0],
				Value: envVar[1],
			})
		}
	}
	if volumesStr := Prompt(np, "Enter volumes (volumeLabel1[/subPath1]=/mountPath1 ... volumeLabelN[/subPathN]=/mountPathN)", volumesValidate); volumesStr != "" {
		for _, volumeStr := range strings.Fields(volumesStr) {
			volume := VolumeRegex.FindStringSubmatch(volumeStr)
			params.Volumes = append(params.Volumes, Volume{
				Label:     volume[1],
				SubPath:   volume[4],
				MountPath: volume[5],
			})
		}
	}
	if cpu := Prompt(np, fmt.Sprintf("Enter CPU cores (*m) [%s]", DefaultCPURequest), cpuValidate); cpu != "" {
		params.CPU = cpu
	} else {
		params.CPU = DefaultCPURequest
	}
	if memory := Prompt(np, fmt.Sprintf("Enter memory size (*Mi | *Gi) [%s]", DefaultMemoryRequest), memValidate); memory != "" {
		params.Memory = memory
	} else {
		params.Memory = DefaultMemoryRequest
	}
	if replicas := Prompt(np, fmt.Sprintf("Enter replicas count [%d]", DefaultReplicas), replicasValidate); replicas != "" {
		params.Replicas, _ = strconv.Atoi(replicas)
	} else {
		params.Replicas = DefaultReplicas
	}
	return
}

func exitIfValidationError(np *jww.Notepad, validationResult bool, message string) {
	if !validationResult {
		np.FEEDBACK.Println(message)
		os.Exit(1)
	}
}

func ParamsFromArgs(np *jww.Notepad, flags *pflag.FlagSet) (params ConfigureParams) {
	chkErr := func(err error) {
		if err != nil {
			np.FEEDBACK.Println("flag get error: %s\n", err)
		}
	}
	var err error
	if flags.Changed("image") {
		params.Image, err = flags.GetString("image")
		chkErr(err)
		exitIfValidationError(np, ImageRegex.MatchString(params.Image), "Invalid image name")
	}
	if flags.Changed("port") {
		params.Ports, err = flags.GetIntSlice("port")
		chkErr(err)
		exitIfValidationError(np, portsValidateInt(params.Ports), "Invalid port found")
	}
	if flags.Changed("labels") {
		labelsSlice, err := flags.GetStringSlice("labels")
		chkErr(err)
		exitIfValidationError(np, labelSliceValidate(labelsSlice), "Invalid label found")
		for _, labelStr := range labelsSlice {
			label := strings.Split(labelStr, "=")
			params.Labels[label[0]] = label[1]
		}
	} else {
		params.Labels = make(map[string]string)
	}
	if flags.Changed("command") {
		params.Command, err = flags.GetStringSlice("command")
		chkErr(err)
	}
	if flags.Changed("env") {
		envSlice, err := flags.GetStringSlice("env")
		chkErr(err)
		exitIfValidationError(np, envSliceValidate(envSlice), "Invalid environment variable found")
		for _, envStr := range envSlice {
			env := strings.Split(envStr, "=")
			params.Env = append(params.Env, EnvVar{
				Name:  env[0],
				Value: env[1],
			})
		}
	}
	if flags.Changed("volumes") {
		volsSlice, err := flags.GetStringSlice("volumes")
		chkErr(err)
		exitIfValidationError(np, volumesSliceValidate(volsSlice), "Invalid volume parameter found")
		for _, volStr := range volsSlice {
			vol := VolumeRegex.FindStringSubmatch(volStr)
			params.Volumes = append(params.Volumes, Volume{
				Label:     vol[1],
				SubPath:   vol[4],
				MountPath: vol[5],
			})
		}
	}
	if flags.Changed("cpu") {
		params.CPU, err = flags.GetString("cpu")
		chkErr(err)
		exitIfValidationError(np, cpuValidate(params.CPU), "Invalid CPU format")
	} else {
		params.CPU = DefaultCPURequest
	}
	if flags.Changed("memory") {
		params.Memory, err = flags.GetString("memory")
		chkErr(err)
		exitIfValidationError(np, memValidate(params.Memory), "Invalid memory format")
	} else {
		params.Memory = DefaultMemoryRequest
	}
	if flags.Changed("replicas") {
		params.Replicas, err = flags.GetInt("replicas")
		chkErr(err)
	} else {
		params.Replicas = DefaultReplicas
	}
	return
}
<commit_msg>Fix "commands" argument processing in "run"<commit_after>package chlib

import (
	"bufio"
	"os"
	"strconv"
	"strings"

	"fmt"

	jww "github.com/spf13/jwalterweatherman"
	"github.com/spf13/pflag"
)

type validationFunc func(string) bool

var reader = bufio.NewReader(os.Stdin)

func Prompt(np *jww.Notepad, prompt string, validator validationFunc) string {
	np.FEEDBACK.Printf("%s: ", prompt)
	ret, _ := reader.ReadString('\n')
	ret = strings.TrimRight(ret, "\r\n")
	for !validator(ret) {
		np.FEEDBACK.Printf("Invalid input\n%s: ", prompt)
		ret, _ = reader.ReadString('\n')
		ret = strings.TrimRight(ret, "\r\n")
	}
	return ret
}

func portsValidateStr(portsStr string) bool {
	if portsStr == "" {
		return true
	}
	for _, portStr := range strings.Split(portsStr, " ") {
		port, err := strconv.Atoi(portStr)
		if err != nil || port <= 0 || port > 65535 {
			return false
		}
	}
	return true
}

func portsValidateInt(ports []int) bool {
	for _, port := range ports {
		if port <= 0 || port > 65535 {
			return false
		}
	}
	return true
}

func labelSliceValidate(labels []string) bool {
	for _, labelStr := range labels {
		label := strings.Split(labelStr, "=")
		if len(label) != 2 || !LabelRegex.MatchString(label[0]) || !LabelRegex.MatchString(label[1]) {
			return false
		}
	}
	return true
}

func labelsValidate(labelsStr string) bool {
	return labelsStr == "" || labelSliceValidate(strings.Split(labelsStr, " "))
}

func envSliceValidate(envVars []string) bool {
	for _, envVarStr := range envVars {
		envVar := strings.Split(envVarStr, "=")
		if len(envVar) != 2 {
			return false
		}
	}
	return true
}

func envVarsValidate(envVarsStr string) bool {
	return envVarsStr == "" || envSliceValidate(strings.Split(envVarsStr, " "))
}

func cpuValidate(cpuStr string) bool {
	if cpuStr == "" {
		return true
	}
	return CpuRegex.MatchString(cpuStr)
}

func memValidate(memStr string) bool {
	if memStr == "" {
		return true
	}
	return MemRegex.MatchString(memStr)
}

func replicasValidate(replicasStr string) bool {
	if replicasStr == "" {
		return true
	}
	ret, err := strconv.Atoi(replicasStr)
	return err == nil && ret > 0
}

func volumesValidate(volumesStr string) bool {
	if volumesStr == "" {
		return true
	}
	for _, v := range strings.Fields(volumesStr) {
		if !VolumeRegex.MatchString(v) {
			return false
		}
		if strings.Count(v, "\"")%2 != 0 {
			return false
		}
	}
	return true
}

func volumesSliceValidate(vols []string) bool {
	for _, v := range vols {
		if !VolumeRegex.MatchString(v) {
			return false
		}
		if strings.Count(v, "\"")%2 != 0 {
			return false
		}
	}
	return true
}

func PromptParams(np *jww.Notepad) (params ConfigureParams) {
	params.Image = Prompt(np, "Enter image", ImageRegex.MatchString)
	if portsStr := Prompt(np, "Enter ports (PORT1 PORT2 ... PORTN)", portsValidateStr); portsStr != "" {
		for _, p := range strings.Split(portsStr, " ") {
			port, _ := strconv.Atoi(p)
			params.Ports = append(params.Ports, port)
		}
	}
	params.Labels = make(map[string]string)
	if labelsStr := Prompt(np, "Enter labels (key1=value1 key2=value2 ... keyN=valueN)", labelsValidate); labelsStr != "" {
		for _, labelStr := range strings.Split(labelsStr, " ") {
			label := strings.Split(labelStr, "=")
			params.Labels[label[0]] = label[1]
		}
	}
	if commands := Prompt(np, "Enter commands (command1 command2 ... commandN)", func(string) bool { return true }); commands != "" {
		params.Command = strings.Split(commands, " ")
	}
	if envVarsStr := Prompt(np, "Enter environment variables (key1=value1 ... keyN=valueN)", envVarsValidate); envVarsStr != "" {
		for _, envVarStr := range strings.Split(envVarsStr, " ") {
			envVar := strings.Split(envVarStr, "=")
			params.Env = append(params.Env, EnvVar{
				Name:  envVar[0],
				Value: envVar[1],
			})
		}
	}
	if volumesStr := Prompt(np, "Enter volumes (volumeLabel1[/subPath1]=/mountPath1 ... volumeLabelN[/subPathN]=/mountPathN)", volumesValidate); volumesStr != "" {
		for _, volumeStr := range strings.Fields(volumesStr) {
			volume := VolumeRegex.FindStringSubmatch(volumeStr)
			params.Volumes = append(params.Volumes, Volume{
				Label:     volume[1],
				SubPath:   volume[4],
				MountPath: volume[5],
			})
		}
	}
	if cpu := Prompt(np, fmt.Sprintf("Enter CPU cores (*m) [%s]", DefaultCPURequest), cpuValidate); cpu != "" {
		params.CPU = cpu
	} else {
		params.CPU = DefaultCPURequest
	}
	if memory := Prompt(np, fmt.Sprintf("Enter memory size (*Mi | *Gi) [%s]", DefaultMemoryRequest), memValidate); memory != "" {
		params.Memory = memory
	} else {
		params.Memory = DefaultMemoryRequest
	}
	if replicas := Prompt(np, fmt.Sprintf("Enter replicas count [%d]", DefaultReplicas), replicasValidate); replicas != "" {
		params.Replicas, _ = strconv.Atoi(replicas)
	} else {
		params.Replicas = DefaultReplicas
	}
	return
}

func exitIfValidationError(np *jww.Notepad, validationResult bool, message string) {
	if !validationResult {
		np.FEEDBACK.Println(message)
		os.Exit(1)
	}
}

func ParamsFromArgs(np *jww.Notepad, flags *pflag.FlagSet) (params ConfigureParams) {
	chkErr := func(err error) {
		if err != nil {
			np.FEEDBACK.Println("flag get error: %s\n", err)
		}
	}
	var err error
	if flags.Changed("image") {
		params.Image, err = flags.GetString("image")
		chkErr(err)
		exitIfValidationError(np, ImageRegex.MatchString(params.Image), "Invalid image name")
	}
	if flags.Changed("port") {
		params.Ports, err = flags.GetIntSlice("port")
		chkErr(err)
		exitIfValidationError(np, portsValidateInt(params.Ports), "Invalid port found")
	}
	if flags.Changed("labels") {
		labelsSlice, err := flags.GetStringSlice("labels")
		chkErr(err)
		exitIfValidationError(np, labelSliceValidate(labelsSlice), "Invalid label found")
		for _, labelStr := range labelsSlice {
			label := strings.Split(labelStr, "=")
			params.Labels[label[0]] = label[1]
		}
	} else {
		params.Labels = make(map[string]string)
	}
	if flags.Changed("commands") {
		params.Command, err = flags.GetStringSlice("commands")
		chkErr(err)
	}
	if flags.Changed("env") {
		envSlice, err := flags.GetStringSlice("env")
		chkErr(err)
		exitIfValidationError(np, envSliceValidate(envSlice), "Invalid environment variable found")
		for _, envStr := range envSlice {
			env := strings.Split(envStr, "=")
			params.Env = append(params.Env, EnvVar{
				Name:  env[0],
				Value: env[1],
			})
		}
	}
	if flags.Changed("volumes") {
		volsSlice, err := flags.GetStringSlice("volumes")
		chkErr(err)
		exitIfValidationError(np, volumesSliceValidate(volsSlice), "Invalid volume parameter found")
		for _, volStr := range volsSlice {
			vol := VolumeRegex.FindStringSubmatch(volStr)
			params.Volumes = append(params.Volumes, Volume{
				Label:     vol[1],
				SubPath:   vol[4],
				MountPath: vol[5],
			})
		}
	}
	if flags.Changed("cpu") {
		params.CPU, err = flags.GetString("cpu")
		chkErr(err)
		exitIfValidationError(np, cpuValidate(params.CPU), "Invalid CPU format")
	} else {
		params.CPU = DefaultCPURequest
	}
	if flags.Changed("memory") {
		params.Memory, err = flags.GetString("memory")
		chkErr(err)
		exitIfValidationError(np, memValidate(params.Memory), "Invalid memory format")
	} else {
		params.Memory = DefaultMemoryRequest
	}
	if flags.Changed("replicas") {
		params.Replicas, err = flags.GetInt("replicas")
		chkErr(err)
	} else {
		params.Replicas = DefaultReplicas
	}
	return
}
<|endoftext|>


<commit_before>// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or 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 constants

// Version number string.
const Version = "0.2.13"
<commit_msg>Version 0.2.15<commit_after>// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or 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 constants

// Version number string.
const Version = "0.2.15"
<|endoftext|>


<commit_before>// Copyright 2015 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 v2

import (
	"fmt"
	"time"

	"github.com/golang/glog"
	"github.com/google/cadvisor/info/v1"
)

func machineFsStatsFromV1(fsStats []v1.FsStats) []MachineFsStats {
	var result []MachineFsStats
	for i := range fsStats {
		stat := fsStats[i]
		readDuration := time.Millisecond * time.Duration(stat.ReadTime)
		writeDuration := time.Millisecond * time.Duration(stat.WriteTime)
		ioDuration := time.Millisecond * time.Duration(stat.IoTime)
		weightedDuration := time.Millisecond * time.Duration(stat.WeightedIoTime)
		machineFsStat := MachineFsStats{
			Device:    stat.Device,
			Type:      stat.Type,
			Capacity:  &stat.Limit,
			Usage:     &stat.Usage,
			Available: &stat.Available,
			DiskStats: DiskStats{
				ReadsCompleted:     &stat.ReadsCompleted,
				ReadsMerged:        &stat.ReadsMerged,
				SectorsRead:        &stat.SectorsRead,
				ReadDuration:       &readDuration,
				WritesCompleted:    &stat.WritesCompleted,
				WritesMerged:       &stat.WritesMerged,
				SectorsWritten:     &stat.SectorsWritten,
				WriteDuration:      &writeDuration,
				IoInProgress:       &stat.IoInProgress,
				IoDuration:         &ioDuration,
				WeightedIoDuration: &weightedDuration,
			},
		}
		if stat.HasInodes {
			machineFsStat.InodesFree = &stat.InodesFree
		}
		result = append(result, machineFsStat)
	}
	return result
}

func MachineStatsFromV1(cont *v1.ContainerInfo) []MachineStats {
	var stats []MachineStats
	var last *v1.ContainerStats
	for i := range cont.Stats {
		val := cont.Stats[i]
		stat := MachineStats{
			Timestamp: val.Timestamp,
		}
		if cont.Spec.HasCpu {
			stat.Cpu = &val.Cpu
			cpuInst, err := InstCpuStats(last, val)
			if err != nil {
				glog.Warningf("Could not get instant cpu stats: %v", err)
			} else {
				stat.CpuInst = cpuInst
			}
			last = val
		}
		if cont.Spec.HasMemory {
			stat.Memory = &val.Memory
		}
		if cont.Spec.HasNetwork {
			stat.Network = &NetworkStats{
				// FIXME: Use reflection instead.
				Tcp:        TcpStat(val.Network.Tcp),
				Tcp6:       TcpStat(val.Network.Tcp6),
				Interfaces: val.Network.Interfaces,
			}
		}
		if cont.Spec.HasFilesystem {
			stat.Filesystem = machineFsStatsFromV1(val.Filesystem)
		}
		// TODO(rjnagal): Handle load stats.
		stats = append(stats, stat)
	}
	return stats
}

func ContainerStatsFromV1(spec *v1.ContainerSpec, stats []*v1.ContainerStats) []*ContainerStats {
	newStats := make([]*ContainerStats, 0, len(stats))
	var last *v1.ContainerStats
	for _, val := range stats {
		stat := &ContainerStats{
			Timestamp: val.Timestamp,
		}
		if spec.HasCpu {
			stat.Cpu = &val.Cpu
			cpuInst, err := InstCpuStats(last, val)
			if err != nil {
				glog.Warningf("Could not get instant cpu stats: %v", err)
			} else {
				stat.CpuInst = cpuInst
			}
			last = val
		}
		if spec.HasMemory {
			stat.Memory = &val.Memory
		}
		if spec.HasNetwork {
			// TODO: Handle TcpStats
			stat.Network = &NetworkStats{
				Interfaces: val.Network.Interfaces,
			}
		}
		if spec.HasFilesystem {
			if len(val.Filesystem) == 1 {
				stat.Filesystem = &FilesystemStats{
					TotalUsageBytes: &val.Filesystem[0].Usage,
					BaseUsageBytes:  &val.Filesystem[0].BaseUsage,
				}
			} else if len(val.Filesystem) > 1 {
				// Cannot handle multiple devices per container.
				glog.V(2).Infof("failed to handle multiple devices for container. Skipping Filesystem stats")
			}
		}
		if spec.HasDiskIo {
			stat.DiskIo = &val.DiskIo
		}
		if spec.HasCustomMetrics {
			stat.CustomMetrics = val.CustomMetrics
		}
		// TODO(rjnagal): Handle load stats.
		newStats = append(newStats, stat)
	}
	return newStats
}

func DeprecatedStatsFromV1(cont *v1.ContainerInfo) []DeprecatedContainerStats {
	stats := make([]DeprecatedContainerStats, 0, len(cont.Stats))
	var last *v1.ContainerStats
	for _, val := range cont.Stats {
		stat := DeprecatedContainerStats{
			Timestamp:        val.Timestamp,
			HasCpu:           cont.Spec.HasCpu,
			HasMemory:        cont.Spec.HasMemory,
			HasNetwork:       cont.Spec.HasNetwork,
			HasFilesystem:    cont.Spec.HasFilesystem,
			HasDiskIo:        cont.Spec.HasDiskIo,
			HasCustomMetrics: cont.Spec.HasCustomMetrics,
		}
		if stat.HasCpu {
			stat.Cpu = val.Cpu
			cpuInst, err := InstCpuStats(last, val)
			if err != nil {
				glog.Warningf("Could not get instant cpu stats: %v", err)
			} else {
				stat.CpuInst = cpuInst
			}
			last = val
		}
		if stat.HasMemory {
			stat.Memory = val.Memory
		}
		if stat.HasNetwork {
			stat.Network.Interfaces = val.Network.Interfaces
		}
		if stat.HasFilesystem {
			stat.Filesystem = val.Filesystem
		}
		if stat.HasDiskIo {
			stat.DiskIo = val.DiskIo
		}
		if stat.HasCustomMetrics {
			stat.CustomMetrics = val.CustomMetrics
		}
		// TODO(rjnagal): Handle load stats.
		stats = append(stats, stat)
	}
	return stats
}

func InstCpuStats(last, cur *v1.ContainerStats) (*CpuInstStats, error) {
	if last == nil {
		return nil, nil
	}
	if !cur.Timestamp.After(last.Timestamp) {
		return nil, fmt.Errorf("container stats move backwards in time")
	}
	if len(last.Cpu.Usage.PerCpu) != len(cur.Cpu.Usage.PerCpu) {
		return nil, fmt.Errorf("different number of cpus")
	}
	timeDelta := cur.Timestamp.Sub(last.Timestamp)
	if timeDelta <= 100*time.Millisecond {
		return nil, fmt.Errorf("time delta unexpectedly small")
	}
	// Nanoseconds to gain precision and avoid having zero seconds if the
	// difference between the timestamps is just under a second
	timeDeltaNs := uint64(timeDelta.Nanoseconds())
	convertToRate := func(lastValue, curValue uint64) (uint64, error) {
		if curValue < lastValue {
			return 0, fmt.Errorf("cumulative stats decrease")
		}
		valueDelta := curValue - lastValue
		// Use float64 to keep precision
		return uint64(float64(valueDelta) / float64(timeDeltaNs) * 1e9), nil
	}
	total, err := convertToRate(last.Cpu.Usage.Total, cur.Cpu.Usage.Total)
	if err != nil {
		return nil, err
	}
	percpu := make([]uint64, len(last.Cpu.Usage.PerCpu))
	for i := range percpu {
		var err error
		percpu[i], err = convertToRate(last.Cpu.Usage.PerCpu[i], cur.Cpu.Usage.PerCpu[i])
		if err != nil {
			return nil, err
		}
	}
	user, err := convertToRate(last.Cpu.Usage.User, cur.Cpu.Usage.User)
	if err != nil {
		return nil, err
	}
	system, err := convertToRate(last.Cpu.Usage.System, cur.Cpu.Usage.System)
	if err != nil {
		return nil, err
	}
	return &CpuInstStats{
		Usage: CpuInstUsage{
			Total:  total,
			PerCpu: percpu,
			User:   user,
			System: system,
		},
	}, nil
}

// Get V2 container spec from v1 container info.
func ContainerSpecFromV1(specV1 *v1.ContainerSpec, aliases []string, namespace string) ContainerSpec {
	specV2 := ContainerSpec{
		CreationTime:     specV1.CreationTime,
		HasCpu:           specV1.HasCpu,
		HasMemory:        specV1.HasMemory,
		HasFilesystem:    specV1.HasFilesystem,
		HasNetwork:       specV1.HasNetwork,
		HasDiskIo:        specV1.HasDiskIo,
		HasCustomMetrics: specV1.HasCustomMetrics,
		Image:            specV1.Image,
		Labels:           specV1.Labels,
	}
	if specV1.HasCpu {
		specV2.Cpu.Limit = specV1.Cpu.Limit
		specV2.Cpu.MaxLimit = specV1.Cpu.MaxLimit
		specV2.Cpu.Mask = specV1.Cpu.Mask
	}
	if specV1.HasMemory {
		specV2.Memory.Limit = specV1.Memory.Limit
		specV2.Memory.Reservation = specV1.Memory.Reservation
		specV2.Memory.SwapLimit = specV1.Memory.SwapLimit
	}
	if specV1.HasCustomMetrics {
		specV2.CustomMetrics = specV1.CustomMetrics
	}
	specV2.Aliases = aliases
	specV2.Namespace = namespace
	return specV2
}
<commit_msg>Add Envs to specV2 from specV1<commit_after>// Copyright 2015 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 v2

import (
	"fmt"
	"time"

	"github.com/golang/glog"
	"github.com/google/cadvisor/info/v1"
)

func machineFsStatsFromV1(fsStats []v1.FsStats) []MachineFsStats {
	var result []MachineFsStats
	for i := range fsStats {
		stat := fsStats[i]
		readDuration := time.Millisecond * time.Duration(stat.ReadTime)
		writeDuration := time.Millisecond * time.Duration(stat.WriteTime)
		ioDuration := time.Millisecond * time.Duration(stat.IoTime)
		weightedDuration := time.Millisecond * time.Duration(stat.WeightedIoTime)
		machineFsStat := MachineFsStats{
			Device:    stat.Device,
			Type:      stat.Type,
			Capacity:  &stat.Limit,
			Usage:     &stat.Usage,
			Available: &stat.Available,
			DiskStats: DiskStats{
				ReadsCompleted:     &stat.ReadsCompleted,
				ReadsMerged:        &stat.ReadsMerged,
				SectorsRead:        &stat.SectorsRead,
				ReadDuration:       &readDuration,
				WritesCompleted:    &stat.WritesCompleted,
				WritesMerged:       &stat.WritesMerged,
				SectorsWritten:     &stat.SectorsWritten,
				WriteDuration:      &writeDuration,
				IoInProgress:       &stat.IoInProgress,
				IoDuration:         &ioDuration,
				WeightedIoDuration: &weightedDuration,
			},
		}
		if stat.HasInodes {
			machineFsStat.InodesFree = &stat.InodesFree
		}
		result = append(result, machineFsStat)
	}
	return result
}

func MachineStatsFromV1(cont *v1.ContainerInfo) []MachineStats {
	var stats []MachineStats
	var last *v1.ContainerStats
	for i := range cont.Stats {
		val := cont.Stats[i]
		stat := MachineStats{
			Timestamp: val.Timestamp,
		}
		if cont.Spec.HasCpu {
			stat.Cpu = &val.Cpu
			cpuInst, err := InstCpuStats(last, val)
			if err != nil {
				glog.Warningf("Could not get instant cpu stats: %v", err)
			} else {
				stat.CpuInst = cpuInst
			}
			last = val
		}
		if cont.Spec.HasMemory {
			stat.Memory = &val.Memory
		}
		if cont.Spec.HasNetwork {
			stat.Network = &NetworkStats{
				// FIXME: Use reflection instead.
				Tcp:        TcpStat(val.Network.Tcp),
				Tcp6:       TcpStat(val.Network.Tcp6),
				Interfaces: val.Network.Interfaces,
			}
		}
		if cont.Spec.HasFilesystem {
			stat.Filesystem = machineFsStatsFromV1(val.Filesystem)
		}
		// TODO(rjnagal): Handle load stats.
		stats = append(stats, stat)
	}
	return stats
}

func ContainerStatsFromV1(spec *v1.ContainerSpec, stats []*v1.ContainerStats) []*ContainerStats {
	newStats := make([]*ContainerStats, 0, len(stats))
	var last *v1.ContainerStats
	for _, val := range stats {
		stat := &ContainerStats{
			Timestamp: val.Timestamp,
		}
		if spec.HasCpu {
			stat.Cpu = &val.Cpu
			cpuInst, err := InstCpuStats(last, val)
			if err != nil {
				glog.Warningf("Could not get instant cpu stats: %v", err)
			} else {
				stat.CpuInst = cpuInst
			}
			last = val
		}
		if spec.HasMemory {
			stat.Memory = &val.Memory
		}
		if spec.HasNetwork {
			// TODO: Handle TcpStats
			stat.Network = &NetworkStats{
				Interfaces: val.Network.Interfaces,
			}
		}
		if spec.HasFilesystem {
			if len(val.Filesystem) == 1 {
				stat.Filesystem = &FilesystemStats{
					TotalUsageBytes: &val.Filesystem[0].Usage,
					BaseUsageBytes:  &val.Filesystem[0].BaseUsage,
				}
			} else if len(val.Filesystem) > 1 {
				// Cannot handle multiple devices per container.
				glog.V(2).Infof("failed to handle multiple devices for container. Skipping Filesystem stats")
			}
		}
		if spec.HasDiskIo {
			stat.DiskIo = &val.DiskIo
		}
		if spec.HasCustomMetrics {
			stat.CustomMetrics = val.CustomMetrics
		}
		// TODO(rjnagal): Handle load stats.
		newStats = append(newStats, stat)
	}
	return newStats
}

func DeprecatedStatsFromV1(cont *v1.ContainerInfo) []DeprecatedContainerStats {
	stats := make([]DeprecatedContainerStats, 0, len(cont.Stats))
	var last *v1.ContainerStats
	for _, val := range cont.Stats {
		stat := DeprecatedContainerStats{
			Timestamp:        val.Timestamp,
			HasCpu:           cont.Spec.HasCpu,
			HasMemory:        cont.Spec.HasMemory,
			HasNetwork:       cont.Spec.HasNetwork,
			HasFilesystem:    cont.Spec.HasFilesystem,
			HasDiskIo:        cont.Spec.HasDiskIo,
			HasCustomMetrics: cont.Spec.HasCustomMetrics,
		}
		if stat.HasCpu {
			stat.Cpu = val.Cpu
			cpuInst, err := InstCpuStats(last, val)
			if err != nil {
				glog.Warningf("Could not get instant cpu stats: %v", err)
			} else {
				stat.CpuInst = cpuInst
			}
			last = val
		}
		if stat.HasMemory {
			stat.Memory = val.Memory
		}
		if stat.HasNetwork {
			stat.Network.Interfaces = val.Network.Interfaces
		}
		if stat.HasFilesystem {
			stat.Filesystem = val.Filesystem
		}
		if stat.HasDiskIo {
			stat.DiskIo = val.DiskIo
		}
		if stat.HasCustomMetrics {
			stat.CustomMetrics = val.CustomMetrics
		}
		// TODO(rjnagal): Handle load stats.
		stats = append(stats, stat)
	}
	return stats
}

func InstCpuStats(last, cur *v1.ContainerStats) (*CpuInstStats, error) {
	if last == nil {
		return nil, nil
	}
	if !cur.Timestamp.After(last.Timestamp) {
		return nil, fmt.Errorf("container stats move backwards in time")
	}
	if len(last.Cpu.Usage.PerCpu) != len(cur.Cpu.Usage.PerCpu) {
		return nil, fmt.Errorf("different number of cpus")
	}
	timeDelta := cur.Timestamp.Sub(last.Timestamp)
	if timeDelta <= 100*time.Millisecond {
		return nil, fmt.Errorf("time delta unexpectedly small")
	}
	// Nanoseconds to gain precision and avoid having zero seconds if the
	// difference between the timestamps is just under a second
	timeDeltaNs := uint64(timeDelta.Nanoseconds())
	convertToRate := func(lastValue, curValue uint64) (uint64, error) {
		if curValue < lastValue {
			return 0, fmt.Errorf("cumulative stats decrease")
		}
		valueDelta := curValue - lastValue
		// Use float64 to keep precision
		return uint64(float64(valueDelta) / float64(timeDeltaNs) * 1e9), nil
	}
	total, err := convertToRate(last.Cpu.Usage.Total, cur.Cpu.Usage.Total)
	if err != nil {
		return nil, err
	}
	percpu := make([]uint64, len(last.Cpu.Usage.PerCpu))
	for i := range percpu {
		var err error
		percpu[i], err = convertToRate(last.Cpu.Usage.PerCpu[i], cur.Cpu.Usage.PerCpu[i])
		if err != nil {
			return nil, err
		}
	}
	user, err := convertToRate(last.Cpu.Usage.User, cur.Cpu.Usage.User)
	if err != nil {
		return nil, err
	}
	system, err := convertToRate(last.Cpu.Usage.System, cur.Cpu.Usage.System)
	if err != nil {
		return nil, err
	}
	return &CpuInstStats{
		Usage: CpuInstUsage{
			Total:  total,
			PerCpu: percpu,
			User:   user,
			System: system,
		},
	}, nil
}

// Get V2 container spec from v1 container info.
func ContainerSpecFromV1(specV1 *v1.ContainerSpec, aliases []string, namespace string) ContainerSpec {
	specV2 := ContainerSpec{
		CreationTime:     specV1.CreationTime,
		HasCpu:           specV1.HasCpu,
		HasMemory:        specV1.HasMemory,
		HasFilesystem:    specV1.HasFilesystem,
		HasNetwork:       specV1.HasNetwork,
		HasDiskIo:        specV1.HasDiskIo,
		HasCustomMetrics: specV1.HasCustomMetrics,
		Image:            specV1.Image,
		Labels:           specV1.Labels,
		Envs:             specV1.Envs,
	}
	if specV1.HasCpu {
		specV2.Cpu.Limit = specV1.Cpu.Limit
		specV2.Cpu.MaxLimit = specV1.Cpu.MaxLimit
		specV2.Cpu.Mask = specV1.Cpu.Mask
	}
	if specV1.HasMemory {
		specV2.Memory.Limit = specV1.Memory.Limit
		specV2.Memory.Reservation = specV1.Memory.Reservation
		specV2.Memory.SwapLimit = specV1.Memory.SwapLimit
	}
	if specV1.HasCustomMetrics {
		specV2.CustomMetrics = specV1.CustomMetrics
	}
	specV2.Aliases = aliases
	specV2.Namespace = namespace
	return specV2
}
<|endoftext|>


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

// +build go1.3

package lxd

import (
	"fmt"

	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/names"
	"github.com/lxc/lxd"

	"github.com/juju/juju/cloudconfig/containerinit"
	"github.com/juju/juju/cloudconfig/instancecfg"
	"github.com/juju/juju/container"
	"github.com/juju/juju/instance"
	"github.com/juju/juju/network"
	"github.com/juju/juju/status"
	"github.com/juju/juju/tools/lxdclient"
)

var (
	logger = loggo.GetLogger("juju.container.lxd")
)

// XXX: should we allow managing containers on other hosts? this is
// functionality LXD gives us and from discussion juju would use eventually for
// the local provider, so the APIs probably need to be changed to pass extra
// args around. I'm punting for now.
type containerManager struct {
	name string
	// A cached client.
	client *lxdclient.Client
	// Custom network profile
	networkProfile string
}

// containerManager implements container.Manager.
var _ container.Manager = (*containerManager)(nil)

func ConnectLocal(namespace string) (*lxdclient.Client, error) {
	cfg := lxdclient.Config{
		Namespace: namespace,
		Remote:    lxdclient.Local,
	}

	cfg, err := cfg.WithDefaults()
	if err != nil {
		return nil, errors.Trace(err)
	}

	client, err := lxdclient.Connect(cfg)
	if err != nil {
		return nil, errors.Trace(err)
	}

	return client, nil
}

// NewContainerManager creates the entity that knows how to create and manage
// LXD containers.
// TODO(jam): This needs to grow support for things like LXC's ImageURLGetter
// functionality.
func NewContainerManager(conf container.ManagerConfig) (container.Manager, error) {
	name := conf.PopValue(container.ConfigName)
	if name == "" {
		return nil, errors.Errorf("name is required")
	}

	conf.WarnAboutUnused()
	return &containerManager{name: name}, nil
}

func (manager *containerManager) CreateContainer(
	instanceConfig *instancecfg.InstanceConfig,
	series string,
	networkConfig *container.NetworkConfig,
	storageConfig *container.StorageConfig,
	callback container.StatusCallback,
) (inst instance.Instance, _ *instance.HardwareCharacteristics, err error) {

	defer func() {
		if err != nil {
			manager.deleteNetworkProfile()
			callback(status.StatusProvisioningError, fmt.Sprintf("Creating container: %v", err), nil)
		}
	}()

	if manager.client == nil {
		manager.client, err = ConnectLocal(manager.name)
		if err != nil {
			err = errors.Annotatef(err, "failed to connect to local LXD")
			return
		}
	}

	err = manager.client.EnsureImageExists(series,
		lxdclient.DefaultImageSources,
		func(progress string) {
			callback(status.StatusProvisioning, progress, nil)
		})
	if err != nil {
		err = errors.Annotatef(err, "failed to ensure LXD image")
		return
	}

	name := names.NewMachineTag(instanceConfig.MachineId).String()
	if manager.name != "" {
		name = fmt.Sprintf("%s-%s", manager.name, name)
	}

	userData, err := containerinit.CloudInitUserData(instanceConfig, networkConfig)
	if err != nil {
		return
	}

	metadata := map[string]string{
		lxdclient.UserdataKey: string(userData),
		// An extra piece of info to let people figure out where this
		// thing came from.
		"user.juju-environment": manager.name,

		// Make sure these come back up on host reboot.
		"boot.autostart": "true",
	}

	networkProfile := fmt.Sprintf("%s-network", name)

	if err = createNetworkProfile(manager.client, networkProfile); err != nil {
		return
	}

	if len(networkConfig.Interfaces) > 0 || networkConfig.Device != "" {
		manager.networkProfile = networkProfile
		if len(networkConfig.Interfaces) > 0 {
			err = networkProfileAddInterfaces(manager.client, networkProfile, networkConfig)
		} else {
			err = networkProfileAddSingleInterface(manager.client, networkProfile, networkConfig)
		}
		if err != nil {
			return
		}
	} else {
		networkProfile = "default"
	}

	spec := lxdclient.InstanceSpec{
		Name:     name,
		Image:    manager.client.ImageNameForSeries(series),
		Metadata: metadata,
		Profiles: []string{
			networkProfile,
		},
	}

	logger.Infof("starting instance %q (image %q)...", spec.Name, spec.Image)
	callback(status.StatusProvisioning, "Starting container", nil)
	_, err = manager.client.AddInstance(spec)
	if err != nil {
		manager.client.ProfileDelete(networkProfile)
		return
	}

	callback(status.StatusRunning, "Container started", nil)
	inst = &lxdInstance{name, manager.client}
	return
}

func (manager *containerManager) DestroyContainer(id instance.Id) error {
	if manager.client == nil {
		var err error
		manager.client, err = ConnectLocal(manager.name)
		if err != nil {
			return err
		}
	}
	manager.deleteNetworkProfile()
	return errors.Trace(manager.client.RemoveInstances(manager.name, string(id)))
}

func (manager *containerManager) ListContainers() (result []instance.Instance, err error) {
	result = []instance.Instance{}
	if manager.client == nil {
		manager.client, err = ConnectLocal(manager.name)
		if err != nil {
			return
		}
	}

	lxdInstances, err := manager.client.Instances(manager.name)
	if err != nil {
		return
	}

	for _, i := range lxdInstances {
		result = append(result, &lxdInstance{i.Name, manager.client})
	}

	return
}

func (manager *containerManager) IsInitialized() bool {
	if manager.client != nil {
		return true
	}

	// NewClient does a roundtrip to the server to make sure it understands
	// the versions, so all we need to do is connect above and we're done.
	var err error
	manager.client, err = ConnectLocal(manager.name)
	return err == nil
}

// HasLXDSupport returns false when this juju binary was not built with LXD
// support (i.e. it was built on a golang version < 1.2
func HasLXDSupport() bool {
	return true
}

func nicProperties(parentDevice, deviceName, hwAddr string, mtu int) ([]string, error) {
	var props = []string{"nictype=bridged"}

	if parentDevice == "" {
		return nil, errors.Errorf("invalid parent device")
	} else {
		props = append(props, fmt.Sprintf("parent=%v", parentDevice))
	}

	if deviceName == "" {
		return nil, errors.Errorf("invalid device name")
	} else {
		props = append(props, fmt.Sprintf("name=%v", deviceName))
	}

	if hwAddr != "" {
		props = append(props, fmt.Sprintf("hwaddr=%v", hwAddr))
	}

	if mtu > 0 {
		props = append(props, fmt.Sprintf("mtu=%v", mtu))
	}

	return props, nil
}

func addNetworkDeviceToProfile(client *lxdclient.Client, profile, parentDevice, deviceName, hwAddr string, mtu int) (*lxd.Response, error) {
	props, err := nicProperties(parentDevice, deviceName, hwAddr, mtu)
	if err != nil {
		return nil, errors.Trace(err)
	}
	logger.Infof("adding nic device %q with properties %+v to profile %q", deviceName, props, profile)
	return client.ProfileDeviceAdd(profile, deviceName, "nic", props)
}

func networkProfileAddSingleInterface(client *lxdclient.Client, profile string, networkConfig *container.NetworkConfig) error {
	_, err := addNetworkDeviceToProfile(client, profile, networkConfig.Device, "eth0", "", networkConfig.MTU)
	return errors.Trace(err)
}

func networkProfileAddInterfaces(client *lxdclient.Client, profile string, networkConfig *container.NetworkConfig) error {
	for _, v := range networkConfig.Interfaces {
		if v.InterfaceType == network.LoopbackInterface {
			continue
		}

		if v.InterfaceType != network.EthernetInterface {
			return errors.Errorf("interface type %q not supported", v.InterfaceType)
		}

		_, err := addNetworkDeviceToProfile(client, profile, v.ParentInterfaceName, v.InterfaceName, v.MACAddress, v.MTU)

		if err != nil {
			return errors.Trace(err)
		}
	}

	return nil
}

func createNetworkProfile(client *lxdclient.Client, profile string) error {
	found, err := client.HasProfile(profile)

	if err != nil {
		return errors.Trace(err)
	}

	if found {
		logger.Infof("deleting existing container profile %q", profile)
		if err := client.ProfileDelete(profile); err != nil {
			return errors.Trace(err)
		}
	}

	err = client.CreateProfile(profile, nil)

	if err == nil {
		logger.Infof("created new network container profile %q", profile)
	}

	return errors.Trace(err)
}

func (manager *containerManager) deleteNetworkProfile() {
	if manager.client != nil && manager.networkProfile != "" {
		logger.Infof("deleting container network profile %q", manager.networkProfile)
		if err := manager.client.ProfileDelete(manager.networkProfile); err != nil {
			logger.Warningf("discarding profile delete error: %v", err)
		}
		manager.networkProfile = ""
	}
}
<commit_msg>Make Add{Single,Multiple}Interfaces() params discrete<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// +build go1.3

package lxd

import (
	"fmt"

	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/names"
	"github.com/lxc/lxd"

	"github.com/juju/juju/cloudconfig/containerinit"
	"github.com/juju/juju/cloudconfig/instancecfg"
	"github.com/juju/juju/container"
	"github.com/juju/juju/instance"
	"github.com/juju/juju/network"
	"github.com/juju/juju/status"
	"github.com/juju/juju/tools/lxdclient"
)

var (
	logger = loggo.GetLogger("juju.container.lxd")
)

// XXX: should we allow managing containers on other hosts? this is
// functionality LXD gives us and from discussion juju would use eventually for
// the local provider, so the APIs probably need to be changed to pass extra
// args around. I'm punting for now.
type containerManager struct {
	name string
	// A cached client.
	client *lxdclient.Client
	// Custom network profile
	networkProfile string
}

// containerManager implements container.Manager.
var _ container.Manager = (*containerManager)(nil)

func ConnectLocal(namespace string) (*lxdclient.Client, error) {
	cfg := lxdclient.Config{
		Namespace: namespace,
		Remote:    lxdclient.Local,
	}

	cfg, err := cfg.WithDefaults()
	if err != nil {
		return nil, errors.Trace(err)
	}

	client, err := lxdclient.Connect(cfg)
	if err != nil {
		return nil, errors.Trace(err)
	}

	return client, nil
}

// NewContainerManager creates the entity that knows how to create and manage
// LXD containers.
// TODO(jam): This needs to grow support for things like LXC's ImageURLGetter
// functionality.
func NewContainerManager(conf container.ManagerConfig) (container.Manager, error) {
	name := conf.PopValue(container.ConfigName)
	if name == "" {
		return nil, errors.Errorf("name is required")
	}

	conf.WarnAboutUnused()
	return &containerManager{name: name}, nil
}

func (manager *containerManager) CreateContainer(
	instanceConfig *instancecfg.InstanceConfig,
	series string,
	networkConfig *container.NetworkConfig,
	storageConfig *container.StorageConfig,
	callback container.StatusCallback,
) (inst instance.Instance, _ *instance.HardwareCharacteristics, err error) {

	defer func() {
		if err != nil {
			manager.deleteNetworkProfile()
			callback(status.StatusProvisioningError, fmt.Sprintf("Creating container: %v", err), nil)
		}
	}()

	if manager.client == nil {
		manager.client, err = ConnectLocal(manager.name)
		if err != nil {
			err = errors.Annotatef(err, "failed to connect to local LXD")
			return
		}
	}

	err = manager.client.EnsureImageExists(series,
		lxdclient.DefaultImageSources,
		func(progress string) {
			callback(status.StatusProvisioning, progress, nil)
		})
	if err != nil {
		err = errors.Annotatef(err, "failed to ensure LXD image")
		return
	}

	name := names.NewMachineTag(instanceConfig.MachineId).String()
	if manager.name != "" {
		name = fmt.Sprintf("%s-%s", manager.name, name)
	}

	userData, err := containerinit.CloudInitUserData(instanceConfig, networkConfig)
	if err != nil {
		return
	}

	metadata := map[string]string{
		lxdclient.UserdataKey: string(userData),
		// An extra piece of info to let people figure out where this
		// thing came from.
		"user.juju-environment": manager.name,

		// Make sure these come back up on host reboot.
		"boot.autostart": "true",
	}

	networkProfile := fmt.Sprintf("%s-network", name)

	if err = createNetworkProfile(manager.client, networkProfile); err != nil {
		return
	}

	if len(networkConfig.Interfaces) > 0 || networkConfig.Device != "" {
		manager.networkProfile = networkProfile
		if len(networkConfig.Interfaces) > 0 {
			err = networkProfileAddMultipleInterfaces(manager.client, networkProfile, networkConfig.Interfaces)
		} else {
			err = networkProfileAddSingleInterface(manager.client, networkProfile, networkConfig.Device, networkConfig.MTU)
		}
		if err != nil {
			return
		}
	} else {
		networkProfile = "default"
	}

	spec := lxdclient.InstanceSpec{
		Name:     name,
		Image:    manager.client.ImageNameForSeries(series),
		Metadata: metadata,
		Profiles: []string{
			networkProfile,
		},
	}

	logger.Infof("starting instance %q (image %q)...", spec.Name, spec.Image)
	callback(status.StatusProvisioning, "Starting container", nil)
	_, err = manager.client.AddInstance(spec)
	if err != nil {
		manager.client.ProfileDelete(networkProfile)
		return
	}

	callback(status.StatusRunning, "Container started", nil)
	inst = &lxdInstance{name, manager.client}
	return
}

func (manager *containerManager) DestroyContainer(id instance.Id) error {
	if manager.client == nil {
		var err error
		manager.client, err = ConnectLocal(manager.name)
		if err != nil {
			return err
		}
	}
	manager.deleteNetworkProfile()
	return errors.Trace(manager.client.RemoveInstances(manager.name, string(id)))
}

func (manager *containerManager) ListContainers() (result []instance.Instance, err error) {
	result = []instance.Instance{}
	if manager.client == nil {
		manager.client, err = ConnectLocal(manager.name)
		if err != nil {
			return
		}
	}

	lxdInstances, err := manager.client.Instances(manager.name)
	if err != nil {
		return
	}

	for _, i := range lxdInstances {
		result = append(result, &lxdInstance{i.Name, manager.client})
	}

	return
}

func (manager *containerManager) IsInitialized() bool {
	if manager.client != nil {
		return true
	}

	// NewClient does a roundtrip to the server to make sure it understands
	// the versions, so all we need to do is connect above and we're done.
	var err error
	manager.client, err = ConnectLocal(manager.name)
	return err == nil
}

// HasLXDSupport returns false when this juju binary was not built with LXD
// support (i.e. it was built on a golang version < 1.2
func HasLXDSupport() bool {
	return true
}

func nicProperties(parentDevice, deviceName, hwAddr string, mtu int) ([]string, error) {
	var props = []string{"nictype=bridged"}

	if parentDevice == "" {
		return nil, errors.Errorf("invalid parent device")
	} else {
		props = append(props, fmt.Sprintf("parent=%v", parentDevice))
	}

	if deviceName == "" {
		return nil, errors.Errorf("invalid device name")
	} else {
		props = append(props, fmt.Sprintf("name=%v", deviceName))
	}

	if hwAddr != "" {
		props = append(props, fmt.Sprintf("hwaddr=%v", hwAddr))
	}

	if mtu > 0 {
		props = append(props, fmt.Sprintf("mtu=%v", mtu))
	}

	return props, nil
}

func addNetworkDeviceToProfile(client *lxdclient.Client, profile, parentDevice, deviceName, hwAddr string, mtu int) (*lxd.Response, error) {
	props, err := nicProperties(parentDevice, deviceName, hwAddr, mtu)
	if err != nil {
		return nil, errors.Trace(err)
	}
	logger.Infof("adding nic device %q with properties %+v to profile %q", deviceName, props, profile)
	return client.ProfileDeviceAdd(profile, deviceName, "nic", props)
}

func networkProfileAddSingleInterface(client *lxdclient.Client, profile, deviceName string, mtu int) error {
	_, err := addNetworkDeviceToProfile(client, profile, deviceName, "eth0", "", mtu)
	return errors.Trace(err)
}

func networkProfileAddMultipleInterfaces(client *lxdclient.Client, profile string, interfaces []network.InterfaceInfo) error {
	for _, v := range interfaces {
		if v.InterfaceType == network.LoopbackInterface {
			continue
		}

		if v.InterfaceType != network.EthernetInterface {
			return errors.Errorf("interface type %q not supported", v.InterfaceType)
		}

		_, err := addNetworkDeviceToProfile(client, profile, v.ParentInterfaceName, v.InterfaceName, v.MACAddress, v.MTU)

		if err != nil {
			return errors.Trace(err)
		}
	}

	return nil
}

func createNetworkProfile(client *lxdclient.Client, profile string) error {
	found, err := client.HasProfile(profile)

	if err != nil {
		return errors.Trace(err)
	}

	if found {
		logger.Infof("deleting existing container profile %q", profile)
		if err := client.ProfileDelete(profile); err != nil {
			return errors.Trace(err)
		}
	}

	err = client.CreateProfile(profile, nil)

	if err == nil {
		logger.Infof("created new network container profile %q", profile)
	}

	return errors.Trace(err)
}

func (manager *containerManager) deleteNetworkProfile() {
	if manager.client != nil && manager.networkProfile != "" {
		logger.Infof("deleting container network profile %q", manager.networkProfile)
		if err := manager.client.ProfileDelete(manager.networkProfile); err != nil {
			logger.Warningf("discarding profile delete error: %v", err)
		}
		manager.networkProfile = ""
	}
}
<|endoftext|>


<commit_before>package internet

import "v2ray.com/core/common/serial"

type ConfigCreator func() interface{}

var (
	globalTransportConfigCreatorCache = make(map[TransportProtocol]ConfigCreator)
	globalTransportSettings           []*TransportConfig
)

func RegisterProtocolConfigCreator(protocol TransportProtocol, creator ConfigCreator) error {
	// TODO: check duplicate
	globalTransportConfigCreatorCache[protocol] = creator
	return nil
}

func CreateTransportConfig(protocol TransportProtocol) (interface{}, error) {
	creator, ok := globalTransportConfigCreatorCache[protocol]
	if !ok {
		return nil, newError("unknown transport protocol: ", protocol)
	}
	return creator(), nil
}

func (v *TransportConfig) GetTypedSettings() (interface{}, error) {
	return v.Settings.GetInstance()
}

func (v *StreamConfig) GetEffectiveProtocol() TransportProtocol {
	if v == nil {
		return TransportProtocol_TCP
	}
	return v.Protocol
}

func (v *StreamConfig) GetEffectiveTransportSettings() (interface{}, error) {
	protocol := v.GetEffectiveProtocol()

	if v != nil {
		for _, settings := range v.TransportSettings {
			if settings.Protocol == protocol {
				return settings.GetTypedSettings()
			}
		}
	}

	for _, settings := range globalTransportSettings {
		if settings.Protocol == protocol {
			return settings.GetTypedSettings()
		}
	}
	return CreateTransportConfig(protocol)
}

func (c *StreamConfig) GetTransportSettingsFor(protocol TransportProtocol) (interface{}, error) {
	if c != nil {
		for _, settings := range c.TransportSettings {
			if settings.Protocol == protocol {
				return settings.GetTypedSettings()
			}
		}
	}

	for _, settings := range globalTransportSettings {
		if settings.Protocol == protocol {
			return settings.GetTypedSettings()
		}
	}

	return CreateTransportConfig(protocol)
}

func (v *StreamConfig) GetEffectiveSecuritySettings() (interface{}, error) {
	for _, settings := range v.SecuritySettings {
		if settings.Type == v.SecurityType {
			return settings.GetInstance()
		}
	}
	return serial.GetInstance(v.SecurityType)
}

func (v *StreamConfig) HasSecuritySettings() bool {
	return len(v.SecurityType) > 0
}

func ApplyGlobalTransportSettings(settings []*TransportConfig) error {
	globalTransportSettings = settings
	return nil
}

func (v *ProxyConfig) HasTag() bool {
	return v != nil && len(v.Tag) > 0
}
<commit_msg>refactor<commit_after>package internet

import "v2ray.com/core/common/serial"

type ConfigCreator func() interface{}

var (
	globalTransportConfigCreatorCache = make(map[TransportProtocol]ConfigCreator)
	globalTransportSettings           []*TransportConfig
)

func RegisterProtocolConfigCreator(protocol TransportProtocol, creator ConfigCreator) error {
	// TODO: check duplicate
	globalTransportConfigCreatorCache[protocol] = creator
	return nil
}

func CreateTransportConfig(protocol TransportProtocol) (interface{}, error) {
	creator, ok := globalTransportConfigCreatorCache[protocol]
	if !ok {
		return nil, newError("unknown transport protocol: ", protocol)
	}
	return creator(), nil
}

func (c *TransportConfig) GetTypedSettings() (interface{}, error) {
	return c.Settings.GetInstance()
}

func (c *StreamConfig) GetEffectiveProtocol() TransportProtocol {
	if c == nil {
		return TransportProtocol_TCP
	}
	return c.Protocol
}

func (c *StreamConfig) GetEffectiveTransportSettings() (interface{}, error) {
	protocol := c.GetEffectiveProtocol()

	if c != nil {
		for _, settings := range c.TransportSettings {
			if settings.Protocol == protocol {
				return settings.GetTypedSettings()
			}
		}
	}

	for _, settings := range globalTransportSettings {
		if settings.Protocol == protocol {
			return settings.GetTypedSettings()
		}
	}
	return CreateTransportConfig(protocol)
}

func (c *StreamConfig) GetTransportSettingsFor(protocol TransportProtocol) (interface{}, error) {
	if c != nil {
		for _, settings := range c.TransportSettings {
			if settings.Protocol == protocol {
				return settings.GetTypedSettings()
			}
		}
	}

	for _, settings := range globalTransportSettings {
		if settings.Protocol == protocol {
			return settings.GetTypedSettings()
		}
	}

	return CreateTransportConfig(protocol)
}

func (c *StreamConfig) GetEffectiveSecuritySettings() (interface{}, error) {
	for _, settings := range c.SecuritySettings {
		if settings.Type == c.SecurityType {
			return settings.GetInstance()
		}
	}
	return serial.GetInstance(c.SecurityType)
}

func (c *StreamConfig) HasSecuritySettings() bool {
	return len(c.SecurityType) > 0
}

func ApplyGlobalTransportSettings(settings []*TransportConfig) error {
	globalTransportSettings = settings
	return nil
}

func (c *ProxyConfig) HasTag() bool {
	return c != nil && len(c.Tag) > 0
}
<|endoftext|>


<commit_before>// Copyright 2015 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package qemu

import (
	"fmt"
	"math/rand"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/google/syzkaller/vm"
)

const (
	hostAddr = "10.0.2.10"
)

func init() {
	vm.Register("qemu", ctor)
}

type instance struct {
	cfg     *vm.Config
	port    int
	rpipe   *os.File
	wpipe   *os.File
	qemu    *exec.Cmd
	readerC chan error
	waiterC chan error

	mu      sync.Mutex
	outputB []byte
	outputC chan []byte
}

func ctor(cfg *vm.Config) (vm.Instance, error) {
	for i := 0; ; i++ {
		inst, err := ctorImpl(cfg)
		if err == nil {
			return inst, nil
		}
		if i < 1000 && strings.Contains(err.Error(), "could not set up host forwarding rule") {
			continue
		}
		return nil, err
	}
}

func ctorImpl(cfg *vm.Config) (vm.Instance, error) {
	inst := &instance{cfg: cfg}
	closeInst := inst
	defer func() {
		if closeInst != nil {
			closeInst.Close()
		}
	}()

	if err := validateConfig(cfg); err != nil {
		return nil, err
	}

	var err error
	inst.rpipe, inst.wpipe, err = os.Pipe()
	if err != nil {
		return nil, fmt.Errorf("failed to create pipe: %v", err)
	}
	for sz := 128 << 10; sz <= 2<<20; sz *= 2 {
		syscall.Syscall(syscall.SYS_FCNTL, inst.wpipe.Fd(), syscall.F_SETPIPE_SZ, uintptr(sz))
	}

	if err := inst.Boot(); err != nil {
		return nil, err
	}

	closeInst = nil
	return inst, nil
}

func validateConfig(cfg *vm.Config) error {
	if cfg.Bin == "" {
		cfg.Bin = "qemu-system-x86_64"
	}
	if _, err := os.Stat(cfg.Image); err != nil {
		return fmt.Errorf("image file '%v' does not exist: %v", cfg.Image, err)
	}
	if _, err := os.Stat(cfg.Sshkey); err != nil {
		return fmt.Errorf("ssh key '%v' does not exist: %v", cfg.Sshkey, err)
	}
	if cfg.Cpu <= 0 || cfg.Cpu > 1024 {
		return fmt.Errorf("bad qemu cpu: %v, want [1-1024]", cfg.Cpu)
	}
	if cfg.Mem < 128 || cfg.Mem > 1048576 {
		return fmt.Errorf("bad qemu mem: %v, want [128-1048576]", cfg.Mem)
	}
	return nil
}

func (inst *instance) Close() {
	if inst.qemu != nil {
		inst.qemu.Process.Kill()
		err := <-inst.waiterC
		inst.waiterC <- err // repost it for waiting goroutines
		<-inst.readerC
	}
	if inst.rpipe != nil {
		inst.rpipe.Close()
	}
	if inst.wpipe != nil {
		inst.wpipe.Close()
	}
	os.RemoveAll(inst.cfg.Workdir)
}

func (inst *instance) Boot() error {
	for {
		// Find an unused TCP port.
		inst.port = rand.Intn(64<<10-1<<10) + 1<<10
		ln, err := net.Listen("tcp", fmt.Sprintf("localhost:%v", inst.port))
		if err == nil {
			ln.Close()
			break
		}
	}
	// TODO: ignores inst.cfg.Cpu
	args := []string{
		"-hda", inst.cfg.Image,
		"-snapshot",
		"-m", strconv.Itoa(inst.cfg.Mem),
		"-net", "nic",
		"-net", fmt.Sprintf("user,host=%v,hostfwd=tcp::%v-:22", hostAddr, inst.port),
		"-display", "none",
		"-serial", "stdio",
		"-no-reboot",
		"-enable-kvm",
		"-numa", "node,nodeid=0,cpus=0-1", "-numa", "node,nodeid=1,cpus=2-3",
		"-smp", "sockets=2,cores=2,threads=1",
		"-usb", "-usbdevice", "mouse", "-usbdevice", "tablet",
		"-soundhw", "all",
	}
	if inst.cfg.Initrd != "" {
		args = append(args,
			"-initrd", inst.cfg.Initrd,
		)
	}
	if inst.cfg.Kernel != "" {
		args = append(args,
			"-kernel", inst.cfg.Kernel,
			"-append", "console=ttyS0 root=/dev/sda debug earlyprintk=serial slub_debug=UZ "+inst.cfg.Cmdline,
		)
	}
	qemu := exec.Command(inst.cfg.Bin, args...)
	qemu.Stdout = inst.wpipe
	qemu.Stderr = inst.wpipe
	if err := qemu.Start(); err != nil {
		return fmt.Errorf("failed to start %v %+v: %v", inst.cfg.Bin, args, err)
	}
	inst.qemu = qemu
	// Qemu has started.

	// Start output reading goroutine.
	inst.readerC = make(chan error)
	go func(rpipe *os.File) {
		var buf [64 << 10]byte
		for {
			n, err := rpipe.Read(buf[:])
			if n != 0 {
				if inst.cfg.Debug {
					os.Stdout.Write(buf[:n])
					os.Stdout.Write([]byte{'\n'})
				}
				inst.mu.Lock()
				inst.outputB = append(inst.outputB, buf[:n]...)
				if inst.outputC != nil {
					select {
					case inst.outputC <- inst.outputB:
						inst.outputB = nil
					default:
					}
				}
				inst.mu.Unlock()
				time.Sleep(time.Millisecond)
			}
			if err != nil {
				rpipe.Close()
				inst.readerC <- err
				return
			}
		}
	}(inst.rpipe)
	inst.rpipe = nil

	// Wait for the qemu asynchronously.
	inst.waiterC = make(chan error, 1)
	go func() {
		err := qemu.Wait()
		inst.wpipe.Close()
		inst.waiterC <- err
	}()

	// Wait for ssh server to come up.
	time.Sleep(10 * time.Second)
	start := time.Now()
	for {
		c, err := net.DialTimeout("tcp", fmt.Sprintf("localhost:%v", inst.port), 3*time.Second)
		if err == nil {
			c.SetDeadline(time.Now().Add(3 * time.Second))
			var tmp [1]byte
			n, err := c.Read(tmp[:])
			c.Close()
			if err == nil && n > 0 {
				break // ssh is up and responding
			}
			time.Sleep(3 * time.Second)
		}
		select {
		case err := <-inst.waiterC:
			inst.waiterC <- err     // repost it for Close
			time.Sleep(time.Second) // wait for any pending output
			inst.mu.Lock()
			output := inst.outputB
			inst.mu.Unlock()
			return fmt.Errorf("qemu stopped:\n%v\n", string(output))
		default:
		}
		if time.Since(start) > 10*time.Minute {
			inst.mu.Lock()
			output := inst.outputB
			inst.mu.Unlock()
			return fmt.Errorf("ssh server did not start:\n%v\n", string(output))
		}
	}
	// Drop boot output. It is not interesting if the VM has successfully booted.
	inst.mu.Lock()
	inst.outputB = nil
	inst.mu.Unlock()
	return nil
}

func (inst *instance) Forward(port int) (string, error) {
	return fmt.Sprintf("%v:%v", hostAddr, port), nil
}

func (inst *instance) Copy(hostSrc string) (string, error) {
	vmDst := filepath.Join("/", filepath.Base(hostSrc))
	args := append(inst.sshArgs("-P"), hostSrc, "root@localhost:"+vmDst)
	cmd := exec.Command("scp", args...)
	if err := cmd.Start(); err != nil {
		return "", err
	}
	done := make(chan bool)
	go func() {
		select {
		case <-time.After(time.Minute):
			cmd.Process.Kill()
		case <-done:
		}
	}()
	err := cmd.Wait()
	close(done)
	if err != nil {
		return "", err
	}
	return vmDst, nil
}

func (inst *instance) Run(timeout time.Duration, command string) (<-chan []byte, <-chan error, error) {
	outputC := make(chan []byte, 10)
	errorC := make(chan error, 1)
	inst.mu.Lock()
	inst.outputB = nil
	inst.outputC = outputC
	inst.mu.Unlock()
	signal := func(err error) {
		time.Sleep(3 * time.Second) // wait for any pending output
		inst.mu.Lock()
		if inst.outputC == outputC {
			inst.outputB = nil
			inst.outputC = nil
		}
		inst.mu.Unlock()
		select {
		case errorC <- err:
		default:
		}
	}
	args := append(inst.sshArgs("-p"), "root@localhost", command)
	cmd := exec.Command("ssh", args...)
	cmd.Stdout = inst.wpipe
	cmd.Stderr = inst.wpipe
	if err := cmd.Start(); err != nil {
		inst.mu.Lock()
		inst.outputC = nil
		inst.mu.Unlock()
		return nil, nil, err
	}
	done := make(chan bool)
	go func() {
		select {
		case <-time.After(timeout):
			signal(vm.TimeoutErr)
			cmd.Process.Kill()
		case <-done:
		}
	}()
	go func() {
		err := cmd.Wait()
		close(done)
		signal(err)
	}()
	return outputC, errorC, nil
}

func (inst *instance) sshArgs(portArg string) []string {
	return []string{
		"-i", inst.cfg.Sshkey,
		portArg, strconv.Itoa(inst.port),
		"-o", "ConnectionAttempts=10",
		"-o", "ConnectTimeout=10",
		"-o", "BatchMode=yes",
		"-o", "UserKnownHostsFile=/dev/null",
		"-o", "IdentitiesOnly=yes",
		"-o", "StrictHostKeyChecking=no",
		"-o", "LogLevel=error",
	}
}
<commit_msg>vm/qemu: support 9p host-based image<commit_after>// Copyright 2015 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package qemu

import (
	"fmt"
	"io/ioutil"
	"math/rand"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/google/syzkaller/vm"
)

const (
	hostAddr = "10.0.2.10"
)

func init() {
	vm.Register("qemu", ctor)
}

type instance struct {
	cfg     *vm.Config
	port    int
	rpipe   *os.File
	wpipe   *os.File
	qemu    *exec.Cmd
	readerC chan error
	waiterC chan error

	mu      sync.Mutex
	outputB []byte
	outputC chan []byte
}

func ctor(cfg *vm.Config) (vm.Instance, error) {
	for i := 0; ; i++ {
		inst, err := ctorImpl(cfg)
		if err == nil {
			return inst, nil
		}
		if i < 1000 && strings.Contains(err.Error(), "could not set up host forwarding rule") {
			continue
		}
		return nil, err
	}
}

func ctorImpl(cfg *vm.Config) (vm.Instance, error) {
	inst := &instance{cfg: cfg}
	closeInst := inst
	defer func() {
		if closeInst != nil {
			closeInst.Close()
		}
	}()

	if err := validateConfig(cfg); err != nil {
		return nil, err
	}

	if cfg.Image == "9p" {
		inst.cfg.Sshkey = filepath.Join(inst.cfg.Workdir, "key")
		keygen := exec.Command("ssh-keygen", "-t", "rsa", "-b", "2048", "-N", "", "-C", "", "-f", inst.cfg.Sshkey)
		if out, err := keygen.CombinedOutput(); err != nil {
			return nil, fmt.Errorf("failed to execute ssh-keygen: %v\n%s", err, out)
		}
		initFile := filepath.Join(cfg.Workdir, "init.sh")
		if err := ioutil.WriteFile(initFile, []byte(strings.Replace(initScript, "{{KEY}}", inst.cfg.Sshkey, -1)), 0777); err != nil {
			return nil, fmt.Errorf("failed to create init file: %v", err)
		}
	}

	var err error
	inst.rpipe, inst.wpipe, err = os.Pipe()
	if err != nil {
		return nil, fmt.Errorf("failed to create pipe: %v", err)
	}
	for sz := 128 << 10; sz <= 2<<20; sz *= 2 {
		syscall.Syscall(syscall.SYS_FCNTL, inst.wpipe.Fd(), syscall.F_SETPIPE_SZ, uintptr(sz))
	}

	if err := inst.Boot(); err != nil {
		return nil, err
	}

	closeInst = nil
	return inst, nil
}

func validateConfig(cfg *vm.Config) error {
	if cfg.Bin == "" {
		cfg.Bin = "qemu-system-x86_64"
	}
	if cfg.Image == "9p" {
		if cfg.Kernel == "" {
			return fmt.Errorf("9p image requires kernel")
		}
	} else {
		if _, err := os.Stat(cfg.Image); err != nil {
			return fmt.Errorf("image file '%v' does not exist: %v", cfg.Image, err)
		}
		if _, err := os.Stat(cfg.Sshkey); err != nil {
			return fmt.Errorf("ssh key '%v' does not exist: %v", cfg.Sshkey, err)
		}
	}
	if cfg.Cpu <= 0 || cfg.Cpu > 1024 {
		return fmt.Errorf("bad qemu cpu: %v, want [1-1024]", cfg.Cpu)
	}
	if cfg.Mem < 128 || cfg.Mem > 1048576 {
		return fmt.Errorf("bad qemu mem: %v, want [128-1048576]", cfg.Mem)
	}
	return nil
}

func (inst *instance) Close() {
	if inst.qemu != nil {
		inst.qemu.Process.Kill()
		err := <-inst.waiterC
		inst.waiterC <- err // repost it for waiting goroutines
		<-inst.readerC
	}
	if inst.rpipe != nil {
		inst.rpipe.Close()
	}
	if inst.wpipe != nil {
		inst.wpipe.Close()
	}
	os.RemoveAll(inst.cfg.Workdir)
}

func (inst *instance) Boot() error {
	for {
		// Find an unused TCP port.
		inst.port = rand.Intn(64<<10-1<<10) + 1<<10
		ln, err := net.Listen("tcp", fmt.Sprintf("localhost:%v", inst.port))
		if err == nil {
			ln.Close()
			break
		}
	}
	// TODO: ignores inst.cfg.Cpu
	args := []string{
		"-m", strconv.Itoa(inst.cfg.Mem),
		"-net", "nic",
		"-net", fmt.Sprintf("user,host=%v,hostfwd=tcp::%v-:22", hostAddr, inst.port),
		"-display", "none",
		"-serial", "stdio",
		"-no-reboot",
		"-enable-kvm",
		"-numa", "node,nodeid=0,cpus=0-1", "-numa", "node,nodeid=1,cpus=2-3",
		"-smp", "sockets=2,cores=2,threads=1",
		"-usb", "-usbdevice", "mouse", "-usbdevice", "tablet",
		"-soundhw", "all",
	}
	if inst.cfg.Image == "9p" {
		args = append(args,
			"-fsdev", "local,id=fsdev0,path=/,security_model=none,readonly",
			"-device", "virtio-9p-pci,fsdev=fsdev0,mount_tag=/dev/root",
		)
	} else {
		args = append(args,
			"-hda", inst.cfg.Image,
			"-snapshot",
		)
	}
	if inst.cfg.Initrd != "" {
		args = append(args,
			"-initrd", inst.cfg.Initrd,
		)
	}
	if inst.cfg.Kernel != "" {
		cmdline := "console=ttyS0 oops=panic panic_on_warn=1 panic=-1 ftrace_dump_on_oops=orig_cpu debug earlyprintk=serial slub_debug=UZ "
		if inst.cfg.Image == "9p" {
			cmdline += "root=/dev/root rootfstype=9p rootflags=trans=virtio,version=9p2000.L,cache=loose "
			cmdline += "init=" + filepath.Join(inst.cfg.Workdir, "init.sh") + " "
		} else {
			cmdline += "root=/dev/sda "
		}
		args = append(args,
			"-kernel", inst.cfg.Kernel,
			"-append", cmdline+inst.cfg.Cmdline,
		)
	}
	qemu := exec.Command(inst.cfg.Bin, args...)
	qemu.Stdout = inst.wpipe
	qemu.Stderr = inst.wpipe
	if err := qemu.Start(); err != nil {
		return fmt.Errorf("failed to start %v %+v: %v", inst.cfg.Bin, args, err)
	}
	inst.qemu = qemu
	// Qemu has started.

	// Start output reading goroutine.
	inst.readerC = make(chan error)
	go func(rpipe *os.File) {
		var buf [64 << 10]byte
		for {
			n, err := rpipe.Read(buf[:])
			if n != 0 {
				if inst.cfg.Debug {
					os.Stdout.Write(buf[:n])
					os.Stdout.Write([]byte{'\n'})
				}
				inst.mu.Lock()
				inst.outputB = append(inst.outputB, buf[:n]...)
				if inst.outputC != nil {
					select {
					case inst.outputC <- inst.outputB:
						inst.outputB = nil
					default:
					}
				}
				inst.mu.Unlock()
				time.Sleep(time.Millisecond)
			}
			if err != nil {
				rpipe.Close()
				inst.readerC <- err
				return
			}
		}
	}(inst.rpipe)
	inst.rpipe = nil

	// Wait for the qemu asynchronously.
	inst.waiterC = make(chan error, 1)
	go func() {
		err := qemu.Wait()
		inst.wpipe.Close()
		inst.waiterC <- err
	}()

	// Wait for ssh server to come up.
	time.Sleep(10 * time.Second)
	start := time.Now()
	for {
		c, err := net.DialTimeout("tcp", fmt.Sprintf("localhost:%v", inst.port), 3*time.Second)
		if err == nil {
			c.SetDeadline(time.Now().Add(3 * time.Second))
			var tmp [1]byte
			n, err := c.Read(tmp[:])
			c.Close()
			if err == nil && n > 0 {
				break // ssh is up and responding
			}
			time.Sleep(3 * time.Second)
		}
		select {
		case err := <-inst.waiterC:
			inst.waiterC <- err     // repost it for Close
			time.Sleep(time.Second) // wait for any pending output
			inst.mu.Lock()
			output := inst.outputB
			inst.mu.Unlock()
			return fmt.Errorf("qemu stopped:\n%v\n", string(output))
		default:
		}
		if time.Since(start) > 10*time.Minute {
			inst.mu.Lock()
			output := inst.outputB
			inst.mu.Unlock()
			return fmt.Errorf("ssh server did not start:\n%v\n", string(output))
		}
	}
	// Drop boot output. It is not interesting if the VM has successfully booted.
	inst.mu.Lock()
	inst.outputB = nil
	inst.mu.Unlock()
	return nil
}

func (inst *instance) Forward(port int) (string, error) {
	return fmt.Sprintf("%v:%v", hostAddr, port), nil
}

func (inst *instance) Copy(hostSrc string) (string, error) {
	basePath := "/"
	if inst.cfg.Image == "9p" {
		basePath = "/tmp"
	}
	vmDst := filepath.Join(basePath, filepath.Base(hostSrc))
	args := append(inst.sshArgs("-P"), hostSrc, "root@localhost:"+vmDst)
	cmd := exec.Command("scp", args...)
	if err := cmd.Start(); err != nil {
		return "", err
	}
	done := make(chan bool)
	go func() {
		select {
		case <-time.After(time.Minute):
			cmd.Process.Kill()
		case <-done:
		}
	}()
	err := cmd.Wait()
	close(done)
	if err != nil {
		return "", err
	}
	return vmDst, nil
}

func (inst *instance) Run(timeout time.Duration, command string) (<-chan []byte, <-chan error, error) {
	outputC := make(chan []byte, 10)
	errorC := make(chan error, 1)
	inst.mu.Lock()
	inst.outputB = nil
	inst.outputC = outputC
	inst.mu.Unlock()
	signal := func(err error) {
		time.Sleep(3 * time.Second) // wait for any pending output
		inst.mu.Lock()
		if inst.outputC == outputC {
			inst.outputB = nil
			inst.outputC = nil
		}
		inst.mu.Unlock()
		select {
		case errorC <- err:
		default:
		}
	}
	args := append(inst.sshArgs("-p"), "root@localhost", command)
	cmd := exec.Command("ssh", args...)
	cmd.Stdout = inst.wpipe
	cmd.Stderr = inst.wpipe
	if err := cmd.Start(); err != nil {
		inst.mu.Lock()
		inst.outputC = nil
		inst.mu.Unlock()
		return nil, nil, err
	}
	done := make(chan bool)
	go func() {
		select {
		case <-time.After(timeout):
			signal(vm.TimeoutErr)
			cmd.Process.Kill()
		case <-done:
		}
	}()
	go func() {
		err := cmd.Wait()
		close(done)
		signal(err)
	}()
	return outputC, errorC, nil
}

func (inst *instance) sshArgs(portArg string) []string {
	return []string{
		"-i", inst.cfg.Sshkey,
		portArg, strconv.Itoa(inst.port),
		"-F", "/dev/null",
		"-o", "ConnectionAttempts=10",
		"-o", "ConnectTimeout=10",
		"-o", "BatchMode=yes",
		"-o", "UserKnownHostsFile=/dev/null",
		"-o", "IdentitiesOnly=yes",
		"-o", "StrictHostKeyChecking=no",
		"-o", "LogLevel=error",
	}
}

const initScript = `#! /bin/bash
set -eux
mount -t proc none /proc
mount -t sysfs none /sys
mount -t debugfs nodev /sys/kernel/debug/
mount -t tmpfs none /tmp
mount -t tmpfs none /var
mount -t tmpfs none /etc
mount -t tmpfs none /root
touch /etc/fstab
echo "root::0:0:root:/root:/bin/bash" > /etc/passwd
mkdir -p /etc/ssh
cp {{KEY}}.pub /root/
chmod 0700 /root
chmod 0600 /root/key.pub
mkdir -p /var/run/sshd/
chmod 700 /var/run/sshd
cat > /etc/ssh/sshd_config <<EOF
          Port 22
          Protocol 2
          UsePrivilegeSeparation no
          HostKey {{KEY}}
          PermitRootLogin yes
          AuthenticationMethods publickey
          ChallengeResponseAuthentication no
          AuthorizedKeysFile /root/key.pub
          IgnoreUserKnownHosts yes
          AllowUsers root
          LogLevel INFO
          TCPKeepAlive yes
          RSAAuthentication yes
          PubkeyAuthentication yes
EOF
/sbin/dhclient eth0
/usr/sbin/sshd -e -D
/sbin/halt -f
`
<|endoftext|>


<commit_before>// +build !windows,!darwin

package volume

import (
	"errors"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
)

var useAmixer bool

func init() {
	if _, err := exec.LookPath("pactl"); err != nil {
		useAmixer = true
	}
}

func cmdEnv() []string {
	return []string{"LANG=C", "LC_ALL=C"}
}

func getVolumeCmd() []string {
	if useAmixer {
		return []string{"amixer", "get", "Master"}
	}
	return []string{"pactl", "list", "sinks"}
}

var volumePattern = regexp.MustCompile(`\d+%`)

func parseVolume(out string) (int, error) {
	lines := strings.Split(out, "\n")
	for _, line := range lines {
		s := strings.TrimLeft(line, " \t")
		if useAmixer && strings.Contains(s, "Playback") && strings.Contains(s, "%") ||
			!useAmixer && strings.HasPrefix(s, "Volume:") {
			volumeStr := volumePattern.FindString(s)
			return strconv.Atoi(volumeStr[:len(volumeStr)-1])
		}
	}
	return 0, errors.New("no volume found")
}

func setVolumeCmd(volume int) []string {
	if useAmixer {
		return []string{"amixer", "set", "Master", strconv.Itoa(volume) + "%"}
	}
	return []string{"pactl", "set-sink-volume", "0", strconv.Itoa(volume) + "%"}
}

func increaseVolumeCmd(diff int) []string {
	var sign string
	if diff >= 0 {
		sign = "+"
	} else if useAmixer {
		diff = -diff
		sign = "-"
	}
	if useAmixer {
		return []string{"amixer", "set", "Master", strconv.Itoa(diff) + "%" + sign}
	}
	return []string{"pactl", "--", "set-sink-volume", "0", sign + strconv.Itoa(diff) + "%"}
}

func getMutedCmd() []string {
	if useAmixer {
		return []string{"amixer", "get", "Master"}
	}
	return []string{"pactl", "list", "sinks"}
}

func parseMuted(out string) (bool, error) {
	lines := strings.Split(out, "\n")
	for _, line := range lines {
		s := strings.TrimLeft(line, " \t")
		if useAmixer && strings.Contains(s, "Playback") && strings.Contains(s, "%") ||
			!useAmixer && strings.HasPrefix(s, "Mute: ") {
			if strings.Contains(s, "[off]") || strings.Contains(s, "yes") {
				return true, nil
			} else if strings.Contains(s, "[on]") || strings.Contains(s, "no") {
				return false, nil
			}
		}
	}
	return false, errors.New("no muted information found")
}

func muteCmd() []string {
	if useAmixer {
		return []string{"amixer", "-D", "pulse", "set", "Master", "mute"}
	}
	return []string{"pactl", "set-sink-mute", "0", "1"}
}

func unmuteCmd() []string {
	if useAmixer {
		return []string{"amixer", "-D", "pulse", "set", "Master", "unmute"}
	}
	return []string{"pactl", "set-sink-mute", "0", "0"}
}
<commit_msg>Linux PulseAudio: Use @DEFAULT_SINK@ at setting values<commit_after>// +build !windows,!darwin

package volume

import (
	"errors"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
)

var useAmixer bool

func init() {
	if _, err := exec.LookPath("pactl"); err != nil {
		useAmixer = true
	}
}

func cmdEnv() []string {
	return []string{"LANG=C", "LC_ALL=C"}
}

func getVolumeCmd() []string {
	if useAmixer {
		return []string{"amixer", "get", "Master"}
	}
	return []string{"pactl", "list", "sinks"}
}

var volumePattern = regexp.MustCompile(`\d+%`)

func parseVolume(out string) (int, error) {
	lines := strings.Split(out, "\n")
	for _, line := range lines {
		s := strings.TrimLeft(line, " \t")
		if useAmixer && strings.Contains(s, "Playback") && strings.Contains(s, "%") ||
			!useAmixer && strings.HasPrefix(s, "Volume:") {
			volumeStr := volumePattern.FindString(s)
			return strconv.Atoi(volumeStr[:len(volumeStr)-1])
		}
	}
	return 0, errors.New("no volume found")
}

func setVolumeCmd(volume int) []string {
	if useAmixer {
		return []string{"amixer", "set", "Master", strconv.Itoa(volume) + "%"}
	}
	return []string{"pactl", "set-sink-volume", "@DEFAULT_SINK@", strconv.Itoa(volume) + "%"}
}

func increaseVolumeCmd(diff int) []string {
	var sign string
	if diff >= 0 {
		sign = "+"
	} else if useAmixer {
		diff = -diff
		sign = "-"
	}
	if useAmixer {
		return []string{"amixer", "set", "Master", strconv.Itoa(diff) + "%" + sign}
	}
	return []string{"pactl", "--", "set-sink-volume", "@DEFAULT_SINK@", sign + strconv.Itoa(diff) + "%"}
}

func getMutedCmd() []string {
	if useAmixer {
		return []string{"amixer", "get", "Master"}
	}
	return []string{"pactl", "list", "sinks"}
}

func parseMuted(out string) (bool, error) {
	lines := strings.Split(out, "\n")
	for _, line := range lines {
		s := strings.TrimLeft(line, " \t")
		if useAmixer && strings.Contains(s, "Playback") && strings.Contains(s, "%") ||
			!useAmixer && strings.HasPrefix(s, "Mute: ") {
			if strings.Contains(s, "[off]") || strings.Contains(s, "yes") {
				return true, nil
			} else if strings.Contains(s, "[on]") || strings.Contains(s, "no") {
				return false, nil
			}
		}
	}
	return false, errors.New("no muted information found")
}

func muteCmd() []string {
	if useAmixer {
		return []string{"amixer", "-D", "pulse", "set", "Master", "mute"}
	}
	return []string{"pactl", "set-sink-mute", "@DEFAULT_SINK@", "1"}
}

func unmuteCmd() []string {
	if useAmixer {
		return []string{"amixer", "-D", "pulse", "set", "Master", "unmute"}
	}
	return []string{"pactl", "set-sink-mute", "@DEFAULT_SINK@", "0"}
}
<|endoftext|>


<commit_before>package control

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"os"

	log "github.com/Sirupsen/logrus"
	"github.com/joyent/containerpilot/events"
)

// Endpoints wraps the EventBus so we can bridge data across the App and
// HTTPServer API boundary
type Endpoints struct {
	bus *events.EventBus
}

// PostHandler is an adapter which allows a normal function to serve itself and
// handle incoming HTTP POST requests, and allows us to pass thru EventBus to
// handlers
type PostHandler func(*http.Request) (interface{}, int)

func (pw PostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		failedStatus := http.StatusNotImplemented
		http.Error(w, http.StatusText(failedStatus), failedStatus)
		return
	}
	resp, status := pw(r)
	switch status {
	case http.StatusOK:
		if resp != nil {
			w.Header().Set("Content-Type", "application/json")
			w.WriteHeader(http.StatusOK)
			json.NewEncoder(w).Encode(resp)
		} else {
			w.WriteHeader(http.StatusOK)
			io.WriteString(w, "\n")
		}
	default:
		http.Error(w, http.StatusText(status), status)
	}
}

// PutEnviron handles incoming HTTP POST requests containing JSON environment
// variables and updates the environment of our current ContainerPilot
// process. Returns empty response or HTTP422.
func (e Endpoints) PutEnviron(r *http.Request) (interface{}, int) {
	var postEnv map[string]string
	jsonBlob, err := ioutil.ReadAll(r.Body)
	defer r.Body.Close()
	if err != nil {
		return nil, http.StatusUnprocessableEntity
	}
	err = json.Unmarshal(jsonBlob, &postEnv)
	if err != nil {
		return nil, http.StatusUnprocessableEntity
	}
	for envKey, envValue := range postEnv {
		os.Setenv(envKey, envValue)
	}
	return nil, http.StatusOK
}

// PostReload handles incoming HTTP POST requests and reloads our current
// ContainerPilot process configuration.  Returns empty response or HTTP422.
func (e Endpoints) PostReload(r *http.Request) (interface{}, int) {
	log.Debug("control: reloading app via control plane")
	defer r.Body.Close()
	e.bus.SetReloadFlag()
	e.bus.Shutdown()
	log.Debug("control: reloaded app via control plane")
	return nil, http.StatusOK
}
<commit_msg>add PutMetric endpoint<commit_after>package control

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

	log "github.com/Sirupsen/logrus"
	"github.com/joyent/containerpilot/events"
)

// Endpoints wraps the EventBus so we can bridge data across the App and
// HTTPServer API boundary
type Endpoints struct {
	bus *events.EventBus
}

// PostHandler is an adapter which allows a normal function to serve itself and
// handle incoming HTTP POST requests, and allows us to pass thru EventBus to
// handlers
type PostHandler func(*http.Request) (interface{}, int)

func (pw PostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		failedStatus := http.StatusNotImplemented
		http.Error(w, http.StatusText(failedStatus), failedStatus)
		return
	}
	resp, status := pw(r)
	switch status {
	case http.StatusOK:
		if resp != nil {
			w.Header().Set("Content-Type", "application/json")
			w.WriteHeader(http.StatusOK)
			json.NewEncoder(w).Encode(resp)
		} else {
			w.WriteHeader(http.StatusOK)
			io.WriteString(w, "\n")
		}
	default:
		http.Error(w, http.StatusText(status), status)
	}
}

// PutEnviron handles incoming HTTP POST requests containing JSON environment
// variables and updates the environment of our current ContainerPilot
// process. Returns empty response or HTTP422.
func (e Endpoints) PutEnviron(r *http.Request) (interface{}, int) {
	var postEnv map[string]string
	jsonBlob, err := ioutil.ReadAll(r.Body)
	defer r.Body.Close()
	if err != nil {
		return nil, http.StatusUnprocessableEntity
	}
	err = json.Unmarshal(jsonBlob, &postEnv)
	if err != nil {
		return nil, http.StatusUnprocessableEntity
	}
	for envKey, envValue := range postEnv {
		os.Setenv(envKey, envValue)
	}
	return nil, http.StatusOK
}

// PostReload handles incoming HTTP POST requests and reloads our current
// ContainerPilot process configuration.  Returns empty response or HTTP422.
func (e Endpoints) PostReload(r *http.Request) (interface{}, int) {
	log.Debug("control: reloading app via control plane")
	defer r.Body.Close()
	e.bus.SetReloadFlag()
	e.bus.Shutdown()
	log.Debug("control: reloaded app via control plane")
	return nil, http.StatusOK
}

// PostMetric handles incoming HTTP POST requests, serializes the metrics
// into Events, and publishes them for sensors to record their values.
// Returns empty response or HTTP422.
func (e Endpoints) PostMetric(r *http.Request) (interface{}, int) {
	var postMetrics map[string]string
	jsonBlob, err := ioutil.ReadAll(r.Body)
	defer r.Body.Close()
	if err != nil {
		return nil, http.StatusUnprocessableEntity
	}
	err = json.Unmarshal(jsonBlob, &postMetrics)
	if err != nil {
		return nil, http.StatusUnprocessableEntity
	}
	for metricKey, metricValue := range postMetrics {
		eventVal := fmt.Sprintf("%v|%v", metricKey, metricValue)
		e.bus.Publish(events.Event{events.Metric, eventVal})
	}
	return nil, http.StatusOK
}
<|endoftext|>


<commit_before>package unionfind

type UFcore struct {
	setcount int
	parent []int
}

func (uf *UFcore) Count() int {
	return uf.setcount
}

type ElementOutOfBounds string
func (eoob ElementOutOfBounds) Error() string {
	return string(eoob)
}

func (uf *UFcore) Find(p int) (int, error) {
	if p < 0 || p >= len(uf.parent) {
		return 0, ElementOutOfBounds("Element is Out of Bounds")
	}
	for p != uf.parent[p] {
		uf.parent[p], p = uf.parent[uf.parent[p]], uf.parent[p]
	}
	return p, nil
}

func (uf *UFcore) Connected(p0, p1 int) (bool, error) {
	root0, err := uf.Find(p0)
	var root1 int; if err == nil {
		root1, err = uf.Find(p1)
	}
	return err == nil && root0 == root1, err
}

func Core(n int) *UFcore {
	parent := make([]int, n)
	for i := 0; i < n; i++ {
		parent[i] = i
	}
	return &UFcore{n, parent}
}

type UFbyrank struct {
	*UFcore
	rank []byte
}

func (uf *UFbyrank) Union(p0, p1 int) error {
	root0, err := uf.Find(p0)
	if err == nil {
		root1, err := uf.Find(p1)
		if err == nil && root0 != root1 {
			if w0, w1 := uf.rank[root0], uf.rank[root1]; w1 > w0 {
				uf.parent[root0] = root1
			} else if w0 > w1 {
				uf.parent[root1] = root0
			} else {
				uf.parent[root1] = root0
				uf.rank[root0]++
			}
			uf.setcount--
		}
	}
	return err
}

type UFbysize struct {
	*UFcore
	size []int
}

func (uf *UFbysize) Union(p0, p1 int) error {
	root0, err := uf.Find(p0)
	if err == nil {
		root1, err := uf.Find(p1)
		if err == nil && root0 != root1 {
			if w0, w1 := uf.size[root0], uf.size[root1]; w1 > w0 {
				uf.parent[root0] = root1
				uf.size[root1] += w0
			} else {
				uf.parent[root1] = root0
				uf.size[root0] += w1
			}
			uf.setcount--
		}
	}
	return err
}

func Byrank(n int) *UFbyrank {
	return &UFbyrank{Core(n), make([]byte, n)}
}

func New(n int) *UFbyrank {
	return Byrank(n)
}

func Bysize(n int) *UFbysize {
	size := make([]int, n)
	for i := 0; i < n; i++ {
		size[i] = 1
	}
	return &UFbysize{Core(n), size}
}<commit_msg>Make Union functions more readable<commit_after>package unionfind

type UFcore struct {
	setcount int
	parent []int
}

func (uf *UFcore) Count() int {
	return uf.setcount
}

type ElementOutOfBounds string
func (eoob ElementOutOfBounds) Error() string {
	return string(eoob)
}

func (uf *UFcore) Find(p int) (int, error) {
	if p < 0 || p >= len(uf.parent) {
		return 0, ElementOutOfBounds("Element is Out of Bounds")
	}
	for p != uf.parent[p] {
		uf.parent[p], p = uf.parent[uf.parent[p]], uf.parent[p]
	}
	return p, nil
}

func (uf *UFcore) Connected(p0, p1 int) (bool, error) {
	root0, err := uf.Find(p0)
	var root1 int; if err == nil {
		root1, err = uf.Find(p1)
	}
	return err == nil && root0 == root1, err
}

func Core(n int) *UFcore {
	parent := make([]int, n)
	for i := 0; i < n; i++ {
		parent[i] = i
	}
	return &UFcore{n, parent}
}

type UFbyrank struct {
	*UFcore
	rank []byte
}

func (uf *UFbyrank) Union(p0, p1 int) (err error) {
	root0, err := uf.Find(p0)
	if err != nil {
		return
	}
	root1, err := uf.Find(p1)
	if err != nil {
		return
	}
	if root0 == root1 {
		return
	}
	if w0, w1 := uf.rank[root0], uf.rank[root1]; w1 > w0 {
		uf.parent[root0] = root1
	} else if w0 > w1 {
		uf.parent[root1] = root0
	} else {
		uf.parent[root1] = root0
		uf.rank[root0]++
	}
	uf.setcount--
	return
}

type UFbysize struct {
	*UFcore
	size []int
}

func (uf *UFbysize) Union(p0, p1 int) (err error) {
	root0, err := uf.Find(p0)
	if err != nil {
		return
	}
	root1, err := uf.Find(p1)
	if err != nil {
		return
	}
	if root0 == root1 {
		return
	}
	if w0, w1 := uf.size[root0], uf.size[root1]; w1 > w0 {
		uf.parent[root0] = root1
		uf.size[root1] += w0
	} else {
		uf.parent[root1] = root0
		uf.size[root0] += w1
	}
	uf.setcount--
	return
}

func Byrank(n int) *UFbyrank {
	return &UFbyrank{Core(n), make([]byte, n)}
}

func New(n int) *UFbyrank {
	return Byrank(n)
}

func Bysize(n int) *UFbysize {
	size := make([]int, n)
	for i := 0; i < n; i++ {
		size[i] = 1
	}
	return &UFbysize{Core(n), size}
}<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"net/http"
	"time"

	"strings"

	"net/url"

	"io/ioutil"

	"github.com/asaskevich/govalidator"
	"github.com/ellcrys/util"
	"github.com/gorilla/mux"
	"github.com/ncodes/cocoon/core/config"
	"github.com/ncodes/cocoon/core/connector/server/proto_connector"
	logging "github.com/op/go-logging"
	context "golang.org/x/net/context"
)

var httpLog *logging.Logger

// MaxBytesRead limits the number of bytes to read per request
const MaxBytesRead = 10000000

// InvokeRequest describes the body of a structured invoke request
type InvokeRequest struct {
	ID       string   `json:"id"`
	Function string   `json:"function"`
	Params   []string `json:"params"`
}

// InvokeError describes an invoke error
type InvokeError struct {
	Code  string `json:"code"`
	Msg   string `json:"msg"`
	Error bool   `json:"error"`
}

// Success describes a successful response from a cocoon code
type Success struct {
	Body []byte `json:"body"`
}

// HTTP defines a structure for an HTTP server
// that provides REST API services.
type HTTP struct {
	rpc *RPC
}

// NewHTTP creates an new http server instance
func NewHTTP(rpc *RPC) *HTTP {
	httpLog = config.MakeLogger("connector.http", "")
	return &HTTP{rpc}
}

// getRouter returns the router
func (s *HTTP) getRouter() *mux.Router {
	r := mux.NewRouter()
	g := r.PathPrefix("/v1").Subrouter()
	g.HandleFunc("/invoke", s.invokeCocoonCode)
	return r
}

// Start starts the http server. Passes true to the startedCh channel
// when started
func (s *HTTP) Start(addr string, startedCh chan bool) error {
	time.AfterFunc(2*time.Second, func() {
		httpLog.Infof("Started HTTP API server @ %s", addr)
		startedCh <- true
	})
	err := http.ListenAndServe(addr, s.getRouter())
	return err
}

// headerToMap converts http.Header to a map[string]string.
// Header keys with multiple values are joined to as a single string with
// a comma `,` as a delimeter.
func headerToMap(header http.Header) map[string]string {
	var newMap = make(map[string]string)
	for h, v := range header {
		newMap[h] = strings.Join(v, ",")
	}
	return newMap
}

// headerToMap converts url.Values to a map[string]string.
// Values keys with multiple values are joined to as a single string with
// a comma `,` as a delimeter.
func valuesToMap(values url.Values) map[string]string {
	var newMap = make(map[string]string)
	for h, v := range values {
		newMap[h] = strings.Join(v, ",")
	}
	return newMap
}

// prepareInvokeHeader collects and sets values to be included in the header
// that will be sent to the cocoon code.
func prepareInvokeHeader(w http.ResponseWriter, r *http.Request) http.Header {
	header := r.Header
	header.Set("Method", r.Method)
	header.Set("Host", r.Host)
	header.Set("Remote-Addr", r.RemoteAddr)
	if len(r.Cookies()) > 0 {
		cookiesToJSON, _ := util.ToJSON(r.Cookies())
		header.Set("Cookies", string(cookiesToJSON))
	}
	header.Set("Referer", r.Referer())
	header.Set("URL", r.URL.String())
	header.Set("Request-Uri", r.RequestURI)
	if len(r.URL.Query()) > 0 {
		queryToJSON, _ := util.ToJSON(valuesToMap(r.URL.Query()))
		header.Set("Query", string(queryToJSON))
	}

	r.ParseForm()
	r.ParseMultipartForm(MaxBytesRead)
	if len(r.PostForm) > 0 {
		formValToJSON, _ := util.ToJSON(valuesToMap(r.PostForm))
		header.Set("Form", string(formValToJSON))
	}
	return header
}

func (s *HTTP) invokeCocoonCode(w http.ResponseWriter, r *http.Request) {

	var err error
	var resp *proto_connector.Response
	var invokeRequest InvokeRequest
	var body []byte
	var ctx context.Context
	var cc context.CancelFunc

	defer r.Body.Close()

	// prepare header
	r.Body = http.MaxBytesReader(w, r.Body, MaxBytesRead)
	preparedHeader := prepareInvokeHeader(w, r)

	// set response content type
	w.Header().Set("Content-Type", "application/json")

	// get request are considered as unstructured
	if r.Method == "GET" {
		goto UNSTRUCTURED
	}

	// get body
	if r.Method == "POST" {
		body, err = ioutil.ReadAll(r.Body)
		if err != nil {
			w.WriteHeader(500)
			json.NewEncoder(w).Encode(InvokeError{
				Error: true,
				Code:  "server_error",
				Msg:   "Invalid invoke body structure",
			})
			return
		}
	}

	// non-json content type are considered as unstructured request
	if r.Header.Get("Content-Type") != "application/json" {
		goto UNSTRUCTURED
	}

	// attempt to decode json body to an invoke request structure
	err = util.FromJSON(body, &invokeRequest)
	if err != nil {
		w.WriteHeader(400)
		json.NewEncoder(w).Encode(InvokeError{
			Error: true,
			Code:  "1",
			Msg:   "Invalid invoke body structure",
		})
		return
	}

	// expect ID, if set to be a UUIDv4 string
	if len(invokeRequest.ID) > 0 && !govalidator.IsUUIDv4(invokeRequest.ID) {
		w.WriteHeader(400)
		json.NewEncoder(w).Encode(InvokeError{
			Error: true,
			Code:  "2",
			Msg:   "Invalid ID. ID must be a UUIDv4 value",
		})
		return
	}

	// return error if request function is not set
	if len(strings.TrimSpace(invokeRequest.Function)) == 0 {
		w.WriteHeader(400)
		json.NewEncoder(w).Encode(InvokeError{
			Error: true,
			Code:  "3",
			Msg:   "Function is required",
		})
		return
	}

	// set ID of not set
	if len(invokeRequest.ID) == 0 {
		invokeRequest.ID = util.UUID4()
	}

	preparedHeader.Set("Transaction-Id", invokeRequest.ID)
	preparedHeader.Set("Structured", "yes")

	ctx, cc = context.WithTimeout(context.Background(), 2*time.Minute)
	defer cc()
	resp, err = s.rpc.cocoonCodeOps.Handle(ctx, &proto_connector.CocoonCodeOperation{
		ID:       invokeRequest.ID,
		Function: invokeRequest.Function,
		Params:   invokeRequest.Params,
		Header:   headerToMap(preparedHeader),
	})
	if err != nil {
		w.WriteHeader(500)
		json.NewEncoder(w).Encode(InvokeError{
			Error: true,
			Code:  "code_error",
			Msg:   err.Error(),
		})
		return
	}

	w.WriteHeader(200)
	json.NewEncoder(w).Encode(Success{
		Body: resp.GetBody(),
	})
	return

UNSTRUCTURED:

	preparedHeader.Set("Structured", "no")

	if r.Method != "GET" {
		preparedHeader.Set("Raw-Body", string(body))
	}

	ctx, cc = context.WithTimeout(context.Background(), 2*time.Minute)
	defer cc()
	resp, err = s.rpc.cocoonCodeOps.Handle(ctx, &proto_connector.CocoonCodeOperation{
		Header: headerToMap(preparedHeader),
	})
	if err != nil {
		w.WriteHeader(500)
		json.NewEncoder(w).Encode(InvokeError{
			Error: true,
			Code:  "code_error",
			Msg:   err.Error(),
		})
		return
	}

	w.WriteHeader(200)
	json.NewEncoder(w).Encode(Success{
		Body: resp.GetBody(),
	})
}
<commit_msg>added comment<commit_after>package server

import (
	"encoding/json"
	"net/http"
	"time"

	"strings"

	"net/url"

	"io/ioutil"

	"github.com/asaskevich/govalidator"
	"github.com/ellcrys/util"
	"github.com/gorilla/mux"
	"github.com/ncodes/cocoon/core/config"
	"github.com/ncodes/cocoon/core/connector/server/proto_connector"
	logging "github.com/op/go-logging"
	context "golang.org/x/net/context"
)

var httpLog *logging.Logger

// MaxBytesRead limits the number of bytes to read per request
const MaxBytesRead = 10000000

// InvokeRequest describes the body of a structured invoke request
type InvokeRequest struct {
	ID       string   `json:"id"`
	Function string   `json:"function"`
	Params   []string `json:"params"`
}

// InvokeError describes an invoke error
type InvokeError struct {
	Code  string `json:"code"`
	Msg   string `json:"msg"`
	Error bool   `json:"error"`
}

// Success describes a successful response from a cocoon code
type Success struct {
	Body []byte `json:"body"`
}

// HTTP defines a structure for an HTTP server
// that provides REST API services.
type HTTP struct {
	rpc *RPC
}

// NewHTTP creates an new http server instance
func NewHTTP(rpc *RPC) *HTTP {
	httpLog = config.MakeLogger("connector.http", "")
	return &HTTP{rpc}
}

// getRouter returns the router
func (s *HTTP) getRouter() *mux.Router {
	r := mux.NewRouter()
	g := r.PathPrefix("/v1").Subrouter()
	g.HandleFunc("/invoke", s.invokeCocoonCode)
	return r
}

// Start starts the http server. Passes true to the startedCh channel
// when started
func (s *HTTP) Start(addr string, startedCh chan bool) error {
	time.AfterFunc(2*time.Second, func() {
		httpLog.Infof("Started HTTP API server @ %s", addr)
		startedCh <- true
	})
	err := http.ListenAndServe(addr, s.getRouter())
	return err
}

// headerToMap converts http.Header to a map[string]string.
// Header keys with multiple values are joined to as a single string with
// a comma `,` as a delimeter.
func headerToMap(header http.Header) map[string]string {
	var newMap = make(map[string]string)
	for h, v := range header {
		newMap[h] = strings.Join(v, ",")
	}
	return newMap
}

// headerToMap converts url.Values to a map[string]string.
// Values keys with multiple values are joined to as a single string with
// a comma `,` as a delimeter.
func valuesToMap(values url.Values) map[string]string {
	var newMap = make(map[string]string)
	for h, v := range values {
		newMap[h] = strings.Join(v, ",")
	}
	return newMap
}

// prepareInvokeHeader collects and sets values to be included in the header
// that will be sent to the cocoon code.
func prepareInvokeHeader(w http.ResponseWriter, r *http.Request) http.Header {
	header := r.Header
	header.Set("Method", r.Method)
	header.Set("Host", r.Host)
	header.Set("Remote-Addr", r.RemoteAddr)
	if len(r.Cookies()) > 0 {
		cookiesToJSON, _ := util.ToJSON(r.Cookies())
		header.Set("Cookies", string(cookiesToJSON))
	}
	header.Set("Referer", r.Referer())
	header.Set("URL", r.URL.String())
	header.Set("Request-Uri", r.RequestURI)
	if len(r.URL.Query()) > 0 {
		queryToJSON, _ := util.ToJSON(valuesToMap(r.URL.Query()))
		header.Set("Query", string(queryToJSON))
	}

	r.ParseForm()
	r.ParseMultipartForm(MaxBytesRead)
	if len(r.PostForm) > 0 {
		formValToJSON, _ := util.ToJSON(valuesToMap(r.PostForm))
		header.Set("Form", string(formValToJSON))
	}
	return header
}

// invokeCocoonCode handles cocoon code invocation
func (s *HTTP) invokeCocoonCode(w http.ResponseWriter, r *http.Request) {

	var err error
	var resp *proto_connector.Response
	var invokeRequest InvokeRequest
	var body []byte
	var ctx context.Context
	var cc context.CancelFunc

	defer r.Body.Close()

	// prepare header
	r.Body = http.MaxBytesReader(w, r.Body, MaxBytesRead)
	preparedHeader := prepareInvokeHeader(w, r)

	// set response content type
	w.Header().Set("Content-Type", "application/json")

	// get request are considered as unstructured
	if r.Method == "GET" {
		goto UNSTRUCTURED
	}

	// get body
	if r.Method == "POST" {
		body, err = ioutil.ReadAll(r.Body)
		if err != nil {
			w.WriteHeader(500)
			json.NewEncoder(w).Encode(InvokeError{
				Error: true,
				Code:  "server_error",
				Msg:   "Invalid invoke body structure",
			})
			return
		}
	}

	// non-json content type are considered as unstructured request
	if r.Header.Get("Content-Type") != "application/json" {
		goto UNSTRUCTURED
	}

	// attempt to decode json body to an invoke request structure
	err = util.FromJSON(body, &invokeRequest)
	if err != nil {
		w.WriteHeader(400)
		json.NewEncoder(w).Encode(InvokeError{
			Error: true,
			Code:  "1",
			Msg:   "Invalid invoke body structure",
		})
		return
	}

	// expect ID, if set to be a UUIDv4 string
	if len(invokeRequest.ID) > 0 && !govalidator.IsUUIDv4(invokeRequest.ID) {
		w.WriteHeader(400)
		json.NewEncoder(w).Encode(InvokeError{
			Error: true,
			Code:  "2",
			Msg:   "Invalid ID. ID must be a UUIDv4 value",
		})
		return
	}

	// return error if request function is not set
	if len(strings.TrimSpace(invokeRequest.Function)) == 0 {
		w.WriteHeader(400)
		json.NewEncoder(w).Encode(InvokeError{
			Error: true,
			Code:  "3",
			Msg:   "Function is required",
		})
		return
	}

	// set ID of not set
	if len(invokeRequest.ID) == 0 {
		invokeRequest.ID = util.UUID4()
	}

	preparedHeader.Set("Transaction-Id", invokeRequest.ID)
	preparedHeader.Set("Structured", "yes")

	ctx, cc = context.WithTimeout(context.Background(), 2*time.Minute)
	defer cc()
	resp, err = s.rpc.cocoonCodeOps.Handle(ctx, &proto_connector.CocoonCodeOperation{
		ID:       invokeRequest.ID,
		Function: invokeRequest.Function,
		Params:   invokeRequest.Params,
		Header:   headerToMap(preparedHeader),
	})
	if err != nil {
		w.WriteHeader(500)
		json.NewEncoder(w).Encode(InvokeError{
			Error: true,
			Code:  "code_error",
			Msg:   err.Error(),
		})
		return
	}

	w.WriteHeader(200)
	json.NewEncoder(w).Encode(Success{
		Body: resp.GetBody(),
	})
	return

UNSTRUCTURED:

	preparedHeader.Set("Structured", "no")

	if r.Method != "GET" {
		preparedHeader.Set("Raw-Body", string(body))
	}

	ctx, cc = context.WithTimeout(context.Background(), 2*time.Minute)
	defer cc()
	resp, err = s.rpc.cocoonCodeOps.Handle(ctx, &proto_connector.CocoonCodeOperation{
		Header: headerToMap(preparedHeader),
	})
	if err != nil {
		w.WriteHeader(500)
		json.NewEncoder(w).Encode(InvokeError{
			Error: true,
			Code:  "code_error",
			Msg:   err.Error(),
		})
		return
	}

	w.WriteHeader(200)
	json.NewEncoder(w).Encode(Success{
		Body: resp.GetBody(),
	})
}
<|endoftext|>