<commit_before>package executor

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"strconv"
	"strings"
	"syscall"

	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/nomad/command"
	"github.com/hashicorp/nomad/helper/discover"
	"github.com/hashicorp/nomad/nomad/structs"

	cgroupFs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
	cgroupConfig "github.com/opencontainers/runc/libcontainer/configs"
)

const (
	cgroupMount = "/sys/fs/cgroup"
)

func NewExecutor() Executor {
	e := LinuxExecutor{}

	// TODO: In a follow-up PR make it so this only happens once per client.
	// Fingerprinting shouldn't happen per task.

	// Check if the process is has root capabilities.
	e.root = syscall.Geteuid() == 0

	// Check if this process can set uid.
	if e.root {
		e.setUidEnabled = true
	}

	// Check that cgroups are available. Must be root to modify it.
	if _, err := os.Stat(cgroupMount); err == nil && e.root {
		e.cgroupEnabled = true
	}

	return &e
}

// Linux executor is designed to run on linux kernel 2.8+.
type LinuxExecutor struct {
	cmd
	user *user.User

	// Finger print capabilities.
	root          bool
	setUidEnabled bool
	cgroupEnabled bool

	// Isolation configurations.
	groups *cgroupConfig.Cgroup

	// Tracking of child process.
	spawnChild        exec.Cmd
	spawnOutputWriter *os.File
	spawnOutputReader *os.File
}

func (e *LinuxExecutor) Limit(resources *structs.Resources) error {
	if resources == nil {
		return nil
	}

	if e.cgroupEnabled {
		e.configureCgroups(resources)
	}

	return nil
}

func (e *LinuxExecutor) configureCgroups(resources *structs.Resources) {
	if !e.cgroupEnabled {
		return
	}

	e.groups = &cgroupConfig.Cgroup{}

	// Groups will be created in a heiarchy according to the resource being
	// constrained, current session, and then this unique name. Restraints are
	// then placed in the corresponding files.
	// Ex: restricting a process to 2048Mhz CPU and 2MB of memory:
	//   $ cat /sys/fs/cgroup/cpu/user/1000.user/4.session/<uuid>/cpu.shares
	//		2028
	//   $ cat /sys/fs/cgroup/memory/user/1000.user/4.session/<uuid>/memory.limit_in_bytes
	//		2097152
	e.groups.Name = structs.GenerateUUID()

	// TODO: verify this is needed for things like network access
	e.groups.AllowAllDevices = true

	if resources.MemoryMB > 0 {
		// Total amount of memory allowed to consume
		e.groups.Memory = int64(resources.MemoryMB * 1024 * 1024)
		// Disable swap to avoid issues on the machine
		e.groups.MemorySwap = int64(-1)
	}

	if resources.CPU > 0.0 {
		// Set the relative CPU shares for this cgroup.
		// The simplest scale is 1 share to 1 MHz so 1024 = 1GHz. This means any
		// given process will have at least that amount of resources, but likely
		// more since it is (probably) rare that the machine will run at 100%
		// CPU. This scale will cease to work if a node is overprovisioned.
		e.groups.CpuShares = int64(resources.CPU)
	}

	if resources.IOPS > 0 {
		e.groups.BlkioThrottleReadIOpsDevice = strconv.FormatInt(int64(resources.IOPS), 10)
		e.groups.BlkioThrottleWriteIOpsDevice = strconv.FormatInt(int64(resources.IOPS), 10)
	}

}

func (e *LinuxExecutor) runAs(userid string) error {
	errs := new(multierror.Error)

	// First, try to lookup the user by uid
	u, err := user.LookupId(userid)
	if err == nil {
		e.user = u
		return nil
	} else {
		errs = multierror.Append(errs, err)
	}

	// Lookup failed, so try by username instead
	u, err = user.Lookup(userid)
	if err == nil {
		e.user = u
		return nil
	} else {
		errs = multierror.Append(errs, err)
	}

	// If we got here we failed to lookup based on id and username, so we'll
	// return those errors.
	return fmt.Errorf("Failed to identify user to run as: %s", errs)
}

func (e *LinuxExecutor) Start() error {
	// Try to run as "nobody" user so we don't leak root privilege to the
	// spawned process. Note that we will only do this if we can call SetUID.
	// Otherwise we'll just run the other process as our current (non-root)
	// user. This means we aren't forced to run nomad as root.
	if e.setUidEnabled {
		if err := e.runAs("nobody"); err == nil && e.user != nil {
			e.cmd.SetUID(e.user.Uid)
			e.cmd.SetGID(e.user.Gid)
		}
	}

	return e.spawnDaemon()
}

// spawnCgroup executes a double fork to start the user command with proper
// isolation. Stores the child process for use in Wait.
func (e *LinuxExecutor) spawnDaemon() error {
	bin, err := discover.NomadExecutable()
	if err != nil {
		return fmt.Errorf("Failed to determine the nomad executable: %v", err)
	}

	// Serialize the cmd and the cgroup configuration so it can be passed to the
	// sub-process.
	var buffer bytes.Buffer
	enc := json.NewEncoder(&buffer)

	// TODO: Do the stdout file handles once there is alloc and task directories
	// set up.
	c := command.DaemonConfig{
		Cmd:        e.cmd.Cmd,
		Groups:     e.groups,
		StdoutFile: "/dev/null",
		StderrFile: "/dev/null",
		StdinFile:  "/dev/null",
	}
	if err := enc.Encode(c); err != nil {
		return fmt.Errorf("Failed to serialize daemon configuration: %v", err)
	}

	// Create a pipe to capture Stdout.
	pr, pw, err := os.Pipe()
	if err != nil {
		return err
	}
	e.spawnOutputWriter = pw
	e.spawnOutputReader = pr

	// Call ourselves using a hidden flag. The new instance of nomad will join
	// the passed cgroup, forkExec the cmd, and output status codes through
	// Stdout.
	escaped := strconv.Quote(buffer.String())
	spawn := exec.Command(bin, "spawn-daemon", escaped)
	spawn.Stdout = e.spawnOutputWriter

	if err := spawn.Start(); err != nil {
		fmt.Errorf("Failed to call spawn-daemon on nomad executable: %v", err)
	}

	// Parse the response.
	dec := json.NewDecoder(e.spawnOutputReader)
	var resp command.SpawnStartStatus
	if err := dec.Decode(&resp); err != nil {
		return fmt.Errorf("Failed to parse spawn-daemon start response: %v", err)
	}

	if resp.ErrorMsg != "" {
		return fmt.Errorf("Failed to execute user command: %s", resp.ErrorMsg)
	}

	e.spawnChild = *spawn
	return nil
}

// Open's behavior is to kill all processes associated with the id and return an
// error. This is done because it is not possible to re-attach to the
// spawn-daemon's stdout to retrieve status messages.
func (e *LinuxExecutor) Open(id string) error {
	parts := strings.SplitN(id, ":", 2)
	if len(parts) != 2 {
		return fmt.Errorf("Invalid id: %v", id)
	}

	switch parts[0] {
	case "PID":
		pid, err := strconv.Atoi(parts[1])
		if err != nil {
			return fmt.Errorf("Invalid id: failed to parse pid %v", parts[1])
		}

		process, err := os.FindProcess(pid)
		if err != nil {
			return fmt.Errorf("Failed to find Pid %v: %v", pid, err)
		}

		if err := process.Kill(); err != nil {
			return fmt.Errorf("Failed to kill Pid %v: %v", pid, err)
		}
	case "CGROUP":
		if !e.cgroupEnabled {
			return errors.New("Passed a a cgroup identifier, but cgroups are disabled")
		}

		// De-serialize the cgroup configuration.
		dec := json.NewDecoder(strings.NewReader(parts[1]))
		var groups cgroupConfig.Cgroup
		if err := dec.Decode(&groups); err != nil {
			return fmt.Errorf("Failed to parse cgroup configuration: %v", err)
		}

		e.groups = &groups
		if err := e.destroyCgroup(); err != nil {
			return err
		}
	default:
		return fmt.Errorf("Invalid id type: %v", parts[0])
	}

	return errors.New("Could not re-open to id")
}

func (e *LinuxExecutor) Wait() error {
	if e.spawnChild.Process == nil {
		return errors.New("Can not find child to wait on")
	}

	defer e.spawnOutputWriter.Close()
	defer e.spawnOutputReader.Close()

	err := e.spawnChild.Wait()
	if err != nil {
		return fmt.Errorf("Wait failed on pid %v: %v", e.spawnChild.Process.Pid, err)
	}

	// Read the exit status of the spawned process.
	dec := json.NewDecoder(e.spawnOutputReader)
	var resp command.SpawnExitStatus
	if err := dec.Decode(&resp); err != nil {
		return fmt.Errorf("Failed to parse spawn-daemon exit response: %v", err)
	}

	if !resp.Success {
		return errors.New("Task exited with error")
	}

	// If they fork/exec and then exit, wait will return but they will be still
	// running processes so we need to kill the full cgroup.
	if e.cgroupEnabled {
		return e.destroyCgroup()
	}

	return nil
}

// If cgroups are used, the ID is the cgroup structurue. Otherwise, it is the
// PID of the spawn-daemon process. An error is returned if the process was
// never started.
func (e *LinuxExecutor) ID() (string, error) {
	if e.spawnChild.Process != nil {
		if e.cgroupEnabled && e.groups != nil {
			// Serialize the cgroup structure so it can be undone on suabsequent
			// opens.
			var buffer bytes.Buffer
			enc := json.NewEncoder(&buffer)
			if err := enc.Encode(e.groups); err != nil {
				return "", fmt.Errorf("Failed to serialize daemon configuration: %v", err)
			}

			return fmt.Sprintf("CGROUP:%v", buffer.String()), nil
		}

		return fmt.Sprintf("PID:%d", e.spawnChild.Process.Pid), nil
	}

	return "", fmt.Errorf("Process has finished or was never started")
}

func (e *LinuxExecutor) Shutdown() error {
	return e.ForceStop()
}

func (e *LinuxExecutor) ForceStop() error {
	if e.spawnOutputReader != nil {
		e.spawnOutputReader.Close()
	}

	if e.spawnOutputWriter != nil {
		e.spawnOutputWriter.Close()
	}

	// If the task is not running inside a cgroup then just the spawn-daemon child is killed.
	// TODO: Find a good way to kill the children of the spawn-daemon.
	if !e.cgroupEnabled {
		if err := e.spawnChild.Process.Kill(); err != nil {
			return fmt.Errorf("Failed to kill child (%v): %v", e.spawnChild.Process.Pid, err)
		}

		return nil
	}

	return e.destroyCgroup()
}

func (e *LinuxExecutor) destroyCgroup() error {
	if e.groups == nil {
		return errors.New("Can't destroy: cgroup configuration empty")
	}

	manager := cgroupFs.Manager{}
	manager.Cgroups = e.groups
	pids, err := manager.GetPids()
	if err != nil {
		return fmt.Errorf("Failed to get pids in the cgroup %v: %v", e.groups.Name, err)
	}

	errs := new(multierror.Error)
	for _, pid := range pids {
		process, err := os.FindProcess(pid)
		if err != nil {
			multierror.Append(errs, fmt.Errorf("Failed to find Pid %v: %v", pid, err))
			continue
		}

		if err := process.Kill(); err != nil {
			multierror.Append(errs, fmt.Errorf("Failed to kill Pid %v: %v", pid, err))
			continue
		}
	}

	// Remove the cgroup.
	if err := manager.Destroy(); err != nil {
		multierror.Append(errs, fmt.Errorf("Failed to delete the cgroup directories: %v", err))
	}

	if len(errs.Errors) != 0 {
		return fmt.Errorf("Failed to destroy cgroup: %v", errs)
	}

	return nil
}

func (e *LinuxExecutor) Command() *cmd {
	return &e.cmd
}
<commit_msg>Fixed function comment<commit_after>package executor

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"strconv"
	"strings"
	"syscall"

	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/nomad/command"
	"github.com/hashicorp/nomad/helper/discover"
	"github.com/hashicorp/nomad/nomad/structs"

	cgroupFs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
	cgroupConfig "github.com/opencontainers/runc/libcontainer/configs"
)

const (
	cgroupMount = "/sys/fs/cgroup"
)

func NewExecutor() Executor {
	e := LinuxExecutor{}

	// TODO: In a follow-up PR make it so this only happens once per client.
	// Fingerprinting shouldn't happen per task.

	// Check if the process is has root capabilities.
	e.root = syscall.Geteuid() == 0

	// Check if this process can set uid.
	if e.root {
		e.setUidEnabled = true
	}

	// Check that cgroups are available. Must be root to modify it.
	if _, err := os.Stat(cgroupMount); err == nil && e.root {
		e.cgroupEnabled = true
	}

	return &e
}

// Linux executor is designed to run on linux kernel 2.8+.
type LinuxExecutor struct {
	cmd
	user *user.User

	// Finger print capabilities.
	root          bool
	setUidEnabled bool
	cgroupEnabled bool

	// Isolation configurations.
	groups *cgroupConfig.Cgroup

	// Tracking of child process.
	spawnChild        exec.Cmd
	spawnOutputWriter *os.File
	spawnOutputReader *os.File
}

func (e *LinuxExecutor) Limit(resources *structs.Resources) error {
	if resources == nil {
		return nil
	}

	if e.cgroupEnabled {
		e.configureCgroups(resources)
	}

	return nil
}

