<commit_before>package main

import (
	"errors"
	"fmt"
	"os"
	"syscall"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/satori/go.uuid"
)

type ebsVolumeDriver struct {
	ec2                 *ec2.EC2
	ec2meta             *ec2metadata.Client
	awsInstanceId       string
	awsRegion           string
	awsAvailabilityZone string
	volumes             map[string]string
}

func NewEbsVolumeDriver() (VolumeDriver, error) {
	d := &ebsVolumeDriver{
		volumes: make(map[string]string),
	}
	d.ec2meta = ec2metadata.New(nil)

	// Fetch AWS information, validating along the way.
	if !d.ec2meta.Available() {
		return nil, errors.New("Not running on an EC2 instance.")
	}
	var err error
	if d.awsInstanceId, err = d.ec2meta.GetMetadata("instance-id"); err != nil {
		return nil, err
	}
	if d.awsRegion, err = d.ec2meta.Region(); err != nil {
		return nil, err
	}
	if d.awsAvailabilityZone, err =
		d.ec2meta.GetMetadata("placement/availability-zone"); err != nil {
		return nil, err
	}

	d.ec2 = ec2.New(aws.NewConfig().WithRegion(d.awsRegion))

	// Print some diagnostic information and then return the driver.
	fmt.Printf("Auto-detected EC2 information:\n")
	fmt.Printf("\tInstanceId        : %v\n", d.awsInstanceId)
	fmt.Printf("\tRegion            : %v\n", d.awsRegion)
	fmt.Printf("\tAvailability Zone : %v\n", d.awsAvailabilityZone)
	return d, nil
}

func (d *ebsVolumeDriver) getEbsInfo(name string) error {
	// Query EC2 to make sure this volume is indeed available.
	volumes, err := d.ec2.DescribeVolumes(&ec2.DescribeVolumesInput{
		VolumeIds: []*string{
			aws.String(name),
		},
	})
	if err != nil {
		return err
	}
	if len(volumes.Volumes) != 1 {
		return errors.New("Cannot find EBS volume.")
	}

	// TODO: check that it's in the right region.

	return nil
}

func (d *ebsVolumeDriver) Create(name string) error {
	m, exists := d.volumes[name]
	if exists {
		// Docker won't always cleanly remove entries.  It's okay so long
		// as the target isn't already mounted by someone else.
		if m != "" {
			return errors.New("Name already in use.")
		}
	}

	d.volumes[name] = ""
	return nil
}

func (d *ebsVolumeDriver) Mount(name string) (string, error) {
	m, exists := d.volumes[name]
	if !exists {
		return "", errors.New("Name not found.")
	}

	if m != "" {
		return "", errors.New("Volume already mounted.")
	}

	return d.doMount(name)
}

func (d *ebsVolumeDriver) Path(name string) (string, error) {
	m, exists := d.volumes[name]
	if !exists {
		return "", errors.New("Name not found.")
	}

	if m == "" {
		return "", errors.New("Volume not mounted.")
	}

	return m, nil
}

func (d *ebsVolumeDriver) Remove(name string) error {
	m, exists := d.volumes[name]
	if !exists {
		return errors.New("Name not found.")
	}

	// If the volume is still mounted, unmount it before removing it.
	if m != "" {
		err := d.doUnmount(name)
		if err != nil {
			return err
		}
	}

	delete(d.volumes, name)
	return nil
}

func (d *ebsVolumeDriver) Unmount(name string) error {
	m, exists := d.volumes[name]
	if !exists {
		return errors.New("Name not found.")
	}

	// If the volume is mounted, go ahead and unmount it.  Ignore requests
	// to unmount volumes that aren't actually mounted.
	if m != "" {
		err := d.doUnmount(name)
		if err != nil {
			return err
		}
	}

	return nil
}

func (d *ebsVolumeDriver) doMount(name string) (string, error) {
	// First, attach the EBS device to the current EC2 instance.
	dev, err := d.attachVolume(name)
	if err != nil {
		return "", err
	}

	// Now auto-generate a random mountpoint.
	mnt := "/mnt/blocker/" + uuid.NewV4().String()

	// Ensure the directory /mnt/blocker/<m> exists.
	if err := os.MkdirAll(mnt, os.ModeDir|0700); err != nil {
		return "", err
	}
	if stat, err := os.Stat(mnt); err != nil || !stat.IsDir() {
		return "", fmt.Errorf("Mountpoint %v is not a directory: %v", mnt, err)
	}

	// Now go ahead and mount the EBS device to the desired mountpoint.
	// TODO: support encrypted filesystems.
	// TODO: detect and auto-format unformatted filesystems.
	// TODO: permit the filesystem type in the name; or auto-detect.
	if err := syscall.Mount(dev, mnt, "ext4", 0, ""); err != nil {
		return "", fmt.Errorf("Mounting device %v to %v failed: %v", dev, mnt, err)
	}

	// And finally set and return it.
	d.volumes[name] = mnt
	return mnt, nil
}

func (d *ebsVolumeDriver) attachVolume(name string) (string, error) {
	// Now find the first free device to attach the EBS volume to.  See
	// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/device_naming.html
	// for recommended naming scheme (/dev/sd[f-p]).
	for _, c := range "fghijklmnop" {
		dev := "/dev/sd" + string(c)

		// TODO: we could check locally first to eliminate a few network
		//     roundtrips in the event that some devices are used.  Even if we
		//     did that, however, we'd need the checks regarding the AWS request
		//     failing below, because of TOCTOU.

		if _, err := d.ec2.AttachVolume(&ec2.AttachVolumeInput{
			Device:     aws.String(dev),
			InstanceId: aws.String(d.awsInstanceId),
			VolumeId:   aws.String(name),
		}); err != nil {
			if awsErr, ok := err.(awserr.Error); ok &&
				awsErr.Code() == "InvalidParameterValue" {
				// If AWS is simply reporting that the device is already in
				// use, then go ahead and check the next one.
				continue
			}

			return "", err
		}

		fmt.Printf("Attached EBS volume %v to %v:%v.\n", name, d.awsInstanceId, dev)
		return dev, nil
	}

	return "", errors.New("No devices available for attach: /dev/sd[f-p] taken.")
}

func (d *ebsVolumeDriver) doUnmount(name string) error {
	mnt := d.volumes[name]

	// First unmount the device.
	if err := syscall.Unmount(mnt, 0); err != nil {
		return err
	}

	// Remove the mountpoint from the filesystem.
	if err := os.Remove(mnt); err != nil {
		return err
	}

	// Detach the EBS volume from this AWS instance.
	if err := d.detachVolume(name); err != nil {
		return err
	}

	// Finally clear out the slot and return.
	d.volumes[name] = ""
	return nil
}

func (d *ebsVolumeDriver) detachVolume(name string) error {
	if _, err := d.ec2.DetachVolume(&ec2.DetachVolumeInput{
		InstanceId: aws.String(d.awsInstanceId),
		VolumeId:   aws.String(name),
	}); err != nil {
		return err
	}

	fmt.Printf("Detached EBS volume %v from %v.", name, d.awsInstanceId)
	return nil
}
<commit_msg>A few attach and mount fixes<commit_after>package main

import (
	"errors"
	"fmt"
	"os"
	"os/exec"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/satori/go.uuid"
)

type ebsVolumeDriver struct {
	ec2                 *ec2.EC2
	ec2meta             *ec2metadata.Client
	awsInstanceId       string
	awsRegion           string
	awsAvailabilityZone string
	volumes             map[string]string
}

func NewEbsVolumeDriver() (VolumeDriver, error) {
	d := &ebsVolumeDriver{
		volumes: make(map[string]string),
	}
	d.ec2meta = ec2metadata.New(nil)

	// Fetch AWS information, validating along the way.
	if !d.ec2meta.Available() {
		return nil, errors.New("Not running on an EC2 instance.")
	}
	var err error
	if d.awsInstanceId, err = d.ec2meta.GetMetadata("instance-id"); err != nil {
		return nil, err
	}
	if d.awsRegion, err = d.ec2meta.Region(); err != nil {
		return nil, err
	}
	if d.awsAvailabilityZone, err =
		d.ec2meta.GetMetadata("placement/availability-zone"); err != nil {
		return nil, err
	}

	d.ec2 = ec2.New(aws.NewConfig().WithRegion(d.awsRegion))

	// Print some diagnostic information and then return the driver.
	fmt.Printf("Auto-detected EC2 information:\n")
	fmt.Printf("\tInstanceId        : %v\n", d.awsInstanceId)
	fmt.Printf("\tRegion            : %v\n", d.awsRegion)
	fmt.Printf("\tAvailability Zone : %v\n", d.awsAvailabilityZone)
	return d, nil
}

func (d *ebsVolumeDriver) getEbsInfo(name string) error {
	// Query EC2 to make sure this volume is indeed available.
	volumes, err := d.ec2.DescribeVolumes(&ec2.DescribeVolumesInput{
		VolumeIds: []*string{
			aws.String(name),
		},
	})
	if err != nil {
		return err
	}
	if len(volumes.Volumes) != 1 {
		return errors.New("Cannot find EBS volume.")
	}

	// TODO: check that it's in the right region.

	return nil
}

func (d *ebsVolumeDriver) Create(name string) error {
	m, exists := d.volumes[name]
	if exists {
		// Docker won't always cleanly remove entries.  It's okay so long
		// as the target isn't already mounted by someone else.
		if m != "" {
			return errors.New("Name already in use.")
		}
	}

	d.volumes[name] = ""
	return nil
}

func (d *ebsVolumeDriver) Mount(name string) (string, error) {
	m, exists := d.volumes[name]
	if !exists {
		return "", errors.New("Name not found.")
	}

	if m != "" {
		return "", errors.New("Volume already mounted.")
	}

	return d.doMount(name)
}

func (d *ebsVolumeDriver) Path(name string) (string, error) {
	m, exists := d.volumes[name]
	if !exists {
		return "", errors.New("Name not found.")
	}

	if m == "" {
		return "", errors.New("Volume not mounted.")
	}

	return m, nil
}

func (d *ebsVolumeDriver) Remove(name string) error {
	m, exists := d.volumes[name]
	if !exists {
		return errors.New("Name not found.")
	}

	// If the volume is still mounted, unmount it before removing it.
	if m != "" {
		err := d.doUnmount(name)
		if err != nil {
			return err
		}
	}

	delete(d.volumes, name)
	return nil
}

func (d *ebsVolumeDriver) Unmount(name string) error {
	m, exists := d.volumes[name]
	if !exists {
		return errors.New("Name not found.")
	}

	// If the volume is mounted, go ahead and unmount it.  Ignore requests
	// to unmount volumes that aren't actually mounted.
	if m != "" {
		err := d.doUnmount(name)
		if err != nil {
			return err
		}
	}

	return nil
}

func (d *ebsVolumeDriver) doMount(name string) (string, error) {
	// Auto-generate a random mountpoint.
	mnt := "/mnt/blocker/" + uuid.NewV4().String()

	// Ensure the directory /mnt/blocker/<m> exists.
	if err := os.MkdirAll(mnt, os.ModeDir|0700); err != nil {
		return "", err
	}
	if stat, err := os.Stat(mnt); err != nil || !stat.IsDir() {
		return "", fmt.Errorf("Mountpoint %v is not a directory: %v", mnt, err)
	}

	// Attach the EBS device to the current EC2 instance.
	dev, err := d.attachVolume(name)
	if err != nil {
		return "", err
	}

	// Now go ahead and mount the EBS device to the desired mountpoint.
	// TODO: support encrypted filesystems.
	// TODO: detect and auto-format unformatted filesystems.
	// TODO: permit the filesystem type in the name; or auto-detect.
	if out, err := exec.Command("mount", dev, mnt).CombinedOutput(); err != nil {
		// Make sure to detach the instance before quitting (ignoring errors).
		d.detachVolume(name)

		return "", fmt.Errorf("Mounting device %v to %v failed: %v\n%v",
			dev, mnt, err, string(out))
	}

	// And finally set and return it.
	d.volumes[name] = mnt
	return mnt, nil
}

