<commit_before>package dfs

import (
	"github.com/zenoss/glog"
	"github.com/zenoss/serviced/dao"
	"github.com/zenoss/serviced/volume"

	docker "github.com/fsouza/go-dockerclient"

	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os/exec"
	"os/user"
	"path"
	"strings"
	"sync"
	"time"
)

const (
	USER_ROOT        string = "root"
	DOCKER_ENDPOINT  string = "unix:///var/run/docker.sock"
	DOCKER_LATEST    string = "latest"
	DOCKER_IMAGEJSON string = "images.json"
)

var (
	unused interface{}
	Lock   *sync.Mutex = new(sync.Mutex)
	// stubs
	getCurrentUser = user.Current
)

var runServiceCommand = func(state *dao.ServiceState, command string) ([]byte, error) {
	lxcAttach, err := exec.LookPath("lxc-attach")
	if err != nil {
		return []byte{}, err
	}
	argv := []string{"-n", state.DockerId, "-e", "--", "/bin/bash", "-c", command}
	glog.V(0).Infof("ServiceId: %s, Command: `%s %s`", state.ServiceId, lxcAttach, argv)
	cmd := exec.Command(lxcAttach, argv...)

	output, err := cmd.CombinedOutput()
	if err != nil {
		glog.Errorf("Error running command: `%s` for serviceId: %s out: %s err: %s", command, state.ServiceId, output, err)
		return output, err
	}
	glog.V(0).Infof("Successfully ran command: `%s` for serviceId: %s out: %s", command, state.ServiceId, output)
	return output, nil
}

type DistributedFileSystem struct {
	client       dao.ControlPlane
	dockerClient *docker.Client
}

// Initiates a New Distributed Filesystem Object given an implementation of a control plane object
func NewDistributedFileSystem(client dao.ControlPlane) (*DistributedFileSystem, error) {
	dockerClient, err := docker.NewClient(DOCKER_ENDPOINT)
	if err != nil {
		glog.V(2).Infof("snapshot.NewDockerClient client=%+v err=%s", client, err)
		return nil, err
	}

	return &DistributedFileSystem{
		client:       client,
		dockerClient: dockerClient,
	}, nil
}

// Pauses a running service
func (d *DistributedFileSystem) Pause(service *dao.Service, state *dao.ServiceState) error {
	if output, err := runServiceCommand(state, service.Snapshot.Pause); err != nil {
		errmsg := fmt.Sprintf("output: %s, err: %s", output, err)
		glog.V(2).Infof("DistributedFileSystem.Pause service=%+v err=%s", service, err)
		return errors.New(errmsg)
	}
	return nil
}

// Resumes a paused service
func (d *DistributedFileSystem) Resume(service *dao.Service, state *dao.ServiceState) error {
	if output, err := runServiceCommand(state, service.Snapshot.Resume); err != nil {
		errmsg := fmt.Sprintf("output: %s, err: %s", output, err)
		glog.V(2).Infof("DistributedFileSystem.Resume service=%+v err=%s", service, err)
		return errors.New(errmsg)
	}
	return nil
}

// Snapshots the DFS
func (d *DistributedFileSystem) Snapshot(serviceId string, label *string) error {
	var tenantId string
	if err := d.client.GetTenantId(serviceId, &tenantId); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Snapshot service=%+v err=%s", serviceId, err)
		return err
	}

	var service dao.Service
	if err := d.client.GetService(tenantId, &service); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Snapshot service=%+v err=%s", serviceId, err)
		return err
	}

	// simplest case - do everything here

	// call quiesce for services with 'DistributedFileSystem.Pause' and
	// 'DistributedFileSystem.Resume' definition.  Only root can run
	// lxc-attach
	if whoami, err := getCurrentUser(); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Snapshot service=%+v err=%s", serviceId, err)
		return err
	} else if USER_ROOT != whoami.Username {
		glog.Warningf("Unable to pause/resume service - User is not %s - whoami:%+v", USER_ROOT, whoami)
	} else {

		var servicesList []*dao.Service
		if err := d.client.GetServices(unused, &servicesList); err != nil {
			glog.V(2).Infof("DistributedFileSystem.Snapshot service=%+v err=%s", serviceId, err)
			return err
		}

		for _, service := range servicesList {
			if service.Snapshot.Pause == "" || service.Snapshot.Resume == "" {
				continue
			}

			var states []*dao.ServiceState
			if err := d.client.GetServiceStates(service.Id, &states); err != nil {
				glog.V(2).Infof("DistributedFileSystem.Snapshot service=%+v, err=%s", serviceId, err)
				return err
			}

			// Pause all running service states
			for i, state := range states {
				glog.V(3).Infof("DEBUG states[%d]: service:%+v state:%+v", i, serviceId, state.DockerId)
				if state.DockerId != "" {
					err := d.Pause(service, state)
					defer d.Resume(service, state) // resume service state when snapshot is done
					if err != nil {
						glog.V(2).Infof("DistributedFileSystem.Snapshot service=%+v err=%s", serviceId, err)
						return err
					}
				}
			}
		}
	}

	// create a snapshot
	var (
		theVolume volume.Volume
	)
	if err := d.client.GetVolume(tenantId, &theVolume); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Snapshot service=%+v err=%s", serviceId, err)
		return err
	} else {
		glog.V(2).Infof("DistributedFileSystem.Snapshot service=%+v volume=%+v", serviceId, theVolume)
		*label = getSnapshotLabel(&theVolume)
		if err := theVolume.Snapshot(*label); err != nil {
			glog.V(2).Infof("DistributedFileSystem.Snapshot service=%+v err=%s", serviceId, err)
			return err
		}
	}

	glog.V(2).Infof("Successfully created snapshot for service Id:%s Name:%s Label:%s", service.Id, service.Name, *label)
	return nil
}

// Commits a container to docker image and updates the DFS
func (d *DistributedFileSystem) Commit(dockerId string, label *string) error {
	Lock.Lock()
	defer Lock.Unlock()

	// Get the container
	container, err := d.dockerClient.InspectContainer(dockerId)
	if err != nil {
		glog.V(2).Infof("DistributedFileSystem.Commit dockerId=%+v err=%s", dockerId, err)
		return err
	} else if container.State.Running {
		err := errors.New("cannot commit a running container")
		glog.V(2).Infof("DistributedFileSystem.Commit dockerId=%+v err=%s", dockerId, err)
		return err
	}

	// Get the service id
	name := strings.Split(container.Name, "_")
	serviceId := name[0]

	// Get tag & repo information
	var (
		images []docker.APIImages
		image  *docker.APIImages
	)

	if err := d.getLatestImages(&images); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Commit dockerId=%+v err=%s", dockerId, err)
		return err
	}
	for _, i := range images {
		if i.ID == container.Image {
			image = &i
			break
		}
	}
	if image == nil {
		err := errors.New("cannot commit a stale container")
		glog.V(2).Infof("DistributedFileSystem.Commit dockerId=%+v err=%s", dockerId, err)
		return err
	}

	// Snapshot the DFS
	if err := d.Snapshot(serviceId, label); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Commit dockerId=%+v err=%s", dockerId, err)
		return err
	}

	// Commit the container to the image
	newImage, err := d.dockerClient.CommitContainer(docker.CommitContainerOptions{
		Container:  container.ID,
		Repository: image.Repository,
		Tag:        image.Tag,
	})
	if err != nil {
		glog.V(2).Infof("DistributedFileSystem.Commit container=%+v err=%s", dockerId, err)
		return err
	}

	// Copy images to the DFS
	*image = docker.APIImages{
		ID:          newImage.ID,
		RepoTags:    image.RepoTags,
		Created:     newImage.Created.Unix(),
		Size:        newImage.Size,
		VirtualSize: image.VirtualSize,
		ParentId:    newImage.Parent,
		Repository:  image.Repository,
		Tag:         image.Tag,
	}

	// Get the path to the volume and write the images
	var (
		tenantId string
		volume   volume.Volume
	)

	if err := d.client.GetTenantId(serviceId, &tenantId); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Commit container=%+v err=%s", dockerId, err)
		return err
	}

	if err := d.client.GetVolume(tenantId, &volume); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Commit container=%+v err=%s", dockerId, err)
		return err
	}

	config := path.Join(volume.Path(), *label, DOCKER_IMAGEJSON)
	if data, err := json.Marshal(images); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Commit container=%+v err=%s", dockerId, err)
		return err
	} else if err := ioutil.WriteFile(config, data, 0644); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Commit container=%+v err=%s", dockerId, err)
		return err
	}

	return nil
}

// Gets the images from docker and filters those marked as latest
func (d *DistributedFileSystem) getLatestImages(images *[]docker.APIImages) error {
	// Get all of the images from docker and find the ones tagged as the latest
	if allImages, err := d.dockerClient.ListImages(true); err != nil {
		return err
	} else {
		for _, image := range allImages {
			if image.Tag == DOCKER_LATEST {
				*images = append(*images, image)
			}
		}
	}

	return nil
}

// Gets the images from the snapshot; returns error if image does not exist in docker
func (d *DistributedFileSystem) getSnapshotImages(snapshotId string, volume *volume.Volume, images *[]docker.APIImages) error {
	config := path.Join(volume.Path(), snapshotId, DOCKER_IMAGEJSON)

	if data, err := ioutil.ReadFile(config); err != nil {
		return err
	} else if err := json.Unmarshal(data, images); err != nil {
		return err
	}

	// Check if the images still exist
	for _, image := range *images {
		if _, err := d.dockerClient.InspectImage(image.ID); err != nil {
			return err
		}
	}

	return nil
}

// Retags containers with the given snapshot
func (d *DistributedFileSystem) retag(images *[]docker.APIImages, volume *volume.Volume) error {

	// Set the tag of the new image
	dockerBin, err := exec.LookPath("docker")
	if err != nil {
		return err
	}

	for _, image := range *images {
		// docker tag %{image.ID} %{image.Repo}:%{image.Tag}
		cmd := exec.Command(dockerBin, "tag", image.ID, fmt.Sprintf("%s:%s", image.Repository, image.Tag))
		if err := cmd.Run(); err != nil {
			return err
		}
	}

	return nil
}

// Rolls back the DFS to a specified state and retags the images
func (d *DistributedFileSystem) Rollback(snapshotId string) error {
	Lock.Lock()
	defer Lock.Unlock()

	var (
		tenantId string
		volume   volume.Volume
	)

	parts := strings.Split(snapshotId, "_")
	if len(parts) != 2 {
		glog.V(2).Infof("DistributedFileSystem.Rollback malformed snapshot Id: %s", snapshotId)
		return errors.New("malformed snapshotId")
	}
	serviceId := parts[0]
	if err := d.client.GetTenantId(serviceId, &tenantId); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Rollback service=%+v err=%s", serviceId, err)
		return err
	}

	// Validate existence of images for this snapshot
	var service dao.Service
	err := d.client.GetService(tenantId, &service)
	glog.V(2).Infof("Getting service instance: %s", tenantId)
	if err != nil {
		glog.V(2).Infof("DistributedFileSystem.Rollback service=%+v err=%s", serviceId, err)
		return err
	}

	if err := d.client.GetVolume(tenantId, &volume); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Rollback service=%+v err=%s", serviceId, err)
		return err
	}

	var latestImages []docker.APIImages
	if err := d.getLatestImages(&latestImages); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Rollback service=%+v err=%s", serviceId, err)
		return err
	}

	var snapshotImages []docker.APIImages
	if err := d.getSnapshotImages(snapshotId, &volume, &snapshotImages); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Rollback service=%+v err=%s", serviceId, err)
		return err
	}

	d.client.StopService(tenantId, nil)
	// TODO: Wait for real event that confirms shutdown
	time.Sleep(time.Second * 5) // wait for shutdown

	if err := d.retag(&snapshotImages, &volume); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Rollback service=%+v err=%s", serviceId, err)
		if err := d.retag(&latestImages, &volume); err != nil {
			glog.Errorf("DistributedFileSystem.Rollback unable to restore images service=%+v err=%s", serviceId, err)
		}
		return err
	}

	glog.V(2).Infof("performing rollback on %s to %s", tenantId, snapshotId)
	if err := volume.Rollback(snapshotId); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Rollback service=%+v err=%s", serviceId, err)
		if err := d.retag(&latestImages, &volume); err != nil {
			glog.Errorf("DistributedFileSystem.Rollback unable to restore images service=%+v err=%s", serviceId, err)
		}
		return err
	}
	var unusedStr string = ""

	return d.client.StartService(tenantId, &unusedStr)
}

func getSnapshotLabel(v *volume.Volume) string {
	format := "20060102-150405"
	loc := time.Now()
	utc := loc.UTC()
	return fmt.Sprintf("%s_%s", v.Name(), utc.Format(format))
}<commit_msg>review feedback<commit_after>package dfs

import (
	"github.com/zenoss/glog"
	"github.com/zenoss/serviced/dao"
	"github.com/zenoss/serviced/volume"

	docker "github.com/fsouza/go-dockerclient"

	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os/exec"
	"os/user"
	"path"
	"strings"
	"sync"
	"time"
)

const (
	USER_ROOT        string = "root"
	DOCKER_ENDPOINT  string = "unix:///var/run/docker.sock"
	DOCKER_LATEST    string = "latest"
	DOCKER_IMAGEJSON string = "images.json"
)

var (
	unused interface{}
	Lock   *sync.Mutex = new(sync.Mutex)
	// stubs
	getCurrentUser = user.Current
)

var runServiceCommand = func(state *dao.ServiceState, command string) ([]byte, error) {
	lxcAttach, err := exec.LookPath("lxc-attach")
	if err != nil {
		return []byte{}, err
	}
	argv := []string{"-n", state.DockerId, "-e", "--", "/bin/bash", "-c", command}
	glog.V(0).Infof("ServiceId: %s, Command: `%s %s`", state.ServiceId, lxcAttach, argv)
	cmd := exec.Command(lxcAttach, argv...)

	output, err := cmd.CombinedOutput()
	if err != nil {
		glog.Errorf("Error running command: `%s` for serviceId: %s out: %s err: %s", command, state.ServiceId, output, err)
		return output, err
	}
	glog.V(0).Infof("Successfully ran command: `%s` for serviceId: %s out: %s", command, state.ServiceId, output)
	return output, nil
}