func (e *LinuxExecutor) configureCgroups(resources *structs.Resources) {
	if !e.cgroupEnabled {
		return
	}

	e.groups = &cgroupConfig.Cgroup{}

	// Groups will be created in a heiarchy according to the resource being
	// constrained, current session, and then this unique name. Restraints are
	// then placed in the corresponding files.
	// Ex: restricting a process to 2048Mhz CPU and 2MB of memory:
	//   $ cat /sys/fs/cgroup/cpu/user/1000.user/4.session/<uuid>/cpu.shares
	//		2028
	//   $ cat /sys/fs/cgroup/memory/user/1000.user/4.session/<uuid>/memory.limit_in_bytes
	//		2097152
	e.groups.Name = structs.GenerateUUID()

	// TODO: verify this is needed for things like network access
	e.groups.AllowAllDevices = true

	if resources.MemoryMB > 0 {
		// Total amount of memory allowed to consume
		e.groups.Memory = int64(resources.MemoryMB * 1024 * 1024)
		// Disable swap to avoid issues on the machine
		e.groups.MemorySwap = int64(-1)
	}

	if resources.CPU > 0.0 {
		// Set the relative CPU shares for this cgroup.
		// The simplest scale is 1 share to 1 MHz so 1024 = 1GHz. This means any
		// given process will have at least that amount of resources, but likely
		// more since it is (probably) rare that the machine will run at 100%
		// CPU. This scale will cease to work if a node is overprovisioned.
		e.groups.CpuShares = int64(resources.CPU)
	}

	if resources.IOPS > 0 {
		e.groups.BlkioThrottleReadIOpsDevice = strconv.FormatInt(int64(resources.IOPS), 10)
		e.groups.BlkioThrottleWriteIOpsDevice = strconv.FormatInt(int64(resources.IOPS), 10)
	}

}

func (e *LinuxExecutor) runAs(userid string) error {
	errs := new(multierror.Error)

	// First, try to lookup the user by uid
	u, err := user.LookupId(userid)
	if err == nil {
		e.user = u
		return nil
	} else {
		errs = multierror.Append(errs, err)
	}

	// Lookup failed, so try by username instead
	u, err = user.Lookup(userid)
	if err == nil {
		e.user = u
		return nil
	} else {
		errs = multierror.Append(errs, err)
	}

	// If we got here we failed to lookup based on id and username, so we'll
	// return those errors.
	return fmt.Errorf("Failed to identify user to run as: %s", errs)
}

func (e *LinuxExecutor) Start() error {
	// Try to run as "nobody" user so we don't leak root privilege to the
	// spawned process. Note that we will only do this if we can call SetUID.
	// Otherwise we'll just run the other process as our current (non-root)
	// user. This means we aren't forced to run nomad as root.
	if e.setUidEnabled {
		if err := e.runAs("nobody"); err == nil && e.user != nil {
			e.cmd.SetUID(e.user.Uid)
			e.cmd.SetGID(e.user.Gid)
		}
	}

	return e.spawnDaemon()
}

// spawnDaemon executes a double fork to start the user command with proper
// isolation. Stores the child process for use in Wait.
func (e *LinuxExecutor) spawnDaemon() error {
	bin, err := discover.NomadExecutable()
	if err != nil {
		return fmt.Errorf("Failed to determine the nomad executable: %v", err)
	}

	// Serialize the cmd and the cgroup configuration so it can be passed to the
	// sub-process.
	var buffer bytes.Buffer
	enc := json.NewEncoder(&buffer)

	// TODO: Do the stdout file handles once there is alloc and task directories
	// set up.
	c := command.DaemonConfig{
		Cmd:        e.cmd.Cmd,
		Groups:     e.groups,
		StdoutFile: "/dev/null",
		StderrFile: "/dev/null",
		StdinFile:  "/dev/null",
	}
	if err := enc.Encode(c); err != nil {
		return fmt.Errorf("Failed to serialize daemon configuration: %v", err)
	}

	// Create a pipe to capture Stdout.
	pr, pw, err := os.Pipe()
	if err != nil {
		return err
	}
	e.spawnOutputWriter = pw
	e.spawnOutputReader = pr

	// Call ourselves using a hidden flag. The new instance of nomad will join
	// the passed cgroup, forkExec the cmd, and output status codes through
	// Stdout.
	escaped := strconv.Quote(buffer.String())
	spawn := exec.Command(bin, "spawn-daemon", escaped)
	spawn.Stdout = e.spawnOutputWriter

	if err := spawn.Start(); err != nil {
		fmt.Errorf("Failed to call spawn-daemon on nomad executable: %v", err)
	}

	// Parse the response.
	dec := json.NewDecoder(e.spawnOutputReader)
	var resp command.SpawnStartStatus
	if err := dec.Decode(&resp); err != nil {
		return fmt.Errorf("Failed to parse spawn-daemon start response: %v", err)
	}

	if resp.ErrorMsg != "" {
		return fmt.Errorf("Failed to execute user command: %s", resp.ErrorMsg)
	}

	e.spawnChild = *spawn
	return nil
}

// Open's behavior is to kill all processes associated with the id and return an
// error. This is done because it is not possible to re-attach to the
// spawn-daemon's stdout to retrieve status messages.
func (e *LinuxExecutor) Open(id string) error {
	parts := strings.SplitN(id, ":", 2)
	if len(parts) != 2 {
		return fmt.Errorf("Invalid id: %v", id)
	}

	switch parts[0] {
	case "PID":
		pid, err := strconv.Atoi(parts[1])
		if err != nil {
			return fmt.Errorf("Invalid id: failed to parse pid %v", parts[1])
		}

		process, err := os.FindProcess(pid)
		if err != nil {
			return fmt.Errorf("Failed to find Pid %v: %v", pid, err)
		}

		if err := process.Kill(); err != nil {
			return fmt.Errorf("Failed to kill Pid %v: %v", pid, err)
		}
	case "CGROUP":
		if !e.cgroupEnabled {
			return errors.New("Passed a a cgroup identifier, but cgroups are disabled")
		}

		// De-serialize the cgroup configuration.
		dec := json.NewDecoder(strings.NewReader(parts[1]))
		var groups cgroupConfig.Cgroup
		if err := dec.Decode(&groups); err != nil {
			return fmt.Errorf("Failed to parse cgroup configuration: %v", err)
		}

		e.groups = &groups
		if err := e.destroyCgroup(); err != nil {
			return err
		}
	default:
		return fmt.Errorf("Invalid id type: %v", parts[0])
	}

	return errors.New("Could not re-open to id")
}

func (e *LinuxExecutor) Wait() error {
	if e.spawnChild.Process == nil {
		return errors.New("Can not find child to wait on")
	}

	defer e.spawnOutputWriter.Close()
	defer e.spawnOutputReader.Close()

	err := e.spawnChild.Wait()
	if err != nil {
		return fmt.Errorf("Wait failed on pid %v: %v", e.spawnChild.Process.Pid, err)
	}

	// Read the exit status of the spawned process.
	dec := json.NewDecoder(e.spawnOutputReader)
	var resp command.SpawnExitStatus
	if err := dec.Decode(&resp); err != nil {
		return fmt.Errorf("Failed to parse spawn-daemon exit response: %v", err)
	}

	if !resp.Success {
		return errors.New("Task exited with error")
	}

	// If they fork/exec and then exit, wait will return but they will be still
	// running processes so we need to kill the full cgroup.
	if e.cgroupEnabled {
		return e.destroyCgroup()
	}

	return nil
}

// If cgroups are used, the ID is the cgroup structurue. Otherwise, it is the
// PID of the spawn-daemon process. An error is returned if the process was
// never started.
func (e *LinuxExecutor) ID() (string, error) {
	if e.spawnChild.Process != nil {
		if e.cgroupEnabled && e.groups != nil {
			// Serialize the cgroup structure so it can be undone on suabsequent
			// opens.
			var buffer bytes.Buffer
			enc := json.NewEncoder(&buffer)
			if err := enc.Encode(e.groups); err != nil {
				return "", fmt.Errorf("Failed to serialize daemon configuration: %v", err)
			}

			return fmt.Sprintf("CGROUP:%v", buffer.String()), nil
		}

		return fmt.Sprintf("PID:%d", e.spawnChild.Process.Pid), nil
	}

	return "", fmt.Errorf("Process has finished or was never started")
}

func (e *LinuxExecutor) Shutdown() error {
	return e.ForceStop()
}

func (e *LinuxExecutor) ForceStop() error {
	if e.spawnOutputReader != nil {
		e.spawnOutputReader.Close()
	}

	if e.spawnOutputWriter != nil {
		e.spawnOutputWriter.Close()
	}

	// If the task is not running inside a cgroup then just the spawn-daemon child is killed.
	// TODO: Find a good way to kill the children of the spawn-daemon.
	if !e.cgroupEnabled {
		if err := e.spawnChild.Process.Kill(); err != nil {
			return fmt.Errorf("Failed to kill child (%v): %v", e.spawnChild.Process.Pid, err)
		}

		return nil
	}

	return e.destroyCgroup()
}

func (e *LinuxExecutor) destroyCgroup() error {
	if e.groups == nil {
		return errors.New("Can't destroy: cgroup configuration empty")
	}

	manager := cgroupFs.Manager{}
	manager.Cgroups = e.groups
	pids, err := manager.GetPids()
	if err != nil {
		return fmt.Errorf("Failed to get pids in the cgroup %v: %v", e.groups.Name, err)
	}

	errs := new(multierror.Error)
	for _, pid := range pids {
		process, err := os.FindProcess(pid)
		if err != nil {
			multierror.Append(errs, fmt.Errorf("Failed to find Pid %v: %v", pid, err))
			continue
		}

		if err := process.Kill(); err != nil {
			multierror.Append(errs, fmt.Errorf("Failed to kill Pid %v: %v", pid, err))
			continue
		}
	}

	// Remove the cgroup.
	if err := manager.Destroy(); err != nil {
		multierror.Append(errs, fmt.Errorf("Failed to delete the cgroup directories: %v", err))
	}

	if len(errs.Errors) != 0 {
		return fmt.Errorf("Failed to destroy cgroup: %v", errs)
	}

	return nil
}