func (d *ebsVolumeDriver) attachVolume(name string) (string, error) {
	// Now find the first free device to attach the EBS volume to.  See
	// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/device_naming.html
	// for recommended naming scheme (/dev/sd[f-p]).
	for _, c := range "fghijklmnop" {
		dev := "/dev/sd" + string(c)

		// TODO: we could check locally first to eliminate a few network
		//     roundtrips in the event that some devices are used.  Even if we
		//     did that, however, we'd need the checks regarding the AWS request
		//     failing below, because of TOCTOU.

		if _, err := d.ec2.AttachVolume(&ec2.AttachVolumeInput{
			Device:     aws.String(dev),
			InstanceId: aws.String(d.awsInstanceId),
			VolumeId:   aws.String(name),
		}); err != nil {
			if awsErr, ok := err.(awserr.Error); ok &&
				awsErr.Code() == "InvalidParameterValue" {
				// If AWS is simply reporting that the device is already in
				// use, then go ahead and check the next one.
				continue
			}

			return "", err
		}

		fmt.Printf("Attached EBS volume %v to %v:%v.\n", name, d.awsInstanceId, dev)

		if _, err := os.Lstat(dev); os.IsNotExist(err) {
			// On newer Linux kernels, /dev/sd* is mapped to /dev/xvd*.  See
			// if that's the case.
			altdev := "/dev/xvd" + string(c)
			if _, err := os.Lstat(altdev); os.IsNotExist(err) {
				detachVolume(name)
				return "", fmt.Errorf("Device %v is missing after attach.", dev)
			}

			fmt.Printf("    (local device name is %v)\n", altdev)
			dev = altdev
		}

		return dev, nil
	}

	return "", errors.New("No devices available for attach: /dev/sd[f-p] taken.")
}

func (d *ebsVolumeDriver) doUnmount(name string) error {
	mnt := d.volumes[name]

	// First unmount the device.
	if out, err := exec.Command("umount", mnt).CombinedOutput(); err != nil {
		return fmt.Errorf("Unmounting %v failed: %v\n%v", mnt, err, string(out))
	}

	// Remove the mountpoint from the filesystem.
	if err := os.Remove(mnt); err != nil {
		return err
	}

	// Detach the EBS volume from this AWS instance.
	if err := d.detachVolume(name); err != nil {
		return err
	}

	// Finally clear out the slot and return.
	d.volumes[name] = ""
	return nil
}

func (d *ebsVolumeDriver) detachVolume(name string) error {
	if _, err := d.ec2.DetachVolume(&ec2.DetachVolumeInput{
		InstanceId: aws.String(d.awsInstanceId),
		VolumeId:   aws.String(name),
	}); err != nil {
		return err
	}

	fmt.Printf("Detached EBS volume %v from %v.\n", name, d.awsInstanceId)
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/docker/docker/opts"
	flag "github.com/docker/docker/pkg/mflag"
	"github.com/docker/docker/registry"
)

var (
	insecureRegistries = []string{"0.0.0.0/16"}
	timeout            = true
)

func init() {
	flag.BoolVar(&timeout, []string{"-timeout"}, timeout, "allow timeout on the registry session")
	opts.ListVar(&insecureRegistries, []string{"-insecure-registry"}, "Enable insecure communication with specified registries (no certificate verification for HTTPS and enable HTTP fallback) (e.g., localhost:5000 or 10.20.0.0/16) (default to 0.0.0.0/16)")
}

func main() {
	flag.Parse()
	var sessions map[string]*registry.Session
	for _, arg := range os.Args[1:] {
		host, imageName, err := registry.ResolveRepositoryName(arg)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}

		e, err := registry.NewEndpoint(host, insecureRegistries)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		fmt.Printf("Pulling from %s\n", e)

		var session *registry.Session
		if s, ok := sessions[e.String()]; ok {
			session = s
		} else {
			// TODO(vbatts) obviously the auth and http factory shouldn't be nil here
			session, err = registry.NewSession(nil, nil, e, timeout)
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
		}
		rd, err := session.GetRepositoryData(imageName)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		for _, img := range rd.ImgList {
			fmt.Printf("%#v\n", img)
		}
	}
}
<commit_msg>docker-fetch: basically works now<commit_after>package main

import (
	"compress/gzip"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/docker/docker/graph"
	"github.com/docker/docker/opts"
	"github.com/docker/docker/pkg/archive"
	flag "github.com/docker/docker/pkg/mflag"
	"github.com/docker/docker/registry"
)

var (
	insecureRegistries = []string{"0.0.0.0/16"}
	timeout            = true
	debug              = len(os.Getenv("DEBUG")) > 0
	outputStream       = "-"
)

func init() {
	flag.BoolVar(&timeout, []string{"t", "-timeout"}, timeout, "allow timeout on the registry session")
	flag.BoolVar(&debug, []string{"D", "-debug"}, debug, "debugging output")
	flag.StringVar(&outputStream, []string{"o", "-output"}, outputStream, "output to file (default stdout)")
	opts.ListVar(&insecureRegistries, []string{"-insecure-registry"}, "Enable insecure communication with specified registries (no certificate verification for HTTPS and enable HTTP fallback) (e.g., localhost:5000 or 10.20.0.0/16) (default to 0.0.0.0/16)")
}

func main() {
	flag.Parse()
	var (
		sessions     map[string]*registry.Session
		repositories = map[string]graph.Repository{}
	)

	// make tempDir
	tempDir, err := ioutil.TempDir("", "docker-fetch-")
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	defer os.RemoveAll(tempDir)

	for _, arg := range flag.Args() {
		var (
			hostName, imageName, tagName string
			err                          error
		)

		hostName, imageName, err = registry.ResolveRepositoryName(arg)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}

		// set up image and tag
		if strings.Contains(imageName, ":") {
			chunks := strings.SplitN(imageName, ":", 2)
			imageName = chunks[0]
			tagName = chunks[1]
		} else {
			tagName = "latest"
		}

		endpoint, err := registry.NewEndpoint(hostName, insecureRegistries)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		fmt.Fprintf(os.Stderr, "Pulling %s:%s from %s\n", imageName, tagName, endpoint)

		var session *registry.Session
		if s, ok := sessions[endpoint.String()]; ok {
			session = s
		} else {
			// TODO(vbatts) obviously the auth and http factory shouldn't be nil here
			session, err = registry.NewSession(nil, nil, endpoint, timeout)
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
		}

		rd, err := session.GetRepositoryData(imageName)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		if debug {
			fmt.Fprintf(os.Stderr, "%#v\n", rd)
		}
		/*
			for _, img := range rd.ImgList {
				fmt.Fprintf(os.Stderr, "%#v\n", img)
			}
		*/

		// produce the "repositories" file for the archive
		if _, ok := repositories[imageName]; !ok {
			repositories[imageName] = graph.Repository{}
		}

		tags, err := session.GetRemoteTags([]string{endpoint.String()}, imageName, rd.Tokens)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		if hash, ok := tags[tagName]; ok {
			repositories[imageName][tagName] = hash
		}

		imgList, err := session.GetRemoteHistory(repositories[imageName][tagName], endpoint.String(), rd.Tokens)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}

		for _, imgID := range imgList {
			// pull layers and jsons
			buf, _, err := session.GetRemoteImageJSON(imgID, endpoint.String(), rd.Tokens)
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
			if err = os.MkdirAll(filepath.Join(tempDir, imgID), 0755); err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
			fh, err := os.Create(filepath.Join(tempDir, imgID, "json"))
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
			if _, err = fh.Write(buf); err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
			fh.Close()
			if debug {
				fmt.Fprintln(os.Stderr, fh.Name())
			}

			tarRdr, err := session.GetRemoteImageLayer(imgID, endpoint.String(), rd.Tokens, 0)
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
			fh, err = os.Create(filepath.Join(tempDir, imgID, "layer.tar"))
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
			// the body is usually compressed
			gzRdr, err := gzip.NewReader(tarRdr)
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
			if _, err = io.Copy(fh, gzRdr); err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
			if err = gzRdr.Close(); err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
			if err = tarRdr.Close(); err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
			if err = fh.Close(); err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
			if debug {
				fmt.Fprintln(os.Stderr, fh.Name())
			}
		}
	}

	// marshal the "repositories" file for writing out
	if debug {
		fmt.Fprintf(os.Stderr, "%q", repositories)
	}
	buf, err := json.Marshal(repositories)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	fh, err := os.Create(filepath.Join(tempDir, "repositories"))
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	if _, err = fh.Write(buf); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	fh.Close()
	if debug {
		fmt.Fprintln(os.Stderr, fh.Name())
	}

	var output io.WriteCloser
	if outputStream == "-" {
		output = os.Stdout
	} else {
		output, err = os.Create(outputStream)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
	}
	defer output.Close()

	if err = os.Chdir(tempDir); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	tarStream, err := archive.Tar(".", archive.Uncompressed)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	if _, err = io.Copy(output, tarStream); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>// Package us020 allows interfacing with the US020 ultrasonic range finder.
package us020

import (
	"sync"
	"time"

	"github.com/golang/glog"
	"github.com/kidoman/embd"
)

const (
	pulseDelay  = 30000 * time.Nanosecond
	defaultTemp = 25
)

type Thermometer interface {
	Temperature() (float64, error)
}

type nullThermometer struct {
}

func (*nullThermometer) Temperature() (float64, error) {
	return defaultTemp, nil
}

var NullThermometer = &nullThermometer{}

// US020 represents a US020 ultrasonic range finder.
type US020 struct {
	EchoPin, TriggerPin embd.DigitalPin

	Thermometer Thermometer

	speedSound float64

	initialized bool
	mu          sync.RWMutex
}

// New creates a new US020 interface. The bus variable controls
// the I2C bus used to communicate with the device.
func New(echoPin, triggerPin embd.DigitalPin, thermometer Thermometer) *US020 {
	return &US020{EchoPin: echoPin, TriggerPin: triggerPin, Thermometer: thermometer}
}

func (d *US020) setup() error {
	d.mu.RLock()
	if d.initialized {
		d.mu.RUnlock()
		return nil
	}
	d.mu.RUnlock()

	d.mu.Lock()
	defer d.mu.Unlock()

	d.TriggerPin.SetDirection(embd.Out)
	d.EchoPin.SetDirection(embd.In)

	if d.Thermometer == nil {
		d.Thermometer = NullThermometer
	}

	if temp, err := d.Thermometer.Temperature(); err == nil {
		d.speedSound = 331.3 + 0.606*temp

		glog.V(1).Infof("us020: read a temperature of %v, so speed of sound = %v", temp, d.speedSound)
	} else {
		d.speedSound = 340
	}

	d.initialized = true

	return nil
}

// Distance computes the distance of the bot from the closest obstruction.
func (d *US020) Distance() (float64, error) {
	if err := d.setup(); err != nil {
		return 0, err
	}

	glog.V(2).Infof("us020: trigerring pulse")

	// Generate a TRIGGER pulse
	d.TriggerPin.Write(embd.High)
	time.Sleep(pulseDelay)
	d.TriggerPin.Write(embd.Low)

	glog.V(2).Infof("us020: waiting for echo to go high")

	// Wait until ECHO goes high
	for {
		v, err := d.EchoPin.Read()
		if err != nil {
			return 0, err
		}

		if v != embd.Low {
			break
		}
	}

	startTime := time.Now() // Record time when ECHO goes high

	glog.V(2).Infof("us020: waiting for echo to go low")

	// Wait until ECHO goes low
	for {
		v, err := d.EchoPin.Read()
		if err != nil {
			return 0, err
		}

		if v != embd.High {
			break
		}
	}

	duration := time.Since(startTime) // Calculate time lapsed for ECHO to transition from high to low

	// Calculate the distance based on the time computed
	distance := float64(duration.Nanoseconds()) / 10000000 * (d.speedSound / 2)

	return distance, nil
}