type DistributedFileSystem struct {
	client       dao.ControlPlane
	dockerClient *docker.Client
}

// Initiates a New Distributed Filesystem Object given an implementation of a control plane object
func NewDistributedFileSystem(client dao.ControlPlane) (*DistributedFileSystem, error) {
	dockerClient, err := docker.NewClient(DOCKER_ENDPOINT)
	if err != nil {
		glog.V(2).Infof("snapshot.NewDockerClient client=%+v err=%s", client, err)
		return nil, err
	}

	return &DistributedFileSystem{
		client:       client,
		dockerClient: dockerClient,
	}, nil
}

// Pauses a running service
func (d *DistributedFileSystem) Pause(service *dao.Service, state *dao.ServiceState) error {
	if output, err := runServiceCommand(state, service.Snapshot.Pause); err != nil {
		errmsg := fmt.Sprintf("output: %s, err: %s", output, err)
		glog.V(2).Infof("DistributedFileSystem.Pause service=%+v err=%s", service, err)
		return errors.New(errmsg)
	}
	return nil
}

// Resumes a paused service
func (d *DistributedFileSystem) Resume(service *dao.Service, state *dao.ServiceState) error {
	if output, err := runServiceCommand(state, service.Snapshot.Resume); err != nil {
		errmsg := fmt.Sprintf("output: %s, err: %s", output, err)
		glog.V(2).Infof("DistributedFileSystem.Resume service=%+v err=%s", service, err)
		return errors.New(errmsg)
	}
	return nil
}

// Snapshots the DFS
func (d *DistributedFileSystem) Snapshot(serviceId string, label *string) error {
	var tenantId string
	if err := d.client.GetTenantId(serviceId, &tenantId); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Snapshot service=%+v err=%s", serviceId, err)
		return err
	}

	var service dao.Service
	if err := d.client.GetService(tenantId, &service); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Snapshot service=%+v err=%s", serviceId, err)
		return err
	}

	if whoami, err := getCurrentUser(); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Snapshot service=%+v err=%s", serviceId, err)
		return err
	} else if USER_ROOT != whoami.Username {
		glog.Warningf("Unable to pause/resume service - User is not %s - whoami:%+v", USER_ROOT, whoami)
	} else {

		var servicesList []*dao.Service
		if err := d.client.GetServices(unused, &servicesList); err != nil {
			glog.V(2).Infof("DistributedFileSystem.Snapshot service=%+v err=%s", serviceId, err)
			return err
		}

		for _, service := range servicesList {
			if service.Snapshot.Pause == "" || service.Snapshot.Resume == "" {
				continue
			}

			var states []*dao.ServiceState
			if err := d.client.GetServiceStates(service.Id, &states); err != nil {
				glog.V(2).Infof("DistributedFileSystem.Snapshot service=%+v, err=%s", serviceId, err)
				return err
			}

			// Pause all running service states
			for i, state := range states {
				glog.V(3).Infof("DEBUG states[%d]: service:%+v state:%+v", i, serviceId, state.DockerId)
				if state.DockerId != "" {
					err := d.Pause(service, state)
					defer d.Resume(service, state) // resume service state when snapshot is done
					if err != nil {
						glog.V(2).Infof("DistributedFileSystem.Snapshot service=%+v err=%s", serviceId, err)
						return err
					}
				}
			}
		}
	}

	// create a snapshot
	var (
		theVolume volume.Volume
	)
	if err := d.client.GetVolume(tenantId, &theVolume); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Snapshot service=%+v err=%s", serviceId, err)
		return err
	} else {
		glog.V(2).Infof("DistributedFileSystem.Snapshot service=%+v volume=%+v", serviceId, theVolume)
		*label = getSnapshotLabel(&theVolume)
		if err := theVolume.Snapshot(*label); err != nil {
			glog.V(2).Infof("DistributedFileSystem.Snapshot service=%+v err=%s", serviceId, err)
			return err
		}
	}

	glog.V(2).Infof("Successfully created snapshot for service Id:%s Name:%s Label:%s", service.Id, service.Name, *label)
	return nil
}

// Commits a container to docker image and updates the DFS
func (d *DistributedFileSystem) Commit(dockerId string, label *string) error {
	Lock.Lock()
	defer Lock.Unlock()

	// Get the container
	container, err := d.dockerClient.InspectContainer(dockerId)
	if err != nil {
		glog.V(2).Infof("DistributedFileSystem.Commit dockerId=%+v err=%s", dockerId, err)
		return err
	} else if container.State.Running {
		err := errors.New("cannot commit a running container")
		glog.V(2).Infof("DistributedFileSystem.Commit dockerId=%+v err=%s", dockerId, err)
		return err
	}

	// Get the service id
	name := strings.Split(container.Name, "_")
	serviceId := name[0]

	// Get tag & repo information
	var (
		images []docker.APIImages
		image  *docker.APIImages
	)

	if err := d.getLatestImages(&images); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Commit dockerId=%+v err=%s", dockerId, err)
		return err
	}
	for _, i := range images {
		if i.ID == container.Image {
			image = &i
			break
		}
	}
	if image == nil {
		err := errors.New("cannot commit a stale container")
		glog.V(2).Infof("DistributedFileSystem.Commit dockerId=%+v err=%s", dockerId, err)
		return err
	}

	// Snapshot the DFS
	if err := d.Snapshot(serviceId, label); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Commit dockerId=%+v err=%s", dockerId, err)
		return err
	}

	// Commit the container to the image
	newImage, err := d.dockerClient.CommitContainer(docker.CommitContainerOptions{
		Container:  container.ID,
		Repository: image.Repository,
		Tag:        image.Tag,
	})
	if err != nil {
		glog.V(2).Infof("DistributedFileSystem.Commit container=%+v err=%s", dockerId, err)
		return err
	}

	// Copy images to the DFS
	*image = docker.APIImages{
		ID:          newImage.ID,
		RepoTags:    image.RepoTags,
		Created:     newImage.Created.Unix(),
		Size:        newImage.Size,
		VirtualSize: image.VirtualSize,
		ParentId:    newImage.Parent,
		Repository:  image.Repository,
		Tag:         image.Tag,
	}

	// Get the path to the volume and write the images
	var (
		tenantId string
		volume   volume.Volume
	)

	if err := d.client.GetTenantId(serviceId, &tenantId); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Commit container=%+v err=%s", dockerId, err)
		return err
	}

	if err := d.client.GetVolume(tenantId, &volume); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Commit container=%+v err=%s", dockerId, err)
		return err
	}

	config := path.Join(volume.Path(), *label, DOCKER_IMAGEJSON)
	if data, err := json.Marshal(images); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Commit container=%+v err=%s", dockerId, err)
		return err
	} else if err := ioutil.WriteFile(config, data, 0644); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Commit container=%+v err=%s", dockerId, err)
		return err
	}

	return nil
}

// Gets the images from docker and filters those marked as latest
func (d *DistributedFileSystem) getLatestImages(images *[]docker.APIImages) error {
	// Get all of the images from docker and find the ones tagged as the latest
	if allImages, err := d.dockerClient.ListImages(true); err != nil {
		return err
	} else {
		for _, image := range allImages {
			if image.Tag == DOCKER_LATEST {
				*images = append(*images, image)
			}
		}
	}

	return nil
}

// Gets the images from the snapshot; returns error if image does not exist in docker
func (d *DistributedFileSystem) getSnapshotImages(snapshotId string, volume *volume.Volume, images *[]docker.APIImages) error {
	config := path.Join(volume.Path(), snapshotId, DOCKER_IMAGEJSON)

	if data, err := ioutil.ReadFile(config); err != nil {
		return err
	} else if err := json.Unmarshal(data, images); err != nil {
		return err
	}

	// Check if the images still exist
	for _, image := range *images {
		if _, err := d.dockerClient.InspectImage(image.ID); err != nil {
			return err
		}
	}

	return nil
}

// Retags containers with the given snapshot
func (d *DistributedFileSystem) retag(images *[]docker.APIImages, volume *volume.Volume) error {

	// Set the tag of the new image
	dockerBin, err := exec.LookPath("docker")
	if err != nil {
		return err
	}

	for _, image := range *images {
		// docker tag %{image.ID} %{image.Repo}:%{image.Tag}
		cmd := exec.Command(dockerBin, "tag", image.ID, fmt.Sprintf("%s:%s", image.Repository, image.Tag))
		if err := cmd.Run(); err != nil {
			return err
		}
	}

	return nil
}

// Rolls back the DFS to a specified state and retags the images
func (d *DistributedFileSystem) Rollback(snapshotId string) error {
	Lock.Lock()
	defer Lock.Unlock()

	var (
		tenantId string
		volume   volume.Volume
	)

	parts := strings.Split(snapshotId, "_")
	if len(parts) != 2 {
		glog.V(2).Infof("DistributedFileSystem.Rollback malformed snapshot Id: %s", snapshotId)
		return errors.New("malformed snapshotId")
	}
	serviceId := parts[0]
	if err := d.client.GetTenantId(serviceId, &tenantId); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Rollback service=%+v err=%s", serviceId, err)
		return err
	}

	// Validate existence of images for this snapshot
	var service dao.Service
	err := d.client.GetService(tenantId, &service)
	glog.V(2).Infof("Getting service instance: %s", tenantId)
	if err != nil {
		glog.V(2).Infof("DistributedFileSystem.Rollback service=%+v err=%s", serviceId, err)
		return err
	}

	if err := d.client.GetVolume(tenantId, &volume); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Rollback service=%+v err=%s", serviceId, err)
		return err
	}

	var latestImages []docker.APIImages
	if err := d.getLatestImages(&latestImages); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Rollback service=%+v err=%s", serviceId, err)
		return err
	}

	var snapshotImages []docker.APIImages
	if err := d.getSnapshotImages(snapshotId, &volume, &snapshotImages); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Rollback service=%+v err=%s", serviceId, err)
		return err
	}

	d.client.StopService(tenantId, nil)
	// TODO: Wait for real event that confirms shutdown
	time.Sleep(time.Second * 5) // wait for shutdown

	if err := d.retag(&snapshotImages, &volume); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Rollback service=%+v err=%s", serviceId, err)
		if err := d.retag(&latestImages, &volume); err != nil {
			glog.Errorf("DistributedFileSystem.Rollback unable to restore images service=%+v err=%s", serviceId, err)
		}
		return err
	}

	glog.V(2).Infof("performing rollback on %s to %s", tenantId, snapshotId)
	if err := volume.Rollback(snapshotId); err != nil {
		glog.V(2).Infof("DistributedFileSystem.Rollback service=%+v err=%s", serviceId, err)
		if err := d.retag(&latestImages, &volume); err != nil {
			glog.Errorf("DistributedFileSystem.Rollback unable to restore images service=%+v err=%s", serviceId, err)
		}
		return err
	}
	var unusedStr string = ""

	return d.client.StartService(tenantId, &unusedStr)
}

func getSnapshotLabel(v *volume.Volume) string {
	format := "20060102-150405"
	loc := time.Now()
	utc := loc.UTC()
	return fmt.Sprintf("%s_%s", v.Name(), utc.Format(format))
}<|endoftext|>


<commit_before>package core

import (
	"fmt"
	"strings"
	"time"
)

type DbType string

type Uri struct {
	DbType  DbType
	Proto   string
	Host    string
	Port    string
	DbName  string
	User    string
	Passwd  string
	Charset string
	Laddr   string
	Raddr   string
	Timeout time.Duration
}

// a dialect is a driver's wrapper
type Dialect interface {
	SetLogger(logger ILogger)
	Init(*DB, *Uri, string, string) error
	URI() *Uri
	DB() *DB
	DBType() DbType
	SqlType(*Column) string
	FormatBytes(b []byte) string

	DriverName() string
	DataSourceName() string

	QuoteStr() string
	IsReserved(string) bool
	Quote(string) string
	AndStr() string
	OrStr() string
	EqStr() string
	RollBackStr() string
	AutoIncrStr() string

	SupportInsertMany() bool
	SupportEngine() bool
	SupportCharset() bool
	SupportDropIfExists() bool
	IndexOnTable() bool
	ShowCreateNull() bool

	IndexCheckSql(tableName, idxName string) (string, []interface{})
	TableCheckSql(tableName string) (string, []interface{})

	IsColumnExist(tableName string, colName string) (bool, error)

	CreateTableSql(table *Table, tableName, storeEngine, charset string) string
	DropTableSql(tableName string) string
	CreateIndexSql(tableName string, index *Index) string
	DropIndexSql(tableName string, index *Index) string

	ModifyColumnSql(tableName string, col *Column) string

	//CreateTableIfNotExists(table *Table, tableName, storeEngine, charset string) error
	//MustDropTable(tableName string) error

	GetColumns(tableName string) ([]string, map[string]*Column, error)
	GetTables() ([]*Table, error)
	GetIndexes(tableName string) (map[string]*Index, error)

	Filters() []Filter
}

func OpenDialect(dialect Dialect) (*DB, error) {
	return Open(dialect.DriverName(), dialect.DataSourceName())
}

type Base struct {
	db             *DB
	dialect        Dialect
	driverName     string
	dataSourceName string
	Logger         ILogger
	*Uri
}

func (b *Base) DB() *DB {
	return b.db
}

func (b *Base) SetLogger(logger ILogger) {
	b.Logger = logger
}

func (b *Base) Init(db *DB, dialect Dialect, uri *Uri, drivername, dataSourceName string) error {
	b.db, b.dialect, b.Uri = db, dialect, uri
	b.driverName, b.dataSourceName = drivername, dataSourceName
	return nil
}