func (e *LinuxExecutor) Command() *cmd {
	return &e.cmd
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Eric Barkie. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package device

// A Weatherlink device is simulated by guessing what commands were
// requested based on the packet sizes.  It's not perfect but is a
// convenient way to allow low level protocol testing.

import (
	"io"
	"math/rand"
	"time"

	"github.com/ebarkie/weatherlink/data"
)

// Sim represents a simulted Weatherlink device.
type Sim struct {
	l            data.Loop // Current loop packet state
	nextLoopType int       // Loop type to send next (so they are interleaved)

	// lastWrite and readsSinceWrite are used within ReadFull() to
	// determine what's trying to be read.  This is simple and avoids
	// implementing a state machine.
	lastWrite       []byte
	readsSinceWrite int
}

// Dial initializes the state of a simulated Weatherlink device.
func (s *Sim) Dial(addr string) error {
	// Starting loop values which will pass typical QC processes.
	s.l.Bar.Altimeter = 29.0
	s.l.Bar.SeaLevel = 29.0
	s.l.Bar.Station = 29.0
	s.l.OutHumidity = 50
	s.l.OutTemp = 65.0
	s.l.Wind.Cur.Speed = 3

	return nil
}

// Close closes the simulated Weatherlink device.
func (s *Sim) Close() error {
	s.l = data.Loop{}
	s.nextLoopType = 0

	return nil
}

// Flush flushes the input buffers of the simulated Weatherlink device.
func (Sim) Flush() error {
	return nil
}

// Read reads up to the size of the provided byte buffer from the
// simulated Weatherlink device.
func (Sim) Read([]byte) (int, error) {
	return 0, io.ErrUnexpectedEOF
}

// ReadFull reads the full size of the provided byte buffer from the
// simulted Weatherlink device.
func (s *Sim) ReadFull(b []byte) (n int, err error) {
	s.readsSinceWrite++

	var p []byte
	switch {
	case len(b) == 1: // Command ack
		p = []byte{0x06}
	case len(b) == 6 && s.readsSinceWrite < 2: // Command OK
		p = []byte("\n\rOK\n\r")
	case string(s.lastWrite) == "GETTIME\n":
		ct := data.ConsTime(time.Now())
		p, err = ct.MarshalBinary()
	case string(s.lastWrite) == "NVER\n":
		fv := data.FirmVer("1.73")
		p, err = fv.MarshalText()
	case string(s.lastWrite) == "TEST\n":
		p = []byte("\n\rTEST\n\r")
	case string(s.lastWrite) == "VER\n":
		ft := data.FirmTime(time.Date(2002, time.April, 24, 0, 0, 0, 0, time.UTC))
		p, err = ft.MarshalText()
	case len(b) == 99: // LPS 3 x
		// Interleave loop types.
		s.l.LoopType = s.nextLoopType + 1
		s.nextLoopType = (s.nextLoopType + 1) % 2

		// Make observation values wander around like they would on a
		// real station.
		s.l.Bar.Altimeter = wander(s.l.Bar.Altimeter, 0.01)
		s.l.Bar.SeaLevel = wander(s.l.Bar.SeaLevel, 0.01)
		s.l.Bar.Station = wander(s.l.Bar.Station, 0.01)
		s.l.OutHumidity = int(wander(float64(s.l.OutHumidity), 1))
		s.l.OutTemp = wander(s.l.OutTemp, 0.5)
		s.l.Wind.Cur.Speed = int(wander(float64(s.l.Wind.Cur.Speed), 1))

		s.l.LoopType = s.nextLoopType + 1
		s.nextLoopType = (s.nextLoopType + 1) % 2

		p, err = s.l.MarshalBinary()

		// Create 2s delay between packets.
		time.Sleep(2 * time.Second)
	default:
		return 0, io.ErrUnexpectedEOF
	}

	n = copy(b, p)
	return
}

// Write simulates a write of the byte buffer.
func (s *Sim) Write(b []byte) (int, error) {
	s.lastWrite = b
	s.readsSinceWrite = 0

	return len(b), nil
}

// wander takes a value and randomly adds +/- step or zero.
func wander(v, step float64) float64 {
	rand.Seed(int64(time.Now().Nanosecond()))
	return v + float64(rand.Intn(3)-1)*step
}
<commit_msg>Comments<commit_after>// Copyright (c) 2016 Eric Barkie. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package device

// A Weatherlink device is simulated by guessing what commands were
// requested based on the packet sizes.  It's not perfect but is a
// convenient way to allow low level protocol testing.

import (
	"io"
	"math/rand"
	"time"

	"github.com/ebarkie/weatherlink/data"
)

// Sim represents a simulted Weatherlink device.
type Sim struct {
	l            data.Loop // Current loop packet state
	nextLoopType int       // Loop type to send next (so they are interleaved)

	// lastWrite and readsSinceWrite are used by ReadFull() to determine
	// what's trying to be read.  This is simple and avoids implementing
	// a state machine.
	lastWrite       []byte
	readsSinceWrite int
}

// Dial initializes the state of a simulated Weatherlink device.
func (s *Sim) Dial(addr string) error {
	// Starting loop values which will pass typical QC processes.
	s.l.Bar.Altimeter = 29.0
	s.l.Bar.SeaLevel = 29.0
	s.l.Bar.Station = 29.0
	s.l.OutHumidity = 50
	s.l.OutTemp = 65.0
	s.l.Wind.Cur.Speed = 3

	return nil
}

// Close closes the simulated Weatherlink device.
func (s *Sim) Close() error {
	s.l = data.Loop{}
	s.nextLoopType = 0

	return nil
}

// Flush flushes the input buffers of the simulated Weatherlink device.
func (Sim) Flush() error {
	return nil
}

// Read reads up to the size of the provided byte buffer from the
// simulated Weatherlink device.
func (Sim) Read([]byte) (int, error) {
	return 0, io.ErrUnexpectedEOF
}

// ReadFull reads the full size of the provided byte buffer from the
// simulted Weatherlink device.
func (s *Sim) ReadFull(b []byte) (n int, err error) {
	s.readsSinceWrite++

	var p []byte
	switch {
	case len(b) == 1: // Command ack
		p = []byte{0x06}
	case len(b) == 6 && s.readsSinceWrite < 2: // Command OK
		p = []byte("\n\rOK\n\r")
	case string(s.lastWrite) == "GETTIME\n":
		ct := data.ConsTime(time.Now())
		p, err = ct.MarshalBinary()
	case string(s.lastWrite) == "NVER\n":
		fv := data.FirmVer("1.73")
		p, err = fv.MarshalText()
	case string(s.lastWrite) == "TEST\n":
		p = []byte("\n\rTEST\n\r")
	case string(s.lastWrite) == "VER\n":
		ft := data.FirmTime(time.Date(2002, time.April, 24, 0, 0, 0, 0, time.UTC))
		p, err = ft.MarshalText()
	case len(b) == 99: // LPS 3 x
		// Interleave loop types.
		s.l.LoopType = s.nextLoopType + 1
		s.nextLoopType = (s.nextLoopType + 1) % 2

		// Make observation values wander around like they would on a
		// real station.
		s.l.Bar.Altimeter = wander(s.l.Bar.Altimeter, 0.01)
		s.l.Bar.SeaLevel = wander(s.l.Bar.SeaLevel, 0.01)
		s.l.Bar.Station = wander(s.l.Bar.Station, 0.01)
		s.l.OutHumidity = int(wander(float64(s.l.OutHumidity), 1))
		s.l.OutTemp = wander(s.l.OutTemp, 0.5)
		s.l.Wind.Cur.Speed = int(wander(float64(s.l.Wind.Cur.Speed), 1))

		s.l.LoopType = s.nextLoopType + 1
		s.nextLoopType = (s.nextLoopType + 1) % 2

		p, err = s.l.MarshalBinary()

		// Create 2s delay between packets.
		time.Sleep(2 * time.Second)
	default:
		return 0, io.ErrUnexpectedEOF
	}

	n = copy(b, p)
	return
}

// Write simulates a write of the byte buffer.
func (s *Sim) Write(b []byte) (int, error) {
	s.lastWrite = b
	s.readsSinceWrite = 0

	return len(b), nil
}

// wander takes a value and randomly adds +/- step or zero.
func wander(v, step float64) float64 {
	rand.Seed(int64(time.Now().Nanosecond()))
	return v + float64(rand.Intn(3)-1)*step
}
<|endoftext|>


<commit_before>// Package ndpcmd provides the commands for the ndp utility.
package ndpcmd

import (
	"context"
	"fmt"
	"log"
	"net"
	"os"
	"time"

	"github.com/mdlayher/ndp"
)

// Run runs the ndp utility.
func Run(ctx context.Context, c *ndp.Conn, ifi *net.Interface, op string, target net.IP) error {
	switch op {
	case "listen":
		return listen(ctx, c)
	case "ns":
		return sendNS(ctx, c, ifi.HardwareAddr, target)
	case "rs":
		return sendRS(ctx, c, ifi.HardwareAddr)
	default:
		return fmt.Errorf("unrecognized operation: %q", op)
	}
}

func listen(ctx context.Context, c *ndp.Conn) error {
	ll := log.New(os.Stderr, "ndp listen> ", 0)
	ll.Println("listening for messages")

	var recv int
	for {
		if err := c.SetReadDeadline(time.Now().Add(1 * time.Second)); err != nil {
			return err
		}

		m, _, from, err := c.ReadFrom()
		if err == nil {
			recv++
			printMessage(ll, m, from)
			continue
		}

		// Was the context canceled already?
		select {
		case <-ctx.Done():
			ll.Printf("received %d message(s)", recv)
			return ctx.Err()
		default:
		}

		// Was the error caused by a read timeout, and should the loop continue?
		if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
			continue
		}

		return fmt.Errorf("failed to read message: %v", err)
	}
}

func printMessage(ll *log.Logger, m ndp.Message, from net.IP) {
	switch m := m.(type) {
	case *ndp.NeighborAdvertisement:
		printNA(ll, m, from)
	case *ndp.RouterAdvertisement:
		printRA(ll, m, from)
	default:
		ll.Printf("%s %#v", from, m)
	}
}
<commit_msg>internal/ndpcmd: listen by default when no op specified<commit_after>// Package ndpcmd provides the commands for the ndp utility.
package ndpcmd

import (
	"context"
	"fmt"
	"log"
	"net"
	"os"
	"time"

	"github.com/mdlayher/ndp"
)

// Run runs the ndp utility.
func Run(ctx context.Context, c *ndp.Conn, ifi *net.Interface, op string, target net.IP) error {
	switch op {
	// listen is the default when no op is specified..
	case "listen", "":
		return listen(ctx, c)
	case "ns":
		return sendNS(ctx, c, ifi.HardwareAddr, target)
	case "rs":
		return sendRS(ctx, c, ifi.HardwareAddr)
	default:
		return fmt.Errorf("unrecognized operation: %q", op)
	}
}

func listen(ctx context.Context, c *ndp.Conn) error {
	ll := log.New(os.Stderr, "ndp listen> ", 0)
	ll.Println("listening for messages")

	var recv int
	for {
		if err := c.SetReadDeadline(time.Now().Add(1 * time.Second)); err != nil {
			return err
		}

		m, _, from, err := c.ReadFrom()
		if err == nil {
			recv++
			printMessage(ll, m, from)
			continue
		}

		// Was the context canceled already?
		select {
		case <-ctx.Done():
			ll.Printf("received %d message(s)", recv)
			return ctx.Err()
		default:
		}

		// Was the error caused by a read timeout, and should the loop continue?
		if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
			continue
		}

		return fmt.Errorf("failed to read message: %v", err)
	}
}

func printMessage(ll *log.Logger, m ndp.Message, from net.IP) {
	switch m := m.(type) {
	case *ndp.NeighborAdvertisement:
		printNA(ll, m, from)
	case *ndp.RouterAdvertisement:
		printRA(ll, m, from)
	default:
		ll.Printf("%s %#v", from, m)
	}
}
<|endoftext|>


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

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"sort"
	"strings"
	"time"

	gbbgolang "github.com/u-root/gobusybox/src/pkg/golang"
	"github.com/u-root/u-root/pkg/golang"
	"github.com/u-root/u-root/pkg/shlex"
	"github.com/u-root/u-root/pkg/ulog"
	"github.com/u-root/u-root/pkg/uroot"
	"github.com/u-root/u-root/pkg/uroot/builder"
	"github.com/u-root/u-root/pkg/uroot/initramfs"
)

// multiFlag is used for flags that support multiple invocations, e.g. -files
type multiFlag []string

func (m *multiFlag) String() string {
	return fmt.Sprint(*m)
}

func (m *multiFlag) Set(value string) error {
	*m = append(*m, value)
	return nil
}

// Flags for u-root builder.
var (
	build, format, tmpDir, base, outputPath *string
	uinitCmd, initCmd                       *string
	defaultShell                            *string
	useExistingInit                         *bool
	noCommands                              *bool
	extraFiles                              multiFlag
	statsOutputPath                         *string
	statsLabel                              *string
	shellbang                               *bool
	tags                                    *string
	// For the new gobusybox support
	usegobusybox *bool
	genDir       *string
	// For the new "filepath only" logic
	urootSourceDir *string
)

func init() {
	var sh string
	switch golang.Default().GOOS {
	case "plan9":
		sh = ""
	default:
		sh = "elvish"
	}

	build = flag.String("build", "gbb", "u-root build format (e.g. bb/gbb or binary).")
	format = flag.String("format", "cpio", "Archival format.")

	tmpDir = flag.String("tmpdir", "", "Temporary directory to put binaries in.")

	base = flag.String("base", "", "Base archive to add files to. By default, this is a couple of directories like /bin, /etc, etc. u-root has a default internally supplied set of files; use base=/dev/null if you don't want any base files.")
	useExistingInit = flag.Bool("useinit", false, "Use existing init from base archive (only if --base was specified).")
	outputPath = flag.String("o", "", "Path to output initramfs file.")

	initCmd = flag.String("initcmd", "init", "Symlink target for /init. Can be an absolute path or a u-root command name. Use initcmd=\"\" if you don't want the symlink.")
	uinitCmd = flag.String("uinitcmd", "", "Symlink target and arguments for /bin/uinit. Can be an absolute path or a u-root command name. Use uinitcmd=\"\" if you don't want the symlink. E.g. -uinitcmd=\"echo foobar\"")
	defaultShell = flag.String("defaultsh", sh, "Default shell. Can be an absolute path or a u-root command name. Use defaultsh=\"\" if you don't want the symlink.")

	noCommands = flag.Bool("nocmd", false, "Build no Go commands; initramfs only")

	flag.Var(&extraFiles, "files", "Additional files, directories, and binaries (with their ldd dependencies) to add to archive. Can be speficified multiple times.")

	shellbang = flag.Bool("shellbang", false, "Use #! instead of symlinks for busybox")

	statsOutputPath = flag.String("stats-output-path", "", "Write build stats to this file (JSON)")
	statsLabel = flag.String("stats-label", "", "Use this statsLabel when writing stats")

	tags = flag.String("tags", "", "Comma separated list of build tags")

	// Flags for the gobusybox, which we hope to move to, since it works with modules.
	genDir = flag.String("gen-dir", "", "Directory to generate source in")

	// Flag for the new filepath only mode. This will be required to find the u-root commands and make templates work
	urootSourceDir = flag.String("uroot-source", "", "Path to the locally checked out u-root source tree in case commands from there are desired.")
}

type buildStats struct {
	Label      string  `json:"label,omitempty"`
	Time       int64   `json:"time"`
	Duration   float64 `json:"duration"`
	OutputSize int64   `json:"output_size"`
}

func writeBuildStats(stats buildStats, path string) error {
	var allStats []buildStats
	if data, err := os.ReadFile(*statsOutputPath); err == nil {
		json.Unmarshal(data, &allStats)
	}
	found := false
	for i, s := range allStats {
		if s.Label == stats.Label {
			allStats[i] = stats
			found = true
			break
		}
	}
	if !found {
		allStats = append(allStats, stats)
		sort.Slice(allStats, func(i, j int) bool {
			return strings.Compare(allStats[i].Label, allStats[j].Label) == -1
		})
	}
	data, err := json.MarshalIndent(allStats, "", "  ")
	if err != nil {
		return err
	}
	if err := os.WriteFile(*statsOutputPath, data, 0o644); err != nil {
		return err
	}
	return nil
}