// Close.
func (d *US020) Close() error {
	return d.EchoPin.SetDirection(embd.Out)
}
<commit_msg>us020: make the state comparisons clearer<commit_after>// Package us020 allows interfacing with the US020 ultrasonic range finder.
package us020

import (
	"sync"
	"time"

	"github.com/golang/glog"
	"github.com/kidoman/embd"
)

const (
	pulseDelay  = 30000 * time.Nanosecond
	defaultTemp = 25
)

type Thermometer interface {
	Temperature() (float64, error)
}

type nullThermometer struct {
}

func (*nullThermometer) Temperature() (float64, error) {
	return defaultTemp, nil
}

var NullThermometer = &nullThermometer{}

// US020 represents a US020 ultrasonic range finder.
type US020 struct {
	EchoPin, TriggerPin embd.DigitalPin

	Thermometer Thermometer

	speedSound float64

	initialized bool
	mu          sync.RWMutex
}

// New creates a new US020 interface. The bus variable controls
// the I2C bus used to communicate with the device.
func New(echoPin, triggerPin embd.DigitalPin, thermometer Thermometer) *US020 {
	return &US020{EchoPin: echoPin, TriggerPin: triggerPin, Thermometer: thermometer}
}

func (d *US020) setup() error {
	d.mu.RLock()
	if d.initialized {
		d.mu.RUnlock()
		return nil
	}
	d.mu.RUnlock()

	d.mu.Lock()
	defer d.mu.Unlock()

	d.TriggerPin.SetDirection(embd.Out)
	d.EchoPin.SetDirection(embd.In)

	if d.Thermometer == nil {
		d.Thermometer = NullThermometer
	}

	if temp, err := d.Thermometer.Temperature(); err == nil {
		d.speedSound = 331.3 + 0.606*temp

		glog.V(1).Infof("us020: read a temperature of %v, so speed of sound = %v", temp, d.speedSound)
	} else {
		d.speedSound = 340
	}

	d.initialized = true

	return nil
}

// Distance computes the distance of the bot from the closest obstruction.
func (d *US020) Distance() (float64, error) {
	if err := d.setup(); err != nil {
		return 0, err
	}

	glog.V(2).Infof("us020: trigerring pulse")

	// Generate a TRIGGER pulse
	d.TriggerPin.Write(embd.High)
	time.Sleep(pulseDelay)
	d.TriggerPin.Write(embd.Low)

	glog.V(2).Infof("us020: waiting for echo to go high")

	// Wait until ECHO goes high
	for {
		v, err := d.EchoPin.Read()
		if err != nil {
			return 0, err
		}

		if v == embd.High {
			break
		}
	}

	startTime := time.Now() // Record time when ECHO goes high

	glog.V(2).Infof("us020: waiting for echo to go low")

	// Wait until ECHO goes low
	for {
		v, err := d.EchoPin.Read()
		if err != nil {
			return 0, err
		}

		if v == embd.Low {
			break
		}
	}

	duration := time.Since(startTime) // Calculate time lapsed for ECHO to transition from high to low

	// Calculate the distance based on the time computed
	distance := float64(duration.Nanoseconds()) / 10000000 * (d.speedSound / 2)

	return distance, nil
}

// Close.
func (d *US020) Close() error {
	return d.EchoPin.SetDirection(embd.Out)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"os"
	//"math/rand"
	"github.com/stephen-soltesz/go/collection"
	"github.com/stephen-soltesz/go/lineserver"
	"regexp"
	"strconv"
	"strings"
	"time"
)

var configPattern = regexp.MustCompile("axis|reset|exit|color|label")
var configError = errors.New("Matches configPattern")

type CollectorClient struct {
	reader    *bufio.ReadWriter
	collector *collection.Collection
	axis      *collection.Axis
	line      *collection.Line
	id        int
}

func startCollectorServer(host string, port int, collector *collection.Collection) {
	addr := fmt.Sprintf("%s:%d", host, port)
	serv := lineserver.NewServer(addr)
	client_count := 0
	for {
		client_count += 1
		reader, err := serv.Accept()
		if err == io.EOF {
			break
		} else if err != nil {
			// TODO: what other errors can be handled here?
			debugLogger.Println(err)
			panic(err)
		}
		client := CollectorClient{}
		client.reader = reader
		client.collector = collector
		client.id = client_count
		go handleClient(&client)
	}
}

func getNextXvalue(last_value float64) float64 {
	var x float64
	if *timestamp {
		x = float64(time.Now().Unix())
	} else {
		x = last_value + 1.0
	}
	return x
}

func (client *CollectorClient) readSettings(val string) {
	fields := strings.Split(val, ":")
	if len(fields) == 1 {
		// single command
		if fields[0] == "EXIT" {
			os.Exit(0)
		} else if fields[0] == "RESET" {
			fmt.Println("NOT YET SUPPORTED")
		} else {
			// unknown command
			fmt.Println("Unknown command.", fields[0])
		}
	} else if len(fields) >= 2 {
		// this is a key-value setting.
		if fields[0] == "axis" {
			debugLogger.Print("CLIENT: axis name: ", fields[1])
			client.axis = client.collector.GetAxis(fields[1])
			if len(fields) >= 4 {
				client.axis.XLabel = fields[2]
				client.axis.YLabel = fields[3]
			}
		} else if fields[0] == "label" {
			if client.axis != nil {
				debugLogger.Print("CLIENT: label name: ", fields[1])
				client.line = client.axis.GetLine(fields[1])
			}
		} else if fields[0] == "color" {
			if client.line != nil {
				debugLogger.Print("CLIENT: color: ", fields[1])
				client.line.SetColor(fields[1])
			}
		}
	}
}

func (client *CollectorClient) getNextYvalue() (float64, error) {
	val, err := client.reader.ReadString('\n')
	debugLogger.Print("CLIENT: received: ", val)
	if err != nil {
		return 0.0, err
	}
	if len(val) > 0 && !((val[0] >= '0' && val[0] <= '9') || val[0] == '.' || val[0] == '-') {
		// read settings
		debugLogger.Print("CLIENT: reading settings: ", val)
		client.readSettings(strings.TrimSpace(val))
		return client.getNextYvalue()
	} else if y, err := strconv.ParseFloat(strings.TrimSpace(val), 64); err != nil {
		ferr := err.(*strconv.NumError)
		return 0.0, ferr.Err
	} else {
		return y, nil
	}
}

func handleClient(client *CollectorClient) {
	debugLogger.Println("handleClient")

	x := 0.0
	for {
		debugLogger.Println("getting xy vals")
		x = getNextXvalue(x)
		y, err := client.getNextYvalue()
		if err == io.EOF {
			debugLogger.Println("Client EOF")
			break
		} else if err == strconv.ErrSyntax || err == strconv.ErrRange {
			// ignore parse errors.
			debugLogger.Println("Ignoring parse error:", err)
			continue
		} else if err != nil {
			// all other errors. TODO: are any fatal?
			debugLogger.Println(err)
			continue
		}
		if client.axis == nil {
			client.axis = client.collector.GetAxis("default")
		}
		if client.line == nil {
			client.line = client.axis.GetLine(fmt.Sprintf("Thread-%d", client.id))
		}
		client.line.Append(x, y)
	}
}

/*
func randRange(min, max int) float64 {
    y := float64(rand.Intn(max-min))
    return float64(min)+y
}

func newFilter(size int) (func(float64) float64) {
  b := make([]float64, size)
  i := 0
  return func(f float64) float64 {
    b[i%len(b)] = f
    i++
    sum := 0.0
    for _, value := range b {
          sum += value
    }
    return sum/float64(len(b))
  }
}

func generateData(min, max int) {
	axis := collection.Default().GetAxis("default")
	line := axis.AddLine("Thread-gen")
  filt := newFilter(3)
  count := 0.0
  for {
    //ts := float64(time.Now().Unix()/10)
    y := filt(randRange(min, max))
    // mock client: add a new point every second.
    line.Append(count, y)
    count++
    time.Sleep(time.Second)
  }
}
*/
<commit_msg>permit clients to tell lineviewer to exit.<commit_after>package main

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	//"os"
	//"math/rand"
	"github.com/stephen-soltesz/go/collection"
	"github.com/stephen-soltesz/go/lineserver"
	"regexp"
	"strconv"
	"strings"
	"time"
)

var configPattern = regexp.MustCompile("axis|reset|exit|color|label")
var configError = errors.New("Matches configPattern")
var exitEarly = false

type CollectorClient struct {
	reader    *bufio.ReadWriter
	collector *collection.Collection
	axis      *collection.Axis
	line      *collection.Line
	id        int
}

func startCollectorServer(host string, port int, collector *collection.Collection) {
	addr := fmt.Sprintf("%s:%d", host, port)
	serv := lineserver.NewServer(addr)
	client_count := 0
	for {
		client_count += 1
		reader, err := serv.Accept()
		if err == io.EOF {
			break
		} else if err != nil {
			// TODO: what other errors can be handled here?
			debugLogger.Println(err)
			panic(err)
		} else if exitEarly {
			break
		}
		client := CollectorClient{}
		client.reader = reader
		client.collector = collector
		client.id = client_count
		go handleClient(&client)
	}
}

func getNextXvalue(last_value float64) float64 {
	var x float64
	if *timestamp {
		x = float64(time.Now().Unix())
	} else {
		x = last_value + 1.0
	}
	return x
}

func (client *CollectorClient) readSettings(val string) {
	fields := strings.Split(val, ":")
	if len(fields) == 1 {
		// single command
		if fields[0] == "EXIT" {
			fmt.Println("Got EXIT signal")
			exitEarly = true
			return
			//os.Exit(0)
		} else if fields[0] == "RESET" {
			fmt.Println("NOT YET SUPPORTED")
		} else {
			// unknown command
			fmt.Println("Unknown command.", fields[0])
		}
	} else if len(fields) >= 2 {
		// this is a key-value setting.
		if fields[0] == "axis" {
			debugLogger.Print("CLIENT: axis name: ", fields[1])
			client.axis = client.collector.GetAxis(fields[1])
			if len(fields) >= 4 {
				client.axis.XLabel = fields[2]
				client.axis.YLabel = fields[3]
			}
		} else if fields[0] == "label" {
			if client.axis != nil {
				debugLogger.Print("CLIENT: label name: ", fields[1])
				client.line = client.axis.GetLine(fields[1])
			}
		} else if fields[0] == "color" {
			if client.line != nil {
				debugLogger.Print("CLIENT: color: ", fields[1])
				client.line.SetColor(fields[1])
			}
		}
	}
}

func (client *CollectorClient) getNextYvalue() (float64, error) {
	val, err := client.reader.ReadString('\n')
	debugLogger.Print("CLIENT: received: ", val)
	if err != nil {
		return 0.0, err
	}
	if len(val) > 0 && !((val[0] >= '0' && val[0] <= '9') || val[0] == '.' || val[0] == '-') {
		// read settings
		debugLogger.Print("CLIENT: reading settings: ", val)
		client.readSettings(strings.TrimSpace(val))
		return client.getNextYvalue()
	} else if y, err := strconv.ParseFloat(strings.TrimSpace(val), 64); err != nil {
		ferr := err.(*strconv.NumError)
		return 0.0, ferr.Err
	} else {
		return y, nil
	}
}