func (b *Base) URI() *Uri {
	return b.Uri
}

func (b *Base) DBType() DbType {
	return b.Uri.DbType
}

func (b *Base) FormatBytes(bs []byte) string {
	return fmt.Sprintf("0x%x", bs)
}

func (b *Base) DriverName() string {
	return b.driverName
}

func (b *Base) ShowCreateNull() bool {
	return true
}

func (b *Base) DataSourceName() string {
	return b.dataSourceName
}

func (b *Base) AndStr() string {
	return "AND"
}

func (b *Base) OrStr() string {
	return "OR"
}

func (b *Base) EqStr() string {
	return "="
}

func (db *Base) RollBackStr() string {
	return "ROLL BACK"
}

func (db *Base) SupportDropIfExists() bool {
	return true
}

func (db *Base) DropTableSql(tableName string) string {
	return fmt.Sprintf("DROP TABLE IF EXISTS `%s`", tableName)
}

func (db *Base) HasRecords(query string, args ...interface{}) (bool, error) {
	rows, err := db.DB().Query(query, args...)
	if db.Logger != nil {
		db.Logger.Info("[sql]", query, args)
	}
	if err != nil {
		return false, err
	}
	defer rows.Close()

	if rows.Next() {
		return true, nil
	}
	return false, nil
}

func (db *Base) IsColumnExist(tableName, colName string) (bool, error) {
	query := "SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `COLUMN_NAME` = ?"
	query = strings.Replace(query, "`", db.dialect.QuoteStr(), -1)
	return db.HasRecords(query, db.DbName, tableName, colName)
}

/*
func (db *Base) CreateTableIfNotExists(table *Table, tableName, storeEngine, charset string) error {
	sql, args := db.dialect.TableCheckSql(tableName)
	rows, err := db.DB().Query(sql, args...)
	if db.Logger != nil {
		db.Logger.Info("[sql]", sql, args)
	}
	if err != nil {
		return err
	}
	defer rows.Close()

	if rows.Next() {
		return nil
	}

	sql = db.dialect.CreateTableSql(table, tableName, storeEngine, charset)
	_, err = db.DB().Exec(sql)
	if db.Logger != nil {
		db.Logger.Info("[sql]", sql)
	}
	return err
}*/

func (db *Base) CreateIndexSql(tableName string, index *Index) string {
	quote := db.dialect.Quote
	var unique string
	var idxName string
	if index.Type == UniqueType {
		unique = " UNIQUE"
	}
	idxName = index.XName(tableName)
	return fmt.Sprintf("CREATE%s INDEX %v ON %v (%v)", unique,
		quote(idxName), quote(tableName),
		quote(strings.Join(index.Cols, quote(","))))
}

func (db *Base) DropIndexSql(tableName string, index *Index) string {
	quote := db.dialect.Quote
	var name string
	if index.IsRegular {
		name = index.XName(tableName)
	} else {
		name = index.Name
	}
	return fmt.Sprintf("DROP INDEX %v ON %s", quote(name), quote(tableName))
}

func (db *Base) ModifyColumnSql(tableName string, col *Column) string {
	return fmt.Sprintf("alter table %s MODIFY COLUMN %s", tableName, col.StringNoPk(db.dialect))
}

func (b *Base) CreateTableSql(table *Table, tableName, storeEngine, charset string) string {
	var sql string
	sql = "CREATE TABLE IF NOT EXISTS "
	if tableName == "" {
		tableName = table.Name
	}

	sql += b.dialect.Quote(tableName)
	sql += " ("

	if len(table.ColumnsSeq()) > 0 {
		pkList := table.PrimaryKeys

		for _, colName := range table.ColumnsSeq() {
			col := table.GetColumn(colName)
			if col.IsPrimaryKey && len(pkList) == 1 {
				sql += col.String(b.dialect)
			} else {
				sql += col.StringNoPk(b.dialect)
			}
			sql = strings.TrimSpace(sql)
			sql += ", "
		}

		if len(pkList) > 1 {
			sql += "PRIMARY KEY ( "
			sql += b.dialect.Quote(strings.Join(pkList, b.dialect.Quote(",")))
			sql += " ), "
		}

		sql = sql[:len(sql)-2]
	}
	sql += ")"

	if b.dialect.SupportEngine() && storeEngine != "" {
		sql += " ENGINE=" + storeEngine
	}
	if b.dialect.SupportCharset() {
		if len(charset) == 0 {
			charset = b.dialect.URI().Charset
		}
		if len(charset) > 0 {
			sql += " DEFAULT CHARSET " + charset
		}
	}

	return sql
}

var (
	dialects = map[DbType]func() Dialect{}
)

func RegisterDialect(dbName DbType, dialectFunc func() Dialect) {
	if dialectFunc == nil {
		panic("core: Register dialect is nil")
	}
	dialects[dbName] = dialectFunc // !nashtsai! allow override dialect
}

func QueryDialect(dbName DbType) Dialect {
	return dialects[dbName]()
}
<commit_msg> Added feature for SELECT ... FOR UPDATE go-xorm/xorm #290<commit_after>package core

import (
	"fmt"
	"strings"
	"time"
)

type DbType string

type Uri struct {
	DbType  DbType
	Proto   string
	Host    string
	Port    string
	DbName  string
	User    string
	Passwd  string
	Charset string
	Laddr   string
	Raddr   string
	Timeout time.Duration
}

// a dialect is a driver's wrapper
type Dialect interface {
	SetLogger(logger ILogger)
	Init(*DB, *Uri, string, string) error
	URI() *Uri
	DB() *DB
	DBType() DbType
	SqlType(*Column) string
	FormatBytes(b []byte) string

	DriverName() string
	DataSourceName() string

	QuoteStr() string
	IsReserved(string) bool
	Quote(string) string
	AndStr() string
	OrStr() string
	EqStr() string
	RollBackStr() string
	AutoIncrStr() string

	SupportInsertMany() bool
	SupportEngine() bool
	SupportCharset() bool
	SupportDropIfExists() bool
	IndexOnTable() bool
	ShowCreateNull() bool

	IndexCheckSql(tableName, idxName string) (string, []interface{})
	TableCheckSql(tableName string) (string, []interface{})

	IsColumnExist(tableName string, colName string) (bool, error)

	CreateTableSql(table *Table, tableName, storeEngine, charset string) string
	DropTableSql(tableName string) string
	CreateIndexSql(tableName string, index *Index) string
	DropIndexSql(tableName string, index *Index) string

	ModifyColumnSql(tableName string, col *Column) string

	ForUpdateSql(query string) string

	//CreateTableIfNotExists(table *Table, tableName, storeEngine, charset string) error
	//MustDropTable(tableName string) error

	GetColumns(tableName string) ([]string, map[string]*Column, error)
	GetTables() ([]*Table, error)
	GetIndexes(tableName string) (map[string]*Index, error)

	Filters() []Filter
}

func OpenDialect(dialect Dialect) (*DB, error) {
	return Open(dialect.DriverName(), dialect.DataSourceName())
}

type Base struct {
	db             *DB
	dialect        Dialect
	driverName     string
	dataSourceName string
	Logger         ILogger
	*Uri
}

func (b *Base) DB() *DB {
	return b.db
}

func (b *Base) SetLogger(logger ILogger) {
	b.Logger = logger
}

func (b *Base) Init(db *DB, dialect Dialect, uri *Uri, drivername, dataSourceName string) error {
	b.db, b.dialect, b.Uri = db, dialect, uri
	b.driverName, b.dataSourceName = drivername, dataSourceName
	return nil
}

func (b *Base) URI() *Uri {
	return b.Uri
}

func (b *Base) DBType() DbType {
	return b.Uri.DbType
}

func (b *Base) FormatBytes(bs []byte) string {
	return fmt.Sprintf("0x%x", bs)
}

func (b *Base) DriverName() string {
	return b.driverName
}

func (b *Base) ShowCreateNull() bool {
	return true
}

func (b *Base) DataSourceName() string {
	return b.dataSourceName
}

func (b *Base) AndStr() string {
	return "AND"
}

func (b *Base) OrStr() string {
	return "OR"
}

func (b *Base) EqStr() string {
	return "="
}

func (db *Base) RollBackStr() string {
	return "ROLL BACK"
}

func (db *Base) SupportDropIfExists() bool {
	return true
}

func (db *Base) DropTableSql(tableName string) string {
	return fmt.Sprintf("DROP TABLE IF EXISTS `%s`", tableName)
}

func (db *Base) HasRecords(query string, args ...interface{}) (bool, error) {
	rows, err := db.DB().Query(query, args...)
	if db.Logger != nil {
		db.Logger.Info("[sql]", query, args)
	}
	if err != nil {
		return false, err
	}
	defer rows.Close()

	if rows.Next() {
		return true, nil
	}
	return false, nil
}

func (db *Base) IsColumnExist(tableName, colName string) (bool, error) {
	query := "SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `COLUMN_NAME` = ?"
	query = strings.Replace(query, "`", db.dialect.QuoteStr(), -1)
	return db.HasRecords(query, db.DbName, tableName, colName)
}

/*
func (db *Base) CreateTableIfNotExists(table *Table, tableName, storeEngine, charset string) error {
	sql, args := db.dialect.TableCheckSql(tableName)
	rows, err := db.DB().Query(sql, args...)
	if db.Logger != nil {
		db.Logger.Info("[sql]", sql, args)
	}
	if err != nil {
		return err
	}
	defer rows.Close()

	if rows.Next() {
		return nil
	}

	sql = db.dialect.CreateTableSql(table, tableName, storeEngine, charset)
	_, err = db.DB().Exec(sql)
	if db.Logger != nil {
		db.Logger.Info("[sql]", sql)
	}
	return err
}*/

func (db *Base) CreateIndexSql(tableName string, index *Index) string {
	quote := db.dialect.Quote
	var unique string
	var idxName string
	if index.Type == UniqueType {
		unique = " UNIQUE"
	}
	idxName = index.XName(tableName)
	return fmt.Sprintf("CREATE%s INDEX %v ON %v (%v)", unique,
		quote(idxName), quote(tableName),
		quote(strings.Join(index.Cols, quote(","))))
}

func (db *Base) DropIndexSql(tableName string, index *Index) string {
	quote := db.dialect.Quote
	var name string
	if index.IsRegular {
		name = index.XName(tableName)
	} else {
		name = index.Name
	}
	return fmt.Sprintf("DROP INDEX %v ON %s", quote(name), quote(tableName))
}

func (db *Base) ModifyColumnSql(tableName string, col *Column) string {
	return fmt.Sprintf("alter table %s MODIFY COLUMN %s", tableName, col.StringNoPk(db.dialect))
}

func (b *Base) CreateTableSql(table *Table, tableName, storeEngine, charset string) string {
	var sql string
	sql = "CREATE TABLE IF NOT EXISTS "
	if tableName == "" {
		tableName = table.Name
	}

	sql += b.dialect.Quote(tableName)
	sql += " ("

	if len(table.ColumnsSeq()) > 0 {
		pkList := table.PrimaryKeys

		for _, colName := range table.ColumnsSeq() {
			col := table.GetColumn(colName)
			if col.IsPrimaryKey && len(pkList) == 1 {
				sql += col.String(b.dialect)
			} else {
				sql += col.StringNoPk(b.dialect)
			}
			sql = strings.TrimSpace(sql)
			sql += ", "
		}

		if len(pkList) > 1 {
			sql += "PRIMARY KEY ( "
			sql += b.dialect.Quote(strings.Join(pkList, b.dialect.Quote(",")))
			sql += " ), "
		}

		sql = sql[:len(sql)-2]
	}
	sql += ")"

	if b.dialect.SupportEngine() && storeEngine != "" {
		sql += " ENGINE=" + storeEngine
	}
	if b.dialect.SupportCharset() {
		if len(charset) == 0 {
			charset = b.dialect.URI().Charset
		}
		if len(charset) > 0 {
			sql += " DEFAULT CHARSET " + charset
		}
	}

	return sql
}

func (b *Base) ForUpdateSql(query string) string {
	return query + " FOR UPDATE"
}

var (
	dialects = map[DbType]func() Dialect{}
)

func RegisterDialect(dbName DbType, dialectFunc func() Dialect) {
	if dialectFunc == nil {
		panic("core: Register dialect is nil")
	}
	dialects[dbName] = dialectFunc // !nashtsai! allow override dialect
}

func QueryDialect(dbName DbType) Dialect {
	return dialects[dbName]()
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"regexp"
	"strings"

	"github.com/bwmarrin/discordgo"
)

type discordConfig struct {
	Boss         string
	Name         string
	ChannelAnime string
	Username     string
	Password     string
	Debug        bool
}

const (
	discordHelp string = "***LIST OF BOT COMMANDS***\n" +
		"Fields in [] are optional\n" +
		"Fields in <> are mandatory\n\n" +
		"```" +
		"!help [bot] (Lists all bot commands)\n" +
		"!uptime [bot](Prints current bot uptime)\n" +
		"!sub <id> (Subscribe to anime)\n" +
		"!unsub <id> (Unsubscribe from anime)\n" +
		"!w <location> (Prints current weather)\n" +
		"```\n" +
		"Find anime list at http://axolotl-422.rhcloud.com/anime"
)

var (
	discordConn   *discordgo.Session           //Discord client
	discordCfg    *discordConfig               //Discord options
	relevantRegex = regexp.MustCompile(`^!\w`) //Discord regex msg parser
)

//Starts discord client
func discordConnStart(c *discordConfig) {
	discordCfg = c

	var err error
	discordConn, err = discordgo.New(c.Username, c.Password)
	if err != nil {
		log.Fatal("discordConnStart() => New() error:\t", err)
	}

	discordConn.Debug = c.Debug
	discordConn.OnMessageCreate = discordMsgHandler
	discordConn.OnReady = discordReadyHandler
	discordConn.Open()
}