func generateLabel() string {
	var baseCmds []string
	env := golang.Default()
	if len(flag.Args()) > 0 {
		// Use the last component of the name to keep the label short
		for _, e := range flag.Args() {
			baseCmds = append(baseCmds, path.Base(e))
		}
	} else {
		baseCmds = []string{"core"}
	}
	return fmt.Sprintf("%s-%s-%s-%s", *build, env.GOOS, env.GOARCH, strings.Join(baseCmds, "_"))
}

func main() {
	gbbOpts := &gbbgolang.BuildOpts{}
	gbbOpts.RegisterFlags(flag.CommandLine)

	l := log.New(os.Stderr, "", log.Ltime)

	// Register an alias for -go-no-strip for backwards compatibility.
	flag.CommandLine.BoolVar(&gbbOpts.NoStrip, "no-strip", false, "Build unstripped binaries")
	flag.Parse()

	if usrc := os.Getenv("UROOT_SOURCE"); usrc != "" && *urootSourceDir == "" {
		*urootSourceDir = usrc
	}

	start := time.Now()

	// Main is in a separate functions so defers run on return.
	if err := Main(l, gbbOpts); err != nil {
		l.Fatalf("Build error: %v", err)
	}

	elapsed := time.Now().Sub(start)

	stats := buildStats{
		Label:    *statsLabel,
		Time:     start.Unix(),
		Duration: float64(elapsed.Milliseconds()) / 1000,
	}
	if stats.Label == "" {
		stats.Label = generateLabel()
	}
	if stat, err := os.Stat(*outputPath); err == nil && stat.ModTime().After(start) {
		l.Printf("Successfully built %q (size %d).", *outputPath, stat.Size())
		stats.OutputSize = stat.Size()
		if *statsOutputPath != "" {
			if err := writeBuildStats(stats, *statsOutputPath); err == nil {
				l.Printf("Wrote stats to %q (label %q)", *statsOutputPath, stats.Label)
			} else {
				l.Printf("Failed to write stats to %s: %v", *statsOutputPath, err)
			}
		}
	}
}

var recommendedVersions = []string{
	"go1.17",
}

func isRecommendedVersion(v string) bool {
	for _, r := range recommendedVersions {
		if strings.HasPrefix(v, r) {
			return true
		}
	}
	return false
}

// Main is a separate function so defers are run on return, which they wouldn't
// on exit.
func Main(l ulog.Logger, buildOpts *gbbgolang.BuildOpts) error {
	env := golang.Default()
	env.BuildTags = strings.Split(*tags, ",")
	if env.CgoEnabled {
		l.Printf("Disabling CGO for u-root...")
		env.CgoEnabled = false
	}
	l.Printf("Build environment: %s", env)
	if env.GOOS != "linux" {
		l.Printf("GOOS is not linux. Did you mean to set GOOS=linux?")
	}

	v, err := env.Version()
	if err != nil {
		l.Printf("Could not get environment's Go version, using runtime's version: %v", err)
		v = runtime.Version()
	}
	if !isRecommendedVersion(v) {
		l.Printf(`WARNING: You are not using one of the recommended Go versions (have = %s, recommended = %v).
			Some packages may not compile.
			Go to https://golang.org/doc/install to find out how to install a newer version of Go,
			or use https://godoc.org/golang.org/dl/%s to install an additional version of Go.`,
			v, recommendedVersions, recommendedVersions[0])
	}

	archiver, err := initramfs.GetArchiver(*format)
	if err != nil {
		return err
	}

	// Open the target initramfs file.
	if *outputPath == "" {
		if len(env.GOOS) == 0 && len(env.GOARCH) == 0 {
			return fmt.Errorf("passed no path, GOOS, and GOARCH to CPIOArchiver.OpenWriter")
		}
		*outputPath = fmt.Sprintf("/tmp/initramfs.%s_%s.cpio", env.GOOS, env.GOARCH)
	}
	w, err := archiver.OpenWriter(l, *outputPath)
	if err != nil {
		return err
	}

	var baseFile initramfs.Reader
	if *base != "" {
		bf, err := os.Open(*base)
		if err != nil {
			return err
		}
		defer bf.Close()
		baseFile = archiver.Reader(bf)
	} else {
		baseFile = uroot.DefaultRamfs().Reader()
	}

	tempDir := *tmpDir
	if tempDir == "" {
		var err error
		tempDir, err = os.MkdirTemp("", "u-root")
		if err != nil {
			return err
		}
		defer os.RemoveAll(tempDir)
	} else if _, err := os.Stat(tempDir); os.IsNotExist(err) {
		if err := os.MkdirAll(tempDir, 0o755); err != nil {
			return fmt.Errorf("temporary directory %q did not exist; tried to mkdir but failed: %v", tempDir, err)
		}
	}

	var (
		c           []uroot.Commands
		initCommand = *initCmd
	)
	if !*noCommands {
		var b builder.Builder
		switch *build {
		case "bb", "gbb":
			l.Printf("NOTE: building with the new gobusybox; to get the old behavior check out commit 8b790de")
			b = builder.GBBBuilder{ShellBang: *shellbang}
		case "binary":
			b = builder.BinaryBuilder{}
		case "source":
			return fmt.Errorf("source mode has been deprecated")
		default:
			return fmt.Errorf("could not find builder %q", *build)
		}

		// Resolve globs into package imports.
		//
		// Currently allowed format:
		//   Paths to Go package directories; e.g. $GOPATH/src/github.com/u-root/u-root/cmds/*
		//   u-root templates; e.g. all, core, minimal (requires uroot-source)
		//   Import paths of u-root commands; e.g. github.com/u-root/u-root/cmds/* (requires uroot-source)
		var pkgs []string
		for _, a := range flag.Args() {
			p, ok := templates[a]
			if !ok {
				if !validateArg(a) {
					l.Printf("%q is not a valid path, allowed are only existing relative or absolute file paths!", a)
					continue
				}
				pkgs = append(pkgs, a)
				continue
			}
			// This is reached if a template was selected, so check uroot source path
			if *urootSourceDir != "" {
				for _, pkg := range p {
					pkg = strings.TrimPrefix(pkg, "github.com/u-root/u-root/")
					pkgs = append(pkgs, filepath.Join(*urootSourceDir, pkg))
				}
			} else {
				return fmt.Errorf("specify the path to u-root's source directory with -uroot-source when using templates")
			}
			pkgs = append(pkgs, p...)
		}
		if len(pkgs) == 0 {
			if *urootSourceDir != "" {
				pkgs = []string{filepath.Join(*urootSourceDir, "cmds/core/*")}
			} else {
				return fmt.Errorf("specify either the path to u-root's source with -uroot-source or the path to at least one Golang command")
			}
		}

		// The command-line tool only allows specifying one build mode
		// right now.
		c = append(c, uroot.Commands{
			Builder:  b,
			Packages: pkgs,
		})
	}

	opts := uroot.Opts{
		Env:             env,
		Commands:        c,
		UrootSource:     *urootSourceDir,
		TempDir:         tempDir,
		ExtraFiles:      extraFiles,
		OutputFile:      w,
		BaseArchive:     baseFile,
		UseExistingInit: *useExistingInit,
		InitCmd:         initCommand,
		DefaultShell:    *defaultShell,
		BuildOpts:       buildOpts,
	}
	uinitArgs := shlex.Argv(*uinitCmd)
	if len(uinitArgs) > 0 {
		opts.UinitCmd = uinitArgs[0]
	}
	if len(uinitArgs) > 1 {
		opts.UinitArgs = uinitArgs[1:]
	}
	return uroot.CreateInitramfs(l, opts)
}

func validateArg(arg string) bool {
	if !checkPrefix(arg) {
		paths, err := filepath.Glob(arg)
		if err != nil {
			return false
		}
		for _, path := range paths {
			if !checkPrefix(path) {
				return false
			}
		}
	}

	return true
}

func checkPrefix(arg string) bool {
	prefixes := []string{".", "/", "-", "cmds", "github.com/u-root/u-root"}
	for _, prefix := range prefixes {
		if strings.HasPrefix(arg, prefix) {
			return true
		}
	}

	return false
}
<commit_msg>u-root.go: set a reasonable and checked default for -uroot-source<commit_after>// Copyright 2015-2018 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"sort"
	"strings"
	"time"

	gbbgolang "github.com/u-root/gobusybox/src/pkg/golang"
	"github.com/u-root/u-root/pkg/golang"
	"github.com/u-root/u-root/pkg/shlex"
	"github.com/u-root/u-root/pkg/ulog"
	"github.com/u-root/u-root/pkg/uroot"
	"github.com/u-root/u-root/pkg/uroot/builder"
	"github.com/u-root/u-root/pkg/uroot/initramfs"
)

// multiFlag is used for flags that support multiple invocations, e.g. -files
type multiFlag []string

func (m *multiFlag) String() string {
	return fmt.Sprint(*m)
}

func (m *multiFlag) Set(value string) error {
	*m = append(*m, value)
	return nil
}

// Flags for u-root builder.
var (
	build, format, tmpDir, base, outputPath *string
	uinitCmd, initCmd                       *string
	defaultShell                            *string
	useExistingInit                         *bool
	noCommands                              *bool
	extraFiles                              multiFlag
	statsOutputPath                         *string
	statsLabel                              *string
	shellbang                               *bool
	tags                                    *string
	// For the new gobusybox support
	usegobusybox *bool
	genDir       *string
	// For the new "filepath only" logic
	urootSourceDir *string
)

func init() {
	var sh string
	switch golang.Default().GOOS {
	case "plan9":
		sh = ""
	default:
		sh = "elvish"
	}

	build = flag.String("build", "gbb", "u-root build format (e.g. bb/gbb or binary).")
	format = flag.String("format", "cpio", "Archival format.")

	tmpDir = flag.String("tmpdir", "", "Temporary directory to put binaries in.")

	base = flag.String("base", "", "Base archive to add files to. By default, this is a couple of directories like /bin, /etc, etc. u-root has a default internally supplied set of files; use base=/dev/null if you don't want any base files.")
	useExistingInit = flag.Bool("useinit", false, "Use existing init from base archive (only if --base was specified).")
	outputPath = flag.String("o", "", "Path to output initramfs file.")

	initCmd = flag.String("initcmd", "init", "Symlink target for /init. Can be an absolute path or a u-root command name. Use initcmd=\"\" if you don't want the symlink.")
	uinitCmd = flag.String("uinitcmd", "", "Symlink target and arguments for /bin/uinit. Can be an absolute path or a u-root command name. Use uinitcmd=\"\" if you don't want the symlink. E.g. -uinitcmd=\"echo foobar\"")
	defaultShell = flag.String("defaultsh", sh, "Default shell. Can be an absolute path or a u-root command name. Use defaultsh=\"\" if you don't want the symlink.")

	noCommands = flag.Bool("nocmd", false, "Build no Go commands; initramfs only")

	flag.Var(&extraFiles, "files", "Additional files, directories, and binaries (with their ldd dependencies) to add to archive. Can be speficified multiple times.")

	shellbang = flag.Bool("shellbang", false, "Use #! instead of symlinks for busybox")

	statsOutputPath = flag.String("stats-output-path", "", "Write build stats to this file (JSON)")
	statsLabel = flag.String("stats-label", "", "Use this statsLabel when writing stats")

	tags = flag.String("tags", "", "Comma separated list of build tags")

	// Flags for the gobusybox, which we hope to move to, since it works with modules.
	genDir = flag.String("gen-dir", "", "Directory to generate source in")

	// Flag for the new filepath only mode. This will be required to find the u-root commands and make templates work
	// In almost every case, "." is fine.
	urootSourceDir = flag.String("uroot-source", ".", "Path to the locally checked out u-root source tree in case commands from there are desired.")
}

type buildStats struct {
	Label      string  `json:"label,omitempty"`
	Time       int64   `json:"time"`
	Duration   float64 `json:"duration"`
	OutputSize int64   `json:"output_size"`
}

func writeBuildStats(stats buildStats, path string) error {
	var allStats []buildStats
	if data, err := os.ReadFile(*statsOutputPath); err == nil {
		json.Unmarshal(data, &allStats)
	}
	found := false
	for i, s := range allStats {
		if s.Label == stats.Label {
			allStats[i] = stats
			found = true
			break
		}
	}
	if !found {
		allStats = append(allStats, stats)
		sort.Slice(allStats, func(i, j int) bool {
			return strings.Compare(allStats[i].Label, allStats[j].Label) == -1
		})
	}
	data, err := json.MarshalIndent(allStats, "", "  ")
	if err != nil {
		return err
	}
	if err := os.WriteFile(*statsOutputPath, data, 0o644); err != nil {
		return err
	}
	return nil
}

func generateLabel() string {
	var baseCmds []string
	env := golang.Default()
	if len(flag.Args()) > 0 {
		// Use the last component of the name to keep the label short
		for _, e := range flag.Args() {
			baseCmds = append(baseCmds, path.Base(e))
		}
	} else {
		baseCmds = []string{"core"}
	}
	return fmt.Sprintf("%s-%s-%s-%s", *build, env.GOOS, env.GOARCH, strings.Join(baseCmds, "_"))
}