func handleClient(client *CollectorClient) {
	debugLogger.Println("handleClient")

	x := 0.0
	for {
		debugLogger.Println("getting xy vals")
		x = getNextXvalue(x)
		y, err := client.getNextYvalue()
		if err == io.EOF {
			debugLogger.Println("Client EOF")
			break
		} else if err == strconv.ErrSyntax || err == strconv.ErrRange {
			// ignore parse errors.
			debugLogger.Println("Ignoring parse error:", err)
			continue
		} else if err != nil {
			// all other errors. TODO: are any fatal?
			debugLogger.Println(err)
			continue
		}
		if client.axis == nil {
			client.axis = client.collector.GetAxis("default")
		}
		if client.line == nil {
			client.line = client.axis.GetLine(fmt.Sprintf("Thread-%d", client.id))
		}
		client.line.Append(x, y)
	}
}

/*
func randRange(min, max int) float64 {
    y := float64(rand.Intn(max-min))
    return float64(min)+y
}

func newFilter(size int) (func(float64) float64) {
  b := make([]float64, size)
  i := 0
  return func(f float64) float64 {
    b[i%len(b)] = f
    i++
    sum := 0.0
    for _, value := range b {
          sum += value
    }
    return sum/float64(len(b))
  }
}

func generateData(min, max int) {
	axis := collection.Default().GetAxis("default")
	line := axis.AddLine("Thread-gen")
  filt := newFilter(3)
  count := 0.0
  for {
    //ts := float64(time.Now().Unix()/10)
    y := filt(randRange(min, max))
    // mock client: add a new point every second.
    line.Append(count, y)
    count++
    time.Sleep(time.Second)
  }
}
*/
<|endoftext|>


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

// +build !windows

package procfs

import (
	"testing"

	"github.com/google/go-cmp/cmp"
)