func discordReadyHandler(s *discordgo.Session, r *discordgo.Ready) {
	discordCfg.Name = strings.ToUpper(r.User.Username) //Sets bot name
	for _, guild := range r.Guilds {
		if guild.Name == "422" {
			for _, channel := range guild.Channels {
				if channel.Name == "anime" {
					discordCfg.ChannelAnime = channel.ID //Sets anime channel id
				}
			}
		}
	}
	log.Println("Connected to discord as", discordCfg.Name)
}

//discord incomming message handler
func discordMsgHandler(s *discordgo.Session, m *discordgo.Message) {
	boss := m.Author.ID == discordCfg.Boss
	relevant := relevantRegex.MatchString(m.Content)

	if relevant {
		args := strings.Fields(m.Content)
		switch strings.ToUpper(args[0]) {
		case "!HELP":
			if len(args) > 1 {
				if strings.ToUpper(args[1]) == discordCfg.Name {
					s.ChannelMessageSend(m.ChannelID, discordHelp)
				}
			} else {
				s.ChannelMessageSend(m.ChannelID, discordHelp)
			}
		case "!SUB":
			if len(args) >= 2 {
				anime := anime{ID: strings.ToLower(args[1])}
				if anime.Exists() {
					anime.AddSub(m.Author.ID)
					s.ChannelMessageSend(m.ChannelID,
						"Successfully subscribed to "+anime.Name)
				} else {
					s.ChannelMessageSend(m.ChannelID, "Invalid ID")
				}
			}
		case "!UNSUB":
			if len(args) >= 2 {
				anime := anime{ID: strings.ToLower(args[1])}
				if anime.Exists() {
					anime.RemoveSub(m.Author.ID)
					s.ChannelMessageSend(m.ChannelID,
						"Successfully unsubscribed from "+anime.Name)
				} else {
					s.ChannelMessageSend(m.ChannelID, "Invalid ID")
				}
			}
		case "!UPTIME":
			if len(args) > 1 {
				if strings.ToUpper(args[1]) == discordCfg.Name {
					s.ChannelMessageSend(m.ChannelID, "Current uptime is "+getUptime())
				}
			} else {
				s.ChannelMessageSend(m.ChannelID, "Current uptime is "+getUptime())
			}
		case "!W":
			if len(args) > 1 {
				var location string
				for i := 1; i < len(args); i++ {
					location += args[i] + " "
				}
				location = location[:len(location)-1]
				s.ChannelMessageSend(m.ChannelID, getWeather(location))
			}
		case "!P":
			if boss {
				if len(args) > 1 {
					var game string
					for i := 1; i < len(args); i++ {
						game += args[i] + " "
					}
					game = game[:len(game)-1]
					discordConn.UpdateStatus(0, game)
				} else {
					discordConn.UpdateStatus(0, "")
				}
			}
		}
	}
}
<commit_msg>updated for discordgo breaking api changes<commit_after>package main

import (
	"log"
	"regexp"
	"strings"

	"github.com/bwmarrin/discordgo"
)

type discordConfig struct {
	Boss         string
	Name         string
	ChannelAnime string
	Username     string
	Password     string
	Debug        bool
}

const (
	discordHelp string = "***LIST OF BOT COMMANDS***\n" +
		"Fields in [] are optional\n" +
		"Fields in <> are mandatory\n\n" +
		"```" +
		"!help [bot] (Lists all bot commands)\n" +
		"!uptime [bot](Prints current bot uptime)\n" +
		"!sub <id> (Subscribe to anime)\n" +
		"!unsub <id> (Unsubscribe from anime)\n" +
		"!w <location> (Prints current weather)\n" +
		"```\n" +
		"Find anime list at http://axolotl-422.rhcloud.com/anime"
)

var (
	discordConn   *discordgo.Session           //Discord client
	discordCfg    *discordConfig               //Discord options
	relevantRegex = regexp.MustCompile(`^!\w`) //Discord regex msg parser
)

//Starts discord client
func discordConnStart(c *discordConfig) {
	discordCfg = c

	var err error
	discordConn, err = discordgo.New(c.Username, c.Password)
	if err != nil {
		log.Fatal("discordConnStart() => New() error:\t", err)
	}

	discordConn.Debug = c.Debug
	discordConn.AddHandler(discordMsgHandler)
	discordConn.AddHandler(discordReadyHandler)
	discordConn.Open()
}

func discordReadyHandler(s *discordgo.Session, r *discordgo.Ready) {
	discordCfg.Name = strings.ToUpper(r.User.Username) //Sets bot name
	for _, guild := range r.Guilds {
		if guild.Name == "422" {
			for _, channel := range guild.Channels {
				if channel.Name == "anime" {
					discordCfg.ChannelAnime = channel.ID //Sets anime channel id
				}
			}
		}
	}
	log.Println("Connected to discord as", discordCfg.Name)
}