func main() {
	gbbOpts := &gbbgolang.BuildOpts{}
	gbbOpts.RegisterFlags(flag.CommandLine)

	l := log.New(os.Stderr, "", log.Ltime)

	// Register an alias for -go-no-strip for backwards compatibility.
	flag.CommandLine.BoolVar(&gbbOpts.NoStrip, "no-strip", false, "Build unstripped binaries")
	flag.Parse()

	if usrc := os.Getenv("UROOT_SOURCE"); usrc != "" && *urootSourceDir == "" {
		*urootSourceDir = usrc
	}

	start := time.Now()

	// Main is in a separate functions so defers run on return.
	if err := Main(l, gbbOpts); err != nil {
		l.Fatalf("Build error: %v", err)
	}

	elapsed := time.Now().Sub(start)

	stats := buildStats{
		Label:    *statsLabel,
		Time:     start.Unix(),
		Duration: float64(elapsed.Milliseconds()) / 1000,
	}
	if stats.Label == "" {
		stats.Label = generateLabel()
	}
	if stat, err := os.Stat(*outputPath); err == nil && stat.ModTime().After(start) {
		l.Printf("Successfully built %q (size %d).", *outputPath, stat.Size())
		stats.OutputSize = stat.Size()
		if *statsOutputPath != "" {
			if err := writeBuildStats(stats, *statsOutputPath); err == nil {
				l.Printf("Wrote stats to %q (label %q)", *statsOutputPath, stats.Label)
			} else {
				l.Printf("Failed to write stats to %s: %v", *statsOutputPath, err)
			}
		}
	}
}

var recommendedVersions = []string{
	"go1.17",
}

func isRecommendedVersion(v string) bool {
	for _, r := range recommendedVersions {
		if strings.HasPrefix(v, r) {
			return true
		}
	}
	return false
}

func canFindSource(dir string) error {
	d := filepath.Join(dir, "cmds", "core")
	if _, err := os.Stat(d); err != nil {
		return fmt.Errorf("can not build u-root in %q:%w (-uroot-source may be incorrect or not set)", *urootSourceDir, os.ErrNotExist)
	}
	return nil
}

// Main is a separate function so defers are run on return, which they wouldn't
// on exit.
func Main(l ulog.Logger, buildOpts *gbbgolang.BuildOpts) error {
	env := golang.Default()
	env.BuildTags = strings.Split(*tags, ",")
	if env.CgoEnabled {
		l.Printf("Disabling CGO for u-root...")
		env.CgoEnabled = false
	}
	l.Printf("Build environment: %s", env)
	if env.GOOS != "linux" {
		l.Printf("GOOS is not linux. Did you mean to set GOOS=linux?")
	}

	v, err := env.Version()
	if err != nil {
		l.Printf("Could not get environment's Go version, using runtime's version: %v", err)
		v = runtime.Version()
	}
	if !isRecommendedVersion(v) {
		l.Printf(`WARNING: You are not using one of the recommended Go versions (have = %s, recommended = %v).
			Some packages may not compile.
			Go to https://golang.org/doc/install to find out how to install a newer version of Go,
			or use https://godoc.org/golang.org/dl/%s to install an additional version of Go.`,
			v, recommendedVersions, recommendedVersions[0])
	}

	archiver, err := initramfs.GetArchiver(*format)
	if err != nil {
		return err
	}

	// Open the target initramfs file.
	if *outputPath == "" {
		if len(env.GOOS) == 0 && len(env.GOARCH) == 0 {
			return fmt.Errorf("passed no path, GOOS, and GOARCH to CPIOArchiver.OpenWriter")
		}
		*outputPath = fmt.Sprintf("/tmp/initramfs.%s_%s.cpio", env.GOOS, env.GOARCH)
	}
	w, err := archiver.OpenWriter(l, *outputPath)
	if err != nil {
		return err
	}

	var baseFile initramfs.Reader
	if *base != "" {
		bf, err := os.Open(*base)
		if err != nil {
			return err
		}
		defer bf.Close()
		baseFile = archiver.Reader(bf)
	} else {
		baseFile = uroot.DefaultRamfs().Reader()
	}

	tempDir := *tmpDir
	if tempDir == "" {
		var err error
		tempDir, err = os.MkdirTemp("", "u-root")
		if err != nil {
			return err
		}
		defer os.RemoveAll(tempDir)
	} else if _, err := os.Stat(tempDir); os.IsNotExist(err) {
		if err := os.MkdirAll(tempDir, 0o755); err != nil {
			return fmt.Errorf("temporary directory %q did not exist; tried to mkdir but failed: %v", tempDir, err)
		}
	}

	var (
		c           []uroot.Commands
		initCommand = *initCmd
	)
	if !*noCommands {
		var b builder.Builder
		switch *build {
		case "bb", "gbb":
			l.Printf("NOTE: building with the new gobusybox; to get the old behavior check out commit 8b790de")
			b = builder.GBBBuilder{ShellBang: *shellbang}
		case "binary":
			b = builder.BinaryBuilder{}
		case "source":
			return fmt.Errorf("source mode has been deprecated")
		default:
			return fmt.Errorf("could not find builder %q", *build)
		}

		// Resolve globs into package imports.
		//
		// Currently allowed format:
		//   Paths to Go package directories; e.g. $GOPATH/src/github.com/u-root/u-root/cmds/*
		//   u-root templates; e.g. all, core, minimal (requires uroot-source be valid)
		//   Import paths of u-root commands; e.g. github.com/u-root/u-root/cmds/* (requires uroot-source)
		var pkgs []string
		for _, a := range flag.Args() {
			p, ok := templates[a]
			if !ok {
				if !validateArg(a) {
					l.Printf("%q is not a valid path, allowed are only existing relative or absolute file paths!", a)
					continue
				}
				pkgs = append(pkgs, a)
				continue
			}
			// This is reached if a template was selected, so check uroot source path
			// To make things a easier on our poor users, do
			// validation so the error is a little less mysterious.
			if err := canFindSource(*urootSourceDir); err != nil {
				return err
			}
			for _, pkg := range p {
				pkg = strings.TrimPrefix(pkg, "github.com/u-root/u-root/")
				pkgs = append(pkgs, filepath.Join(*urootSourceDir, pkg))
			}
			pkgs = append(pkgs, p...)
		}
		if len(pkgs) == 0 {
			if err := canFindSource(*urootSourceDir); err != nil {
				return err
			}
			pkgs = []string{filepath.Join(*urootSourceDir, "cmds/core/*")}
		}

		// The command-line tool only allows specifying one build mode
		// right now.
		c = append(c, uroot.Commands{
			Builder:  b,
			Packages: pkgs,
		})
	}

	opts := uroot.Opts{
		Env:             env,
		Commands:        c,
		UrootSource:     *urootSourceDir,
		TempDir:         tempDir,
		ExtraFiles:      extraFiles,
		OutputFile:      w,
		BaseArchive:     baseFile,
		UseExistingInit: *useExistingInit,
		InitCmd:         initCommand,
		DefaultShell:    *defaultShell,
		BuildOpts:       buildOpts,
	}
	uinitArgs := shlex.Argv(*uinitCmd)
	if len(uinitArgs) > 0 {
		opts.UinitCmd = uinitArgs[0]
	}
	if len(uinitArgs) > 1 {
		opts.UinitArgs = uinitArgs[1:]
	}
	return uroot.CreateInitramfs(l, opts)
}

func validateArg(arg string) bool {
	if !checkPrefix(arg) {
		paths, err := filepath.Glob(arg)
		if err != nil {
			return false
		}
		for _, path := range paths {
			if !checkPrefix(path) {
				return false
			}
		}
	}

	return true
}

func checkPrefix(arg string) bool {
	prefixes := []string{".", "/", "-", "cmds", "github.com/u-root/u-root"}
	for _, prefix := range prefixes {
		if strings.HasPrefix(arg, prefix) {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package log

import (
	"encoding/json"
	"fmt"
	"net"
	"time"
)

const (
	UDPLoggerName = "udplog"

	DefaultHost = "127.0.0.1"
	DefaultPort = 55647

	DefaultCategory = "go_logging"
)

type udpLogRecord struct {
	AppName   string `json:"appname"`
	HostName  string `json:"hostname"`
	LogLevel  string `json:"logLevel"`
	FileName  string `json:"filename"`
	FuncName  string `json:"funcName"`
	LineNo    int    `json:"lineno"`
	Message   string `json:"message"`
	Timestamp int64  `json:"timestamp"`
}

// udpLogger is a type of writerLogger that sends messages in a special format to a udplog server.
type udpLogger struct {
	*writerLogger
}

func NewUDPLogger(conf LogConfig) (Logger, error) {
	addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%v", DefaultHost, DefaultPort))
	if err != nil {
		return nil, err
	}

	conn, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		return nil, err
	}

	sev, err := SeverityFromString(conf.Severity)
	if err != nil {
		return nil, err
	}

	return &udpLogger{&writerLogger{sev, conn}}, nil
}

func (l *udpLogger) FormatMessage(sev Severity, caller *callerInfo, format string, args ...interface{}) string {
	rec := &udpLogRecord{
		appname, hostname, sev.String(), caller.filePath, caller.funcName, caller.lineNo, fmt.Sprintf(format, args...), time.Now().UnixNano() / 1000000}

	dump, err := json.Marshal(rec)
	if err != nil {
		return ""
	}

	return fmt.Sprintf("%s:%s", DefaultCategory, dump)
}

func (l *udpLogger) String() string {
	return fmt.Sprintf("udpLogger(%s)", l.sev)
}
<commit_msg>Fix timestamp<commit_after>package log

import (
	"encoding/json"
	"fmt"
	"net"
	"time"
)

const (
	UDPLoggerName = "udplog"

	DefaultHost = "127.0.0.1"
	DefaultPort = 55647

	DefaultCategory = "go_logging"
)

type udpLogRecord struct {
	AppName   string  `json:"appname"`
	HostName  string  `json:"hostname"`
	LogLevel  string  `json:"logLevel"`
	FileName  string  `json:"filename"`
	FuncName  string  `json:"funcName"`
	LineNo    int     `json:"lineno"`
	Message   string  `json:"message"`
	Timestamp float64 `json:"timestamp"`
}

// udpLogger is a type of writerLogger that sends messages in a special format to a udplog server.
type udpLogger struct {
	*writerLogger
}

func NewUDPLogger(conf LogConfig) (Logger, error) {
	addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%v", DefaultHost, DefaultPort))
	if err != nil {
		return nil, err
	}

	conn, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		return nil, err
	}

	sev, err := SeverityFromString(conf.Severity)
	if err != nil {
		return nil, err
	}

	return &udpLogger{&writerLogger{sev, conn}}, nil
}

func (l *udpLogger) FormatMessage(sev Severity, caller *callerInfo, format string, args ...interface{}) string {
	rec := &udpLogRecord{
		appname, hostname, sev.String(), caller.filePath, caller.funcName, caller.lineNo, fmt.Sprintf(format, args...), float64(time.Now().UnixNano()) / 1000000000}

	dump, err := json.Marshal(rec)
	if err != nil {
		return ""
	}

	return fmt.Sprintf("%s:%s", DefaultCategory, dump)
}

func (l *udpLogger) String() string {
	return fmt.Sprintf("udpLogger(%s)", l.sev)
}
<|endoftext|>


<commit_before>/**
 * Copyright 2014 @ z3q.net.
 * name :
 * author : jarryliu
 * date : 2013-12-16 21:47
 * description :
 * history :
 */

package app

import (
	"github.com/jsix/gof"
	"github.com/jsix/gof/crypto"
	//"go2o/src/app/front/master"
	//"go2o/src/app/front/partner"
	"go2o/src/app/front/shop/ols"
	"go2o/src/app/front/ucenter"
	"go2o/src/core/variable"
	"go2o/src/x/echox"
	"gopkg.in/labstack/echo.v1"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"
)

// 静态文件
type StaticHandler struct {
}

func (s *StaticHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, "./public/static"+r.URL.Path)
}

// 图片处理
type ImageFileHandler struct {
	app       gof.App
	upSaveDir string
}

func (i *ImageFileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path
	if path[1:4] == "res" {
		http.ServeFile(w, r, "public/static"+r.URL.Path)
	} else {
		if len(i.upSaveDir) == 0 {
			i.upSaveDir = i.app.Config().GetString(variable.UploadSaveDir)
		}
		http.ServeFile(w, r, i.upSaveDir+r.URL.Path)
	}
}

// 运行Web,监听到3个端口
func Run(ch chan bool, app gof.App, addr string) {
	defer func() {
		ch <- true
	}()
	if app.Debug() {
		log.Println("** [ Go2o][ Web][ Booted] - Web server (with debug) running on", addr)
	} else {
		log.Println("** [ Go2o][ Web][ Booted] - Web server running on", addr)
	}

	c := app.Config()
	m := map[string]interface{}{
		"static_serve": c.GetString(variable.StaticServer),
		"img_serve":    c.GetString(variable.ImageServer),
		"domain":       c.GetString(variable.ServerDomain),
		"version":      c.GetString(variable.Version),
		"spam":         crypto.Md5([]byte(strconv.Itoa(int(time.Now().Unix()))))[8:14],
	}
	w := func(e echo.Renderer) { //当改动文件时,自动创建spam
		m := echox.GetGlobTemplateVars()
		m["spam"] = crypto.Md5([]byte(strconv.Itoa(int(time.Now().Unix()))))[8:14]
	}
	echox.GlobSet(m, w)
	hosts := make(MyHttpHosts)
	//hosts[variable.DOMAIN_PREFIX_WEBMASTER] = master.GetServe()
	//hosts[variable.DOMAIN_PREFIX_PARTNER] = partner.GetServe()
	hosts[variable.DOMAIN_PREFIX_STATIC] = new(StaticHandler)
	hosts[variable.DOMAIN_PREFIX_IMAGE] = &ImageFileHandler{app: app}
	http.ListenAndServe(addr, hosts)
}