func newPInt64(i int64) *int64 {
	return &i
}
func TestZoneinfo(t *testing.T) {
	fs := getProcFixtures(t)
	protection1 := []*int64{newPInt64(0), newPInt64(2877), newPInt64(7826), newPInt64(7826), newPInt64(7826)}
	protection2 := []*int64{newPInt64(0), newPInt64(0), newPInt64(4949), newPInt64(4949), newPInt64(4949)}
	refs := []Zoneinfo{
		{Node: "0", Zone: "", NrFreePages: newPInt64(3952), Min: newPInt64(33), Low: newPInt64(41), High: newPInt64(49), Spanned: newPInt64(4095), Present: newPInt64(3975), Managed: newPInt64(3956), NrActiveAnon: newPInt64(547580), NrInactiveAnon: newPInt64(230981), NrIsolatedAnon: newPInt64(0), NrAnonPages: newPInt64(795576), NrAnonTransparentHugepages: newPInt64(0), NrActiveFile: newPInt64(346282), NrInactiveFile: newPInt64(316904), NrIsolatedFile: newPInt64(0), NrFilePages: newPInt64(761874), NrSlabReclaimable: newPInt64(131220), NrSlabUnreclaimable: newPInt64(47320), NrKernelStack: newPInt64(0), NrMapped: newPInt64(215483), NrDirty: newPInt64(908), NrWriteback: newPInt64(0), NrUnevictable: newPInt64(115467), NrShmem: newPInt64(224925), NrDirtied: newPInt64(8007423), NrWritten: newPInt64(7752121), NumaHit: newPInt64(1), NumaMiss: newPInt64(0), NumaForeign: newPInt64(0), NumaInterleave: newPInt64(0), NumaLocal: newPInt64(1), NumaOther: newPInt64(0), Protection: protection1},
		{Node: "0", Zone: "DMA32", NrFreePages: newPInt64(204252), Min: newPInt64(19510), Low: newPInt64(21059), High: newPInt64(22608), Spanned: newPInt64(1044480), Present: newPInt64(759231), Managed: newPInt64(742806), NrKernelStack: newPInt64(2208), NumaHit: newPInt64(113952967), NumaMiss: newPInt64(0), NumaForeign: newPInt64(0), NumaInterleave: newPInt64(0), NumaLocal: newPInt64(113952967), NumaOther: newPInt64(0), Protection: protection2},
	}
	data, err := fs.Zoneinfo()
	if err != nil {
		t.Fatalf("failed to parse zoneinfo: %v", err)
	}

	for index, ref := range refs {
		want, got := ref, data[index]
		if diff := cmp.Diff(want, got); diff != "" {
			t.Fatalf("unexpected crypto entry (-want +got):\n%s", diff)
		}

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

// +build !windows

package procfs

import (
	"testing"

	"github.com/google/go-cmp/cmp"
)

func TestZoneinfo(t *testing.T) {
	fs := getProcFixtures(t)
	protection1 := []*int64{newPInt64(0), newPInt64(2877), newPInt64(7826), newPInt64(7826), newPInt64(7826)}
	protection2 := []*int64{newPInt64(0), newPInt64(0), newPInt64(4949), newPInt64(4949), newPInt64(4949)}
	refs := []Zoneinfo{
		{Node: "0", Zone: "", NrFreePages: newPInt64(3952), Min: newPInt64(33), Low: newPInt64(41), High: newPInt64(49), Spanned: newPInt64(4095), Present: newPInt64(3975), Managed: newPInt64(3956), NrActiveAnon: newPInt64(547580), NrInactiveAnon: newPInt64(230981), NrIsolatedAnon: newPInt64(0), NrAnonPages: newPInt64(795576), NrAnonTransparentHugepages: newPInt64(0), NrActiveFile: newPInt64(346282), NrInactiveFile: newPInt64(316904), NrIsolatedFile: newPInt64(0), NrFilePages: newPInt64(761874), NrSlabReclaimable: newPInt64(131220), NrSlabUnreclaimable: newPInt64(47320), NrKernelStack: newPInt64(0), NrMapped: newPInt64(215483), NrDirty: newPInt64(908), NrWriteback: newPInt64(0), NrUnevictable: newPInt64(115467), NrShmem: newPInt64(224925), NrDirtied: newPInt64(8007423), NrWritten: newPInt64(7752121), NumaHit: newPInt64(1), NumaMiss: newPInt64(0), NumaForeign: newPInt64(0), NumaInterleave: newPInt64(0), NumaLocal: newPInt64(1), NumaOther: newPInt64(0), Protection: protection1},
		{Node: "0", Zone: "DMA32", NrFreePages: newPInt64(204252), Min: newPInt64(19510), Low: newPInt64(21059), High: newPInt64(22608), Spanned: newPInt64(1044480), Present: newPInt64(759231), Managed: newPInt64(742806), NrKernelStack: newPInt64(2208), NumaHit: newPInt64(113952967), NumaMiss: newPInt64(0), NumaForeign: newPInt64(0), NumaInterleave: newPInt64(0), NumaLocal: newPInt64(113952967), NumaOther: newPInt64(0), Protection: protection2},
	}
	data, err := fs.Zoneinfo()
	if err != nil {
		t.Fatalf("failed to parse zoneinfo: %v", err)
	}

	for index, ref := range refs {
		want, got := ref, data[index]
		if diff := cmp.Diff(want, got); diff != "" {
			t.Fatalf("unexpected crypto entry (-want +got):\n%s", diff)
		}

	}
}
<|endoftext|>


<commit_before>package io

import (
	"fmt"
	"math"
	"strconv"
	"strings"

	artio "github.com/phil-mansfield/go-artio"
	"github.com/phil-mansfield/shellfish/cosmo"
)

const (
	// emulateHubble is used for debugging purposes. I've never had access to
	// a cosmological simulation, so this is necessary. Don't worry: even if
	// this flag is set, an error will still be returned if called on invalid
	// header contents. It will just occur late enough to allow for illustrative
	// logging.
	emulateHubble = true
)

type ARTIOBuffer struct {
	open    bool
	xsBuf   [][3]float32
	msBuf   []float32
	xsBufs  [][][3]float32
	msBufs  [][]float32
	sMasses []float32
	sFlags  []bool // True if the species is "N-BODY" type.
	fileset string
}

func NewARTIOBuffer(fileset string) (VectorBuffer, error) {
	h, err := artio.FilesetOpen(fileset, artio.OpenHeader, artio.NullContext)
	if err != nil {
		return nil, err
	}
	defer h.Close()

	numSpecies := h.GetInt(h.Key("num_particle_species"))[0]

	sMasses := h.GetFloat(h.Key("particle_species_mass"))

	var h100 float64
	if !h.HasKey("hubble") {
		if emulateHubble {
			h100 = 0.7
		} else {
			return nil, fmt.Errorf(
				"ARTIO header does not contain 'hubble' field.",
			)
		}
	} else {
		h100 = h.GetDouble(h.Key("hubble"))[0]
	}
	massUnit := (h100 / cosmo.MSunMks * 1000) *
		h.GetDouble(h.Key("mass_unit"))[0]
	for i := range sMasses {
		sMasses[i] *= float32(massUnit)
	}

	sFlags, err := nBodyFlags(h, fileset)
	if err != nil {
		return nil, err
	}

	return &ARTIOBuffer{
		xsBufs:  make([][][3]float32, numSpecies),
		sMasses: sMasses,
		sFlags:  sFlags,
	}, nil
}

func parseARTIOFilename(fname string) (fileset string, block int, err error) {
	split := strings.LastIndex(fname, ".")
	if split == -1 || split == len(fname)-1 {
		return "", -1, fmt.Errorf(
			"'%s' is not the name of an ARTIO block.", fname,
		)
	}

	fileset, blockString := fname[:split], fname[split+1:]
	block, err = strconv.Atoi(blockString)
	if err != nil {
		return "", -1, fmt.Errorf(
			"'%s' is not the name of an ARTIO block.", fname,
		)
	}

	return fileset, block, nil
}

func (buf *ARTIOBuffer) Read(
	fileNumStr string,
) ([][3]float32, []float32, error) {
	// Open the file.
	if buf.open {
		panic("Buffer already open.")
	}
	buf.open = true

	h, err := artio.FilesetOpen(
		buf.fileset, artio.OpenHeader, artio.NullContext,
	)
	if err != nil {
		return nil, nil, err
	}
	defer h.Close()

	// I'm not sure if this can just be replaced with putting an
	// artio.OpenParticles flag in artio.FilesetOpen(). Someone with more
	// knowledge about ARTIO than me should figure this out.
	err = h.OpenParticles()
	if err != nil {
		return nil, nil, err
	}

	// Flag N_BODY particles.
	flags, err := nBodyFlags(h, buf.fileset)
	if err != nil {
		return nil, nil, err
	}

	// Get SFC range.
	fIdx, err := strconv.Atoi(fileNumStr)
	fileIdxs := h.GetLong(h.Key("particle_file_sfc_index"))
	sfcStart, sfcEnd := fileIdxs[fIdx], fileIdxs[fIdx+1]-1

	// Counts and buffer manipulation. Do the reading.
	sCounts, err := h.CountInRange(sfcStart, sfcEnd)
	totCount := int64(0)
	for i := range sCounts {
		if flags[i] {
			totCount += sCounts[i]
			expandVectors(buf.xsBufs[i][:0], int(sCounts[i]))
			err = h.GetPositionsAt(i, sfcStart, sfcEnd, buf.xsBufs[i])
			if err != nil {
				return nil, nil, err
			}

			expandScalars(buf.msBufs[i][:0], int(sCounts[i]))
			for j := range buf.msBufs[i] {
				buf.msBufs[i][j] = buf.sMasses[i]
			}
		}
	}

	// Copy to output buffer.
	expandVectors(buf.xsBuf, int(totCount))
	expandScalars(buf.msBuf, int(totCount))
	k := 0
	for j := range buf.xsBufs {
		for i := range buf.xsBufs[j] {
			buf.xsBuf[k] = buf.xsBufs[j][i]
			buf.msBuf[k] = buf.msBufs[j][i]
			k++
		}
	}

	var h100 float32
	if !h.HasKey("hubble") {
		if emulateHubble {
			h100 = 0.7
		} else {
			return nil, nil, fmt.Errorf(
				"ARTIO header does not contain 'hubble' field.",
			)
		}
	} else {
		h100 = float32(h.GetDouble(h.Key("hubble"))[0])
	}

	lengthUnit := float32(h100) / (cosmo.MpcMks * 100)
	for i := range buf.xsBuf {
		buf.xsBuf[i][0] *= lengthUnit
		buf.xsBuf[i][1] *= lengthUnit
		buf.xsBuf[i][2] *= lengthUnit
	}

	return buf.xsBuf, buf.msBuf, nil
}

func nBodyFlags(h artio.Fileset, fname string) ([]bool, error) {
	speciesLabels := h.GetString(h.Key("particle_species_labels"))
	isNBody, nBodyCount := make([]bool, len(speciesLabels)), 0
	for i := range isNBody {
		isNBody[i] = speciesLabels[i] == "N-BODY"
		nBodyCount++
	}
	if nBodyCount == 0 {
		return nil, fmt.Errorf("ARTIO fileset '%s' does not contain any "+
			"particle species of type 'N-BODY'.", fname)
	}
	return isNBody, nil
}

func (buf *ARTIOBuffer) Close() {
	if !buf.open {
		panic("Buffer not open.")
	}
	buf.open = false
}

func (buf *ARTIOBuffer) IsOpen() bool {
	return buf.open
}

func (buf *ARTIOBuffer) ReadHeader(fileNumStr string, out *Header) error {
	xs, _, err := buf.Read(fileNumStr)

	h, err := artio.FilesetOpen(
		buf.fileset, artio.OpenHeader, artio.NullContext,
	)
	if err != nil {
		return err
	}
	defer h.Close()

	var h100 float64
	if !h.HasKey("hubble") {
		if emulateHubble {
			h100 = 0.7
		} else {
			return fmt.Errorf(
				"ARTIO header does not contain 'hubble' field.",
			)
		}
	} else {
		h100 = h.GetDouble(h.Key("hubble"))[0]
	}

	out.TotalWidth = h.GetDouble(h.Key("box_size"))[0] *
		(h100 / (cosmo.MpcMks * 100))
	out.Origin, out.Width = boundingBox(xs, out.TotalWidth)
	out.N = int64(len(xs))

	switch {
	case !h.HasKey("auni"):
		return fmt.Errorf("ARTIO header does not contain 'auni' field.")
	case !h.HasKey("OmegaM"):
		return fmt.Errorf("ARTIO header does not contain 'OmegaM' field.")
	case !h.HasKey("OmegaL"):
		return fmt.Errorf("ARTIO header does not contain 'OmegaL' field.")

	}

	out.Cosmo.Z = 1/h.GetDouble(h.Key("auni"))[0] - 1
	out.Cosmo.OmegaM = h.GetDouble(h.Key("OmegaM"))[0]
	out.Cosmo.OmegaL = h.GetDouble(h.Key("OmegaL"))[0]
	out.Cosmo.H100 = h.GetDouble(h.Key("hubble"))[0]

	if out.Cosmo.H100 > 10 {
		panic("Oops, Phil misunderstood the meaning of an ARTIO field. " +
			"Please submit an issue.")
	}

	return nil
}

func (buf *ARTIOBuffer) MinMass() float32 {
	minMass := float32(math.Inf(+1))
	for i := range buf.sMasses {
		if buf.sFlags[i] && buf.sMasses[i] < minMass {
			minMass = buf.sMasses[i]
		}
	}
	return minMass
}
<commit_msg>Fixed file name parsing in artio buffers<commit_after>package io

import (
	"fmt"
	"math"
	"strconv"
	"strings"

	artio "github.com/phil-mansfield/go-artio"
	"github.com/phil-mansfield/shellfish/cosmo"
)

const (
	// emulateHubble is used for debugging purposes. I've never had access to
	// a cosmological simulation, so this is necessary. Don't worry: even if
	// this flag is set, an error will still be returned if called on invalid
	// header contents. It will just occur late enough to allow for illustrative
	// logging.
	emulateHubble = true
)

type ARTIOBuffer struct {
	open    bool
	xsBuf   [][3]float32
	msBuf   []float32
	xsBufs  [][][3]float32
	msBufs  [][]float32
	sMasses []float32
	sFlags  []bool // True if the species is "N-BODY" type.
	fileset string
}

func NewARTIOBuffer(filename string) (VectorBuffer, error) {
	fileset, _, err := parseARTIOFilename(filename)
	if err != nil {
		return nil, err
	}

	h, err := artio.FilesetOpen(fileset, artio.OpenHeader, artio.NullContext)
	if err != nil {
		return nil, err
	}
	defer h.Close()

	numSpecies := h.GetInt(h.Key("num_particle_species"))[0]
	sMasses := h.GetFloat(h.Key("particle_species_mass"))

	var h100 float64
	if !h.HasKey("hubble") {
		if emulateHubble {
			h100 = 0.7
		} else {
			return nil, fmt.Errorf(
				"ARTIO header does not contain 'hubble' field.",
			)
		}
	} else {
		h100 = h.GetDouble(h.Key("hubble"))[0]
	}
	massUnit := (h100 / cosmo.MSunMks * 1000) *
		h.GetDouble(h.Key("mass_unit"))[0]
	for i := range sMasses {
		sMasses[i] *= float32(massUnit)
	}

	sFlags, err := nBodyFlags(h, fileset)
	if err != nil {
		return nil, err
	}

	return &ARTIOBuffer{
		xsBufs:  make([][][3]float32, numSpecies),
		sMasses: sMasses,
		sFlags:  sFlags,
	}, nil
}

func parseARTIOFilename(fname string) (fileset string, block int, err error) {
	split := strings.LastIndex(fname, ".")
	if split == -1 || split == len(fname)-1 {
		return "", -1, fmt.Errorf(
			"'%s' is not the name of an ARTIO block.", fname,
		)
	}

	fileset, blockString := fname[:split], fname[split+1:]
	block, err = strconv.Atoi(strings.Trim(blockString, "p"))
	if err != nil {
		return "", -1, fmt.Errorf(
			"'%s' is not the name of an ARTIO block.", fname,
		)
	}

	return fileset, block, nil
}

func (buf *ARTIOBuffer) Read(
	filename string,
) ([][3]float32, []float32, error) {
	// Open the file.
	if buf.open {
		panic("Buffer already open.")
	}
	buf.open = true

	h, err := artio.FilesetOpen(
		buf.fileset, artio.OpenHeader, artio.NullContext,
	)
	if err != nil {
		return nil, nil, err
	}
	defer h.Close()

	// I'm not sure if this can just be replaced with putting an
	// artio.OpenParticles flag in artio.FilesetOpen(). Someone with more
	// knowledge about ARTIO than me should figure this out.
	err = h.OpenParticles()
	if err != nil {
		return nil, nil, err
	}

	// Flag N_BODY particles.
	flags, err := nBodyFlags(h, buf.fileset)
	if err != nil {
		return nil, nil, err
	}

	// Get SFC range.
	_, fIdx, err := parseARTIOFilename(filename)
	fileIdxs := h.GetLong(h.Key("particle_file_sfc_index"))
	sfcStart, sfcEnd := fileIdxs[fIdx], fileIdxs[fIdx+1]-1

	// Counts and buffer manipulation. Do the reading.
	sCounts, err := h.CountInRange(sfcStart, sfcEnd)
	totCount := int64(0)
	for i := range sCounts {
		if flags[i] {
			totCount += sCounts[i]
			expandVectors(buf.xsBufs[i][:0], int(sCounts[i]))
			err = h.GetPositionsAt(i, sfcStart, sfcEnd, buf.xsBufs[i])
			if err != nil {
				return nil, nil, err
			}

			expandScalars(buf.msBufs[i][:0], int(sCounts[i]))
			for j := range buf.msBufs[i] {
				buf.msBufs[i][j] = buf.sMasses[i]
			}
		}
	}

	// Copy to output buffer.
	expandVectors(buf.xsBuf, int(totCount))
	expandScalars(buf.msBuf, int(totCount))
	k := 0
	for j := range buf.xsBufs {
		for i := range buf.xsBufs[j] {
			buf.xsBuf[k] = buf.xsBufs[j][i]
			buf.msBuf[k] = buf.msBufs[j][i]
			k++
		}
	}

	var h100 float32
	if !h.HasKey("hubble") {
		if emulateHubble {
			h100 = 0.7
		} else {
			return nil, nil, fmt.Errorf(
				"ARTIO header does not contain 'hubble' field.",
			)
		}
	} else {
		h100 = float32(h.GetDouble(h.Key("hubble"))[0])
	}

	lengthUnit := float32(h100) / (cosmo.MpcMks * 100)
	for i := range buf.xsBuf {
		buf.xsBuf[i][0] *= lengthUnit
		buf.xsBuf[i][1] *= lengthUnit
		buf.xsBuf[i][2] *= lengthUnit
	}

	return buf.xsBuf, buf.msBuf, nil
}

func nBodyFlags(h artio.Fileset, fname string) ([]bool, error) {
	speciesLabels := h.GetString(h.Key("particle_species_labels"))
	isNBody, nBodyCount := make([]bool, len(speciesLabels)), 0
	for i := range isNBody {
		isNBody[i] = speciesLabels[i] == "N-BODY"
		nBodyCount++
	}
	if nBodyCount == 0 {
		return nil, fmt.Errorf("ARTIO fileset '%s' does not contain any "+
			"particle species of type 'N-BODY'.", fname)
	}
	return isNBody, nil
}

func (buf *ARTIOBuffer) Close() {
	if !buf.open {
		panic("Buffer not open.")
	}
	buf.open = false
}

func (buf *ARTIOBuffer) IsOpen() bool {
	return buf.open
}

func (buf *ARTIOBuffer) ReadHeader(fileNumStr string, out *Header) error {
	xs, _, err := buf.Read(fileNumStr)

	h, err := artio.FilesetOpen(
		buf.fileset, artio.OpenHeader, artio.NullContext,
	)
	if err != nil {
		return err
	}
	defer h.Close()

	var h100 float64
	if !h.HasKey("hubble") {
		if emulateHubble {
			h100 = 0.7
		} else {
			return fmt.Errorf(
				"ARTIO header does not contain 'hubble' field.",
			)
		}
	} else {
		h100 = h.GetDouble(h.Key("hubble"))[0]
	}

	out.TotalWidth = h.GetDouble(h.Key("box_size"))[0] *
		(h100 / (cosmo.MpcMks * 100))
	out.Origin, out.Width = boundingBox(xs, out.TotalWidth)
	out.N = int64(len(xs))

	switch {
	case !h.HasKey("auni"):
		return fmt.Errorf("ARTIO header does not contain 'auni' field.")
	case !h.HasKey("OmegaM"):
		return fmt.Errorf("ARTIO header does not contain 'OmegaM' field.")
	case !h.HasKey("OmegaL"):
		return fmt.Errorf("ARTIO header does not contain 'OmegaL' field.")

	}

	out.Cosmo.Z = 1/h.GetDouble(h.Key("auni"))[0] - 1
	out.Cosmo.OmegaM = h.GetDouble(h.Key("OmegaM"))[0]
	out.Cosmo.OmegaL = h.GetDouble(h.Key("OmegaL"))[0]
	out.Cosmo.H100 = h.GetDouble(h.Key("hubble"))[0]

	if out.Cosmo.H100 > 10 {
		panic("Oops, Phil misunderstood the meaning of an ARTIO field. " +
			"Please submit an issue.")
	}

	return nil
}

func (buf *ARTIOBuffer) MinMass() float32 {
	minMass := float32(math.Inf(+1))
	for i := range buf.sMasses {
		if buf.sFlags[i] && buf.sMasses[i] < minMass {
			minMass = buf.sMasses[i]
		}
	}
	return minMass
}
<|endoftext|>


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

package remote

import (
	"fmt"
	"log"

	"net/http"

	"github.com/IBM/ubiquity/resources"

	"reflect"

	"github.com/IBM/ubiquity/remote/mounter"
	"github.com/IBM/ubiquity/utils"
	"io/ioutil"
)

type remoteClient struct {
	logger            *log.Logger
	isActivated       bool
	isMounted         bool
	httpClient        *http.Client
	storageApiURL     string
	config            resources.UbiquityPluginConfig
	mounterPerBackend map[string]resources.Mounter
}

func (s *remoteClient) Activate(activateRequest resources.ActivateRequest) error {
	s.logger.Println("remoteClient: Activate start")
	defer s.logger.Println("remoteClient: Activate end")

	if s.isActivated {
		return nil
	}

	// call remote activate
	activateURL := utils.FormatURL(s.storageApiURL, "activate")
	activateRequest.CredentialInfo = s.config.CredentialInfo
	response, err := utils.HttpExecute(s.httpClient, s.logger, "POST", activateURL, activateRequest)
	if err != nil {
		s.logger.Printf("Error in activate remote call %#v", err)
		return fmt.Errorf("Error in activate remote call")
	}

	if response.StatusCode != http.StatusOK {
		s.logger.Printf("Error in activate remote call %#v\n", response)
		return utils.ExtractErrorResponse(response)
	}
	s.logger.Println("remoteClient: Activate success")
	s.isActivated = true
	return nil
}

func (s *remoteClient) CreateVolume(createVolumeRequest resources.CreateVolumeRequest) error {
	s.logger.Println("remoteClient: create start")
	defer s.logger.Println("remoteClient: create end")

	createRemoteURL := utils.FormatURL(s.storageApiURL, "volumes")

	if reflect.DeepEqual(s.config.SpectrumNfsRemoteConfig, resources.SpectrumNfsRemoteConfig{}) == false {
		createVolumeRequest.Opts["nfsClientConfig"] = s.config.SpectrumNfsRemoteConfig.ClientConfig
	}

	createVolumeRequest.CredentialInfo = s.config.CredentialInfo
	response, err := utils.HttpExecute(s.httpClient, s.logger, "POST", createRemoteURL, createVolumeRequest)
	if err != nil {
		s.logger.Printf("Error in create volume remote call %s", err.Error())
		return fmt.Errorf("Error in create volume remote call(http error)")
	}
	_, err = ioutil.ReadAll(response.Body)
	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		s.logger.Printf("Error in create volume remote call %#v", response)
		return utils.ExtractErrorResponse(response)
	}

	return nil
}

func (s *remoteClient) RemoveVolume(removeVolumeRequest resources.RemoveVolumeRequest) error {
	s.logger.Println("remoteClient: remove start")
	defer s.logger.Println("remoteClient: remove end")

	removeRemoteURL := utils.FormatURL(s.storageApiURL, "volumes", removeVolumeRequest.Name)

	removeVolumeRequest.CredentialInfo = s.config.CredentialInfo
	response, err := utils.HttpExecute(s.httpClient, s.logger, "DELETE", removeRemoteURL, removeVolumeRequest)
	if err != nil {
		s.logger.Printf("Error in remove volume remote call %#v", err)
		return fmt.Errorf("Error in remove volume remote call")
	}
	_, err = ioutil.ReadAll(response.Body)
	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		s.logger.Printf("Error in remove volume remote call %#v", response)
		return utils.ExtractErrorResponse(response)
	}

	return nil
}