//discord incomming message handler
func discordMsgHandler(s *discordgo.Session, m *discordgo.MessageCreate) {
	boss := m.Author.ID == discordCfg.Boss
	relevant := relevantRegex.MatchString(m.Content)

	if relevant {
		args := strings.Fields(m.Content)
		switch strings.ToUpper(args[0]) {
		case "!HELP":
			if len(args) > 1 {
				if strings.ToUpper(args[1]) == discordCfg.Name {
					s.ChannelMessageSend(m.ChannelID, discordHelp)
				}
			} else {
				s.ChannelMessageSend(m.ChannelID, discordHelp)
			}
		case "!SUB":
			if len(args) >= 2 {
				anime := anime{ID: strings.ToLower(args[1])}
				if anime.Exists() {
					anime.AddSub(m.Author.ID)
					s.ChannelMessageSend(m.ChannelID,
						"Successfully subscribed to "+anime.Name)
				} else {
					s.ChannelMessageSend(m.ChannelID, "Invalid ID")
				}
			}
		case "!UNSUB":
			if len(args) >= 2 {
				anime := anime{ID: strings.ToLower(args[1])}
				if anime.Exists() {
					anime.RemoveSub(m.Author.ID)
					s.ChannelMessageSend(m.ChannelID,
						"Successfully unsubscribed from "+anime.Name)
				} else {
					s.ChannelMessageSend(m.ChannelID, "Invalid ID")
				}
			}
		case "!UPTIME":
			if len(args) > 1 {
				if strings.ToUpper(args[1]) == discordCfg.Name {
					s.ChannelMessageSend(m.ChannelID, "Current uptime is "+getUptime())
				}
			} else {
				s.ChannelMessageSend(m.ChannelID, "Current uptime is "+getUptime())
			}
		case "!W":
			if len(args) > 1 {
				var location string
				for i := 1; i < len(args); i++ {
					location += args[i] + " "
				}
				location = location[:len(location)-1]
				s.ChannelMessageSend(m.ChannelID, getWeather(location))
			}
		case "!P":
			if boss {
				if len(args) > 1 {
					var game string
					for i := 1; i < len(args); i++ {
						game += args[i] + " "
					}
					game = game[:len(game)-1]
					discordConn.UpdateStatus(0, game)
				} else {
					discordConn.UpdateStatus(0, "")
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains high level helper functions and easy entry points for the
// entire discordgo package.  These functions are beling developed and are very
// experimental at this point.  They will most likley change so please use the
// low level functions if that's a problem.

// package discordgo provides Discord binding for Go
package discordgo

import "fmt"

// Discordgo Version, follows Symantic Versioning. (http://semver.org/)
const VERSION = "0.10.0-alpha"

// New creates a new Discord session and will automate some startup
// tasks if given enough information to do so.  Currently you can pass zero
// arguments and it will return an empty Discord session.
// There are 3 ways to call New:
//     With a single auth token - All requests will use the token blindly,
//         no verification of the token will be done and requests may fail.
//     With an email and password - Discord will sign in with the provided
//         credentials.
//     With an email, password and auth token - Discord will verify the auth
//         token, if it is invalid it will sign in with the provided
//         credentials. This is the Discord recommended way to sign in.
func New(args ...interface{}) (s *Session, err error) {

	// Create an empty Session interface.
	s = &Session{
		State:                  NewState(),
		StateEnabled:           true,
		Compress:               true,
		ShouldReconnectOnError: true,
	}

	// If no arguments are passed return the empty Session interface.
	// Later I will add default values, if appropriate.
	if args == nil {
		return
	}

	// Variables used below when parsing func arguments
	var auth, pass string

	// Parse passed arguments
	for _, arg := range args {

		switch v := arg.(type) {

		case []string:
			if len(v) > 3 {
				err = fmt.Errorf("Too many string parameters provided.")
				return
			}

			// First string is either token or username
			if len(v) > 0 {
				auth = v[0]
			}

			// If second string exists, it must be a password.
			if len(v) > 1 {
				pass = v[1]
			}

			// If third string exists, it must be an auth token.
			if len(v) > 2 {
				s.Token = v[2]
			}

		case string:
			// First string must be either auth token or username.
			// Second string must be a password.
			// Only 2 input strings are supported.

			if auth == "" {
				auth = v
			} else if pass == "" {
				pass = v
			} else if s.Token == "" {
				s.Token = v
			} else {
				err = fmt.Errorf("Too many string parameters provided.")
				return
			}

			//		case Config:
			// TODO: Parse configuration

		default:
			err = fmt.Errorf("Unsupported parameter type provided.")
			return
		}
	}

	// If only one string was provided, assume it is an auth token.
	// Otherwise get auth token from Discord, if a token was specified
	// Discord will verify it for free, or log the user in if it is
	// invalid.
	if pass == "" {
		s.Token = auth
	} else {
		err = s.Login(auth, pass)
		if err != nil || s.Token == "" {
			err = fmt.Errorf("Unable to fetch discord authentication token. %v", err)
			return
		}
	}

	// The Session is now able to have RestAPI methods called on it.
	// It is recommended that you now call Open() so that events will trigger.

	return
}
<commit_msg>Bump version to v0.10.0<commit_after>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains high level helper functions and easy entry points for the
// entire discordgo package.  These functions are beling developed and are very
// experimental at this point.  They will most likley change so please use the
// low level functions if that's a problem.

// package discordgo provides Discord binding for Go
package discordgo

import "fmt"

// Discordgo Version, follows Symantic Versioning. (http://semver.org/)
const VERSION = "0.10.0"

// New creates a new Discord session and will automate some startup
// tasks if given enough information to do so.  Currently you can pass zero
// arguments and it will return an empty Discord session.
// There are 3 ways to call New:
//     With a single auth token - All requests will use the token blindly,
//         no verification of the token will be done and requests may fail.
//     With an email and password - Discord will sign in with the provided
//         credentials.
//     With an email, password and auth token - Discord will verify the auth
//         token, if it is invalid it will sign in with the provided
//         credentials. This is the Discord recommended way to sign in.
func New(args ...interface{}) (s *Session, err error) {

	// Create an empty Session interface.
	s = &Session{
		State:                  NewState(),
		StateEnabled:           true,
		Compress:               true,
		ShouldReconnectOnError: true,
	}

	// If no arguments are passed return the empty Session interface.
	// Later I will add default values, if appropriate.
	if args == nil {
		return
	}

	// Variables used below when parsing func arguments
	var auth, pass string

	// Parse passed arguments
	for _, arg := range args {

		switch v := arg.(type) {

		case []string:
			if len(v) > 3 {
				err = fmt.Errorf("Too many string parameters provided.")
				return
			}

			// First string is either token or username
			if len(v) > 0 {
				auth = v[0]
			}

			// If second string exists, it must be a password.
			if len(v) > 1 {
				pass = v[1]
			}

			// If third string exists, it must be an auth token.
			if len(v) > 2 {
				s.Token = v[2]
			}

		case string:
			// First string must be either auth token or username.
			// Second string must be a password.
			// Only 2 input strings are supported.

			if auth == "" {
				auth = v
			} else if pass == "" {
				pass = v
			} else if s.Token == "" {
				s.Token = v
			} else {
				err = fmt.Errorf("Too many string parameters provided.")
				return
			}

			//		case Config:
			// TODO: Parse configuration

		default:
			err = fmt.Errorf("Unsupported parameter type provided.")
			return
		}
	}

	// If only one string was provided, assume it is an auth token.
	// Otherwise get auth token from Discord, if a token was specified
	// Discord will verify it for free, or log the user in if it is
	// invalid.
	if pass == "" {
		s.Token = auth
	} else {
		err = s.Login(auth, pass)
		if err != nil || s.Token == "" {
			err = fmt.Errorf("Unable to fetch discord authentication token. %v", err)
			return
		}
	}

	// The Session is now able to have RestAPI methods called on it.
	// It is recommended that you now call Open() so that events will trigger.

	return
}
<|endoftext|>


<commit_before>package mango

import (
	"http"
	"io/ioutil"
	"testing"
	"bytes"
	"log"
	"runtime"
)

var testServer = Stack{}
var loggerBuffer = &bytes.Buffer{}
var client = http.Client{}

func helloWorld(env Env) (Status, Headers, Body) {
	return 200, Headers{}, Body("Hello World!")
}

func loggerTestServer(env Env) (Status, Headers, Body) {
	env.Logger().Println("Never gonna give you up")
	return 200, Headers{}, Body("Hello World!")
}

func sessionsTestServer(env Env) (Status, Headers, Body) {
	env.Session()["test_attribute"] = "Never gonna give you up"
	return 200, Headers{}, Body("Hello World!")
}

func showErrorsTestServer(env Env) (Status, Headers, Body) {
	panic("foo!")
	return 200, Headers{}, Body("Hello World!")
}

func routingATestServer(env Env) (Status, Headers, Body) {
	return 200, Headers{}, Body("Server A")
}

func routingBTestServer(env Env) (Status, Headers, Body) {
	return 200, Headers{}, Body("Server B")
}

func init() {
	runtime.GOMAXPROCS(4)

	testRoutes := make(map[string]App)

	testRoutes["/hello"] = new(Stack).Compile(helloWorld)

	loggerStack := new(Stack)
	custom_logger := log.New(loggerBuffer, "prefixed:", 0)
	loggerStack.Middleware(Logger(custom_logger))
	testRoutes["/logger"] = loggerStack.Compile(loggerTestServer)

	sessionsStack := new(Stack)
	sessionsStack.Middleware(Sessions("my_secret", "my_key", ".my.domain.com"))
	testRoutes["/sessions"] = sessionsStack.Compile(sessionsTestServer)

	showErrorsStack := new(Stack)
	showErrorsStack.Middleware(ShowErrors("{Error|html}"))
	testRoutes["/show_errors"] = showErrorsStack.Compile(showErrorsTestServer)

	routingStack := new(Stack)
	routingTestRoutes := make(map[string]App)
	routingTestRoutes["/routing/[0-9]+"] = routingATestServer
	routingTestRoutes["/routing/[a-z]+"] = routingBTestServer
	routingStack.Middleware(Routing(routingTestRoutes))
	testRoutes["/routing(.*)"] = routingStack.Compile(helloWorld)

	testServer.Middleware(Routing(testRoutes))
	testServer.Address = "localhost:3000"
	go testServer.Run(helloWorld)
}

func TestHelloWorld(t *testing.T) {
	// Request against it
	response, _, err := client.Get("http://localhost:3000/hello")

	if err != nil {
		t.Error(err)
	}

	if response.StatusCode != 200 {
		t.Error("Expected status to equal 200, got:", response.StatusCode)
	}

	body, _ := ioutil.ReadAll(response.Body)
	if string(body) != "Hello World!" {
		t.Error("Expected body:", body, "to equal: \"Hello World!\"")
	}
}

func BenchmarkHelloWorld(b *testing.B) {
	for i := 0; i < b.N; i++ {
		client.Get("http://localhost:3000/hello")
	}
}

func TestLogger(t *testing.T) {
	// Request against it
	response, _, err := client.Get("http://localhost:3000/logger")

	if err != nil {
		t.Error(err)
	}

	if response.StatusCode != 200 {
		t.Error("Expected status to equal 200, got:", response.StatusCode)
	}

	expected := "prefixed:Never gonna give you up\n"
	if loggerBuffer.String() != expected {
		t.Error("Expected logger to print: \"", expected, "\" got: \"", loggerBuffer.String(), "\"")
	}
}

func BenchmarkLogger(b *testing.B) {
	for i := 0; i < b.N; i++ {
		client.Get("http://localhost:3000/logger")
	}
}

func TestSessions(t *testing.T) {
	// Request against it
	response, _, err := client.Get("http://localhost:3000/sessions")

	if err != nil {
		t.Error(err)
	}

	if response.StatusCode != 200 {
		t.Error("Expected status to equal 200, got:", response.StatusCode)
	}

	expected_name := "my_key"
	if response.SetCookie[0].Name != expected_name {
		t.Error("Expected Set-Cookie name to equal: \"", expected_name, "\" got: \"", response.SetCookie[0].Name, "\"")
	}

	// base 64 encoded, hashed, and gob encoded stuff
	expected_value := "Dv+BBAEC/4IAAQwBEAAANf+CAAEOdGVzdF9hdHRyaWJ1dGUGc3RyaW5nDBkAF05ldmVyIGdvbm5hIGdpdmUgeW91IHVw--q0x2Xt9XBekiKpL2/MlQ50TcOqg="
	if response.SetCookie[0].Value != expected_value {
		t.Error("Expected Set-Cookie value to equal: \"", expected_value, "\" got: \"", response.SetCookie[0].Value, "\"")
	}

	expected_domain := ".my.domain.com"
	if response.SetCookie[0].Domain != expected_domain {
		t.Error("Expected Set-Cookie domain to equal: \"", expected_domain, "\" got: \"", response.SetCookie[0].Domain, "\"")
	}
}

func BenchmarkSessions(b *testing.B) {
	for i := 0; i < b.N; i++ {
		client.Get("http://localhost:3000/sessions")
	}
}

func TestShowErrors(t *testing.T) {
	// Request against it
	response, _, err := client.Get("http://localhost:3000/show_errors")

	if err != nil {
		t.Error(err)
	}

	if response.StatusCode != 500 {
		t.Error("Expected status to equal 500, got:", response.StatusCode)
	}

	expected := "foo!"
	got, _ := ioutil.ReadAll(response.Body)
	if string(got) != expected {
		t.Error("Expected response body to equal: \"", expected, "\" got: \"", string(got), "\"")
	}
}

func BenchmarkShowErrors(b *testing.B) {
	for i := 0; i < b.N; i++ {
		client.Get("http://localhost:3000/show_errors")
	}
}

func TestRouting(t *testing.T) {
	// Request server a
	response, _, err := client.Get("http://localhost:3000/routing/123")

	if err != nil {
		t.Error(err)
	}

	if response.StatusCode != 200 {
		t.Error("Expected status to equal 200, got:", response.StatusCode)
	}

	expected := "Server A"
	body, _ := ioutil.ReadAll(response.Body)
	if string(body) != expected {
		t.Error("Expected response body to equal: \"", expected, "\" got: \"", string(body), "\"")
	}

	// Request server b
	response, _, err = client.Get("http://localhost:3000/routing/abc")

	if err != nil {
		t.Error(err)
	}

	if response.StatusCode != 200 {
		t.Error("Expected status to equal 200, got:", response.StatusCode)
	}

	expected = "Server B"
	body, _ = ioutil.ReadAll(response.Body)
	if string(body) != expected {
		t.Error("Expected response body to equal: \"", expected, "\" got: \"", string(body), "\"")
	}
}

func BenchmarkRouting(b *testing.B) {
	for i := 0; i < b.N; i++ {
		client.Get("http://localhost:3000/routing/123")
	}
}
<commit_msg>Adding full stack benchmark<commit_after>package mango

import (
	"http"
	"io/ioutil"
	"testing"
	"bytes"
	"log"
	"runtime"
)

var testServer = Stack{}
var loggerBuffer = &bytes.Buffer{}
var client = http.Client{}

func helloWorld(env Env) (Status, Headers, Body) {
	return 200, Headers{}, Body("Hello World!")
}

func loggerTestServer(env Env) (Status, Headers, Body) {
	env.Logger().Println("Never gonna give you up")
	return 200, Headers{}, Body("Hello World!")
}

func sessionsTestServer(env Env) (Status, Headers, Body) {
	env.Session()["test_attribute"] = "Never gonna give you up"
	return 200, Headers{}, Body("Hello World!")
}

func showErrorsTestServer(env Env) (Status, Headers, Body) {
	panic("foo!")
	return 200, Headers{}, Body("Hello World!")
}

func routingATestServer(env Env) (Status, Headers, Body) {
	return 200, Headers{}, Body("Server A")
}

func routingBTestServer(env Env) (Status, Headers, Body) {
	return 200, Headers{}, Body("Server B")
}

func init() {
	runtime.GOMAXPROCS(4)

	testRoutes := make(map[string]App)

	testRoutes["/hello"] = new(Stack).Compile(helloWorld)

	loggerStack := new(Stack)
	custom_logger := log.New(loggerBuffer, "prefixed:", 0)
	loggerStack.Middleware(Logger(custom_logger))
	testRoutes["/logger"] = loggerStack.Compile(loggerTestServer)

	sessionsStack := new(Stack)
	sessionsStack.Middleware(Sessions("my_secret", "my_key", ".my.domain.com"))
	testRoutes["/sessions"] = sessionsStack.Compile(sessionsTestServer)

	showErrorsStack := new(Stack)
	showErrorsStack.Middleware(ShowErrors("{Error|html}"))
	testRoutes["/show_errors"] = showErrorsStack.Compile(showErrorsTestServer)

	routingStack := new(Stack)
	routingTestRoutes := make(map[string]App)
	routingTestRoutes["/routing/[0-9]+"] = routingATestServer
	routingTestRoutes["/routing/[a-z]+"] = routingBTestServer
	routingStack.Middleware(Routing(routingTestRoutes))
	testRoutes["/routing(.*)"] = routingStack.Compile(helloWorld)

	fullStack := new(Stack)
	fullStackTestRoutes := make(map[string]App)
	fullStackTestRoutes["/full_stack/[0-9]+"] = routingATestServer
	fullStackTestRoutes["/full_stack/[a-z]+"] = routingBTestServer
	fullStack.Middleware(ShowErrors("{Error|html}"),
		Logger(custom_logger),
		Routing(fullStackTestRoutes))
	testRoutes["/full_stack(.*)"] = fullStack.Compile(helloWorld)

	testServer.Middleware(Routing(testRoutes))
	testServer.Address = "localhost:3000"
	go testServer.Run(helloWorld)
}

func TestHelloWorld(t *testing.T) {
	// Request against it
	response, _, err := client.Get("http://localhost:3000/hello")

	if err != nil {
		t.Error(err)
	}

	if response.StatusCode != 200 {
		t.Error("Expected status to equal 200, got:", response.StatusCode)
	}

	body, _ := ioutil.ReadAll(response.Body)
	if string(body) != "Hello World!" {
		t.Error("Expected body:", body, "to equal: \"Hello World!\"")
	}
}

func BenchmarkHelloWorld(b *testing.B) {
	for i := 0; i < b.N; i++ {
		client.Get("http://localhost:3000/hello")
	}
}

func TestLogger(t *testing.T) {
	// Request against it
	response, _, err := client.Get("http://localhost:3000/logger")

	if err != nil {
		t.Error(err)
	}

	if response.StatusCode != 200 {
		t.Error("Expected status to equal 200, got:", response.StatusCode)
	}

	expected := "prefixed:Never gonna give you up\n"
	if loggerBuffer.String() != expected {
		t.Error("Expected logger to print: \"", expected, "\" got: \"", loggerBuffer.String(), "\"")
	}
}

func BenchmarkLogger(b *testing.B) {
	for i := 0; i < b.N; i++ {
		client.Get("http://localhost:3000/logger")
	}
}

func TestSessions(t *testing.T) {
	// Request against it
	response, _, err := client.Get("http://localhost:3000/sessions")

	if err != nil {
		t.Error(err)
	}

	if response.StatusCode != 200 {
		t.Error("Expected status to equal 200, got:", response.StatusCode)
	}

	expected_name := "my_key"
	if response.SetCookie[0].Name != expected_name {
		t.Error("Expected Set-Cookie name to equal: \"", expected_name, "\" got: \"", response.SetCookie[0].Name, "\"")
	}

	// base 64 encoded, hashed, and gob encoded stuff
	expected_value := "Dv+BBAEC/4IAAQwBEAAANf+CAAEOdGVzdF9hdHRyaWJ1dGUGc3RyaW5nDBkAF05ldmVyIGdvbm5hIGdpdmUgeW91IHVw--q0x2Xt9XBekiKpL2/MlQ50TcOqg="
	if response.SetCookie[0].Value != expected_value {
		t.Error("Expected Set-Cookie value to equal: \"", expected_value, "\" got: \"", response.SetCookie[0].Value, "\"")
	}

	expected_domain := ".my.domain.com"
	if response.SetCookie[0].Domain != expected_domain {
		t.Error("Expected Set-Cookie domain to equal: \"", expected_domain, "\" got: \"", response.SetCookie[0].Domain, "\"")
	}
}

func BenchmarkSessions(b *testing.B) {
	for i := 0; i < b.N; i++ {
		client.Get("http://localhost:3000/sessions")
	}
}

func TestShowErrors(t *testing.T) {
	// Request against it
	response, _, err := client.Get("http://localhost:3000/show_errors")

	if err != nil {
		t.Error(err)
	}

	if response.StatusCode != 500 {
		t.Error("Expected status to equal 500, got:", response.StatusCode)
	}

	expected := "foo!"
	got, _ := ioutil.ReadAll(response.Body)
	if string(got) != expected {
		t.Error("Expected response body to equal: \"", expected, "\" got: \"", string(got), "\"")
	}
}

func BenchmarkShowErrors(b *testing.B) {
	for i := 0; i < b.N; i++ {
		client.Get("http://localhost:3000/show_errors")
	}
}

func TestRouting(t *testing.T) {
	// Request server a
	response, _, err := client.Get("http://localhost:3000/routing/123")

	if err != nil {
		t.Error(err)
	}

	if response.StatusCode != 200 {
		t.Error("Expected status to equal 200, got:", response.StatusCode)
	}

	expected := "Server A"
	body, _ := ioutil.ReadAll(response.Body)
	if string(body) != expected {
		t.Error("Expected response body to equal: \"", expected, "\" got: \"", string(body), "\"")
	}

	// Request server b
	response, _, err = client.Get("http://localhost:3000/routing/abc")

	if err != nil {
		t.Error(err)
	}

	if response.StatusCode != 200 {
		t.Error("Expected status to equal 200, got:", response.StatusCode)
	}

	expected = "Server B"
	body, _ = ioutil.ReadAll(response.Body)
	if string(body) != expected {
		t.Error("Expected response body to equal: \"", expected, "\" got: \"", string(body), "\"")
	}
}

func BenchmarkRouting(b *testing.B) {
	for i := 0; i < b.N; i++ {
		client.Get("http://localhost:3000/routing/123")
	}
}

func BenchmarkFullStack(b *testing.B) {
	for i := 0; i < b.N; i++ {
		client.Get("http://localhost:3000/full_stack/123")
	}
}
<|endoftext|>


<commit_before>package env_test

import (
	"fmt"
	"os"
	"testing"

	"github.com/caarlos0/env"
	"github.com/stretchr/testify/assert"
)

type Config struct {
	Some        string `env:"somevar"`
	Other       bool   `env:"othervar"`
	Port        int    `env:"PORT"`
	NotAnEnv    string
	DatabaseURL string   `env:"DATABASE_URL" envDefault:"postgres://localhost:5432/db"`
	Strings     []string `env:"STRINGS"`
	SepStrings  []string `env:"SEPSTRINGS" envSeparator:":"`
	Numbers     []int    `env:"NUMBERS"`
	Bools       []bool   `env:"BOOLS"`
}

func TestParsesEnv(t *testing.T) {
	os.Setenv("somevar", "somevalue")
	os.Setenv("othervar", "true")
	os.Setenv("PORT", "8080")
	os.Setenv("STRINGS", "string1,string2,string3")
	os.Setenv("SEPSTRINGS", "string1:string2:string3")
	os.Setenv("NUMBERS", "1,2,3,4")
	os.Setenv("BOOLS", "t,TRUE,0,1")

	defer os.Setenv("somevar", "")
	defer os.Setenv("othervar", "")
	defer os.Setenv("PORT", "")
	defer os.Setenv("STRINGS", "")
	defer os.Setenv("SEPSTRINGS", "")
	defer os.Setenv("NUMBERS", "")
	defer os.Setenv("BOOLS", "")

	cfg := Config{}
	assert.NoError(t, env.Parse(&cfg))
	assert.Equal(t, "somevalue", cfg.Some)
	assert.Equal(t, true, cfg.Other)
	assert.Equal(t, 8080, cfg.Port)
	assert.Equal(t, []string{"string1", "string2", "string3"}, cfg.Strings)
	assert.Equal(t, []string{"string1", "string2", "string3"}, cfg.SepStrings)
	assert.Equal(t, []int{1, 2, 3, 4}, cfg.Numbers)
	assert.Equal(t, []bool{true, true, false, true}, cfg.Bools)
}

func TestEmptyVars(t *testing.T) {
	cfg := Config{}
	assert.NoError(t, env.Parse(&cfg))
	assert.Equal(t, "", cfg.Some)
	assert.Equal(t, false, cfg.Other)
	assert.Equal(t, 0, cfg.Port)
}

func TestPassAnInvalidPtr(t *testing.T) {
	var thisShouldBreak int
	assert.Error(t, env.Parse(&thisShouldBreak))
}

func TestPassReference(t *testing.T) {
	cfg := Config{}
	assert.Error(t, env.Parse(cfg))
}

func TestInvalidBool(t *testing.T) {
	os.Setenv("othervar", "should-be-a-bool")
	defer os.Setenv("othervar", "")

	cfg := Config{}
	assert.Error(t, env.Parse(&cfg))
}

func TestInvalidInt(t *testing.T) {
	os.Setenv("PORT", "should-be-an-int")
	defer os.Setenv("PORT", "")

	cfg := Config{}
	assert.Error(t, env.Parse(&cfg))
}

func TestInvalidBoolsSlice(t *testing.T) {
	type config struct {
		BadBools []bool `env:"BADBOOLS"`
	}

	os.Setenv("BADBOOLS", "t,f,TRUE,faaaalse")
	cfg := &config{}
	assert.Error(t, env.Parse(cfg))
}

func TestParsesDefaultConfig(t *testing.T) {
	cfg := Config{}
	assert.NoError(t, env.Parse(&cfg))
	assert.Equal(t, "postgres://localhost:5432/db", cfg.DatabaseURL)
}

func TestParseStructWithoutEnvTag(t *testing.T) {
	cfg := Config{}
	assert.NoError(t, env.Parse(&cfg))
	assert.Empty(t, cfg.NotAnEnv)
}

func TestParseStructWithInvalidFieldKind(t *testing.T) {
	type config struct {
		WontWork int64 `env:"BLAH"`
	}
	os.Setenv("BLAH", "10")
	cfg := config{}
	assert.Error(t, env.Parse(&cfg))
}

func TestUnsupportedSliceType(t *testing.T) {
	type config struct {
		WontWork []map[int]int `env:"WONTWORK"`
	}

	os.Setenv("WONTWORK", "1,2,3")
	defer os.Setenv("WONTWORK", "")

	cfg := &config{}
	assert.Error(t, env.Parse(cfg))
}

func TestBadSeparator(t *testing.T) {
	type config struct {
		WontWork []int `env:"WONTWORK" envSeparator:":"`
	}

	cfg := &config{}
	os.Setenv("WONTWORK", "1,2,3,4")
	defer os.Setenv("WONTWORK", "")

	assert.Error(t, env.Parse(cfg))
}

func ExampleParse() {
	type config struct {
		Home         string `env:"HOME"`
		Port         int    `env:"PORT" envDefault:"3000"`
		IsProduction bool   `env:"PRODUCTION"`
	}
	os.Setenv("HOME", "/tmp/fakehome")
	cfg := config{}
	env.Parse(&cfg)
	fmt.Println(cfg)
	// Output: {/tmp/fakehome 3000 false}
}
<commit_msg>Add tests<commit_after>package env_test

import (
	"fmt"
	"os"
	"testing"

	"github.com/caarlos0/env"
	"github.com/stretchr/testify/assert"
)

type Config struct {
	Some        string `env:"somevar"`
	Other       bool   `env:"othervar"`
	Port        int    `env:"PORT"`
	NotAnEnv    string
	DatabaseURL string   `env:"DATABASE_URL" envDefault:"postgres://localhost:5432/db"`
	Strings     []string `env:"STRINGS"`
	SepStrings  []string `env:"SEPSTRINGS" envSeparator:":"`
	Numbers     []int    `env:"NUMBERS"`
	Bools       []bool   `env:"BOOLS"`
}

func TestParsesEnv(t *testing.T) {
	os.Setenv("somevar", "somevalue")
	os.Setenv("othervar", "true")
	os.Setenv("PORT", "8080")
	os.Setenv("STRINGS", "string1,string2,string3")
	os.Setenv("SEPSTRINGS", "string1:string2:string3")
	os.Setenv("NUMBERS", "1,2,3,4")
	os.Setenv("BOOLS", "t,TRUE,0,1")

	defer os.Setenv("somevar", "")
	defer os.Setenv("othervar", "")
	defer os.Setenv("PORT", "")
	defer os.Setenv("STRINGS", "")
	defer os.Setenv("SEPSTRINGS", "")
	defer os.Setenv("NUMBERS", "")
	defer os.Setenv("BOOLS", "")

	cfg := Config{}
	assert.NoError(t, env.Parse(&cfg))
	assert.Equal(t, "somevalue", cfg.Some)
	assert.Equal(t, true, cfg.Other)
	assert.Equal(t, 8080, cfg.Port)
	assert.Equal(t, []string{"string1", "string2", "string3"}, cfg.Strings)
	assert.Equal(t, []string{"string1", "string2", "string3"}, cfg.SepStrings)
	assert.Equal(t, []int{1, 2, 3, 4}, cfg.Numbers)
	assert.Equal(t, []bool{true, true, false, true}, cfg.Bools)
}

func TestEmptyVars(t *testing.T) {
	cfg := Config{}
	assert.NoError(t, env.Parse(&cfg))
	assert.Equal(t, "", cfg.Some)
	assert.Equal(t, false, cfg.Other)
	assert.Equal(t, 0, cfg.Port)
	assert.Equal(t, 0, len(cfg.Strings))
	assert.Equal(t, 0, len(cfg.SepStrings))
	assert.Equal(t, 0, len(cfg.Numbers))
	assert.Equal(t, 0, len(cfg.Bools))
}

func TestPassAnInvalidPtr(t *testing.T) {
	var thisShouldBreak int
	assert.Error(t, env.Parse(&thisShouldBreak))
}

func TestPassReference(t *testing.T) {
	cfg := Config{}
	assert.Error(t, env.Parse(cfg))
}

func TestInvalidBool(t *testing.T) {
	os.Setenv("othervar", "should-be-a-bool")
	defer os.Setenv("othervar", "")

	cfg := Config{}
	assert.Error(t, env.Parse(&cfg))
}

func TestInvalidInt(t *testing.T) {
	os.Setenv("PORT", "should-be-an-int")
	defer os.Setenv("PORT", "")

	cfg := Config{}
	assert.Error(t, env.Parse(&cfg))
}

func TestInvalidBoolsSlice(t *testing.T) {
	type config struct {
		BadBools []bool `env:"BADBOOLS"`
	}

	os.Setenv("BADBOOLS", "t,f,TRUE,faaaalse")
	cfg := &config{}
	assert.Error(t, env.Parse(cfg))
}

func TestParsesDefaultConfig(t *testing.T) {
	cfg := Config{}
	assert.NoError(t, env.Parse(&cfg))
	assert.Equal(t, "postgres://localhost:5432/db", cfg.DatabaseURL)
}

func TestParseStructWithoutEnvTag(t *testing.T) {
	cfg := Config{}
	assert.NoError(t, env.Parse(&cfg))
	assert.Empty(t, cfg.NotAnEnv)
}

func TestParseStructWithInvalidFieldKind(t *testing.T) {
	type config struct {
		WontWork int64 `env:"BLAH"`
	}
	os.Setenv("BLAH", "10")
	cfg := config{}
	assert.Error(t, env.Parse(&cfg))
}

func TestUnsupportedSliceType(t *testing.T) {
	type config struct {
		WontWork []map[int]int `env:"WONTWORK"`
	}

	os.Setenv("WONTWORK", "1,2,3")
	defer os.Setenv("WONTWORK", "")

	cfg := &config{}
	assert.Error(t, env.Parse(cfg))
}

func TestBadSeparator(t *testing.T) {
	type config struct {
		WontWork []int `env:"WONTWORK" envSeparator:":"`
	}

	cfg := &config{}
	os.Setenv("WONTWORK", "1,2,3,4")
	defer os.Setenv("WONTWORK", "")

	assert.Error(t, env.Parse(cfg))
}

func ExampleParse() {
	type config struct {
		Home         string `env:"HOME"`
		Port         int    `env:"PORT" envDefault:"3000"`
		IsProduction bool   `env:"PRODUCTION"`
	}
	os.Setenv("HOME", "/tmp/fakehome")
	cfg := config{}
	env.Parse(&cfg)
	fmt.Println(cfg)
	// Output: {/tmp/fakehome 3000 false}
}
<|endoftext|>


<commit_before>package goroon

import (
	"encoding/xml"
	"time"
)

type SoapEnvelope struct {
	XMLName    xml.Name    `xml:"http://www.w3.org/2003/05/soap-envelope Envelope"`
	SoapHeader *SoapHeader `xml:"http://www.w3.org/2003/05/soap-envelope Header,omitempty"`
	SoapBody   *SoapBody   `xml:"http://www.w3.org/2003/05/soap-envelope Body"`
}

type SoapBody struct {
	XMLName xml.Name    `xml:"Body"`
	Content interface{} `xml:",omitempty"`
	Fault   *SoapFault  `xml:",omitempty"`
}

type SoapHeader struct {
	Action    string    `xml:"Action"`
	Security  Security  `xml:"Security"`
	Timestamp Timestamp `xml:"Timestamp"`
	Locale    string    `xml:"Locale"`
}

type SoapFault struct {
	XMLName xml.Name `xml:"http://www.w3.org/2003/05/soap-envelope Fault"`

	Code   string `xml:"faultcode,omitempty"`
	String string `xml:"faultstring,omitempty"`
	Actor  string `xml:"faultactor,omitempty"`
	Detail string `xml:"detail,omitempty"`
}

type Security struct {
	UsernameToken UsernameToken `xml:"UsernameToken,omitempty`
}

type UsernameToken struct {
	Username string `xml:"Username"`
	Password string `xml:"Password"`
}

type Timestamp struct {
	Created time.Time `xml:"Created"`
	Expires time.Time `xml:"Expires"`
}

type ScheduleGetEventsByTargetRequest struct {
	XMLName    xml.Name    `xml:"ScheduleGetEventsByTarget"`
	Parameters *Parameters `xml:"parameters"`
}

type Parameters struct {
	Start     time.Time `xml:"start,attr,omitempty"`
	End       time.Time `xml:"end,attr,omitempty"`
	User      User      `xml:"user,omitempty"`
	LoginName []string  `xml:"login_name,omitempty"`
	TopicId   int       `xml:"topic_id,attr"`
	Offset    int       `xml:"offset,attr"`
	Limit     int       `xml:"limit,attr"`
}

type ScheduleGetEventsByTargetResponse struct {
	XMLName xml.Name `xml:"ScheduleGetEventsByTargetResponse"`
	Returns *Returns `xml:"returns"`
}

type Returns struct {
	ScheduleEvents []ScheduleEvent `xml:"schedule_event,omitempty"`
	Follow         []Follow        `xml:"follow, omitempty`
	UserId         int             `xml:"user_id, omitempty"`
	User           []User          `xml:"user,omitempty"`
}

type Follow struct {
	XMLName xml.Name `xml:"follow"`
	Creator *Creator `xml:"http://schemas.cybozu.co.jp/bulletin/2008 creator"`
	TopicId int      `xml:"topic_id"`
	Id      int      `xml:"id"`
	Number  int      `xml:"number"`
	Text    string   `xml:"text"`
}

type Creator struct {
	UserId int       `xml:"user_id"`
	Name   string    `xml:"huy"`
	Date   time.Time `xml:"date"`
}

type ScheduleEvent struct {
	Members     Members    `xml:"members"`
	RepeatInfo  []RepeatInfo `xml:"repeat_info"`
	When        When         `xml:"when"`
	Detail      string       `xml:"detail,attr"`
	Description string       `xml:"description,attr"`
	Id          int          `xml:"id,attr"`
	EventType   string       `xml:"event_type,attr"`
}

type RepeatInfo struct {
	XMLName xml.Name `xml:"repeat_info"`
}

type Members struct {
	XMLName xml.Name `xml:"members"`
	Member  []Member   `xml:"member`
}

type Member struct {
	XMLName xml.Name `xml:"member"`
	User    User     `xml:"user"`
}

type User struct {
	XMLName     xml.Name `xml:"user,omitempty"`
	Id          int      `xml:"id,attr,omitempty"`
	Name        string   `xml:"name,attr,omitempty"`
	Key         int      `xml:"key,omitempty"`
	Version     int      `xml:"version,omitempty"`
	Order       int      `xml:"order,omitempty"`
	LoginName   int      `xml:"login_name,omitempty"`
	Status      int      `xml:"status,omitempty"`
	URL         string   `xml:"url,omitempty"`
	Email       string   `xml:"email,omitempty"`
	Phone       string   `xml:"phone,omitempty"`
	Description string   `xml:"description,omitempty"`
	Title       string   `xml:"title,omitempty"`
}

type When struct {
	XMLName  xml.Name `xml:"when"`
	Datetime Datetime `xml:"datetime"`
	Date     Date     `xml:"date"`
}

type Datetime struct {
	XMLName xml.Name  `xml:"datetime"`
	Start   time.Time `xml:"start,attr"`
	End     time.Time `xml:"end,attr"`
}

type Date struct {
	XMLName xml.Name `xml:"date"`
	Start   xmlDate  `xml:"start,attr"`
	End     xmlDate  `xml:"end,attr"`
}

type xmlDate struct {
	time.Time
}

type BaseGetUserByLoginNameRequest struct {
	XMLName    xml.Name    `xml:"BaseGetUsersByLoginName"`
	Parameters *Parameters `xml:"parameters"`
}

type BaseGetUserByLoginNameResponse struct {
	XMLName xml.Name `xml:"BaseGetUserByLoginNameResponse"`
	Returns *Returns `xml:"returns"`
}

type UtilGetLoginUserIdRequest struct {
	XMLName    xml.Name    `xml:"UtilGetLoginUserId"`
	Parameters *Parameters `xml:"parameters"`
}

type UtilGetLoginUserIdResponse struct {
	XMLName xml.Name `xml:"UtilGetLoginUserIdResponse"`
	Returns *Returns `xml:"returns"`
}

type ScheduleGetEventsRequest struct {
	XMLName    xml.Name    `xml:"ScheduleGetEvents"`
	Parameters *Parameters `xml:"parameters"`
}

type ScheduleGetEventsResponse struct {
	XMLName xml.Name `xml:"ScheduleGetEventsResponse"`
	Returns *Returns `xml:"returns"`
}

type BulletinGetFollowsRequest struct {
	XMLName    xml.Name    `xml:"BulletinGetFollows"`
	Parameters *Parameters `xml:"parameters"`
}

type BulletinGetFollowsResponse struct {
	XMLName xml.Name `xml:"BulletinGetFollowsResponse"`
	Returns *Returns `xml:"returns"`
}

func (c *xmlDate) UnmarshalXMLAttr(attr xml.Attr) error {
	const shortForm = "2006-01-02"
	parse, err := time.Parse(shortForm, attr.Value)
	if err != nil {
		return err
	}
	*c = xmlDate{parse}
	return nil
}

func (b *SoapBody) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	if b.Content == nil {
		return xml.UnmarshalError("Content must be a pointer to a struct")
	}

	var (
		token    xml.Token
		err      error
		consumed bool
	)

Loop:
	for {
		if token, err = d.Token(); err != nil {
			return err
		}

		if token == nil {
			break
		}

		switch se := token.(type) {
		case xml.StartElement:
			if consumed {
				return xml.UnmarshalError("Found multiple elements inside SOAP body; not wrapped-document/literal WS-I compliant")
			} else if se.Name.Space == "http://schemas.xmlsoap.org/soap/envelope/" && se.Name.Local == "Fault" {
				b.Fault = &SoapFault{}
				b.Content = nil
				err = d.DecodeElement(b.Fault, &se)
				if err != nil {
					return err
				}

				consumed = true
			} else {
				if err = d.DecodeElement(b.Content, &se); err != nil {
					return err
				}

				consumed = true
			}
		case xml.EndElement:
			break Loop
		}
	}

	return nil
}
<commit_msg>Fix bug for member envelope<commit_after>package goroon

import (
	"encoding/xml"
	"time"
)

type SoapEnvelope struct {
	XMLName    xml.Name    `xml:"http://www.w3.org/2003/05/soap-envelope Envelope"`
	SoapHeader *SoapHeader `xml:"http://www.w3.org/2003/05/soap-envelope Header,omitempty"`
	SoapBody   *SoapBody   `xml:"http://www.w3.org/2003/05/soap-envelope Body"`
}

type SoapBody struct {
	XMLName xml.Name    `xml:"Body"`
	Content interface{} `xml:",omitempty"`
	Fault   *SoapFault  `xml:",omitempty"`
}

type SoapHeader struct {
	Action    string    `xml:"Action"`
	Security  Security  `xml:"Security"`
	Timestamp Timestamp `xml:"Timestamp"`
	Locale    string    `xml:"Locale"`
}

type SoapFault struct {
	XMLName xml.Name `xml:"http://www.w3.org/2003/05/soap-envelope Fault"`

	Code   string `xml:"faultcode,omitempty"`
	String string `xml:"faultstring,omitempty"`
	Actor  string `xml:"faultactor,omitempty"`
	Detail string `xml:"detail,omitempty"`
}

type Security struct {
	UsernameToken UsernameToken `xml:"UsernameToken,omitempty`
}

type UsernameToken struct {
	Username string `xml:"Username"`
	Password string `xml:"Password"`
}

type Timestamp struct {
	Created time.Time `xml:"Created"`
	Expires time.Time `xml:"Expires"`
}

type ScheduleGetEventsByTargetRequest struct {
	XMLName    xml.Name    `xml:"ScheduleGetEventsByTarget"`
	Parameters *Parameters `xml:"parameters"`
}

type Parameters struct {
	Start     time.Time `xml:"start,attr,omitempty"`
	End       time.Time `xml:"end,attr,omitempty"`
	User      User      `xml:"user,omitempty"`
	LoginName []string  `xml:"login_name,omitempty"`
	TopicId   int       `xml:"topic_id,attr"`
	Offset    int       `xml:"offset,attr"`
	Limit     int       `xml:"limit,attr"`
}

type ScheduleGetEventsByTargetResponse struct {
	XMLName xml.Name `xml:"ScheduleGetEventsByTargetResponse"`
	Returns *Returns `xml:"returns"`
}

type Returns struct {
	ScheduleEvents []ScheduleEvent `xml:"schedule_event,omitempty"`
	Follow         []Follow        `xml:"follow, omitempty`
	UserId         int             `xml:"user_id, omitempty"`
	User           []User          `xml:"user,omitempty"`
}

type Follow struct {
	XMLName xml.Name `xml:"follow"`
	Creator *Creator `xml:"http://schemas.cybozu.co.jp/bulletin/2008 creator"`
	TopicId int      `xml:"topic_id"`
	Id      int      `xml:"id"`
	Number  int      `xml:"number"`
	Text    string   `xml:"text"`
}

type Creator struct {
	UserId int       `xml:"user_id"`
	Name   string    `xml:"huy"`
	Date   time.Time `xml:"date"`
}

type ScheduleEvent struct {
	Members     Members      `xml:"members"`
	RepeatInfo  []RepeatInfo `xml:"repeat_info"`
	When        When         `xml:"when"`
	Detail      string       `xml:"detail,attr"`
	Description string       `xml:"description,attr"`
	Id          int          `xml:"id,attr"`
	EventType   string       `xml:"event_type,attr"`
}

type RepeatInfo struct {
	XMLName xml.Name `xml:"repeat_info"`
}

type Members struct {
	XMLName xml.Name `xml:"members"`
	Member  []Member `xml:"member"`
}

type Member struct {
	XMLName xml.Name `xml:"member"`
	User    User     `xml:"user"`
}

type User struct {
	XMLName     xml.Name `xml:"user,omitempty"`
	Id          int      `xml:"id,attr,omitempty"`
	Name        string   `xml:"name,attr,omitempty"`
	Key         int      `xml:"key,omitempty"`
	Version     int      `xml:"version,omitempty"`
	Order       int      `xml:"order,omitempty"`
	LoginName   int      `xml:"login_name,omitempty"`
	Status      int      `xml:"status,omitempty"`
	URL         string   `xml:"url,omitempty"`
	Email       string   `xml:"email,omitempty"`
	Phone       string   `xml:"phone,omitempty"`
	Description string   `xml:"description,omitempty"`
	Title       string   `xml:"title,omitempty"`
}

type When struct {
	XMLName  xml.Name `xml:"when"`
	Datetime Datetime `xml:"datetime"`
	Date     Date     `xml:"date"`
}

type Datetime struct {
	XMLName xml.Name  `xml:"datetime"`
	Start   time.Time `xml:"start,attr"`
	End     time.Time `xml:"end,attr"`
}

type Date struct {
	XMLName xml.Name `xml:"date"`
	Start   xmlDate  `xml:"start,attr"`
	End     xmlDate  `xml:"end,attr"`
}

type xmlDate struct {
	time.Time
}

type BaseGetUserByLoginNameRequest struct {
	XMLName    xml.Name    `xml:"BaseGetUsersByLoginName"`
	Parameters *Parameters `xml:"parameters"`
}

type BaseGetUserByLoginNameResponse struct {
	XMLName xml.Name `xml:"BaseGetUserByLoginNameResponse"`
	Returns *Returns `xml:"returns"`
}

type UtilGetLoginUserIdRequest struct {
	XMLName    xml.Name    `xml:"UtilGetLoginUserId"`
	Parameters *Parameters `xml:"parameters"`
}

type UtilGetLoginUserIdResponse struct {
	XMLName xml.Name `xml:"UtilGetLoginUserIdResponse"`
	Returns *Returns `xml:"returns"`
}

type ScheduleGetEventsRequest struct {
	XMLName    xml.Name    `xml:"ScheduleGetEvents"`
	Parameters *Parameters `xml:"parameters"`
}

type ScheduleGetEventsResponse struct {
	XMLName xml.Name `xml:"ScheduleGetEventsResponse"`
	Returns *Returns `xml:"returns"`
}

type BulletinGetFollowsRequest struct {
	XMLName    xml.Name    `xml:"BulletinGetFollows"`
	Parameters *Parameters `xml:"parameters"`
}

type BulletinGetFollowsResponse struct {
	XMLName xml.Name `xml:"BulletinGetFollowsResponse"`
	Returns *Returns `xml:"returns"`
}

func (c *xmlDate) UnmarshalXMLAttr(attr xml.Attr) error {
	const shortForm = "2006-01-02"
	parse, err := time.Parse(shortForm, attr.Value)
	if err != nil {
		return err
	}
	*c = xmlDate{parse}
	return nil
}

func (b *SoapBody) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	if b.Content == nil {
		return xml.UnmarshalError("Content must be a pointer to a struct")
	}

	var (
		token    xml.Token
		err      error
		consumed bool
	)

Loop:
	for {
		if token, err = d.Token(); err != nil {
			return err
		}

		if token == nil {
			break
		}

		switch se := token.(type) {
		case xml.StartElement:
			if consumed {
				return xml.UnmarshalError("Found multiple elements inside SOAP body; not wrapped-document/literal WS-I compliant")
			} else if se.Name.Space == "http://schemas.xmlsoap.org/soap/envelope/" && se.Name.Local == "Fault" {
				b.Fault = &SoapFault{}
				b.Content = nil
				err = d.DecodeElement(b.Fault, &se)
				if err != nil {
					return err
				}

				consumed = true
			} else {
				if err = d.DecodeElement(b.Content, &se); err != nil {
					return err
				}

				consumed = true
			}
		case xml.EndElement:
			break Loop
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package libvirt_test

import (
	"github.com/mistifyio/mistify-agent-libvirt"
	"github.com/mistifyio/mistify-agent/client"
	"github.com/mistifyio/mistify-agent/rpc"
	"testing"
	"time"
)

var port uint = 9001

type TestClient struct {
	rpc      *rpc.Client
	guest    *client.Guest
	request  *rpc.GuestRequest
	response *rpc.GuestResponse
}

func setup(t *testing.T) *TestClient {
	lv, err := libvirt.NewLibvirt("qemu:///system", 1)
	if err != nil {
		t.Fatalf("NewLibvirt failed: %s\n", err.Error())
	}

	go lv.RunHTTP(port)
	time.Sleep(1 * time.Second)

	cli := new(TestClient)

	cli.rpc, err = rpc.NewClient(port)
	if err != nil {
		t.Fatalf("Can't create RPC client: %s\n", err.Error())
	}

	cli.guest = new(client.Guest)
	cli.guest.Id = "testlibvirt"
	cli.guest.Memory = 1024
	cli.guest.Cpu = 1
	cli.guest.Type = "qemu"

	disk := client.Disk{
		Bus:    "sata",
		Device: "hda",
		Size:   1024,
		Source: "/dev/zvol/guests/images/testlibvirt",
	}
	cli.guest.Disks = append(cli.guest.Disks, disk)

	nic := client.Nic{
		Name:    "eth0",
		Mac:     "00:0c:29:2f:00:00",
		Network: "default",
	}
	cli.guest.Nics = append(cli.guest.Nics, nic)

	cli.request = new(rpc.GuestRequest)
	cli.request.Guest = cli.guest

	cli.response = new(rpc.GuestResponse)

	return cli
}

func do(action string, t *testing.T, cli *TestClient, expectedState string) {
	err := cli.rpc.Do(action, cli.request, cli.response)
	if err != nil {
		t.Fatalf("Error running %s: %s\n", action, err.Error())
	}

	if expectedState != "" {
		for i := 0; i < 10; i++ {
			time.Sleep(1 * time.Second)

			err := cli.rpc.Do("Libvirt.Status", cli.request, cli.response)
			if err != nil {
				t.Fatalf("Error running Libvirt.Status: %s\n", err.Error())
			}

			if cli.response.Guest.State == expectedState {
				t.Logf("Ran %s, state is now %s\n", action, cli.response.Guest.State)
				return
			}
		}

		t.Fatalf("After %s, expected state %s, got state %s\n", action, expectedState, cli.response.Guest.State)
	}

	t.Logf("Ran %s, state is now %s\n", action, cli.response.Guest.State)
}

func TestLibvirt(t *testing.T) {
	cli := setup(t)

	do("Libvirt.Create", t, cli, "Running")
	do("Libvirt.Shutdown", t, cli, "Shutoff")
	do("Libvirt.Run", t, cli, "Running")
	do("Libvirt.Reboot", t, cli, "Running")
	do("Libvirt.Delete", t, cli, "")
}
<commit_msg>MIST-138: test with qemu and dummy url separately<commit_after>package libvirt_test

import (
	"github.com/mistifyio/mistify-agent-libvirt"
	"github.com/mistifyio/mistify-agent/client"
	"github.com/mistifyio/mistify-agent/rpc"
	"testing"
	"time"
)

type TestClient struct {
	rpc      *rpc.Client
	guest    *client.Guest
	request  *rpc.GuestRequest
	response *rpc.GuestResponse
}

func setup(t *testing.T, url string, port uint) *TestClient {
	lv, err := libvirt.NewLibvirt(url, 1)
	if err != nil {
		t.Fatalf("NewLibvirt failed: %s\n", err.Error())
	}

	go lv.RunHTTP(port)
	time.Sleep(1 * time.Second)

	cli := new(TestClient)

	cli.rpc, err = rpc.NewClient(port)
	if err != nil {
		t.Fatalf("Can't create RPC client: %s\n", err.Error())
	}

	cli.guest = new(client.Guest)
	cli.guest.Id = "testlibvirt"
	cli.guest.Memory = 1024
	cli.guest.Cpu = 1

	disk := client.Disk{
		Bus:    "sata",
		Device: "hda",
		Size:   1024,
		Source: "/dev/zvol/guests/images/testlibvirt",
	}
	cli.guest.Disks = append(cli.guest.Disks, disk)

	nic := client.Nic{
		Name:    "eth0",
		Mac:     "00:0c:29:2f:00:00",
		Network: "default",
	}
	cli.guest.Nics = append(cli.guest.Nics, nic)

	cli.request = new(rpc.GuestRequest)
	cli.request.Guest = cli.guest

	cli.response = new(rpc.GuestResponse)

	return cli
}

func do(action string, t *testing.T, cli *TestClient, expectedState string) {
	err := cli.rpc.Do(action, cli.request, cli.response)
	if err != nil {
		t.Fatalf("Error running %s: %s\n", action, err.Error())
	}

	if expectedState != "" {
		for i := 0; i < 10; i++ {
			time.Sleep(1 * time.Second)

			err := cli.rpc.Do("Libvirt.Status", cli.request, cli.response)
			if err != nil {
				t.Fatalf("Error running Libvirt.Status: %s\n", err.Error())
			}

			if cli.response.Guest.State == expectedState {
				t.Logf("Ran %s, state is now %s\n", action, cli.response.Guest.State)
				return
			}
		}

		t.Fatalf("After %s, expected state %s, got state %s\n", action, expectedState, cli.response.Guest.State)
	}

	t.Logf("Ran %s, state is now %s\n", action, cli.response.Guest.State)
}

func TestDummy(t *testing.T) {
	cli := setup(t, "test:///default", 9001)
	cli.guest.Type = "test"

	do("Libvirt.Create", t, cli, "Running")
	do("Libvirt.Shutdown", t, cli, "Shutoff")
	do("Libvirt.Run", t, cli, "Running")
	do("Libvirt.Reboot", t, cli, "Running")
	do("Libvirt.Delete", t, cli, "")
}

func TestQemu(t *testing.T) {
	cli := setup(t, "qemu:///system", 9002)
	cli.guest.Type = "qemu"

	do("Libvirt.Create", t, cli, "Running")
	do("Libvirt.Delete", t, cli, "")
}
<|endoftext|>


<commit_before><commit_msg>Fix error in test<commit_after><|endoftext|>


<commit_before><commit_msg>Refactored ssl test setup.<commit_after><|endoftext|>


<commit_before><commit_msg>add new<commit_after><|endoftext|>


<commit_before>package ssh

import (
	"context"
	"testing"

	"github.com/hashicorp/vault/logical"
)

func TestSSH_ConfigCAStorageUpgrade(t *testing.T) {
	var err error

	config := logical.TestBackendConfig()
	config.StorageView = &logical.InmemStorage{}

	b, err := Backend(config)
	if err != nil {
		t.Fatal(err)
	}

	err = b.Setup(context.Background(), config)
	if err != nil {
		t.Fatal(err)
	}

	// Store at an older path
	err = config.StorageView.Put(context.Background(), &logical.StorageEntry{
		Key:   caPrivateKeyStoragePathDeprecated,
		Value: []byte(privateKey),
	})
	if err != nil {
		t.Fatal(err)
	}

	// Reading it should return the key as well as upgrade the storage path
	privateKeyEntry, err := caKey(context.Background(), config.StorageView, caPrivateKey)
	if err != nil {
		t.Fatal(err)
	}
	if privateKeyEntry == nil || privateKeyEntry.Key == "" {
		t.Fatalf("failed to read the stored private key")
	}

	entry, err := config.StorageView.Get(context.Background(), caPrivateKeyStoragePathDeprecated)
	if err != nil {
		t.Fatal(err)
	}
	if entry != nil {
		t.Fatalf("bad: expected a nil entry after upgrade")
	}

	entry, err = config.StorageView.Get(context.Background(), caPrivateKeyStoragePath)
	if err != nil {
		t.Fatal(err)
	}
	if entry == nil {
		t.Fatalf("bad: expected a non-nil entry after upgrade")
	}

	// Store at an older path
	err = config.StorageView.Put(context.Background(), &logical.StorageEntry{
		Key:   caPublicKeyStoragePathDeprecated,
		Value: []byte(publicKey),
	})
	if err != nil {
		t.Fatal(err)
	}

	// Reading it should return the key as well as upgrade the storage path
	publicKeyEntry, err := caKey(context.Background(), config.StorageView, caPublicKey)
	if err != nil {
		t.Fatal(err)
	}
	if publicKeyEntry == nil || publicKeyEntry.Key == "" {
		t.Fatalf("failed to read the stored public key")
	}

	entry, err = config.StorageView.Get(context.Background(), caPublicKeyStoragePathDeprecated)
	if err != nil {
		t.Fatal(err)
	}
	if entry != nil {
		t.Fatalf("bad: expected a nil entry after upgrade")
	}

	entry, err = config.StorageView.Get(context.Background(), caPublicKeyStoragePath)
	if err != nil {
		t.Fatal(err)
	}
	if entry == nil {
		t.Fatalf("bad: expected a non-nil entry after upgrade")
	}
}

func TestSSH_ConfigCAUpdateDelete(t *testing.T) {
	var resp *logical.Response
	var err error
	config := logical.TestBackendConfig()
	config.StorageView = &logical.InmemStorage{}

	b, err := Factory(context.Background(), config)
	if err != nil {
		t.Fatalf("Cannot create backend: %s", err)
	}

	caReq := &logical.Request{
		Path:      "config/ca",
		Operation: logical.UpdateOperation,
		Storage:   config.StorageView,
	}

	// Auto-generate the keys
	resp, err = b.HandleRequest(context.Background(), caReq)
	if err != nil || (resp != nil && resp.IsError()) {
		t.Fatalf("bad: err: %v, resp:%v", err, resp)
	}

	// Fail to overwrite it
	resp, err = b.HandleRequest(context.Background(), caReq)
	if err == nil {
		t.Fatalf("expected an error")
	}

	caReq.Operation = logical.DeleteOperation
	// Delete the configured keys
	resp, err = b.HandleRequest(context.Background(), caReq)
	if err != nil || (resp != nil && resp.IsError()) {
		t.Fatalf("bad: err: %v, resp:%v", err, resp)
	}

	caReq.Operation = logical.UpdateOperation
	caReq.Data = map[string]interface{}{
		"public_key":  publicKey,
		"private_key": privateKey,
	}

	// Successfully create a new one
	resp, err = b.HandleRequest(context.Background(), caReq)
	if err != nil || (resp != nil && resp.IsError()) {
		t.Fatalf("bad: err: %v, resp:%v", err, resp)
	}

	// Fail to overwrite it
	resp, err = b.HandleRequest(context.Background(), caReq)
	if err == nil {
		t.Fatalf("expected an error")
	}

	caReq.Operation = logical.DeleteOperation
	// Delete the configured keys
	resp, err = b.HandleRequest(context.Background(), caReq)
	if err != nil || (resp != nil && resp.IsError()) {
		t.Fatalf("bad: err: %v, resp:%v", err, resp)
	}

	caReq.Operation = logical.UpdateOperation
	caReq.Data = nil

	// Successfully create a new one
	resp, err = b.HandleRequest(context.Background(), caReq)
	if err != nil || (resp != nil && resp.IsError()) {
		t.Fatalf("bad: err: %v, resp:%v", err, resp)
	}
}
<commit_msg>Fix tests<commit_after>package ssh

import (
	"context"
	"testing"

	"github.com/hashicorp/vault/logical"
)

func TestSSH_ConfigCAStorageUpgrade(t *testing.T) {
	var err error

	config := logical.TestBackendConfig()
	config.StorageView = &logical.InmemStorage{}

	b, err := Backend(config)
	if err != nil {
		t.Fatal(err)
	}

	err = b.Setup(context.Background(), config)
	if err != nil {
		t.Fatal(err)
	}

	// Store at an older path
	err = config.StorageView.Put(context.Background(), &logical.StorageEntry{
		Key:   caPrivateKeyStoragePathDeprecated,
		Value: []byte(privateKey),
	})
	if err != nil {
		t.Fatal(err)
	}

	// Reading it should return the key as well as upgrade the storage path
	privateKeyEntry, err := caKey(context.Background(), config.StorageView, caPrivateKey)
	if err != nil {
		t.Fatal(err)
	}
	if privateKeyEntry == nil || privateKeyEntry.Key == "" {
		t.Fatalf("failed to read the stored private key")
	}

	entry, err := config.StorageView.Get(context.Background(), caPrivateKeyStoragePathDeprecated)
	if err != nil {
		t.Fatal(err)
	}
	if entry != nil {
		t.Fatalf("bad: expected a nil entry after upgrade")
	}

	entry, err = config.StorageView.Get(context.Background(), caPrivateKeyStoragePath)
	if err != nil {
		t.Fatal(err)
	}
	if entry == nil {
		t.Fatalf("bad: expected a non-nil entry after upgrade")
	}

	// Store at an older path
	err = config.StorageView.Put(context.Background(), &logical.StorageEntry{
		Key:   caPublicKeyStoragePathDeprecated,
		Value: []byte(publicKey),
	})
	if err != nil {
		t.Fatal(err)
	}

	// Reading it should return the key as well as upgrade the storage path
	publicKeyEntry, err := caKey(context.Background(), config.StorageView, caPublicKey)
	if err != nil {
		t.Fatal(err)
	}
	if publicKeyEntry == nil || publicKeyEntry.Key == "" {
		t.Fatalf("failed to read the stored public key")
	}

	entry, err = config.StorageView.Get(context.Background(), caPublicKeyStoragePathDeprecated)
	if err != nil {
		t.Fatal(err)
	}
	if entry != nil {
		t.Fatalf("bad: expected a nil entry after upgrade")
	}

	entry, err = config.StorageView.Get(context.Background(), caPublicKeyStoragePath)
	if err != nil {
		t.Fatal(err)
	}
	if entry == nil {
		t.Fatalf("bad: expected a non-nil entry after upgrade")
	}
}

func TestSSH_ConfigCAUpdateDelete(t *testing.T) {
	var resp *logical.Response
	var err error
	config := logical.TestBackendConfig()
	config.StorageView = &logical.InmemStorage{}

	b, err := Factory(context.Background(), config)
	if err != nil {
		t.Fatalf("Cannot create backend: %s", err)
	}

	caReq := &logical.Request{
		Path:      "config/ca",
		Operation: logical.UpdateOperation,
		Storage:   config.StorageView,
	}

	// Auto-generate the keys
	resp, err = b.HandleRequest(context.Background(), caReq)
	if err != nil || (resp != nil && resp.IsError()) {
		t.Fatalf("bad: err: %v, resp:%v", err, resp)
	}

	// Fail to overwrite it
	resp, err = b.HandleRequest(context.Background(), caReq)
	if err != nil {
		t.Fatal(err)
	}
	if !resp.IsError() {
		t.Fatalf("expected an error, got %#v", *resp)
	}

	caReq.Operation = logical.DeleteOperation
	// Delete the configured keys
	resp, err = b.HandleRequest(context.Background(), caReq)
	if err != nil || (resp != nil && resp.IsError()) {
		t.Fatalf("bad: err: %v, resp:%v", err, resp)
	}

	caReq.Operation = logical.UpdateOperation
	caReq.Data = map[string]interface{}{
		"public_key":  publicKey,
		"private_key": privateKey,
	}

	// Successfully create a new one
	resp, err = b.HandleRequest(context.Background(), caReq)
	if err != nil || (resp != nil && resp.IsError()) {
		t.Fatalf("bad: err: %v, resp:%v", err, resp)
	}

	// Fail to overwrite it
	resp, err = b.HandleRequest(context.Background(), caReq)
	if err != nil {
		t.Fatal(err)
	}
	if !resp.IsError() {
		t.Fatalf("expected an error, got %#v", *resp)
	}

	caReq.Operation = logical.DeleteOperation
	// Delete the configured keys
	resp, err = b.HandleRequest(context.Background(), caReq)
	if err != nil || (resp != nil && resp.IsError()) {
		t.Fatalf("bad: err: %v, resp:%v", err, resp)
	}

	caReq.Operation = logical.UpdateOperation
	caReq.Data = nil

	// Successfully create a new one
	resp, err = b.HandleRequest(context.Background(), caReq)
	if err != nil || (resp != nil && resp.IsError()) {
		t.Fatalf("bad: err: %v, resp:%v", err, resp)
	}
}
<|endoftext|>