type MyHttpHosts echox.HttpHosts

func (this MyHttpHosts) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	subName := r.Host[:strings.Index(r.Host, ".")+1]
	if subName == variable.DOMAIN_PREFIX_MEMBER {
		ucenter.ServeHTTP(w, r)
	} else if h, ok := this[subName]; ok {
		h.ServeHTTP(w, r)
	} else {
		ols.ServeHTTP(w, r)
	}
}
<commit_msg>commit<commit_after>/**
 * Copyright 2014 @ z3q.net.
 * name :
 * author : jarryliu
 * date : 2013-12-16 21:47
 * description :
 * history :
 */

package app

import (
	"github.com/jsix/gof"
	"github.com/jsix/gof/crypto"
	"go2o/src/app/front/master"
	"go2o/src/app/front/partner"
	"go2o/src/app/front/shop/ols"
	"go2o/src/app/front/ucenter"
	"go2o/src/core/variable"
	"go2o/src/x/echox"
	"gopkg.in/labstack/echo.v1"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"
)

// 静态文件
type StaticHandler struct {
}

func (s *StaticHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, "./public/static"+r.URL.Path)
}

// 图片处理
type ImageFileHandler struct {
	app       gof.App
	upSaveDir string
}

func (i *ImageFileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path
	if path[1:4] == "res" {
		http.ServeFile(w, r, "public/static"+r.URL.Path)
	} else {
		if len(i.upSaveDir) == 0 {
			i.upSaveDir = i.app.Config().GetString(variable.UploadSaveDir)
		}
		http.ServeFile(w, r, i.upSaveDir+r.URL.Path)
	}
}

// 运行Web,监听到3个端口
func Run(ch chan bool, app gof.App, addr string) {
	defer func() {
		ch <- true
	}()
	if app.Debug() {
		log.Println("** [ Go2o][ Web][ Booted] - Web server (with debug) running on", addr)
	} else {
		log.Println("** [ Go2o][ Web][ Booted] - Web server running on", addr)
	}

	c := app.Config()
	m := map[string]interface{}{
		"static_serve": c.GetString(variable.StaticServer),
		"img_serve":    c.GetString(variable.ImageServer),
		"domain":       c.GetString(variable.ServerDomain),
		"version":      c.GetString(variable.Version),
		"spam":         crypto.Md5([]byte(strconv.Itoa(int(time.Now().Unix()))))[8:14],
	}
	w := func(e echo.Renderer) { //当改动文件时,自动创建spam
		m := echox.GetGlobTemplateVars()
		m["spam"] = crypto.Md5([]byte(strconv.Itoa(int(time.Now().Unix()))))[8:14]
	}
	echox.GlobSet(m, w)
	hosts := make(MyHttpHosts)
	hosts[variable.DOMAIN_PREFIX_WEBMASTER] = master.GetServe()
	hosts[variable.DOMAIN_PREFIX_PARTNER] = partner.GetServe()
	hosts[variable.DOMAIN_PREFIX_STATIC] = new(StaticHandler)
	hosts[variable.DOMAIN_PREFIX_IMAGE] = &ImageFileHandler{app: app}
	http.ListenAndServe(addr, hosts)
}

type MyHttpHosts echox.HttpHosts

func (this MyHttpHosts) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	subName := r.Host[:strings.Index(r.Host, ".")+1]
	if subName == variable.DOMAIN_PREFIX_MEMBER {
		ucenter.ServeHTTP(w, r)
	} else if h, ok := this[subName]; ok {
		h.ServeHTTP(w, r)
	} else {
		ols.ServeHTTP(w, r)
	}
}
<|endoftext|>


<commit_before>package moof

import (
		"strconv"
		)

type MoofLevel2 interface{
	String() string
}

type mfhd struct{
	size uint32
	largeSize uint64
	boxType uint32
	version uint8
	flags [3]byte
}

func NewMfhd(s uint64, box uint32, ver uint8, flag [3]byte){
	newMfhd:=new(mfhd)
	newMfhd.SetSize(s)
	newMfhd.boxType=box
	newMfhd.version=ver
	newMfhd.flags=flag
}

func (m* mfhd) SetSize(s uint64){
	if s == 0 {
		m.size = 0
	} else if s > 4294967295{
		m.size = 1
		m.largeSize = s
	} else {
		m.size = uint32(s)
	}
}

func (m *mfhd) String() string{
	return strconv.FormatUint(uint64(m.size),10)
}

func (m *mfhd) Write() []byte{
	var data []byte
	// Size
	if m.size!=1{
		data = strconv.AppendUint(data, uint64(m.size), 2)
	} else {
		data = strconv.AppendUint(data, m.largeSize, 2)
	}
	// BoxType
	// Contained boxes write
	return data
}<commit_msg>Updated mfhd<commit_after>package moof

import (
		"strconv"
		"encoding/binary"
		"fmt"
		"bytes"
		)

type MoofLevel2 interface{
	String() string
}

type mfhd struct{
	size uint32
	largeSize uint64
	boxType uint32
	version uint8
	flags [3]byte
}

func NewMfhd(s uint64, box uint32, ver uint8, flag [3]byte){
	newMfhd:=new(mfhd)
	newMfhd.SetSize(s)
	newMfhd.boxType=0x6d666864
	newMfhd.version=ver
	newMfhd.flags=flag
}

func (m* mfhd) SetSize(s uint64){
	if s == 0 {
		m.size = 0
	} else if s > 4294967295{
		m.size = 1
		m.largeSize = s
	} else {
		m.size = uint32(s)
	}
}

func (m *mfhd) String() string{
	return strconv.FormatUint(uint64(m.size),10)
}

func (m *mfhd) Write() []byte{
	buf := new(bytes.Buffer)
	var err error
	// Size
	err=binary.Write(buf, binary.BigEndian, m.size)
	if err!=nil{
		fmt.Println("binary.Write failed:", err)
	}
	// BoxType
	err = binary.Write(buf,binary.BigEndian,m.boxType)
	if err!=nil{
		fmt.Println("binary.Write failed:", err)
	}
	//version
	err = binary.Write(buf,binary.BigEndian,m.version)
	if err!=nil{
		fmt.Println("binary.Write failed:", err)
	}
	//flags
	err = binary.Write(buf,binary.BigEndian,m.flags)
	if err!=nil{
		fmt.Println("binary.Write failed:", err)
	}
	//sequnce
	err = binary.Write(buf,binary.BigEndian,m.sequnce)
	if err!=nil{
		fmt.Println("binary.Write failed:", err)
	}
	return buf.Bytes()
}<|endoftext|>


<commit_before>package openapi3edit

import (
	"fmt"
	"net/http"
	"strings"

	oas3 "github.com/getkin/kin-openapi/openapi3"
	"github.com/grokify/swaggman/openapi3"
)

func OperationAddExternalDocs(op *oas3.Operation, url, description string, preserveIfReqEmpty bool) {
	if op == nil {
		return
	}
	url = strings.TrimSpace(url)
	description = strings.TrimSpace(description)
	if len(url) > 0 || len(description) > 0 {
		if preserveIfReqEmpty {
			if op.ExternalDocs == nil {
				op.ExternalDocs = &oas3.ExternalDocs{}
			}
			if len(url) > 0 {
				op.ExternalDocs.URL = url
			}
			if len(description) > 0 {
				op.ExternalDocs.Description = description
			}
		} else {
			op.ExternalDocs = &oas3.ExternalDocs{
				Description: description,
				URL:         url}
		}
	}
}

func SpecOperationsCount(spec *oas3.Swagger) uint {
	count := uint(0)
	openapi3.VisitOperations(spec, func(skipPath, skipMethod string, op *oas3.Operation) {
		count++
	})
	return count
}

func SpecSetOperation(spec *oas3.Swagger, path, method string, op oas3.Operation) {
	pathItem, ok := spec.Paths[path]
	if !ok {
		pathItem = &oas3.PathItem{}
	}
	method = strings.ToUpper(strings.TrimSpace(method))
	switch method {
	case http.MethodGet:
		pathItem.Get = &op
	case http.MethodPost:
		pathItem.Post = &op
	case http.MethodPut:
		pathItem.Put = &op
	case http.MethodPatch:
		pathItem.Patch = &op
	}

}

func SpecOperationIds(spec *oas3.Swagger) map[string]int {
	msi := map[string]int{}
	openapi3.VisitOperations(spec, func(skipPath, skipMethod string, op *oas3.Operation) {
		op.OperationID = strings.TrimSpace(op.OperationID)
		if _, ok := msi[op.OperationID]; !ok {
			msi[op.OperationID] = 0
		}
		msi[op.OperationID]++
	})
	return msi
}

func SpecOperationIdsFromSummaries(spec *oas3.Swagger, errorOnEmpty bool) error {
	empty := []string{}
	openapi3.VisitOperations(spec, func(path, method string, op *oas3.Operation) {
		op.Summary = strings.Join(strings.Split(op.Summary, " "), " ")
		op.OperationID = op.Summary
		if len(op.OperationID) == 0 {
			empty = append(empty, path+" "+method)
		}
	})
	if errorOnEmpty && len(empty) > 0 {
		return fmt.Errorf("no_opid: [%s]", strings.Join(empty, ", "))
	}
	return nil
}

func SpecAddCustomProperties(spec *oas3.Swagger, custom map[string]interface{}, addToOperations, addToSchemas bool) {
	if spec == nil || len(custom) == 0 {
		return
	}
	if addToOperations {
		openapi3.VisitOperations(spec, func(skipPath, skipMethod string, op *oas3.Operation) {
			for key, val := range custom {
				op.Extensions[key] = val
			}
		})
	}
	if addToSchemas {
		for _, schema := range spec.Components.Schemas {
			if schema.Value != nil {
				for key, val := range custom {
					schema.Value.Extensions[key] = val
				}
			}
		}
	}
}

func SpecAddOperationMetas(spec *oas3.Swagger, metas map[string]openapi3.OperationMeta, overwrite bool) {
	if spec == nil || len(metas) == 0 {
		return
	}
	openapi3.VisitOperations(spec, func(skipPath, skipMethod string, op *oas3.Operation) {
		if op == nil {
			return
		}
		opMeta, ok := metas[op.OperationID]
		if !ok {
			return
		}
		opMeta.TrimSpace()
		writeDocs := false
		writeScopes := false
		writeThrottling := false
		if overwrite {
			writeDocs = true
			writeScopes = true
			writeThrottling = true
		}
		if writeDocs {
			OperationAddExternalDocs(op, opMeta.DocsURL, opMeta.DocsDescription, true)
		}
		if writeScopes {
			if len(opMeta.SecurityScopes) > 0 {
				op.Security = &oas3.SecurityRequirements{
					map[string][]string{"oauth": opMeta.SecurityScopes},
				}
			} else {
				op.Security = nil
			}
		}
		if writeThrottling {
			if op.ExtensionProps.Extensions == nil {
				op.ExtensionProps.Extensions = map[string]interface{}{}
			}
			op.ExtensionProps.Extensions[openapi3.XThrottlingGroup] = opMeta.XThrottlingGroup
		}
	})
}

type OperationMoreSet struct {
	OperationMores []OperationMore
}

type OperationMore struct {
	UrlPath   string
	Method    string
	Operation *oas3.Operation
}

func QueryOperationsByTags(spec *oas3.Swagger, tags []string) *OperationMoreSet {
	tagsWantMatch := map[string]int{}
	for _, tag := range tags {
		tagsWantMatch[tag] = 1
	}
	opmSet := &OperationMoreSet{OperationMores: []OperationMore{}}
	// for path, pathInfo := range spec.Paths {
	openapi3.VisitOperations(spec, func(url, method string, op *oas3.Operation) {
		if op == nil {
			return
		}
		for _, tagTry := range op.Tags {
			if _, ok := tagsWantMatch[tagTry]; ok {
				opmSet.OperationMores = append(opmSet.OperationMores,
					OperationMore{
						UrlPath:   url,
						Method:    method,
						Operation: op})
				return
			}
		}
	})
	// }
	return opmSet
}
<commit_msg>enhance: openapi3edit: refactor to `OperatorEditor`<commit_after>package openapi3edit

import (
	"fmt"
	"net/http"
	"strings"

	oas3 "github.com/getkin/kin-openapi/openapi3"
	"github.com/grokify/swaggman/openapi3"
)

type OperationEditor struct {
	Operation *oas3.Operation
}

func (oedit *OperationEditor) AddExternalDocs(docURL, docDescription string, preserveIfReqEmpty bool) {
	operationAddExternalDocs(oedit.Operation, docURL, docDescription, preserveIfReqEmpty)
}

func (oedit *OperationEditor) AddRequestBodySchemaRef(description string, required bool, contentType string, schemaRef *oas3.SchemaRef) error {
	return operationAddRequestBodySchemaRef(oedit.Operation, description, required, contentType, schemaRef)
}

func (oedit *OperationEditor) AddResponseBodySchemaRef(statusCode, description, contentType string, schemaRef *oas3.SchemaRef) error {
	return operationAddResponseBodySchemaRef(oedit.Operation, statusCode, description, contentType, schemaRef)
}