func (s *remoteClient) GetVolume(getVolumeRequest resources.GetVolumeRequest) (resources.Volume, error) {
	s.logger.Println("remoteClient: get start")
	defer s.logger.Println("remoteClient: get finish")

	getRemoteURL := utils.FormatURL(s.storageApiURL, "volumes", getVolumeRequest.Name)
	getVolumeRequest.CredentialInfo = s.config.CredentialInfo
	response, err := utils.HttpExecute(s.httpClient, s.logger, "GET", getRemoteURL, getVolumeRequest)
	if err != nil {
		s.logger.Printf("Error in get volume remote call %#v", err)
		return resources.Volume{}, fmt.Errorf("Error in get volume remote call")
	}

	if response.StatusCode != http.StatusOK {
		s.logger.Printf("Error in get volume remote call %#v", response)
		return resources.Volume{}, utils.ExtractErrorResponse(response)
	}

	getResponse := resources.GetResponse{}
	err = utils.UnmarshalResponse(response, &getResponse)
	if err != nil {
		s.logger.Printf("Error in unmarshalling response for get remote call %#v for response %#v", err, response)
		return resources.Volume{}, fmt.Errorf("Error in unmarshalling response for get remote call")
	}

	return getResponse.Volume, nil
}

func (s *remoteClient) GetVolumeConfig(getVolumeConfigRequest resources.GetVolumeConfigRequest) (map[string]interface{}, error) {
	s.logger.Println("remoteClient: GetVolumeConfig start")
	defer s.logger.Println("remoteClient: GetVolumeConfig finish")

	getRemoteURL := utils.FormatURL(s.storageApiURL, "volumes", getVolumeConfigRequest.Name, "config")
	getVolumeConfigRequest.CredentialInfo = s.config.CredentialInfo
	response, err := utils.HttpExecute(s.httpClient, s.logger, "GET", getRemoteURL, getVolumeConfigRequest)
	if err != nil {
		s.logger.Printf("Error in get volume remote call %#v", err)
		return nil, fmt.Errorf("Error in get volume remote call")
	}

	if response.StatusCode != http.StatusOK {
		s.logger.Printf("Error in get volume remote call %#v", response)
		return nil, utils.ExtractErrorResponse(response)
	}

	getResponse := resources.GetConfigResponse{}
	err = utils.UnmarshalResponse(response, &getResponse)
	if err != nil {
		s.logger.Printf("Error in unmarshalling response for get remote call %#v for response %#v", err, response)
		return nil, fmt.Errorf("Error in unmarshalling response for get remote call")
	}

	return getResponse.VolumeConfig, nil
}

func (s *remoteClient) Attach(attachRequest resources.AttachRequest) (string, error) {
	s.logger.Println("remoteClient: attach start")
	defer s.logger.Println("remoteClient: attach end")

	attachRemoteURL := utils.FormatURL(s.storageApiURL, "volumes", attachRequest.Name, "attach")
	attachRequest.CredentialInfo = s.config.CredentialInfo
	response, err := utils.HttpExecute(s.httpClient, s.logger, "PUT", attachRemoteURL, attachRequest)
	if err != nil {
		s.logger.Printf("Error in attach volume remote call %#v", err)
		return "", fmt.Errorf("Error in attach volume remote call")
	}

	if response.StatusCode != http.StatusOK {
		s.logger.Printf("Error in attach volume remote call %#v", response)

		return "", utils.ExtractErrorResponse(response)
	}

	attachResponse := resources.AttachResponse{}
	err = utils.UnmarshalResponse(response, &attachResponse)
	if err != nil {
		return "", fmt.Errorf("Error in unmarshalling response for attach remote call")
	}
	getVolumeConfigRequest := resources.GetVolumeConfigRequest{Name: attachRequest.Name}
	volumeConfig, err := s.GetVolumeConfig(getVolumeConfigRequest)
	if err != nil {
		return "", err
	}
	getVolumeRequest := resources.GetVolumeRequest{Name: attachRequest.Name}
	volume, err := s.GetVolume(getVolumeRequest)

	mounter, err := s.getMounterForBackend(volume.Backend)
	if err != nil {
		return "", fmt.Errorf("Error determining mounter for volume: %s", err.Error())
	}
	mountRequest := resources.MountRequest{Mountpoint: attachResponse.Mountpoint, VolumeConfig: volumeConfig}
	mountpoint, err := mounter.Mount(mountRequest)
	if err != nil {
		return "", err
	}

	return mountpoint, nil
}

func (s *remoteClient) Detach(detachRequest resources.DetachRequest) error {
	s.logger.Println("remoteClient: detach start")
	defer s.logger.Println("remoteClient: detach end")

	getVolumeRequest := resources.GetVolumeRequest{Name: detachRequest.Name}
	volume, err := s.GetVolume(getVolumeRequest)

	mounter, err := s.getMounterForBackend(volume.Backend)
	if err != nil {
		return fmt.Errorf("Volume not found")
	}

	getVolumeConfigRequest := resources.GetVolumeConfigRequest{Name: detachRequest.Name}
	volumeConfig, err := s.GetVolumeConfig(getVolumeConfigRequest)
	if err != nil {
		return err
	}
	unmountRequest := resources.UnmountRequest{VolumeConfig: volumeConfig}
	err = mounter.Unmount(unmountRequest)
	if err != nil {
		return err
	}

	detachRemoteURL := utils.FormatURL(s.storageApiURL, "volumes", detachRequest.Name, "detach")
	detachRequest.CredentialInfo = s.config.CredentialInfo
	response, err := utils.HttpExecute(s.httpClient, s.logger, "PUT", detachRemoteURL, detachRequest)
	if err != nil {
		s.logger.Printf("Error in detach volume remote call %#v", err)
		return fmt.Errorf("Error in detach volume remote call")
	}
	_, err = ioutil.ReadAll(response.Body)
	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		s.logger.Printf("Error in detach volume remote call %#v", response)
		return utils.ExtractErrorResponse(response)
	}

	afterDetachRequest := resources.AfterDetachRequest{VolumeConfig: volumeConfig}
	if err := mounter.ActionAfterDetach(afterDetachRequest); err != nil {
		s.logger.Printf(fmt.Sprintf("Error execute action after detaching the volume : %#v", err))
		return err
	}
	return nil

}

func (s *remoteClient) ListVolumes(listVolumesRequest resources.ListVolumesRequest) ([]resources.Volume, error) {
	s.logger.Println("remoteClient: list start")
	defer s.logger.Println("remoteClient: list end")

	listRemoteURL := utils.FormatURL(s.storageApiURL, "volumes")
	listVolumesRequest.CredentialInfo = s.config.CredentialInfo
	response, err := utils.HttpExecute(s.httpClient, s.logger, "GET", listRemoteURL, listVolumesRequest)
	if err != nil {
		s.logger.Printf("Error in list volume remote call %#v", err)
		return nil, fmt.Errorf("Error in list volume remote call")
	}

	if response.StatusCode != http.StatusOK {
		s.logger.Printf("Error in list volume remote call %#v", err)
		return nil, utils.ExtractErrorResponse(response)
	}

	listResponse := resources.ListResponse{}
	err = utils.UnmarshalResponse(response, &listResponse)
	if err != nil {
		s.logger.Printf("Error in unmarshalling response for get remote call %#v for response %#v", err, response)
		return []resources.Volume{}, nil
	}

	return listResponse.Volumes, nil

}

// Return the mounter object. If mounter object already used(in the map mounterPerBackend) then just reuse it
func (s *remoteClient) getMounterForBackend(backend string) (resources.Mounter, error) {
	s.logger.Println("remoteClient: getMounterForVolume start")
	defer s.logger.Println("remoteClient: getMounterForVolume end")
	mounterInst, ok := s.mounterPerBackend[backend]
	if ok {
		s.logger.Printf("getMounterForVolume reuse existing mounter for backend " + backend)
		return mounterInst, nil
	} else if backend == resources.SpectrumScale {
		s.mounterPerBackend[backend] = mounter.NewSpectrumScaleMounter(s.logger)
	} else if backend == resources.SoftlayerNFS || backend == resources.SpectrumScaleNFS {
		s.mounterPerBackend[backend] = mounter.NewNfsMounter(s.logger)
	} else if backend == resources.SCBE {
		s.mounterPerBackend[backend] = mounter.NewScbeMounter(s.config.ScbeRemoteConfig)
	} else {
		return nil, fmt.Errorf("Mounter not found for backend: %s", backend)
	}
	return s.mounterPerBackend[backend], nil
}
<commit_msg>refactor remote client  attach/detach to not mount/unmount<commit_after>/**
 * Copyright 2017 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package remote

import (
	"fmt"
	"log"

	"net/http"

	"github.com/IBM/ubiquity/resources"

	"reflect"

	"github.com/IBM/ubiquity/remote/mounter"
	"github.com/IBM/ubiquity/utils"
	"io/ioutil"
)

type remoteClient struct {
	logger            *log.Logger
	isActivated       bool
	isMounted         bool
	httpClient        *http.Client
	storageApiURL     string
	config            resources.UbiquityPluginConfig
	mounterPerBackend map[string]resources.Mounter
}

func (s *remoteClient) Activate(activateRequest resources.ActivateRequest) error {
	s.logger.Println("remoteClient: Activate start")
	defer s.logger.Println("remoteClient: Activate end")

	if s.isActivated {
		return nil
	}

	// call remote activate
	activateURL := utils.FormatURL(s.storageApiURL, "activate")
	activateRequest.CredentialInfo = s.config.CredentialInfo
	response, err := utils.HttpExecute(s.httpClient, s.logger, "POST", activateURL, activateRequest)
	if err != nil {
		s.logger.Printf("Error in activate remote call %#v", err)
		return fmt.Errorf("Error in activate remote call")
	}

	if response.StatusCode != http.StatusOK {
		s.logger.Printf("Error in activate remote call %#v\n", response)
		return utils.ExtractErrorResponse(response)
	}
	s.logger.Println("remoteClient: Activate success")
	s.isActivated = true
	return nil
}

func (s *remoteClient) CreateVolume(createVolumeRequest resources.CreateVolumeRequest) error {
	s.logger.Println("remoteClient: create start")
	defer s.logger.Println("remoteClient: create end")

	createRemoteURL := utils.FormatURL(s.storageApiURL, "volumes")

	if reflect.DeepEqual(s.config.SpectrumNfsRemoteConfig, resources.SpectrumNfsRemoteConfig{}) == false {
		createVolumeRequest.Opts["nfsClientConfig"] = s.config.SpectrumNfsRemoteConfig.ClientConfig
	}

	createVolumeRequest.CredentialInfo = s.config.CredentialInfo
	response, err := utils.HttpExecute(s.httpClient, s.logger, "POST", createRemoteURL, createVolumeRequest)
	if err != nil {
		s.logger.Printf("Error in create volume remote call %s", err.Error())
		return fmt.Errorf("Error in create volume remote call(http error)")
	}
	_, err = ioutil.ReadAll(response.Body)
	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		s.logger.Printf("Error in create volume remote call %#v", response)
		return utils.ExtractErrorResponse(response)
	}

	return nil
}

func (s *remoteClient) RemoveVolume(removeVolumeRequest resources.RemoveVolumeRequest) error {
	s.logger.Println("remoteClient: remove start")
	defer s.logger.Println("remoteClient: remove end")

	removeRemoteURL := utils.FormatURL(s.storageApiURL, "volumes", removeVolumeRequest.Name)

	removeVolumeRequest.CredentialInfo = s.config.CredentialInfo
	response, err := utils.HttpExecute(s.httpClient, s.logger, "DELETE", removeRemoteURL, removeVolumeRequest)
	if err != nil {
		s.logger.Printf("Error in remove volume remote call %#v", err)
		return fmt.Errorf("Error in remove volume remote call")
	}
	_, err = ioutil.ReadAll(response.Body)
	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		s.logger.Printf("Error in remove volume remote call %#v", response)
		return utils.ExtractErrorResponse(response)
	}

	return nil
}

func (s *remoteClient) GetVolume(getVolumeRequest resources.GetVolumeRequest) (resources.Volume, error) {
	s.logger.Println("remoteClient: get start")
	defer s.logger.Println("remoteClient: get finish")

	getRemoteURL := utils.FormatURL(s.storageApiURL, "volumes", getVolumeRequest.Name)
	getVolumeRequest.CredentialInfo = s.config.CredentialInfo
	response, err := utils.HttpExecute(s.httpClient, s.logger, "GET", getRemoteURL, getVolumeRequest)
	if err != nil {
		s.logger.Printf("Error in get volume remote call %#v", err)
		return resources.Volume{}, fmt.Errorf("Error in get volume remote call")
	}

	if response.StatusCode != http.StatusOK {
		s.logger.Printf("Error in get volume remote call %#v", response)
		return resources.Volume{}, utils.ExtractErrorResponse(response)
	}

	getResponse := resources.GetResponse{}
	err = utils.UnmarshalResponse(response, &getResponse)
	if err != nil {
		s.logger.Printf("Error in unmarshalling response for get remote call %#v for response %#v", err, response)
		return resources.Volume{}, fmt.Errorf("Error in unmarshalling response for get remote call")
	}

	return getResponse.Volume, nil
}

func (s *remoteClient) GetVolumeConfig(getVolumeConfigRequest resources.GetVolumeConfigRequest) (map[string]interface{}, error) {
	s.logger.Println("remoteClient: GetVolumeConfig start")
	defer s.logger.Println("remoteClient: GetVolumeConfig finish")

	getRemoteURL := utils.FormatURL(s.storageApiURL, "volumes", getVolumeConfigRequest.Name, "config")
	getVolumeConfigRequest.CredentialInfo = s.config.CredentialInfo
	response, err := utils.HttpExecute(s.httpClient, s.logger, "GET", getRemoteURL, getVolumeConfigRequest)
	if err != nil {
		s.logger.Printf("Error in get volume remote call %#v", err)
		return nil, fmt.Errorf("Error in get volume remote call")
	}

	if response.StatusCode != http.StatusOK {
		s.logger.Printf("Error in get volume remote call %#v", response)
		return nil, utils.ExtractErrorResponse(response)
	}

	getResponse := resources.GetConfigResponse{}
	err = utils.UnmarshalResponse(response, &getResponse)
	if err != nil {
		s.logger.Printf("Error in unmarshalling response for get remote call %#v for response %#v", err, response)
		return nil, fmt.Errorf("Error in unmarshalling response for get remote call")
	}

	return getResponse.VolumeConfig, nil
}

func (s *remoteClient) Attach(attachRequest resources.AttachRequest) (string, error) {
	s.logger.Println("remoteClient: attach start")
	defer s.logger.Println("remoteClient: attach end")

	attachRemoteURL := utils.FormatURL(s.storageApiURL, "volumes", attachRequest.Name, "attach")
	attachRequest.CredentialInfo = s.config.CredentialInfo
	response, err := utils.HttpExecute(s.httpClient, s.logger, "PUT", attachRemoteURL, attachRequest)
	if err != nil {
		s.logger.Printf("Error in attach volume remote call %#v", err)
		return "", fmt.Errorf("Error in attach volume remote call")
	}

	if response.StatusCode != http.StatusOK {
		s.logger.Printf("Error in attach volume remote call %#v", response)

		return "", utils.ExtractErrorResponse(response)
	}

	attachResponse := resources.AttachResponse{}
	err = utils.UnmarshalResponse(response, &attachResponse)
	if err != nil {
		return "", fmt.Errorf("Error in unmarshalling response for attach remote call")
	}

	return "", nil
}

func (s *remoteClient) Detach(detachRequest resources.DetachRequest) error {
	s.logger.Println("remoteClient: detach start")
	defer s.logger.Println("remoteClient: detach end")

	detachRemoteURL := utils.FormatURL(s.storageApiURL, "volumes", detachRequest.Name, "detach")
	detachRequest.CredentialInfo = s.config.CredentialInfo
	response, err := utils.HttpExecute(s.httpClient, s.logger, "PUT", detachRemoteURL, detachRequest)
	if err != nil {
		s.logger.Printf("Error in detach volume remote call %#v", err)
		return fmt.Errorf("Error in detach volume remote call")
	}
	_, err = ioutil.ReadAll(response.Body)
	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		s.logger.Printf("Error in detach volume remote call %#v", response)
		return utils.ExtractErrorResponse(response)
	}

	return nil
}

func (s *remoteClient) ListVolumes(listVolumesRequest resources.ListVolumesRequest) ([]resources.Volume, error) {
	s.logger.Println("remoteClient: list start")
	defer s.logger.Println("remoteClient: list end")

	listRemoteURL := utils.FormatURL(s.storageApiURL, "volumes")
	listVolumesRequest.CredentialInfo = s.config.CredentialInfo
	response, err := utils.HttpExecute(s.httpClient, s.logger, "GET", listRemoteURL, listVolumesRequest)
	if err != nil {
		s.logger.Printf("Error in list volume remote call %#v", err)
		return nil, fmt.Errorf("Error in list volume remote call")
	}

	if response.StatusCode != http.StatusOK {
		s.logger.Printf("Error in list volume remote call %#v", err)
		return nil, utils.ExtractErrorResponse(response)
	}

	listResponse := resources.ListResponse{}
	err = utils.UnmarshalResponse(response, &listResponse)
	if err != nil {
		s.logger.Printf("Error in unmarshalling response for get remote call %#v for response %#v", err, response)
		return []resources.Volume{}, nil
	}

	return listResponse.Volumes, nil

}

// Return the mounter object. If mounter object already used(in the map mounterPerBackend) then just reuse it
func (s *remoteClient) getMounterForBackend(backend string) (resources.Mounter, error) {
	s.logger.Println("remoteClient: getMounterForVolume start")
	defer s.logger.Println("remoteClient: getMounterForVolume end")
	mounterInst, ok := s.mounterPerBackend[backend]
	if ok {
		s.logger.Printf("getMounterForVolume reuse existing mounter for backend " + backend)
		return mounterInst, nil
	} else if backend == resources.SpectrumScale {
		s.mounterPerBackend[backend] = mounter.NewSpectrumScaleMounter(s.logger)
	} else if backend == resources.SoftlayerNFS || backend == resources.SpectrumScaleNFS {
		s.mounterPerBackend[backend] = mounter.NewNfsMounter(s.logger)
	} else if backend == resources.SCBE {
		s.mounterPerBackend[backend] = mounter.NewScbeMounter(s.config.ScbeRemoteConfig)
	} else {
		return nil, fmt.Errorf("Mounter not found for backend: %s", backend)
	}
	return s.mounterPerBackend[backend], nil
}
<|endoftext|>


<commit_before>package main

import "fmt"

func main() {

	// Here we create an array `a` that will hold exactly
	// 5 `int`s. The type of elements and length are both
	// part of the array's type. By default an array is
	// zero-valued, which for `int`s means `0`s.
	var a [10]int
	fmt.Println("emp:", a)

	// We can set a value at an index using the
	// `array[index] = value` syntax, and get a value with
	// `array[index]`.
	a[4] = 100
	fmt.Println("set:", a)
	fmt.Println("get:", a[4])

	// The builtin `len` returns the length of an array.
	fmt.Println("len:", len(a))

	// Use this syntax to declare and initialize an array
	// in one line.
	b := [5]int{1, 2, 3, 4, 5}
	fmt.Println("dcl:", b)

	// Array types are one-dimensional, but you can
	// compose types to build multi-dimensional data
	// structures.
	var twoD [2][3]int
	for i := 0; i < 2; i++ {
		for j := 0; j < 3; j++ {
			twoD[i][j] = i + j
		}
	}
	fmt.Println("2d: ", twoD)
	fmt.Println("2d lenth:", len(twoD))
	fmt.Println("arr section length:", len(twoD[0]))
}
<commit_msg>3d array init<commit_after>package main

import "fmt"

func main() {

	// Here we create an array `a` that will hold exactly
	// 5 `int`s. The type of elements and length are both
	// part of the array's type. By default an array is
	// zero-valued, which for `int`s means `0`s.
	var a [10]int
	fmt.Println("emp:", a)

	// We can set a value at an index using the
	// `array[index] = value` syntax, and get a value with
	// `array[index]`.
	a[4] = 100
	fmt.Println("set:", a)
	fmt.Println("get:", a[4])

	// The builtin `len` returns the length of an array.
	fmt.Println("len:", len(a))

	// Use this syntax to declare and initialize an array
	// in one line.
	b := [5]int{1, 2, 3, 4, 5}
	fmt.Println("dcl:", b)

	// Array types are one-dimensional, but you can
	// compose types to build multi-dimensional data
	// structures.
	var twoD [2][3]int
	for i := 0; i < 2; i++ {
		for j := 0; j < 3; j++ {
			twoD[i][j] = i + j
		}
	}
	fmt.Println("2d: ", twoD)
	fmt.Println("2d lenth:", len(twoD))
	fmt.Println("arr section length:", len(twoD[0]))

	var threeD [2][2][2]int
	fmt.Println("3d len:",len(threeD))

	//var aa  = [3][3]int{{1,2,3},{4,5,6},{7,8,9}}

	var aaa  = [3][3][3]int{{{1,2,3},{4,5,6},{7,8,9}},{{1,2,3},{4,5,6},{7,8,9}}}

	fmt.Println("aaa:",aaa)

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"
	"net/http"
	"net/http/httptest"
	"os"
	"os/exec"
	"path"
	"syscall"
	"testing"
	"time"
)

const servAddr = "127.0.0.1:8181"
const servWaitListen = 10000 // milliseconds to wait for server to start listening
const servWaitSleep = 100    // milliseconds sleep interval
const scratchDir = "test/scratch"
const testRepoRoot = "test/data"
const testRepo = "test.git"

var remote = fmt.Sprintf("http://%s/%s", servAddr, testRepo)
var checkoutDir = path.Join(scratchDir, "test")

func TestAllowedClone(t *testing.T) {
	// Prepare clone directory
	if err := os.RemoveAll(scratchDir); err != nil {
		t.Fatal(err)
	}

	// Prepare test server and backend
	ts := testAuthServer(200, `{"GL_ID":"user-123"}`)
	defer ts.Close()
	cmd, err := startServer(ts)
	if err != nil {
		t.Fatal(err)
	}
	defer cleanUpProcessGroup(cmd)
	if err := waitServer(); err != nil {
		t.Fatal(err)
	}

	// Do the git clone
	cloneCmd := exec.Command("git", "clone", remote, checkoutDir)
	runOrFail(t, cloneCmd)

	// We may have cloned an 'empty' repository, 'git log' will fail in it
	logCmd := exec.Command("git", "log", "-1", "--oneline")
	logCmd.Dir = checkoutDir
	runOrFail(t, logCmd)
}

func TestDeniedClone(t *testing.T) {
	// Prepare clone directory
	if err := os.RemoveAll(scratchDir); err != nil {
		t.Fatal(err)
	}

	// Prepare test server and backend
	ts := testAuthServer(403, "Access denied")
	defer ts.Close()
	cmd, err := startServer(ts)
	if err != nil {
		t.Fatal(err)
	}
	defer cleanUpProcessGroup(cmd)
	if err := waitServer(); err != nil {
		t.Fatal(err)
	}

	// Do the git clone
	cloneCmd := exec.Command("git", "clone", remote, checkoutDir)
	if err := cloneCmd.Run(); err == nil {
		t.Fatal("git clone should have failed")
	}
}

func TestAllowedPush(t *testing.T) {
	branch := preparePushRepo(t)

	// Prepare the test server and backend
	ts := testAuthServer(200, `{"GL_ID":"user-123"}`)
	defer ts.Close()
	cmd, err := startServer(ts)
	if err != nil {
		t.Fatal(err)
	}
	defer cleanUpProcessGroup(cmd)
	if err := waitServer(); err != nil {
		t.Fatal(err)
	}

	// Perform the git push
	pushCmd := exec.Command("git", "push", remote, branch)
	pushCmd.Dir = checkoutDir
	runOrFail(t, pushCmd)
}

func TestDeniedPush(t *testing.T) {
	branch := preparePushRepo(t)

	// Prepare the test server and backend
	ts := testAuthServer(403, "Access denied")
	defer ts.Close()
	cmd, err := startServer(ts)
	if err != nil {
		t.Fatal(err)
	}
	defer cleanUpProcessGroup(cmd)
	if err := waitServer(); err != nil {
		t.Fatal(err)
	}

	// Perform the git push
	pushCmd := exec.Command("git", "push", remote, branch)
	pushCmd.Dir = checkoutDir
	if err := pushCmd.Run(); err == nil {
		t.Fatal("git push should have failed")
	}
}

func preparePushRepo(t *testing.T) string {
	if err := os.RemoveAll(scratchDir); err != nil {
		t.Fatal(err)
	}
	cloneCmd := exec.Command("git", "clone", path.Join(testRepoRoot, testRepo), checkoutDir)
	runOrFail(t, cloneCmd)
	branch := fmt.Sprintf("branch-%d", time.Now().UnixNano())
	branchCmd := exec.Command("git", "branch", branch)
	branchCmd.Dir = checkoutDir
	runOrFail(t, branchCmd)
	return branch
}

func testAuthServer(code int, body string) *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(code)
		fmt.Fprint(w, body)
	}))
}

func startServer(ts *httptest.Server) (*exec.Cmd, error) {
	cmd := exec.Command("go", "run", "main.go", fmt.Sprintf("-authBackend=%s", ts.URL), fmt.Sprintf("-listenAddr=%s", servAddr), testRepoRoot)
	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd, cmd.Start()
}

func waitServer() (err error) {
	var conn net.Conn

	for i := 0; i < servWaitListen/servWaitSleep; i++ {
		conn, err = net.Dial("tcp", servAddr)
		if err == nil {
			conn.Close()
			return
		}
		time.Sleep(servWaitSleep * time.Millisecond)
	}
	return
}

func runOrFail(t *testing.T, cmd *exec.Cmd) {
	if out, err := cmd.CombinedOutput(); err != nil {
		t.Logf("%s", out)
		t.Fatal(err)
	}
}
<commit_msg>More output during `go test -v`<commit_after>package main

import (
	"fmt"
	"net"
	"net/http"
	"net/http/httptest"
	"os"
	"os/exec"
	"path"
	"syscall"
	"testing"
	"time"
)

const servAddr = "127.0.0.1:8181"
const servWaitListen = 10000 // milliseconds to wait for server to start listening
const servWaitSleep = 100    // milliseconds sleep interval
const scratchDir = "test/scratch"
const testRepoRoot = "test/data"
const testRepo = "test.git"

var remote = fmt.Sprintf("http://%s/%s", servAddr, testRepo)
var checkoutDir = path.Join(scratchDir, "test")

func TestAllowedClone(t *testing.T) {
	// Prepare clone directory
	if err := os.RemoveAll(scratchDir); err != nil {
		t.Fatal(err)
	}

	// Prepare test server and backend
	ts := testAuthServer(200, `{"GL_ID":"user-123"}`)
	defer ts.Close()
	cmd, err := startServer(ts)
	if err != nil {
		t.Fatal(err)
	}
	defer cleanUpProcessGroup(cmd)
	if err := waitServer(); err != nil {
		t.Fatal(err)
	}

	// Do the git clone
	cloneCmd := exec.Command("git", "clone", remote, checkoutDir)
	runOrFail(t, cloneCmd)

	// We may have cloned an 'empty' repository, 'git log' will fail in it
	logCmd := exec.Command("git", "log", "-1", "--oneline")
	logCmd.Dir = checkoutDir
	runOrFail(t, logCmd)
}

func TestDeniedClone(t *testing.T) {
	// Prepare clone directory
	if err := os.RemoveAll(scratchDir); err != nil {
		t.Fatal(err)
	}

	// Prepare test server and backend
	ts := testAuthServer(403, "Access denied")
	defer ts.Close()
	cmd, err := startServer(ts)
	if err != nil {
		t.Fatal(err)
	}
	defer cleanUpProcessGroup(cmd)
	if err := waitServer(); err != nil {
		t.Fatal(err)
	}

	// Do the git clone
	cloneCmd := exec.Command("git", "clone", remote, checkoutDir)
	out, err := cloneCmd.CombinedOutput()
	t.Logf("%s", out)
	if err == nil {
		t.Fatal("git clone should have failed")
	}
}

func TestAllowedPush(t *testing.T) {
	branch := preparePushRepo(t)

	// Prepare the test server and backend
	ts := testAuthServer(200, `{"GL_ID":"user-123"}`)
	defer ts.Close()
	cmd, err := startServer(ts)
	if err != nil {
		t.Fatal(err)
	}
	defer cleanUpProcessGroup(cmd)
	if err := waitServer(); err != nil {
		t.Fatal(err)
	}

	// Perform the git push
	pushCmd := exec.Command("git", "push", remote, branch)
	pushCmd.Dir = checkoutDir
	runOrFail(t, pushCmd)
}

func TestDeniedPush(t *testing.T) {
	branch := preparePushRepo(t)

	// Prepare the test server and backend
	ts := testAuthServer(403, "Access denied")
	defer ts.Close()
	cmd, err := startServer(ts)
	if err != nil {
		t.Fatal(err)
	}
	defer cleanUpProcessGroup(cmd)
	if err := waitServer(); err != nil {
		t.Fatal(err)
	}

	// Perform the git push
	pushCmd := exec.Command("git", "push", remote, branch)
	pushCmd.Dir = checkoutDir
	out, err := pushCmd.CombinedOutput()
	t.Logf("%s", out)
	if err == nil {
		t.Fatal("git push should have failed")
	}
}

func preparePushRepo(t *testing.T) string {
	if err := os.RemoveAll(scratchDir); err != nil {
		t.Fatal(err)
	}
	cloneCmd := exec.Command("git", "clone", path.Join(testRepoRoot, testRepo), checkoutDir)
	runOrFail(t, cloneCmd)
	branch := fmt.Sprintf("branch-%d", time.Now().UnixNano())
	branchCmd := exec.Command("git", "branch", branch)
	branchCmd.Dir = checkoutDir
	runOrFail(t, branchCmd)
	return branch
}

func testAuthServer(code int, body string) *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(code)
		fmt.Fprint(w, body)
	}))
}

func startServer(ts *httptest.Server) (*exec.Cmd, error) {
	cmd := exec.Command("go", "run", "main.go", fmt.Sprintf("-authBackend=%s", ts.URL), fmt.Sprintf("-listenAddr=%s", servAddr), testRepoRoot)
	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd, cmd.Start()
}

func waitServer() (err error) {
	var conn net.Conn

	for i := 0; i < servWaitListen/servWaitSleep; i++ {
		conn, err = net.Dial("tcp", servAddr)
		if err == nil {
			conn.Close()
			return
		}
		time.Sleep(servWaitSleep * time.Millisecond)
	}
	return
}

func runOrFail(t *testing.T, cmd *exec.Cmd) {
	out, err := cmd.CombinedOutput()
	t.Logf("%s", out)
	if err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>