func operationAddRequestBodySchemaRef(op *oas3.Operation, description string, required bool, contentType string, schemaRef *oas3.SchemaRef) error {
	if op == nil {
		return fmt.Errorf("operation to edit is nil")
	}
	if op.RequestBody == nil {
		op.RequestBody = &oas3.RequestBodyRef{}
	}
	description = strings.TrimSpace(description)
	contentType = strings.ToLower(strings.TrimSpace(contentType))
	if len(contentType) == 0 {
		return fmt.Errorf("content type [%s] is empty", contentType)
	}
	if len(op.RequestBody.Ref) > 0 {
		return fmt.Errorf("request body is reference for operationId [%s]", op.OperationID)
	}
	op.RequestBody.Value.Description = description
	op.RequestBody.Value.Required = required
	if op.RequestBody.Value.Content == nil {
		op.RequestBody.Value.Content = oas3.NewContent()
	}
	op.RequestBody.Value.Content[contentType] = oas3.NewMediaType().WithSchemaRef(schemaRef)
	return nil
}

func operationAddResponseBodySchemaRef(op *oas3.Operation, statusCode, description, contentType string, schemaRef *oas3.SchemaRef) error {
	if op == nil {
		return fmt.Errorf("operation to edit is nil")
	}
	if schemaRef == nil {
		return fmt.Errorf("operation response to body to add is nil")
	}
	statusCode = strings.TrimSpace(statusCode)
	description = strings.TrimSpace(description)
	contentType = strings.ToLower(strings.TrimSpace(contentType))
	if statusCode == "" || contentType == "" {
		return fmt.Errorf("status code [%s] or content type [%s] is empty", statusCode, contentType)
	}
	if op.Responses[statusCode] == nil {
		op.Responses[statusCode] = &oas3.ResponseRef{}
	}
	if len(op.Responses[statusCode].Ref) > 0 {
		return fmt.Errorf("response is a reference and not actual")
	}
	if op.Responses[statusCode].Value == nil {
		op.Responses[statusCode].Value = &oas3.Response{
			Description: &description,
		}
	}
	if op.Responses[statusCode].Value.Content == nil {
		op.Responses[statusCode].Value.Content = oas3.NewContent()
	}
	op.Responses[statusCode].Value.Content[contentType] = oas3.NewMediaType().WithSchemaRef(schemaRef)
	return nil
}

func operationAddExternalDocs(op *oas3.Operation, docURL, docDescription string, preserveIfReqEmpty bool) error {
	if op == nil {
		return fmt.Errorf("operation to edit is nil")
	}
	docURL = strings.TrimSpace(docURL)
	docDescription = strings.TrimSpace(docDescription)
	if len(docURL) > 0 || len(docDescription) > 0 {
		if preserveIfReqEmpty {
			if op.ExternalDocs == nil {
				op.ExternalDocs = &oas3.ExternalDocs{}
			}
			if len(docURL) > 0 {
				op.ExternalDocs.URL = docURL
			}
			if len(docDescription) > 0 {
				op.ExternalDocs.Description = docDescription
			}
		} else {
			op.ExternalDocs = &oas3.ExternalDocs{
				Description: docDescription,
				URL:         docURL}
		}
	}
	return nil
}

func SpecOperationsCount(spec *oas3.Swagger) uint {
	count := uint(0)
	openapi3.VisitOperations(spec, func(skipPath, skipMethod string, op *oas3.Operation) {
		count++
	})
	return count
}

func SpecSetOperation(spec *oas3.Swagger, path, method string, op oas3.Operation) {
	pathItem, ok := spec.Paths[path]
	if !ok {
		pathItem = &oas3.PathItem{}
	}
	method = strings.ToUpper(strings.TrimSpace(method))
	switch method {
	case http.MethodGet:
		pathItem.Get = &op
	case http.MethodPost:
		pathItem.Post = &op
	case http.MethodPut:
		pathItem.Put = &op
	case http.MethodPatch:
		pathItem.Patch = &op
	}

}

func SpecOperationIds(spec *oas3.Swagger) map[string]int {
	msi := map[string]int{}
	openapi3.VisitOperations(spec, func(skipPath, skipMethod string, op *oas3.Operation) {
		op.OperationID = strings.TrimSpace(op.OperationID)
		if _, ok := msi[op.OperationID]; !ok {
			msi[op.OperationID] = 0
		}
		msi[op.OperationID]++
	})
	return msi
}

func SpecOperationIdsFromSummaries(spec *oas3.Swagger, errorOnEmpty bool) error {
	empty := []string{}
	openapi3.VisitOperations(spec, func(path, method string, op *oas3.Operation) {
		op.Summary = strings.Join(strings.Split(op.Summary, " "), " ")
		op.OperationID = op.Summary
		if len(op.OperationID) == 0 {
			empty = append(empty, path+" "+method)
		}
	})
	if errorOnEmpty && len(empty) > 0 {
		return fmt.Errorf("no_opid: [%s]", strings.Join(empty, ", "))
	}
	return nil
}

func SpecAddCustomProperties(spec *oas3.Swagger, custom map[string]interface{}, addToOperations, addToSchemas bool) {
	if spec == nil || len(custom) == 0 {
		return
	}
	if addToOperations {
		openapi3.VisitOperations(spec, func(skipPath, skipMethod string, op *oas3.Operation) {
			for key, val := range custom {
				op.Extensions[key] = val
			}
		})
	}
	if addToSchemas {
		for _, schema := range spec.Components.Schemas {
			if schema.Value != nil {
				for key, val := range custom {
					schema.Value.Extensions[key] = val
				}
			}
		}
	}
}

func SpecAddOperationMetas(spec *oas3.Swagger, metas map[string]openapi3.OperationMeta, overwrite bool) {
	if spec == nil || len(metas) == 0 {
		return
	}
	openapi3.VisitOperations(spec, func(skipPath, skipMethod string, op *oas3.Operation) {
		if op == nil {
			return
		}
		opMeta, ok := metas[op.OperationID]
		if !ok {
			return
		}
		opMeta.TrimSpace()
		writeDocs := false
		writeScopes := false
		writeThrottling := false
		if overwrite {
			writeDocs = true
			writeScopes = true
			writeThrottling = true
		}
		if writeDocs {
			operationAddExternalDocs(op, opMeta.DocsURL, opMeta.DocsDescription, true)
		}
		if writeScopes {
			if len(opMeta.SecurityScopes) > 0 {
				op.Security = &oas3.SecurityRequirements{
					map[string][]string{"oauth": opMeta.SecurityScopes},
				}
			} else {
				op.Security = nil
			}
		}
		if writeThrottling {
			if op.ExtensionProps.Extensions == nil {
				op.ExtensionProps.Extensions = map[string]interface{}{}
			}
			op.ExtensionProps.Extensions[openapi3.XThrottlingGroup] = opMeta.XThrottlingGroup
		}
	})
}

type OperationMoreSet struct {
	OperationMores []OperationMore
}

type OperationMore struct {
	UrlPath   string
	Method    string
	Operation *oas3.Operation
}

func QueryOperationsByTags(spec *oas3.Swagger, tags []string) *OperationMoreSet {
	tagsWantMatch := map[string]int{}
	for _, tag := range tags {
		tagsWantMatch[tag] = 1
	}
	opmSet := &OperationMoreSet{OperationMores: []OperationMore{}}
	// for path, pathInfo := range spec.Paths {
	openapi3.VisitOperations(spec, func(url, method string, op *oas3.Operation) {
		if op == nil {
			return
		}
		for _, tagTry := range op.Tags {
			if _, ok := tagsWantMatch[tagTry]; ok {
				opmSet.OperationMores = append(opmSet.OperationMores,
					OperationMore{
						UrlPath:   url,
						Method:    method,
						Operation: op})
				return
			}
		}
	})
	// }
	return opmSet
}
<|endoftext|>


<commit_before>package ipam

import (
	"fmt"
	"math/rand"
	"sync"
	"testing"
	"time"

	"github.com/weaveworks/mesh"

	"github.com/stretchr/testify/require"
	"github.com/weaveworks/weave/net/address"
	"github.com/weaveworks/weave/testing/gossip"
)

type mockMessage struct {
	dst     mesh.PeerName
	msgType byte
	buf     []byte
}

func (m *mockMessage) String() string {
	return fmt.Sprintf("-> %s [%x]", m.dst, m.buf)
}

func toStringArray(messages []mockMessage) []string {
	out := make([]string, len(messages))
	for i := range out {
		out[i] = messages[i].String()
	}
	return out
}

type mockGossipComms struct {
	sync.RWMutex
	*testing.T
	name     string
	messages []mockMessage
}

func (m *mockGossipComms) String() string {
	m.RLock()
	defer m.RUnlock()
	return fmt.Sprintf("[mockGossipComms %s]", m.name)
}

// Note: this style of verification, using equalByteBuffer, requires
// that the contents of messages are never re-ordered.  Which, for instance,
// requires they are not based off iterating through a map.

func (m *mockGossipComms) GossipBroadcast(update mesh.GossipData) {
	m.Lock()
	defer m.Unlock()
	buf := []byte{}
	if len(m.messages) == 0 {
		require.FailNow(m, fmt.Sprintf("%s: Gossip broadcast message unexpected: \n%x", m.name, buf))
	} else if msg := m.messages[0]; msg.dst != mesh.UnknownPeerName {
		require.FailNow(m, fmt.Sprintf("%s: Expected Gossip message to %s but got broadcast", m.name, msg.dst))
	} else if msg.buf != nil && !equalByteBuffer(msg.buf, buf) {
		require.FailNow(m, fmt.Sprintf("%s: Gossip message not sent as expected: \nwant: %x\ngot : %x", m.name, msg.buf, buf))
	} else {
		// Swallow this message
		m.messages = m.messages[1:]
	}
}

func equalByteBuffer(a, b []byte) bool {
	if len(a) != len(b) {
		return false
	}
	for i := 0; i < len(a); i++ {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

func (m *mockGossipComms) GossipUnicast(dstPeerName mesh.PeerName, buf []byte) error {
	m.Lock()
	defer m.Unlock()
	if len(m.messages) == 0 {
		require.FailNow(m, fmt.Sprintf("%s: Gossip message to %s unexpected: \n%s", m.name, dstPeerName, buf))
	} else if msg := m.messages[0]; msg.dst == mesh.UnknownPeerName {
		require.FailNow(m, fmt.Sprintf("%s: Expected Gossip broadcast message but got dest %s", m.name, dstPeerName))
	} else if msg.dst != dstPeerName {
		require.FailNow(m, fmt.Sprintf("%s: Expected Gossip message to %s but got dest %s", m.name, msg.dst, dstPeerName))
	} else if buf[0] != msg.msgType {
		require.FailNow(m, fmt.Sprintf("%s: Expected Gossip message of type %d but got type %d", m.name, msg.msgType, buf[0]))
	} else if msg.buf != nil && !equalByteBuffer(msg.buf, buf[1:]) {
		require.FailNow(m, fmt.Sprintf("%s: Gossip message not sent as expected: \nwant: %x\ngot : %x", m.name, msg.buf, buf[1:]))
	} else {
		// Swallow this message
		m.messages = m.messages[1:]
	}
	return nil
}

func ExpectMessage(alloc *Allocator, dst string, msgType byte, buf []byte) {
	m := alloc.gossip.(*mockGossipComms)
	dstPeerName, _ := mesh.PeerNameFromString(dst)
	m.Lock()
	m.messages = append(m.messages, mockMessage{dstPeerName, msgType, buf})
	m.Unlock()
}

func ExpectBroadcastMessage(alloc *Allocator, buf []byte) {
	m := alloc.gossip.(*mockGossipComms)
	m.Lock()
	m.messages = append(m.messages, mockMessage{mesh.UnknownPeerName, 0, buf})
	m.Unlock()
}

func CheckAllExpectedMessagesSent(allocs ...*Allocator) {
	for _, alloc := range allocs {
		m := alloc.gossip.(*mockGossipComms)
		m.RLock()
		if len(m.messages) > 0 {
			require.FailNow(m, fmt.Sprintf("%s: Gossip message(s) not sent as expected: \n%x", m.name, m.messages))
		}
		m.RUnlock()
	}
}

type mockDB struct{}

func (d *mockDB) Load(_ string, _ interface{}) error { return nil }
func (d *mockDB) Save(_ string, _ interface{}) error { return nil }

func makeAllocator(name string, cidrStr string, quorum uint) (*Allocator, address.CIDR) {
	peername, err := mesh.PeerNameFromString(name)
	if err != nil {
		panic(err)
	}

	cidr, err := address.ParseCIDR(cidrStr)
	if err != nil {
		panic(err)
	}

	config := Config{peername, mesh.PeerUID(rand.Int63()),
		"nick-" + name, cidr.Range(), quorum, new(mockDB), func(mesh.PeerName) bool { return true }}

	alloc := NewAllocator(config)

	return alloc, cidr
}

func makeAllocatorWithMockGossip(t *testing.T, name string, universeCIDR string, quorum uint) (*Allocator, address.CIDR) {
	alloc, subnet := makeAllocator(name, universeCIDR, quorum)
	gossip := &mockGossipComms{T: t, name: name}
	alloc.SetInterfaces(gossip)
	alloc.Start()
	return alloc, subnet
}

func (alloc *Allocator) claimRingForTesting(allocs ...*Allocator) {
	peers := []mesh.PeerName{alloc.ourName}
	for _, alloc2 := range allocs {
		peers = append(peers, alloc2.ourName)
	}
	alloc.ring.ClaimForPeers(normalizeConsensus(peers))
	alloc.space.AddRanges(alloc.ring.OwnedRanges())
}

func (alloc *Allocator) NumFreeAddresses(r address.Range) address.Offset {
	resultChan := make(chan address.Offset)
	alloc.actionChan <- func() {
		resultChan <- alloc.space.NumFreeAddressesInRange(r)
	}
	return <-resultChan
}

// Check whether or not something was sent on a channel
func AssertSent(t *testing.T, ch <-chan bool) {
	timeout := time.After(10 * time.Second)
	select {
	case <-ch:
		// This case is ok
	case <-timeout:
		require.FailNow(t, "Nothing sent on channel")
	}
}

func AssertNothingSent(t *testing.T, ch <-chan bool) {
	select {
	case val := <-ch:
		require.FailNow(t, fmt.Sprintf("Unexpected value on channel: %v", val))
	default:
		// no message received
	}
}

func AssertNothingSentErr(t *testing.T, ch <-chan error) {
	select {
	case val := <-ch:
		require.FailNow(t, fmt.Sprintf("Unexpected value on channel: %v", val))
	default:
		// no message received
	}
}

func makeNetworkOfAllocators(size int, cidr string) ([]*Allocator, *gossip.TestRouter, address.CIDR) {
	gossipRouter := gossip.NewTestRouter(0.0)
	allocs := make([]*Allocator, size)
	var subnet address.CIDR

	for i := 0; i < size; i++ {
		var alloc *Allocator
		alloc, subnet = makeAllocator(fmt.Sprintf("%02d:00:00:02:00:00", i),
			cidr, uint(size/2+1))
		alloc.SetInterfaces(gossipRouter.Connect(alloc.ourName, alloc))
		alloc.Start()
		allocs[i] = alloc
	}

	allocs[size-1].gossip.GossipBroadcast(allocs[size-1].Gossip())
	gossipRouter.Flush()
	return allocs, gossipRouter, subnet
}

func stopNetworkOfAllocators(allocs []*Allocator) {
	for _, alloc := range allocs {
		alloc.Stop()
	}
}
<commit_msg>cosmetic(ish) refactor<commit_after>package ipam

import (
	"fmt"
	"math/rand"
	"sync"
	"testing"
	"time"

	"github.com/weaveworks/mesh"

	"github.com/stretchr/testify/require"
	"github.com/weaveworks/weave/net/address"
	"github.com/weaveworks/weave/testing/gossip"
)

type mockMessage struct {
	dst     mesh.PeerName
	msgType byte
	buf     []byte
}

func (m *mockMessage) String() string {
	return fmt.Sprintf("-> %s [%x]", m.dst, m.buf)
}

func toStringArray(messages []mockMessage) []string {
	out := make([]string, len(messages))
	for i := range out {
		out[i] = messages[i].String()
	}
	return out
}

type mockGossipComms struct {
	sync.RWMutex
	*testing.T
	name     string
	messages []mockMessage
}

func (m *mockGossipComms) String() string {
	m.RLock()
	defer m.RUnlock()
	return fmt.Sprintf("[mockGossipComms %s]", m.name)
}

// Note: this style of verification, using equalByteBuffer, requires
// that the contents of messages are never re-ordered.  Which, for instance,
// requires they are not based off iterating through a map.

func (m *mockGossipComms) GossipBroadcast(update mesh.GossipData) {
	m.Lock()
	defer m.Unlock()
	buf := []byte{}
	if len(m.messages) == 0 {
		require.FailNow(m, fmt.Sprintf("%s: Gossip broadcast message unexpected: \n%x", m.name, buf))
	} else if msg := m.messages[0]; msg.dst != mesh.UnknownPeerName {
		require.FailNow(m, fmt.Sprintf("%s: Expected Gossip message to %s but got broadcast", m.name, msg.dst))
	} else if msg.buf != nil && !equalByteBuffer(msg.buf, buf) {
		require.FailNow(m, fmt.Sprintf("%s: Gossip message not sent as expected: \nwant: %x\ngot : %x", m.name, msg.buf, buf))
	} else {
		// Swallow this message
		m.messages = m.messages[1:]
	}
}

func equalByteBuffer(a, b []byte) bool {
	if len(a) != len(b) {
		return false
	}
	for i := 0; i < len(a); i++ {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

func (m *mockGossipComms) GossipUnicast(dstPeerName mesh.PeerName, buf []byte) error {
	m.Lock()
	defer m.Unlock()
	if len(m.messages) == 0 {
		require.FailNow(m, fmt.Sprintf("%s: Gossip message to %s unexpected: \n%s", m.name, dstPeerName, buf))
	} else if msg := m.messages[0]; msg.dst == mesh.UnknownPeerName {
		require.FailNow(m, fmt.Sprintf("%s: Expected Gossip broadcast message but got dest %s", m.name, dstPeerName))
	} else if msg.dst != dstPeerName {
		require.FailNow(m, fmt.Sprintf("%s: Expected Gossip message to %s but got dest %s", m.name, msg.dst, dstPeerName))
	} else if buf[0] != msg.msgType {
		require.FailNow(m, fmt.Sprintf("%s: Expected Gossip message of type %d but got type %d", m.name, msg.msgType, buf[0]))
	} else if msg.buf != nil && !equalByteBuffer(msg.buf, buf[1:]) {
		require.FailNow(m, fmt.Sprintf("%s: Gossip message not sent as expected: \nwant: %x\ngot : %x", m.name, msg.buf, buf[1:]))
	} else {
		// Swallow this message
		m.messages = m.messages[1:]
	}
	return nil
}

func ExpectMessage(alloc *Allocator, dst string, msgType byte, buf []byte) {
	m := alloc.gossip.(*mockGossipComms)
	dstPeerName, _ := mesh.PeerNameFromString(dst)
	m.Lock()
	m.messages = append(m.messages, mockMessage{dstPeerName, msgType, buf})
	m.Unlock()
}

func ExpectBroadcastMessage(alloc *Allocator, buf []byte) {
	m := alloc.gossip.(*mockGossipComms)
	m.Lock()
	m.messages = append(m.messages, mockMessage{mesh.UnknownPeerName, 0, buf})
	m.Unlock()
}

func CheckAllExpectedMessagesSent(allocs ...*Allocator) {
	for _, alloc := range allocs {
		m := alloc.gossip.(*mockGossipComms)
		m.RLock()
		if len(m.messages) > 0 {
			require.FailNow(m, fmt.Sprintf("%s: Gossip message(s) not sent as expected: \n%x", m.name, m.messages))
		}
		m.RUnlock()
	}
}

type mockDB struct{}

func (d *mockDB) Load(_ string, _ interface{}) error { return nil }
func (d *mockDB) Save(_ string, _ interface{}) error { return nil }

func makeAllocator(name string, cidrStr string, quorum uint) (*Allocator, address.CIDR) {
	peername, err := mesh.PeerNameFromString(name)
	if err != nil {
		panic(err)
	}

	cidr, err := address.ParseCIDR(cidrStr)
	if err != nil {
		panic(err)
	}

	return NewAllocator(Config{
		OurName:     peername,
		OurUID:      mesh.PeerUID(rand.Int63()),
		OurNickname: "nick-" + name,
		Universe:    cidr.Range(),
		Quorum:      quorum,
		Db:          new(mockDB),
		IsKnownPeer: func(mesh.PeerName) bool { return true },
	}), cidr
}

func makeAllocatorWithMockGossip(t *testing.T, name string, universeCIDR string, quorum uint) (*Allocator, address.CIDR) {
	alloc, subnet := makeAllocator(name, universeCIDR, quorum)
	gossip := &mockGossipComms{T: t, name: name}
	alloc.SetInterfaces(gossip)
	alloc.Start()
	return alloc, subnet
}

func (alloc *Allocator) claimRingForTesting(allocs ...*Allocator) {
	peers := []mesh.PeerName{alloc.ourName}
	for _, alloc2 := range allocs {
		peers = append(peers, alloc2.ourName)
	}
	alloc.ring.ClaimForPeers(normalizeConsensus(peers))
	alloc.space.AddRanges(alloc.ring.OwnedRanges())
}

func (alloc *Allocator) NumFreeAddresses(r address.Range) address.Offset {
	resultChan := make(chan address.Offset)
	alloc.actionChan <- func() {
		resultChan <- alloc.space.NumFreeAddressesInRange(r)
	}
	return <-resultChan
}

// Check whether or not something was sent on a channel
func AssertSent(t *testing.T, ch <-chan bool) {
	timeout := time.After(10 * time.Second)
	select {
	case <-ch:
		// This case is ok
	case <-timeout:
		require.FailNow(t, "Nothing sent on channel")
	}
}

func AssertNothingSent(t *testing.T, ch <-chan bool) {
	select {
	case val := <-ch:
		require.FailNow(t, fmt.Sprintf("Unexpected value on channel: %v", val))
	default:
		// no message received
	}
}

func AssertNothingSentErr(t *testing.T, ch <-chan error) {
	select {
	case val := <-ch:
		require.FailNow(t, fmt.Sprintf("Unexpected value on channel: %v", val))
	default:
		// no message received
	}
}

func makeNetworkOfAllocators(size int, cidr string) ([]*Allocator, *gossip.TestRouter, address.CIDR) {
	gossipRouter := gossip.NewTestRouter(0.0)
	allocs := make([]*Allocator, size)
	var subnet address.CIDR

	for i := 0; i < size; i++ {
		var alloc *Allocator
		alloc, subnet = makeAllocator(fmt.Sprintf("%02d:00:00:02:00:00", i),
			cidr, uint(size/2+1))
		alloc.SetInterfaces(gossipRouter.Connect(alloc.ourName, alloc))
		alloc.Start()
		allocs[i] = alloc
	}

	allocs[size-1].gossip.GossipBroadcast(allocs[size-1].Gossip())
	gossipRouter.Flush()
	return allocs, gossipRouter, subnet
}

func stopNetworkOfAllocators(allocs []*Allocator) {
	for _, alloc := range allocs {
		alloc.Stop()
	}
}
<|endoftext|>


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

package runtime

import "unsafe"

// Declarations for runtime services implemented in C or assembly.

const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const
const regSize = 4 << (^uintreg(0) >> 63) // unsafe.Sizeof(uintreg(0)) but an ideal const

// Should be a built-in for unsafe.Pointer?
//go:nosplit
func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
	return unsafe.Pointer(uintptr(p) + x)
}

// noescape hides a pointer from escape analysis.  noescape is
// the identity function but escape analysis doesn't think the
// output depends on the input.  noescape is inlined and currently
// compiles down to a single xor instruction.
// USE CAREFULLY!
//go:nosplit
func noescape(p unsafe.Pointer) unsafe.Pointer {
	x := uintptr(p)
	return unsafe.Pointer(x ^ 0)
}

func getg() *g

//go:noescape
func getcallersp(unsafe.Pointer) uintptr

//go:noescape
func getcallerpc(unsafe.Pointer) uintptr

//go:noescape
func jmpdefer(fv *funcval, argp uintptr)

func return0()

func close(fd int32) int32

func brk(addr uintptr) uintptr

func Exit(x int32) {
	exit(x)
}

// defined in mem{clr,move}_$GOARCH.s
//go:noescape
func memmove(to unsafe.Pointer, frm unsafe.Pointer, length uintptr)

//go:noescape
func memclr(ptr unsafe.Pointer, length uintptr)

//go:noescape
func asmcgocall(fn, arg unsafe.Pointer) int32
<commit_msg>tiny: enter/exitsyscall<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package runtime

import "unsafe"

// Declarations for runtime services implemented in C or assembly.

const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const
const regSize = 4 << (^uintreg(0) >> 63) // unsafe.Sizeof(uintreg(0)) but an ideal const

// Should be a built-in for unsafe.Pointer?
//go:nosplit
func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
	return unsafe.Pointer(uintptr(p) + x)
}

// noescape hides a pointer from escape analysis.  noescape is
// the identity function but escape analysis doesn't think the
// output depends on the input.  noescape is inlined and currently
// compiles down to a single xor instruction.
// USE CAREFULLY!
//go:nosplit
func noescape(p unsafe.Pointer) unsafe.Pointer {
	x := uintptr(p)
	return unsafe.Pointer(x ^ 0)
}

func getg() *g

//go:noescape
func getcallersp(unsafe.Pointer) uintptr

//go:noescape
func getcallerpc(unsafe.Pointer) uintptr

//go:noescape
func jmpdefer(fv *funcval, argp uintptr)

func return0()

func close(fd int32) int32

func brk(addr uintptr) uintptr

func Exit(x int32) {
	exit(x)
}

// defined in mem{clr,move}_$GOARCH.s
//go:noescape
func memmove(to unsafe.Pointer, frm unsafe.Pointer, length uintptr)

//go:noescape
func memclr(ptr unsafe.Pointer, length uintptr)

//go:noescape
func asmcgocall(fn, arg unsafe.Pointer) int32

//go:nosplit
func exitsyscall(dummy int32) {}

//go:nosplit
func entersyscallblock(dummy int32) {}
<|endoftext|>