<commit_before>package commands

import (
	"encoding/json"
	"fmt"
	"log"
	"path/filepath"
	"sort"
	"strings"

	"github.com/spf13/cobra"
	"github.com/yungsang/tablewriter"

	"github.com/ailispaw/talk2docker/api"
	"github.com/ailispaw/talk2docker/client"
)

// https://github.com/docker/docker/blob/master/daemon%2Fvolumes.go#L21
type Mount struct {
	hostPath      string
	MountToPath   string
	ContainerId   string
	ContainerName string
	Writable      bool
}

// https://github.com/docker/docker/blob/master/volumes%2Fvolume.go#L16
type Volume struct {
	ID          string
	Path        string
	IsBindMount bool
	Writable    bool

	MountedOn []*Mount
}

type Volumes []*Volume

func (volumes Volumes) Len() int {
	return len(volumes)
}

func (volumes Volumes) Swap(i, j int) {
	volumes[i], volumes[j] = volumes[j], volumes[i]
}

func (volumes Volumes) Less(i, j int) bool {
	return volumes[i].Path < volumes[j].Path
}

func (volumes Volumes) Find(id string) *Volume {
	l := len(id)
	for _, volume := range volumes {
		if len(volume.ID) < l {
			continue
		}
		if id == volume.ID[:l] {
			return volume
		}
	}
	return nil
}

var cmdVs = &cobra.Command{
	Use:     "vs",
	Aliases: []string{"volumes"},
	Short:   "List volumes",
	Long:    APP_NAME + " vs - List volumes",
	Run:     listVolumes,
}

var cmdVolume = &cobra.Command{
	Use:     "volume [command]",
	Aliases: []string{"vol"},
	Short:   "Manage volumes",
	Long:    APP_NAME + " volume - Manage volumes",
	Run: func(ctx *cobra.Command, args []string) {
		ctx.Help()
	},
}

var cmdListVolumes = &cobra.Command{
	Use:     "list",
	Aliases: []string{"ls"},
	Short:   "List volumes",
	Long:    APP_NAME + " volume list - List volumes",
	Run:     listVolumes,
}

var cmdInspectVolumes = &cobra.Command{
	Use:     "inspect <ID>...",
	Aliases: []string{"ins", "info"},
	Short:   "Inspect volumes",
	Long:    APP_NAME + " volume inspect - Inspect volumes",
	Run:     inspectVolumes,
}

func init() {
	flags := cmdVs.Flags()
	flags.BoolVarP(&boolAll, "all", "a", false, "Show all volumes. Only active volumes are shown by default.")
	flags.BoolVarP(&boolQuiet, "quiet", "q", false, "Only display numeric IDs")
	flags.BoolVarP(&boolNoHeader, "no-header", "n", false, "Omit the header")

	flags = cmdListVolumes.Flags()
	flags.BoolVarP(&boolAll, "all", "a", false, "Show all volumes. Only active volumes are shown by default.")
	flags.BoolVarP(&boolQuiet, "quiet", "q", false, "Only display numeric IDs")
	flags.BoolVarP(&boolNoHeader, "no-header", "n", false, "Omit the header")
	cmdVolume.AddCommand(cmdListVolumes)

	cmdVolume.AddCommand(cmdInspectVolumes)
}

func listVolumes(ctx *cobra.Command, args []string) {
	volumes, err := getVolumes(ctx)
	if err != nil {
		log.Fatal(err)
	}

	sort.Sort(volumes)

	var _volumes Volumes
	for _, volume := range volumes {
		if boolAll || (len(volume.MountedOn) > 0) {
			_volumes = append(_volumes, volume)
		}
	}
	volumes = _volumes

	if boolQuiet {
		for _, volume := range volumes {
			ctx.Println(Truncate(volume.ID, 12))
		}
		return
	}

	if boolYAML || boolJSON {
		if err := FormatPrint(ctx.Out(), volumes); err != nil {
			log.Fatal(err)
		}
		return
	}

	formatNames := func(mounts []*Mount) string {
		names := []string{}
		for _, mount := range mounts {
			var name string
			if mount.Writable {
				name = fmt.Sprintf("%s:%s", mount.ContainerName, mount.MountToPath)
			} else {
				name = fmt.Sprintf("%s:%s:ro", mount.ContainerName, mount.MountToPath)
			}
			names = append(names, name)
		}
		return strings.Join(names, ", ")
	}

	var items [][]string
	for _, volume := range volumes {
		out := []string{
			Truncate(volume.ID, 12),
			formatNames(volume.MountedOn),
			volume.Path,
		}
		items = append(items, out)
	}

	header := []string{
		"ID",
		"Mounted On",
		"Path",
	}

	PrintInTable(ctx.Out(), header, items, 0, tablewriter.ALIGN_DEFAULT)
}

func inspectVolumes(ctx *cobra.Command, args []string) {
	if len(args) < 1 {
		ctx.Println("Needs an argument <ID> at least to inspect")
		ctx.Usage()
		return
	}

	volumes, err := getVolumes(ctx)
	if err != nil {
		log.Fatal(err)
	}

	var _volumes Volumes
	var gotError = false

	for _, id := range args {
		if volume := volumes.Find(id); volume == nil {
			log.Printf("No such volume: %s\n", id)
			gotError = true
		} else {
			_volumes = append(_volumes, volume)
		}
	}

	if len(_volumes) > 0 {
		if err := FormatPrint(ctx.Out(), _volumes); err != nil {
			log.Fatal(err)
		}
	}

	if gotError {
		log.Fatal("Error: failed to inspect one or more volumes")
	}
}

func getVolumes(ctx *cobra.Command) (Volumes, error) {
	docker, err := client.NewDockerClient(configPath, hostName, ctx.Out())
	if err != nil {
		return nil, err
	}

	info, err := docker.Info()
	if err != nil {
		return nil, err
	}

	rootDir := "/var/lib/docker"

	if (info.Debug != 0) && (info.DockerRootDir != "") {
		rootDir = info.DockerRootDir
	} else {
		for _, pair := range info.DriverStatus {
			if pair[0] == "Root Dir" {
				rootDir = filepath.Dir(pair[0])
			}
		}
	}

	path := filepath.Join(rootDir, "/volumes")

	var (
		config     api.Config
		hostConfig api.HostConfig
	)

	config.Cmd = []string{"/bin/sh", "-c", "awk '{print $0}' /.docker_volumes/*/config.json"}
	config.Image = "busybox:latest"

	hostConfig.Binds = []string{path + ":/.docker_volumes:ro"}

	cid, err := docker.CreateContainer("", config, hostConfig)
	if err != nil {
		return nil, err
	}
	defer docker.RemoveContainer(cid, true)

	if err := docker.StartContainer(cid); err != nil {
		return nil, err
	}

	if _, err := docker.WaitContainer(cid); err != nil {
		return nil, err
	}

	logs, err := docker.GetContainerLogs(cid, false, true, true, false, 0)
	if err != nil {
		return nil, err
	}

	jsonVolumes := strings.Split(strings.TrimSpace(logs[0]), "\n")

	var volumes Volumes
	for _, v := range jsonVolumes {
		volume := &Volume{}
		if err := json.Unmarshal([]byte(v), volume); err != nil {
			return nil, err
		}
		volumes = append(volumes, volume)
	}

	if err := docker.RemoveContainer(cid, true); err != nil {
		return nil, err
	}

	mounts, err := getMounts(ctx)
	if err != nil {
		return nil, err
	}

	for _, volume := range volumes {
		for _, mount := range mounts {
			if mount.hostPath == volume.Path {
				volume.MountedOn = append(volume.MountedOn, mount)
			}
		}
	}

	return volumes, nil
}

func getMounts(ctx *cobra.Command) ([]*Mount, error) {
	docker, err := client.NewDockerClient(configPath, hostName, ctx.Out())
	if err != nil {
		return nil, err
	}

	containers, err := docker.ListContainers(true, false, 0, "", "", nil)
	if err != nil {
		return nil, err
	}

	var mounts []*Mount

	for _, container := range containers {
		localMounts := map[string]*Mount{}

		containerInfo, err := docker.InspectContainer(container.Id)
		if err != nil {
			log.Println(err)
			continue
		}

		for _, bind := range containerInfo.HostConfig.Binds {
			var (
				arr   = strings.Split(bind, ":")
				mount Mount
			)

			mount.ContainerId = containerInfo.Id

			switch len(arr) {
			case 1:
				mount.MountToPath = bind
				mount.Writable = true
			case 2:
				mount.hostPath = arr[0]
				mount.MountToPath = arr[1]
				mount.Writable = true
			case 3:
				mount.hostPath = arr[0]
				mount.MountToPath = arr[1]
				switch arr[2] {
				case "ro":
					mount.Writable = false
				case "rw":
					mount.Writable = true
				default:
					continue
				}
			default:
				continue
			}

			mount.ContainerName = strings.TrimPrefix(containerInfo.Name, "/")

			localMounts[mount.MountToPath] = &mount
		}

		for mountToPath, hostPath := range containerInfo.Volumes {
			if _, exists := localMounts[mountToPath]; !exists {
				localMounts[mountToPath] = &Mount{
					hostPath:      hostPath,
					MountToPath:   mountToPath,
					ContainerId:   containerInfo.Id,
					ContainerName: strings.TrimPrefix(containerInfo.Name, "/"),
					Writable:      containerInfo.VolumesRW[mountToPath],
				}
			}
		}

		for _, mount := range localMounts {
			mounts = append(mounts, mount)
		}
	}

	return mounts, nil
}
<commit_msg>Add volume remove command<commit_after>package commands

import (
	"encoding/json"
	"fmt"
	"log"
	"path/filepath"
	"sort"
	"strings"

	"github.com/spf13/cobra"
	"github.com/yungsang/tablewriter"

	"github.com/ailispaw/talk2docker/api"
	"github.com/ailispaw/talk2docker/client"
)

// https://github.com/docker/docker/blob/master/daemon%2Fvolumes.go#L21
type Mount struct {
	hostPath      string
	MountToPath   string
	ContainerId   string
	ContainerName string
	Writable      bool
}

// https://github.com/docker/docker/blob/master/volumes%2Fvolume.go#L16
type Volume struct {
	ID          string
	Path        string
	IsBindMount bool
	Writable    bool

	MountedOn []*Mount

	configPath string
}

type Volumes []*Volume

func (volumes Volumes) Len() int {
	return len(volumes)
}

func (volumes Volumes) Swap(i, j int) {
	volumes[i], volumes[j] = volumes[j], volumes[i]
}

func (volumes Volumes) Less(i, j int) bool {
	return volumes[i].Path < volumes[j].Path
}

func (volumes Volumes) Find(id string) *Volume {
	l := len(id)
	for _, volume := range volumes {
		if len(volume.ID) < l {
			continue
		}
		if id == volume.ID[:l] {
			return volume
		}
	}
	return nil
}

var cmdVs = &cobra.Command{
	Use:     "vs",
	Aliases: []string{"volumes"},
	Short:   "List volumes",
	Long:    APP_NAME + " vs - List volumes",
	Run:     listVolumes,
}

var cmdVolume = &cobra.Command{
	Use:     "volume [command]",
	Aliases: []string{"vol"},
	Short:   "Manage volumes",
	Long:    APP_NAME + " volume - Manage volumes",
	Run: func(ctx *cobra.Command, args []string) {
		ctx.Help()
	},
}

var cmdListVolumes = &cobra.Command{
	Use:     "list",
	Aliases: []string{"ls"},
	Short:   "List volumes",
	Long:    APP_NAME + " volume list - List volumes",
	Run:     listVolumes,
}

var cmdInspectVolumes = &cobra.Command{
	Use:     "inspect <ID>...",
	Aliases: []string{"ins", "info"},
	Short:   "Inspect volumes",
	Long:    APP_NAME + " volume inspect - Inspect volumes",
	Run:     inspectVolumes,
}

var cmdRemoveVolumes = &cobra.Command{
	Use:     "remove <ID>...",
	Aliases: []string{"rm"},
	Short:   "Remove volumes",
	Long:    APP_NAME + " volume remove - Remove volumes",
	Run:     removeVolumes,
}

func init() {
	flags := cmdVs.Flags()
	flags.BoolVarP(&boolAll, "all", "a", false, "Show all volumes. Only active volumes are shown by default.")
	flags.BoolVarP(&boolQuiet, "quiet", "q", false, "Only display numeric IDs")
	flags.BoolVarP(&boolNoHeader, "no-header", "n", false, "Omit the header")

	flags = cmdListVolumes.Flags()
	flags.BoolVarP(&boolAll, "all", "a", false, "Show all volumes. Only active volumes are shown by default.")
	flags.BoolVarP(&boolQuiet, "quiet", "q", false, "Only display numeric IDs")
	flags.BoolVarP(&boolNoHeader, "no-header", "n", false, "Omit the header")
	cmdVolume.AddCommand(cmdListVolumes)

	cmdVolume.AddCommand(cmdInspectVolumes)

	cmdVolume.AddCommand(cmdRemoveVolumes)
}

func listVolumes(ctx *cobra.Command, args []string) {
	volumes, err := getVolumes(ctx)
	if err != nil {
		log.Fatal(err)
	}

	sort.Sort(volumes)

	var _volumes Volumes
	for _, volume := range volumes {
		if boolAll || (len(volume.MountedOn) > 0) {
			_volumes = append(_volumes, volume)
		}
	}
	volumes = _volumes

	if boolQuiet {
		for _, volume := range volumes {
			ctx.Println(Truncate(volume.ID, 12))
		}
		return
	}

	if boolYAML || boolJSON {
		if err := FormatPrint(ctx.Out(), volumes); err != nil {
			log.Fatal(err)
		}
		return
	}

	formatNames := func(mounts []*Mount) string {
		names := []string{}
		for _, mount := range mounts {
			var name string
			if mount.Writable {
				name = fmt.Sprintf("%s:%s", mount.ContainerName, mount.MountToPath)
			} else {
				name = fmt.Sprintf("%s:%s:ro", mount.ContainerName, mount.MountToPath)
			}
			names = append(names, name)
		}
		return strings.Join(names, ", ")
	}

	var items [][]string
	for _, volume := range volumes {
		out := []string{
			Truncate(volume.ID, 12),
			formatNames(volume.MountedOn),
			volume.Path,
		}
		items = append(items, out)
	}

	header := []string{
		"ID",
		"Mounted On",
		"Path",
	}

	PrintInTable(ctx.Out(), header, items, 0, tablewriter.ALIGN_DEFAULT)
}

func inspectVolumes(ctx *cobra.Command, args []string) {
	if len(args) < 1 {
		ctx.Println("Needs an argument <ID> at least to inspect")
		ctx.Usage()
		return
	}

	volumes, err := getVolumes(ctx)
	if err != nil {
		log.Fatal(err)
	}

	var _volumes Volumes
	var gotError = false

	for _, id := range args {
		if volume := volumes.Find(id); volume == nil {
			log.Printf("No such volume: %s\n", id)
			gotError = true
		} else {
			_volumes = append(_volumes, volume)
		}
	}

	if len(_volumes) > 0 {
		if err := FormatPrint(ctx.Out(), _volumes); err != nil {
			log.Fatal(err)
		}
	}

	if gotError {
		log.Fatal("Error: failed to inspect one or more volumes")
	}
}

func removeVolumes(ctx *cobra.Command, args []string) {
	if len(args) < 1 {
		ctx.Println("Needs an argument <ID> at least to inspect")
		ctx.Usage()
		return
	}

	volumes, err := getVolumes(ctx)
	if err != nil {
		log.Fatal(err)
	}

	var gotError = false

	for _, id := range args {
		volume := volumes.Find(id)
		if volume == nil {
			log.Printf("No such volume: %s\n", id)
			gotError = true
			continue
		}

		if len(volume.MountedOn) > 0 {
			log.Printf("The volume is in use, cannot remove: %s\n", volume.ID)
			gotError = true
			continue
		}

		if err := removeVolume(ctx, volume); err != nil {
			log.Println(err)
			gotError = true
		} else {
			ctx.Println(volume.ID)
		}
	}

	if gotError {
		log.Fatal("Error: failed to remove one or more volumes")
	}
}

func getVolumes(ctx *cobra.Command) (Volumes, error) {
	docker, err := client.NewDockerClient(configPath, hostName, ctx.Out())
	if err != nil {
		return nil, err
	}

	info, err := docker.Info()
	if err != nil {
		return nil, err
	}

	rootDir := "/var/lib/docker"

	if (info.Debug != 0) && (info.DockerRootDir != "") {
		rootDir = info.DockerRootDir
	} else {
		for _, pair := range info.DriverStatus {
			if pair[0] == "Root Dir" {
				rootDir = filepath.Dir(pair[0])
			}
		}
	}

	path := filepath.Join(rootDir, "/volumes")

	var (
		config     api.Config
		hostConfig api.HostConfig
	)

	config.Cmd = []string{"/bin/sh", "-c", "awk '{print $0}' /.docker_volumes/*/config.json"}
	config.Image = "busybox:latest"

	hostConfig.Binds = []string{path + ":/.docker_volumes:ro"}

	cid, err := docker.CreateContainer("", config, hostConfig)
	if err != nil {
		return nil, err
	}
	defer docker.RemoveContainer(cid, true)

	if err := docker.StartContainer(cid); err != nil {
		return nil, err
	}

	if _, err := docker.WaitContainer(cid); err != nil {
		return nil, err
	}

	logs, err := docker.GetContainerLogs(cid, false, true, true, false, 0)
	if err != nil {
		return nil, err
	}

	jsonVolumes := strings.Split(strings.TrimSpace(logs[0]), "\n")

	var volumes Volumes
	for _, v := range jsonVolumes {
		volume := &Volume{}
		if err := json.Unmarshal([]byte(v), volume); err != nil {
			return nil, err
		}
		volume.configPath = filepath.Join(path, "/"+volume.ID)
		volumes = append(volumes, volume)
	}

	if err := docker.RemoveContainer(cid, true); err != nil {
		return nil, err
	}

	mounts, err := getMounts(ctx)
	if err != nil {
		return nil, err
	}

	for _, volume := range volumes {
		for _, mount := range mounts {
			if mount.hostPath == volume.Path {
				volume.MountedOn = append(volume.MountedOn, mount)
			}
		}
	}

	return volumes, nil
}

func getMounts(ctx *cobra.Command) ([]*Mount, error) {
	docker, err := client.NewDockerClient(configPath, hostName, ctx.Out())
	if err != nil {
		return nil, err
	}

	containers, err := docker.ListContainers(true, false, 0, "", "", nil)
	if err != nil {
		return nil, err
	}

	var mounts []*Mount

	for _, container := range containers {
		localMounts := map[string]*Mount{}

		containerInfo, err := docker.InspectContainer(container.Id)
		if err != nil {
			log.Println(err)
			continue
		}

		for _, bind := range containerInfo.HostConfig.Binds {
			var (
				arr   = strings.Split(bind, ":")
				mount Mount
			)

			mount.ContainerId = containerInfo.Id

			switch len(arr) {
			case 1:
				mount.MountToPath = bind
				mount.Writable = true
			case 2:
				mount.hostPath = arr[0]
				mount.MountToPath = arr[1]
				mount.Writable = true
			case 3:
				mount.hostPath = arr[0]
				mount.MountToPath = arr[1]
				switch arr[2] {
				case "ro":
					mount.Writable = false
				case "rw":
					mount.Writable = true
				default:
					continue
				}
			default:
				continue
			}

			mount.ContainerName = strings.TrimPrefix(containerInfo.Name, "/")

			localMounts[mount.MountToPath] = &mount
		}

		for mountToPath, hostPath := range containerInfo.Volumes {
			if _, exists := localMounts[mountToPath]; !exists {
				localMounts[mountToPath] = &Mount{
					hostPath:      hostPath,
					MountToPath:   mountToPath,
					ContainerId:   containerInfo.Id,
					ContainerName: strings.TrimPrefix(containerInfo.Name, "/"),
					Writable:      containerInfo.VolumesRW[mountToPath],
				}
			}
		}

		for _, mount := range localMounts {
			mounts = append(mounts, mount)
		}
	}

	return mounts, nil
}

func removeVolume(ctx *cobra.Command, volume *Volume) error {
	docker, err := client.NewDockerClient(configPath, hostName, ctx.Out())
	if err != nil {
		return err
	}

	var (
		config     api.Config
		hostConfig api.HostConfig
	)

	config.Cmd = []string{"/bin/sh", "-c", "rm -rf /.docker_volume_config/" + volume.ID}
	config.Image = "busybox:latest"

	hostConfig.Binds = []string{filepath.Dir(volume.configPath) + ":/.docker_volume_config"}

	if !volume.IsBindMount {
		config.Cmd[2] = config.Cmd[2] + (" && rm -rf /.docker_volume/" + volume.ID)

		hostConfig.Binds = append(hostConfig.Binds, filepath.Dir(volume.Path)+":/.docker_volume")
	}

	cid, err := docker.CreateContainer("", config, hostConfig)
	if err != nil {
		return err
	}
	defer docker.RemoveContainer(cid, true)

	if err := docker.StartContainer(cid); err != nil {
		return err
	}

	if _, err := docker.WaitContainer(cid); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package bkdtree

import (
	"math/rand"
	"testing"
)

type PointBase struct {
	Point
	Vec   []int
	DocId uint64
}

func (b PointBase) GetValue(dim int) (val uint64) {
	val = uint64(b.Vec[dim])
	return
}

func (b PointBase) GetUserData() (userData uint64) {
	userData = b.DocId
	return
}

func NewPointBase(vals []int, docId uint64) PointBase {
	ret := PointBase{}
	for _, val := range vals {
		ret.Vec = append(ret.Vec, val)
	}
	ret.DocId = docId
	return ret
}

func NewRandPoints(numDims, maxVal, size int) (points []Point) {
	for i := 0; i < size; i++ {
		vals := make([]int, 0)
		for j := 0; j < numDims; j++ {
			vals = append(vals, rand.Intn(maxVal))
		}
		point := NewPointBase(vals, uint64(i))
		points = append(points, point)
	}
	return
}

func TestSplitPoints(t *testing.T) {
	numDims := 3
	maxVal := 100
	size := 1000
	numStrips := 4
	points := NewRandPoints(numDims, maxVal, size)
	for dim := 0; dim < numDims; dim++ {
		splitValues, splitPoses := SplitPoints(points, dim, numStrips)
		//fmt.Printf("points: %v\nsplitValues: %v\nsplitPoses:%v\n", points, splitValues, splitPoses)
		if len(splitValues) != numStrips-1 || len(splitValues) != len(splitPoses) {
			t.Errorf("incorrect size of splitValues or splitPoses\n")
		}
		numSplits := len(splitValues)
		for strip := 0; strip < numStrips; strip++ {
			posBegin := 0
			minValue := uint64(0)
			if strip != 0 {
				posBegin = splitPoses[strip-1]
				minValue = splitValues[strip-1]
			}
			posEnd := size
			maxValue := uint64(maxVal)
			if strip != numSplits {
				posEnd = splitPoses[strip]
				maxValue = splitValues[strip]
			}

			for pos := posBegin; pos < posEnd; pos++ {
				val := points[pos].GetValue(dim)
				if val < minValue {
					t.Errorf("points[%v][%v] %v is less than minValue %v", pos, dim, val, minValue)
				}
				if val > maxValue {
					t.Errorf("points[%v][%v] %v is larger than maxValue %v", pos, dim, val, maxValue)
				}
			}
		}
	}
}

type CaseInside struct {
	point, lowPoint, highPoint Point
	numDims                    int
	isInside                   bool
}

func TestIsInside(t *testing.T) {
	cases := []CaseInside{
		{
			NewPointBase([]int{30, 80, 40}, 0),
			NewPointBase([]int{30, 80, 40}, 0),
			NewPointBase([]int{50, 90, 50}, 0),
			3,
			true,
		},
		{
			NewPointBase([]int{30, 79, 40}, 0),
			NewPointBase([]int{30, 80, 40}, 0),
			NewPointBase([]int{50, 90, 50}, 0),
			3,
			false,
		},
		{ //invalid range
			NewPointBase([]int{30, 80, 40}, 0),
			NewPointBase([]int{30, 80, 40}, 0),
			NewPointBase([]int{50, 90, 39}, 0),
			3,
			false,
		},
	}

	for i, tc := range cases {
		res := IsInside(tc.point, tc.lowPoint, tc.highPoint, tc.numDims)
		if res != tc.isInside {
			t.Errorf("case %v failed\n", i)
		}
	}
}
<commit_msg>improved point_test<commit_after>package bkdtree

import (
	"math/rand"
	"testing"
)

type PointBase struct {
	Vec   []int
	DocId uint64
}

func (b *PointBase) GetValue(dim int) (val uint64) {
	val = uint64(b.Vec[dim])
	return
}

func (b *PointBase) GetUserData() (userData uint64) {
	userData = b.DocId
	return
}

func NewPointBase(vals []int, docId uint64) *PointBase {
	ret := &PointBase{}
	for _, val := range vals {
		ret.Vec = append(ret.Vec, val)
	}
	ret.DocId = docId
	return ret
}

func NewRandPoints(numDims, maxVal, size int) (points []Point) {
	for i := 0; i < size; i++ {
		vals := make([]int, 0)
		for j := 0; j < numDims; j++ {
			vals = append(vals, rand.Intn(maxVal))
		}
		point := NewPointBase(vals, uint64(i))
		points = append(points, point)
	}
	return
}

func TestSplitPoints(t *testing.T) {
	numDims := 3
	maxVal := 100
	size := 1000
	numStrips := 4
	points := NewRandPoints(numDims, maxVal, size)
	for dim := 0; dim < numDims; dim++ {
		splitValues, splitPoses := SplitPoints(points, dim, numStrips)
		//fmt.Printf("points: %v\nsplitValues: %v\nsplitPoses:%v\n", points, splitValues, splitPoses)
		if len(splitValues) != numStrips-1 || len(splitValues) != len(splitPoses) {
			t.Errorf("incorrect size of splitValues or splitPoses\n")
		}
		numSplits := len(splitValues)
		for strip := 0; strip < numStrips; strip++ {
			posBegin := 0
			minValue := uint64(0)
			if strip != 0 {
				posBegin = splitPoses[strip-1]
				minValue = splitValues[strip-1]
			}
			posEnd := size
			maxValue := uint64(maxVal)
			if strip != numSplits {
				posEnd = splitPoses[strip]
				maxValue = splitValues[strip]
			}

			for pos := posBegin; pos < posEnd; pos++ {
				val := points[pos].GetValue(dim)
				if val < minValue {
					t.Errorf("points[%v][%v] %v is less than minValue %v", pos, dim, val, minValue)
				}
				if val > maxValue {
					t.Errorf("points[%v][%v] %v is larger than maxValue %v", pos, dim, val, maxValue)
				}
			}
		}
	}
}

type CaseInside struct {
	point, lowPoint, highPoint Point
	numDims                    int
	isInside                   bool
}

func TestIsInside(t *testing.T) {
	cases := []CaseInside{
		{
			NewPointBase([]int{30, 80, 40}, 0),
			NewPointBase([]int{30, 80, 40}, 0),
			NewPointBase([]int{50, 90, 50}, 0),
			3,
			true,
		},
		{
			NewPointBase([]int{30, 79, 40}, 0),
			NewPointBase([]int{30, 80, 40}, 0),
			NewPointBase([]int{50, 90, 50}, 0),
			3,
			false,
		},
		{ //invalid range
			NewPointBase([]int{30, 80, 40}, 0),
			NewPointBase([]int{30, 80, 40}, 0),
			NewPointBase([]int{50, 90, 39}, 0),
			3,
			false,
		},
	}

	for i, tc := range cases {
		res := IsInside(tc.point, tc.lowPoint, tc.highPoint, tc.numDims)
		if res != tc.isInside {
			t.Errorf("case %v failed\n", i)
		}
	}
}
<|endoftext|>


<commit_before>package gopencils

import "fmt"

func (resource *Resource) ProcessedError() error {
	if resource.Raw == nil {
		return fmt.Errorf("Error: empty response")
	}

	if resource.Raw.StatusCode < 200 && resource.Raw.StatusCode >= 300 {
		return fmt.Errorf("Error(%s) %s %s", resource.Raw.Status, resource.Raw.Request.Method, resource.Url)
	} else {
		return nil
	}

}
<commit_msg>fix HTTP status code check<commit_after>package gopencils

import "fmt"

func (resource *Resource) ProcessedError() error {
	if resource.Raw == nil {
		return fmt.Errorf("Error: empty response")
	}

	if resource.Raw.StatusCode < 200 || resource.Raw.StatusCode >= 300 {
		return fmt.Errorf("Error(%s) %s %s", resource.Raw.Status, resource.Raw.Request.Method, resource.Url)
	} else {
		return nil
	}

}
<|endoftext|>


<commit_before>package toolbox

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

var TrueProvider = func(input interface{}) bool {
	return true
}

type withinSecPredicate struct {
	baseTime        time.Time
	deltaInSeconds  int
	dateLayout      string
	elapsed         time.Duration
	maxAllowedDelay time.Duration
}

func (p *withinSecPredicate) String() string {
	return fmt.Sprintf("(elapsed: %v, max allowed delay: %v)", p.elapsed, p.maxAllowedDelay)
}

//Apply returns true if passed in time is within deltaInSeconds from baseTime
func (p *withinSecPredicate) Apply(value interface{}) bool {
	timeValue, _ := ToTime(value, p.dateLayout)
	if timeValue == nil {
		return false
	}

	elapsed := timeValue.Sub(p.baseTime)
	if elapsed < 0 {
		elapsed *= -1
	}
	var maxAllowedDelay = time.Duration(p.deltaInSeconds) * time.Second
	var passed = maxAllowedDelay >= elapsed
	if !passed {
		p.elapsed = elapsed
		p.maxAllowedDelay = maxAllowedDelay
	}
	return passed
}

func (p *withinSecPredicate) ToString() string {
	return fmt.Sprintf(" %v within %v s", p.baseTime, p.deltaInSeconds)
}

//NewWithinPredicate returns new NewWithinPredicate predicate, it takes base time, delta in second, and dateLayout
func NewWithinPredicate(baseTime time.Time, deltaInSeconds int, dateLayout string) Predicate {
	return &withinSecPredicate{
		baseTime:       baseTime,
		deltaInSeconds: deltaInSeconds,
		dateLayout:     dateLayout,
	}
}

type betweenPredicate struct {
	from float64
	to   float64
}

func (p *betweenPredicate) Apply(value interface{}) bool {
	floatValue := AsFloat(value)
	return floatValue >= p.from && floatValue <= p.to
}

func (p *betweenPredicate) String() string {
	return fmt.Sprintf("x BETWEEN %v AND %v", p.from, p.to)
}

//NewBetweenPredicate creates a new BETWEEN predicate, it takes from, and to.
func NewBetweenPredicate(from, to interface{}) Predicate {
	return &betweenPredicate{
		from: AsFloat(from),
		to:   AsFloat(to),
	}
}

type inPredicate struct {
	predicate Predicate
}

func (p *inPredicate) Apply(value interface{}) bool {
	return p.predicate.Apply(value)
}

//NewInPredicate creates a new IN predicate
func NewInPredicate(values ...interface{}) Predicate {
	converted, kind := DiscoverCollectionValuesAndKind(values)
	switch kind {
	case reflect.Int:
		predicate := inIntPredicate{values: make(map[int]bool)}
		SliceToMap(converted, predicate.values, func(item interface{}) int {
			return AsInt(item)
		}, TrueProvider)
		return &predicate
	case reflect.Float64:
		predicate := inFloatPredicate{values: make(map[float64]bool)}
		SliceToMap(converted, predicate.values, func(item interface{}) float64 {
			return AsFloat(item)
		}, TrueProvider)
		return &predicate
	default:
		predicate := inStringPredicate{values: make(map[string]bool)}
		SliceToMap(converted, predicate.values, func(item interface{}) string {
			return AsString(item)
		}, TrueProvider)
		return &predicate
	}
}

type inFloatPredicate struct {
	values map[float64]bool
}

func (p *inFloatPredicate) Apply(value interface{}) bool {
	candidate := AsFloat(value)
	return p.values[candidate]
}

type inIntPredicate struct {
	values map[int]bool
}

func (p *inIntPredicate) Apply(value interface{}) bool {
	candidate := AsInt(value)
	return p.values[int(candidate)]
}

type inStringPredicate struct {
	values map[string]bool
}

func (p *inStringPredicate) Apply(value interface{}) bool {
	candidate := AsString(value)
	return p.values[candidate]
}

type numericComparablePredicate struct {
	rightOperand float64
	operator     string
}

func (p *numericComparablePredicate) Apply(value interface{}) bool {
	leftOperand := AsFloat(value)
	switch p.operator {
	case ">":
		return leftOperand > p.rightOperand
	case ">=":
		return leftOperand >= p.rightOperand
	case "<":
		return leftOperand < p.rightOperand
	case "<=":
		return leftOperand <= p.rightOperand
	case "=":
		return leftOperand == p.rightOperand
	case "!=":
		return leftOperand != p.rightOperand
	}
	return false
}

type stringComparablePredicate struct {
	rightOperand string
	operator     string
}

func (p *stringComparablePredicate) Apply(value interface{}) bool {
	leftOperand := AsString(value)

	switch p.operator {
	case "=":
		return leftOperand == p.rightOperand
	case "!=":
		return leftOperand != p.rightOperand
	}
	return false
}

//NewComparablePredicate create a new comparable predicate for =, !=, >=, <=
func NewComparablePredicate(operator string, leftOperand interface{}) Predicate {
	if CanConvertToFloat(leftOperand) {
		return &numericComparablePredicate{AsFloat(leftOperand), operator}
	}
	return &stringComparablePredicate{AsString(leftOperand), operator}
}

type nilPredicate struct{}

func (p *nilPredicate) Apply(value interface{}) bool {
	return value == nil || reflect.ValueOf(value).IsNil()
}

//NewNilPredicate returns a new nil predicate
func NewNilPredicate() Predicate {
	return &nilPredicate{}
}

type likePredicate struct {
	matchingFragments []string
}

func (p *likePredicate) Apply(value interface{}) bool {
	textValue := strings.ToLower(AsString(value))
	for _, matchingFragment := range p.matchingFragments {
		matchingIndex := strings.Index(textValue, matchingFragment)
		if matchingIndex == -1 {
			return false
		}
		if matchingIndex < len(textValue) {
			textValue = textValue[matchingIndex:]
		}
	}
	return true
}

//NewLikePredicate create a new like predicate
func NewLikePredicate(matching string) Predicate {
	return &likePredicate{matchingFragments: strings.Split(strings.ToLower(matching), "%")}
}
<commit_msg>patched error handling in withinSecPredicate<commit_after>package toolbox

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

var TrueProvider = func(input interface{}) bool {
	return true
}

type withinSecPredicate struct {
	baseTime        time.Time
	deltaInSeconds  int
	dateLayout      string
	actual          string
	elapsed         time.Duration
	maxAllowedDelay time.Duration
}

func (p *withinSecPredicate) String() string {
	return fmt.Sprintf("(elapsed: %d, max allowed delay: %d)\n", int(p.elapsed), int(p.maxAllowedDelay))
}

//Apply returns true if passed in time is within deltaInSeconds from baseTime
func (p *withinSecPredicate) Apply(value interface{}) bool {
	timeValue, err := ToTime(value, p.dateLayout)
	if err != nil {
		return false
	}
	elapsed := timeValue.Sub(p.baseTime)
	if elapsed < 0 {
		elapsed *= -1
	}
	var maxAllowedDelay = time.Duration(p.deltaInSeconds) * time.Second
	var passed = maxAllowedDelay >= elapsed
	if !passed {
		p.elapsed = elapsed
		p.maxAllowedDelay = maxAllowedDelay
	}
	return passed
}

func (p *withinSecPredicate) ToString() string {
	return fmt.Sprintf(" %v within %v s", p.baseTime, p.deltaInSeconds)
}

//NewWithinPredicate returns new NewWithinPredicate predicate, it takes base time, delta in second, and dateLayout
func NewWithinPredicate(baseTime time.Time, deltaInSeconds int, dateLayout string) Predicate {
	return &withinSecPredicate{
		baseTime:       baseTime,
		deltaInSeconds: deltaInSeconds,
		dateLayout:     dateLayout,
	}
}

type betweenPredicate struct {
	from float64
	to   float64
}

func (p *betweenPredicate) Apply(value interface{}) bool {
	floatValue := AsFloat(value)
	return floatValue >= p.from && floatValue <= p.to
}

func (p *betweenPredicate) String() string {
	return fmt.Sprintf("x BETWEEN %v AND %v", p.from, p.to)
}

//NewBetweenPredicate creates a new BETWEEN predicate, it takes from, and to.
func NewBetweenPredicate(from, to interface{}) Predicate {
	return &betweenPredicate{
		from: AsFloat(from),
		to:   AsFloat(to),
	}
}

type inPredicate struct {
	predicate Predicate
}

func (p *inPredicate) Apply(value interface{}) bool {
	return p.predicate.Apply(value)
}

//NewInPredicate creates a new IN predicate
func NewInPredicate(values ...interface{}) Predicate {
	converted, kind := DiscoverCollectionValuesAndKind(values)
	switch kind {
	case reflect.Int:
		predicate := inIntPredicate{values: make(map[int]bool)}
		SliceToMap(converted, predicate.values, func(item interface{}) int {
			return AsInt(item)
		}, TrueProvider)
		return &predicate
	case reflect.Float64:
		predicate := inFloatPredicate{values: make(map[float64]bool)}
		SliceToMap(converted, predicate.values, func(item interface{}) float64 {
			return AsFloat(item)
		}, TrueProvider)
		return &predicate
	default:
		predicate := inStringPredicate{values: make(map[string]bool)}
		SliceToMap(converted, predicate.values, func(item interface{}) string {
			return AsString(item)
		}, TrueProvider)
		return &predicate
	}
}

type inFloatPredicate struct {
	values map[float64]bool
}

func (p *inFloatPredicate) Apply(value interface{}) bool {
	candidate := AsFloat(value)
	return p.values[candidate]
}

type inIntPredicate struct {
	values map[int]bool
}

func (p *inIntPredicate) Apply(value interface{}) bool {
	candidate := AsInt(value)
	return p.values[int(candidate)]
}

type inStringPredicate struct {
	values map[string]bool
}

func (p *inStringPredicate) Apply(value interface{}) bool {
	candidate := AsString(value)
	return p.values[candidate]
}

type numericComparablePredicate struct {
	rightOperand float64
	operator     string
}

func (p *numericComparablePredicate) Apply(value interface{}) bool {
	leftOperand := AsFloat(value)
	switch p.operator {
	case ">":
		return leftOperand > p.rightOperand
	case ">=":
		return leftOperand >= p.rightOperand
	case "<":
		return leftOperand < p.rightOperand
	case "<=":
		return leftOperand <= p.rightOperand
	case "=":
		return leftOperand == p.rightOperand
	case "!=":
		return leftOperand != p.rightOperand
	}
	return false
}

type stringComparablePredicate struct {
	rightOperand string
	operator     string
}

func (p *stringComparablePredicate) Apply(value interface{}) bool {
	leftOperand := AsString(value)

	switch p.operator {
	case "=":
		return leftOperand == p.rightOperand
	case "!=":
		return leftOperand != p.rightOperand
	}
	return false
}

//NewComparablePredicate create a new comparable predicate for =, !=, >=, <=
func NewComparablePredicate(operator string, leftOperand interface{}) Predicate {
	if CanConvertToFloat(leftOperand) {
		return &numericComparablePredicate{AsFloat(leftOperand), operator}
	}
	return &stringComparablePredicate{AsString(leftOperand), operator}
}

type nilPredicate struct{}

func (p *nilPredicate) Apply(value interface{}) bool {
	return value == nil || reflect.ValueOf(value).IsNil()
}

//NewNilPredicate returns a new nil predicate
func NewNilPredicate() Predicate {
	return &nilPredicate{}
}

type likePredicate struct {
	matchingFragments []string
}

func (p *likePredicate) Apply(value interface{}) bool {
	textValue := strings.ToLower(AsString(value))
	for _, matchingFragment := range p.matchingFragments {
		matchingIndex := strings.Index(textValue, matchingFragment)
		if matchingIndex == -1 {
			return false
		}
		if matchingIndex < len(textValue) {
			textValue = textValue[matchingIndex:]
		}
	}
	return true
}

//NewLikePredicate create a new like predicate
func NewLikePredicate(matching string) Predicate {
	return &likePredicate{matchingFragments: strings.Split(strings.ToLower(matching), "%")}
}
<|endoftext|>


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

// The object package defines various types used to represent
// JavaScript values (objects and primitive values).
package object

import (
	"bytes"
	"fmt"
)

// Value represents any JavaScript value (primitive, object, etc.).
type Value interface {
	// Type() returns name of type (as given by the JavaScript typeof
	// operator).
	Type() string

	// IsPrimitive() returns true for primitive data (number, string,
	// boolean, etc.).
	IsPrimitive() bool

	// Parent() returns the parent (prototype) object for this object.
	Parent() Value

	// GetProperty returns the current value of the given property or
	// an ErrorMsg if that was not possible.
	GetProperty(name string) (Value, *ErrorMsg)

	// SetProperty sets the given property to the specified value or
	// returns an ErrorMsg if that was not possible.
	SetProperty(name string, value Value) *ErrorMsg

	// ToString returns a string representation of the object.  This
	// needn't be very informative (most objects will return "[object
	// Object]").  N.B.:
	//
	// - The value returned by this method is used as a property
	// key, in the case of a passing a non-string to a
	// MemberExpression (i.e., in foo[bar], where bar is not a
	// string).  It is therefore important that it do the same thing
	// as other JS interpreters.
	//
	// - The JS .toString() method just wraps this one; note however
	// that for primitives, overriding .toString on a primitive's
	// prototype won't change how numbers are implicitly stringified;
	// they'll still use the value returned by this method.  E.g.:
	//
	//     Number.proto.toString = function() { "42" };
	//     (10).toString();    // => "42"
	//     '' + 10;            // => "10"
	//
	// FIXME: move most of this comment somewhere better
	ToString() string

	// ToJSON returns a JSON representation of the object.
	//
	// The JS .toJSON() method just wraps this one.

	//	ToJSON() string

	// We also implement fmt.Stringer; this is intended to provide
	// more useful Go test error messages and/or REPL output.
	//
	// FIXME: at the moment the String method defined here is just a
	// synonym for ToJSON.  Provide additional info (type, proto,
	// etc.)

	//	fmt.Stringer
}

// Object represents typical JavaScript objects with (optional)
// prototype, properties, etc.
type Object struct {
	owner      *Owner
	parent     Value
	properties map[string]property
	f          bool
}

// property is a property descriptor, with the following fields:
// owner: Who owns the property (has permission to write it)?
// v:     The actual value of the property.
// r:     Is the property world-readable?
// e:     Is the property enumerable
// i:     Is the property ownership inherited on children?
type property struct {
	owner *Owner
	v     Value
	r     bool
	e     bool
	i     bool
}

// *Object must satisfy Value.
var _ Value = (*Object)(nil)

func (Object) Type() string {
	return "object"
}

func (Object) IsPrimitive() bool {
	return false
}

func (this Object) Parent() Value {
	return this.parent
}

func (this Object) GetProperty(name string) (Value, *ErrorMsg) {
	pd, ok := this.properties[name]
	// FIXME: permissions check for property readability goes here
	if !ok {
		return Undefined{}, nil
	}
	return pd.v, nil
}

func (this *Object) SetProperty(name string, value Value) *ErrorMsg {
	pd, ok := this.properties[name]
	if ok { // Updating existing property
		// FIXME: permissions check for property writeability goes here
		pd.v = value
		this.properties[name] = pd
		return nil
	} else { // Creating new property
		// FIXME: permissions check for object writability goes here
		this.properties[name] = property{
			owner: this.owner, // FIXME: should be caller
			v:     value,
			r:     true,
			e:     true,
			i:     false,
		}
		return nil
	}
}

func (Object) ToString() string {
	return "[object Object]"
}

func (this *Object) ToJSON() string {
	var b = new(bytes.Buffer)
	var comma bool
	b.WriteString("{ ")
	for k, p := range this.properties {
		if comma {
			b.WriteString(", ")
		}
		fmt.Fprintf(b, "%s: %v", k, p.v)
		comma = true
	}
	b.WriteString(" }")
	return b.String()
}

func (this *Object) String() string {
	return this.ToJSON()
}

func New(owner *Owner, parent Value) *Object {
	return &Object{
		owner:      owner,
		parent:     parent,
		properties: make(map[string]property),
		f:          true,
	}
}

// ObjectProto is the default prototype for (plain) JavaScript objects
// (i.e., ones created from object literals and not via
// Object.create(nil)).
var ObjectProto = &Object{
	parent:     Null{},
	properties: make(map[string]property),
}
<commit_msg>Remove ToJSON, String methods<commit_after>/* Copyright 2017 Google Inc.
 * https://github.com/NeilFraser/CodeCity
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// The object package defines various types used to represent
// JavaScript values (objects and primitive values).
package object

import (
//	"fmt"
)

// Value represents any JavaScript value (primitive, object, etc.).
type Value interface {
	// Type() returns name of type (as given by the JavaScript typeof
	// operator).
	Type() string

	// IsPrimitive() returns true for primitive data (number, string,
	// boolean, etc.).
	IsPrimitive() bool

	// Parent() returns the parent (prototype) object for this object.
	Parent() Value

	// GetProperty returns the current value of the given property or
	// an ErrorMsg if that was not possible.
	GetProperty(name string) (Value, *ErrorMsg)

	// SetProperty sets the given property to the specified value or
	// returns an ErrorMsg if that was not possible.
	SetProperty(name string, value Value) *ErrorMsg

	// ToString returns a string representation of the object.  This
	// needn't be very informative (most objects will return "[object
	// Object]").  N.B.:
	//
	// - The value returned by this method is used as a property
	// key, in the case of a passing a non-string to a
	// MemberExpression (i.e., in foo[bar], where bar is not a
	// string).  It is therefore important that it do the same thing
	// as other JS interpreters.
	//
	// - The JS .toString() method just wraps this one; note however
	// that for primitives, overriding .toString on a primitive's
	// prototype won't change how numbers are implicitly stringified;
	// they'll still use the value returned by this method.  E.g.:
	//
	//     Number.proto.toString = function() { "42" };
	//     (10).toString();    // => "42"
	//     '' + 10;            // => "10"
	//
	// FIXME: move most of this comment somewhere better
	ToString() string
}

// Object represents typical JavaScript objects with (optional)
// prototype, properties, etc.
type Object struct {
	owner      *Owner
	parent     Value
	properties map[string]property
	f          bool
}

// property is a property descriptor, with the following fields:
// owner: Who owns the property (has permission to write it)?
// v:     The actual value of the property.
// r:     Is the property world-readable?
// e:     Is the property enumerable
// i:     Is the property ownership inherited on children?
type property struct {
	owner *Owner
	v     Value
	r     bool
	e     bool
	i     bool
}

// *Object must satisfy Value.
var _ Value = (*Object)(nil)

func (Object) Type() string {
	return "object"
}

func (Object) IsPrimitive() bool {
	return false
}

func (this Object) Parent() Value {
	return this.parent
}

func (this Object) GetProperty(name string) (Value, *ErrorMsg) {
	pd, ok := this.properties[name]
	// FIXME: permissions check for property readability goes here
	if !ok {
		return Undefined{}, nil
	}
	return pd.v, nil
}

func (this *Object) SetProperty(name string, value Value) *ErrorMsg {
	pd, ok := this.properties[name]
	if ok { // Updating existing property
		// FIXME: permissions check for property writeability goes here
		pd.v = value
		this.properties[name] = pd
		return nil
	} else { // Creating new property
		// FIXME: permissions check for object writability goes here
		this.properties[name] = property{
			owner: this.owner, // FIXME: should be caller
			v:     value,
			r:     true,
			e:     true,
			i:     false,
		}
		return nil
	}
}

func (Object) ToString() string {
	return "[object Object]"
}

func New(owner *Owner, parent Value) *Object {
	return &Object{
		owner:      owner,
		parent:     parent,
		properties: make(map[string]property),
		f:          true,
	}
}

// ObjectProto is the default prototype for (plain) JavaScript objects
// (i.e., ones created from object literals and not via
// Object.create(nil)).
var ObjectProto = &Object{
	parent:     Null{},
	properties: make(map[string]property),
}
<|endoftext|>


<commit_before>package scheduler

import (
	"context"
	"fmt"
	"sync"
	"time"

	"code.cloudfoundry.org/lager"
	"github.com/concourse/concourse/atc/db"
	"github.com/concourse/concourse/atc/db/lock"
	"github.com/concourse/concourse/atc/metric"
	"github.com/concourse/concourse/atc/scheduler/algorithm"
)

//go:generate counterfeiter . BuildScheduler

type BuildScheduler interface {
	Schedule(
		ctx context.Context,
		logger lager.Logger,
		pipeline db.Pipeline,
		job db.Job,
		resources db.Resources,
		relatedJobIDs algorithm.NameToIDMap,
	) (bool, error)
}

type schedulerRunner struct {
	logger     lager.Logger
	jobFactory db.JobFactory
	scheduler  BuildScheduler

	guardJobScheduling chan struct{}
	running            *sync.Map
}

func NewRunner(logger lager.Logger, jobFactory db.JobFactory, scheduler BuildScheduler, maxJobs uint64) Runner {
	newGuardJobScheduling := make(chan struct{}, maxJobs)
	return &schedulerRunner{
		logger:     logger,
		jobFactory: jobFactory,
		scheduler:  scheduler,

		guardJobScheduling: newGuardJobScheduling,
		running:            &sync.Map{},
	}
}

func (s *schedulerRunner) Run(ctx context.Context) error {
	sLog := s.logger.Session("run")

	sLog.Debug("start")
	defer sLog.Debug("done")

	jobs, err := s.jobFactory.JobsToSchedule()
	if err != nil {
		return fmt.Errorf("find jobs to schedule: %w", err)
	}

	pipelineIDToPipeline, pipelineIDToJobs, err := s.constructPipelineIDMaps(jobs)
	if err != nil {
		return err
	}

	for pipelineID, jobsToSchedule := range pipelineIDToJobs {
		pipeline := pipelineIDToPipeline[pipelineID]

		pLog := s.logger.Session("pipeline", lager.Data{"pipeline": pipeline.Name()})

		err := s.schedulePipeline(ctx, pLog, pipeline, jobsToSchedule)
		if err != nil {
			pLog.Error("failed-to-schedule", err)
		}
	}

	return nil
}

func (s *schedulerRunner) schedulePipeline(ctx context.Context, logger lager.Logger, pipeline db.Pipeline, jobsToSchedule db.Jobs) error {
	resources, err := pipeline.Resources()
	if err != nil {
		return fmt.Errorf("find resources: %w", err)
	}

	jobs, err := pipeline.Jobs()
	if err != nil {
		return fmt.Errorf("find jobs: %w", err)
	}

	jobsMap := map[string]int{}
	for _, job := range jobs {
		jobsMap[job.Name()] = job.ID()
	}

	for _, j := range jobsToSchedule {
		if _, exists := s.running.LoadOrStore(j.ID(), true); exists {
			// already scheduling this job
			continue
		}

		s.guardJobScheduling <- struct{}{}

		jLog := logger.Session("job", lager.Data{"job": j.Name()})

		go func(job db.Job) {
			defer func() {
				<-s.guardJobScheduling
				s.running.Delete(job.ID())
			}()

			schedulingLock, acquired, err := job.AcquireSchedulingLock(logger)
			if err != nil {
				jLog.Error("failed-to-acquire-lock", err)
				return
			}

			if !acquired {
				return
			}

			err = s.scheduleJob(ctx, logger, schedulingLock, pipeline, job, resources, jobsMap)
			if err != nil {
				jLog.Error("failed-to-schedule-job", err)
			}
		}(j)
	}

	return nil
}

func (s *schedulerRunner) scheduleJob(ctx context.Context, logger lager.Logger, schedulingLock lock.Lock, pipeline db.Pipeline, job db.Job, resources db.Resources, jobs algorithm.NameToIDMap) error {
	logger = logger.Session("schedule-job", lager.Data{"job": job.Name()})

	logger.Debug("schedule")

	defer schedulingLock.Release()

	// Grabs out the requested time that triggered off the job schedule in
	// order to set the last scheduled to the exact time of this triggering
	// request
	requestedTime := job.ScheduleRequestedTime()

	found, err := job.Reload()
	if err != nil {
		return fmt.Errorf("reload job: %w", err)
	}

	if !found {
		logger.Debug("could-not-find-job-to-reload")
		return nil
	}

	jStart := time.Now()

	needsRetry, err := s.scheduler.Schedule(
		ctx,
		logger,
		pipeline,
		job,
		resources,
		jobs,
	)
	if err != nil {
		return fmt.Errorf("schedule job: %w", err)
	}

	if !needsRetry {
		err = job.UpdateLastScheduled(requestedTime)
		if err != nil {
			logger.Error("failed-to-update-last-scheduled", err, lager.Data{"job": job.Name()})
			return fmt.Errorf("update last scheduled: %w", err)
		}
	}

	metric.SchedulingJobDuration{
		PipelineName: job.PipelineName(),
		JobName:      job.Name(),
		JobID:        job.ID(),
		Duration:     time.Since(jStart),
	}.Emit(logger)

	return nil
}

func (s *schedulerRunner) constructPipelineIDMaps(jobs db.Jobs) (map[int]db.Pipeline, map[int]db.Jobs, error) {
	pipelineIDToPipeline := make(map[int]db.Pipeline)
	pipelineIDToJobs := make(map[int]db.Jobs)

	for _, job := range jobs {
		pipelineID := job.PipelineID()

		_, found := pipelineIDToPipeline[pipelineID]
		if !found {
			pipeline, found, err := job.Pipeline()
			if err != nil {
				return nil, nil, fmt.Errorf("find pipeline for job: %w", err)
			}

			if !found {
				s.logger.Info("could-not-find-pipeline-for-job", lager.Data{"job": job.Name()})
				continue
			}

			pipelineIDToPipeline[pipelineID] = pipeline
		}

		pipelineIDToJobs[pipelineID] = append(pipelineIDToJobs[pipelineID], job)
	}

	return pipelineIDToPipeline, pipelineIDToJobs, nil
}
<commit_msg>scheduler: release lock in original goroutine func<commit_after>package scheduler

import (
	"context"
	"fmt"
	"sync"
	"time"

	"code.cloudfoundry.org/lager"
	"github.com/concourse/concourse/atc/db"
	"github.com/concourse/concourse/atc/metric"
	"github.com/concourse/concourse/atc/scheduler/algorithm"
)

//go:generate counterfeiter . BuildScheduler

type BuildScheduler interface {
	Schedule(
		ctx context.Context,
		logger lager.Logger,
		pipeline db.Pipeline,
		job db.Job,
		resources db.Resources,
		relatedJobIDs algorithm.NameToIDMap,
	) (bool, error)
}

type schedulerRunner struct {
	logger     lager.Logger
	jobFactory db.JobFactory
	scheduler  BuildScheduler

	guardJobScheduling chan struct{}
	running            *sync.Map
}

func NewRunner(logger lager.Logger, jobFactory db.JobFactory, scheduler BuildScheduler, maxJobs uint64) Runner {
	newGuardJobScheduling := make(chan struct{}, maxJobs)
	return &schedulerRunner{
		logger:     logger,
		jobFactory: jobFactory,
		scheduler:  scheduler,

		guardJobScheduling: newGuardJobScheduling,
		running:            &sync.Map{},
	}
}

func (s *schedulerRunner) Run(ctx context.Context) error {
	sLog := s.logger.Session("run")

	sLog.Debug("start")
	defer sLog.Debug("done")

	jobs, err := s.jobFactory.JobsToSchedule()
	if err != nil {
		return fmt.Errorf("find jobs to schedule: %w", err)
	}

	pipelineIDToPipeline, pipelineIDToJobs, err := s.constructPipelineIDMaps(jobs)
	if err != nil {
		return err
	}

	for pipelineID, jobsToSchedule := range pipelineIDToJobs {
		pipeline := pipelineIDToPipeline[pipelineID]

		pLog := s.logger.Session("pipeline", lager.Data{"pipeline": pipeline.Name()})

		err := s.schedulePipeline(ctx, pLog, pipeline, jobsToSchedule)
		if err != nil {
			pLog.Error("failed-to-schedule", err)
		}
	}

	return nil
}

func (s *schedulerRunner) schedulePipeline(ctx context.Context, logger lager.Logger, pipeline db.Pipeline, jobsToSchedule db.Jobs) error {
	resources, err := pipeline.Resources()
	if err != nil {
		return fmt.Errorf("find resources: %w", err)
	}

	jobs, err := pipeline.Jobs()
	if err != nil {
		return fmt.Errorf("find jobs: %w", err)
	}

	jobsMap := map[string]int{}
	for _, job := range jobs {
		jobsMap[job.Name()] = job.ID()
	}

	for _, j := range jobsToSchedule {
		if _, exists := s.running.LoadOrStore(j.ID(), true); exists {
			// already scheduling this job
			continue
		}

		s.guardJobScheduling <- struct{}{}

		jLog := logger.Session("job", lager.Data{"job": j.Name()})

		go func(job db.Job) {
			defer func() {
				<-s.guardJobScheduling
				s.running.Delete(job.ID())
			}()

			schedulingLock, acquired, err := job.AcquireSchedulingLock(logger)
			if err != nil {
				jLog.Error("failed-to-acquire-lock", err)
				return
			}

			if !acquired {
				return
			}

			defer schedulingLock.Release()

			err = s.scheduleJob(ctx, logger, pipeline, job, resources, jobsMap)
			if err != nil {
				jLog.Error("failed-to-schedule-job", err)
			}
		}(j)
	}

	return nil
}

func (s *schedulerRunner) scheduleJob(ctx context.Context, logger lager.Logger, pipeline db.Pipeline, job db.Job, resources db.Resources, jobs algorithm.NameToIDMap) error {
	logger = logger.Session("schedule-job", lager.Data{"job": job.Name()})

	logger.Debug("schedule")

	// Grabs out the requested time that triggered off the job schedule in
	// order to set the last scheduled to the exact time of this triggering
	// request
	requestedTime := job.ScheduleRequestedTime()

	found, err := job.Reload()
	if err != nil {
		return fmt.Errorf("reload job: %w", err)
	}

	if !found {
		logger.Debug("could-not-find-job-to-reload")
		return nil
	}

	jStart := time.Now()

	needsRetry, err := s.scheduler.Schedule(
		ctx,
		logger,
		pipeline,
		job,
		resources,
		jobs,
	)
	if err != nil {
		return fmt.Errorf("schedule job: %w", err)
	}

	if !needsRetry {
		err = job.UpdateLastScheduled(requestedTime)
		if err != nil {
			logger.Error("failed-to-update-last-scheduled", err, lager.Data{"job": job.Name()})
			return fmt.Errorf("update last scheduled: %w", err)
		}
	}

	metric.SchedulingJobDuration{
		PipelineName: job.PipelineName(),
		JobName:      job.Name(),
		JobID:        job.ID(),
		Duration:     time.Since(jStart),
	}.Emit(logger)

	return nil
}

func (s *schedulerRunner) constructPipelineIDMaps(jobs db.Jobs) (map[int]db.Pipeline, map[int]db.Jobs, error) {
	pipelineIDToPipeline := make(map[int]db.Pipeline)
	pipelineIDToJobs := make(map[int]db.Jobs)

	for _, job := range jobs {
		pipelineID := job.PipelineID()

		_, found := pipelineIDToPipeline[pipelineID]
		if !found {
			pipeline, found, err := job.Pipeline()
			if err != nil {
				return nil, nil, fmt.Errorf("find pipeline for job: %w", err)
			}

			if !found {
				s.logger.Info("could-not-find-pipeline-for-job", lager.Data{"job": job.Name()})
				continue
			}

			pipelineIDToPipeline[pipelineID] = pipeline
		}

		pipelineIDToJobs[pipelineID] = append(pipelineIDToJobs[pipelineID], job)
	}

	return pipelineIDToPipeline, pipelineIDToJobs, nil
}
<|endoftext|>


<commit_before>package influxql

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"runtime/debug"
	"sync"
	"sync/atomic"
	"time"

	"github.com/influxdata/influxdb/models"
)

var (
	// ErrInvalidQuery is returned when executing an unknown query type.
	ErrInvalidQuery = errors.New("invalid query")

	// ErrNotExecuted is returned when a statement is not executed in a query.
	// This can occur when a previous statement in the same query has errored.
	ErrNotExecuted = errors.New("not executed")

	// ErrQueryInterrupted is an error returned when the query is interrupted.
	ErrQueryInterrupted = errors.New("query interrupted")

	// ErrMaxConcurrentQueriesReached is an error when a query cannot be run
	// because the maximum number of queries has been reached.
	ErrMaxConcurrentQueriesReached = errors.New("max concurrent queries reached")

	// ErrQueryEngineShutdown is an error sent when the query cannot be
	// created because the query engine was shutdown.
	ErrQueryEngineShutdown = errors.New("query engine shutdown")

	// ErrMaxPointsReached is an error when a query hits the maximum number of
	// points.
	ErrMaxPointsReached = errors.New("max number of points reached")

	// ErrQueryTimeoutReached is an error when a query hits the timeout.
	ErrQueryTimeoutReached = errors.New("query timeout reached")
)

// Statistics for the QueryExecutor
const (
	statQueriesActive          = "queriesActive"   // Number of queries currently being executed
	statQueriesExecuted        = "queriesExecuted" // Number of queries that have been executed (started).
	statQueriesFinished        = "queriesFinished" // Number of queries that have finished.
	statQueryExecutionDuration = "queryDurationNs" // Total (wall) time spent executing queries
)

// ErrDatabaseNotFound returns a database not found error for the given database name.
func ErrDatabaseNotFound(name string) error { return fmt.Errorf("database not found: %s", name) }

// ErrMeasurementNotFound returns a measurement not found error for the given measurement name.
func ErrMeasurementNotFound(name string) error { return fmt.Errorf("measurement not found: %s", name) }

// ExecutionOptions contains the options for executing a query.
type ExecutionOptions struct {
	// The database the query is running against.
	Database string

	// The requested maximum number of points to return in each result.
	ChunkSize int

	// If this query is being executed in a read-only context.
	ReadOnly bool

	// Node to execute on.
	NodeID uint64

	// Quiet suppresses non-essential output from the query executor.
	Quiet bool
}

// ExecutionContext contains state that the query is currently executing with.
type ExecutionContext struct {
	// The statement ID of the executing query.
	StatementID int

	// The query ID of the executing query.
	QueryID uint64

	// The query task information available to the StatementExecutor.
	Query *QueryTask

	// Output channel where results and errors should be sent.
	Results chan *Result

	// Hold the query executor's logger.
	Log *log.Logger

	// A channel that is closed when the query is interrupted.
	InterruptCh <-chan struct{}

	// Options used to start this query.
	ExecutionOptions
}

// StatementExecutor executes a statement within the QueryExecutor.
type StatementExecutor interface {
	// ExecuteStatement executes a statement. Results should be sent to the
	// results channel in the ExecutionContext.
	ExecuteStatement(stmt Statement, ctx ExecutionContext) error
}

// StatementNormalizer normalizes a statement before it is executed.
type StatementNormalizer interface {
	// NormalizeStatement adds a default database and policy to the
	// measurements in the statement.
	NormalizeStatement(stmt Statement, database string) error
}

// QueryExecutor executes every statement in an Query.
type QueryExecutor struct {
	// Used for executing a statement in the query.
	StatementExecutor StatementExecutor

	// Used for tracking running queries.
	TaskManager *TaskManager

	// Logger to use for all logging.
	// Defaults to discarding all log output.
	Logger *log.Logger

	// expvar-based stats.
	stats *QueryStatistics
}

// NewQueryExecutor returns a new instance of QueryExecutor.
func NewQueryExecutor() *QueryExecutor {
	return &QueryExecutor{
		TaskManager: NewTaskManager(),
		Logger:      log.New(ioutil.Discard, "[query] ", log.LstdFlags),
		stats:       &QueryStatistics{},
	}
}

// QueryStatistics keeps statistics related to the QueryExecutor.
type QueryStatistics struct {
	ActiveQueries          int64
	ExecutedQueries        int64
	FinishedQueries        int64
	QueryExecutionDuration int64
}

// Statistics returns statistics for periodic monitoring.
func (e *QueryExecutor) Statistics(tags map[string]string) []models.Statistic {
	return []models.Statistic{{
		Name: "queryExecutor",
		Tags: tags,
		Values: map[string]interface{}{
			statQueriesActive:          atomic.LoadInt64(&e.stats.ActiveQueries),
			statQueriesExecuted:        atomic.LoadInt64(&e.stats.ExecutedQueries),
			statQueriesFinished:        atomic.LoadInt64(&e.stats.FinishedQueries),
			statQueryExecutionDuration: atomic.LoadInt64(&e.stats.QueryExecutionDuration),
		},
	}}
}

// Close kills all running queries and prevents new queries from being attached.
func (e *QueryExecutor) Close() error {
	return e.TaskManager.Close()
}

// SetLogOutput sets the writer to which all logs are written. It must not be
// called after Open is called.
func (e *QueryExecutor) SetLogOutput(w io.Writer) {
	e.Logger = log.New(w, "[query] ", log.LstdFlags)
	e.TaskManager.Logger = e.Logger
}

// ExecuteQuery executes each statement within a query.
func (e *QueryExecutor) ExecuteQuery(query *Query, opt ExecutionOptions, closing chan struct{}) <-chan *Result {
	results := make(chan *Result)
	go e.executeQuery(query, opt, closing, results)
	return results
}

func (e *QueryExecutor) executeQuery(query *Query, opt ExecutionOptions, closing <-chan struct{}, results chan *Result) {
	defer close(results)
	defer e.recover(query, results)

	atomic.AddInt64(&e.stats.ActiveQueries, 1)
	atomic.AddInt64(&e.stats.ExecutedQueries, 1)
	defer func(start time.Time) {
		atomic.AddInt64(&e.stats.ActiveQueries, -1)
		atomic.AddInt64(&e.stats.FinishedQueries, 1)
		atomic.AddInt64(&e.stats.QueryExecutionDuration, time.Since(start).Nanoseconds())
	}(time.Now())

	qid, task, err := e.TaskManager.AttachQuery(query, opt.Database, closing)
	if err != nil {
		results <- &Result{Err: err}
		return
	}
	defer e.TaskManager.KillQuery(qid)

	// Setup the execution context that will be used when executing statements.
	ctx := ExecutionContext{
		QueryID:          qid,
		Query:            task,
		Results:          results,
		Log:              e.Logger,
		InterruptCh:      task.closing,
		ExecutionOptions: opt,
	}

	var i int
	for ; i < len(query.Statements); i++ {
		ctx.StatementID = i
		stmt := query.Statements[i]

		// If a default database wasn't passed in by the caller, check the statement.
		defaultDB := opt.Database
		if defaultDB == "" {
			if s, ok := stmt.(HasDefaultDatabase); ok {
				defaultDB = s.DefaultDatabase()
			}
		}

		// Rewrite statements, if necessary.
		// This can occur on meta read statements which convert to SELECT statements.
		newStmt, err := RewriteStatement(stmt)
		if err != nil {
			results <- &Result{Err: err}
			break
		}
		stmt = newStmt

		// Normalize each statement if possible.
		if normalizer, ok := e.StatementExecutor.(StatementNormalizer); ok {
			if err := normalizer.NormalizeStatement(stmt, defaultDB); err != nil {
				results <- &Result{Err: err}
				break
			}
		}

		// Log each normalized statement.
		if !ctx.Quiet {
			e.Logger.Println(stmt.String())
		}

		// Send any other statements to the underlying statement executor.
		err = e.StatementExecutor.ExecuteStatement(stmt, ctx)
		if err == ErrQueryInterrupted {
			// Query was interrupted so retrieve the real interrupt error from
			// the query task if there is one.
			if qerr := task.Error(); qerr != nil {
				err = qerr
			}
		}

		// Send an error for this result if it failed for some reason.
		if err != nil {
			results <- &Result{
				StatementID: i,
				Err:         err,
			}
			// Stop after the first error.
			break
		}
	}

	// Send error results for any statements which were not executed.
	for ; i < len(query.Statements)-1; i++ {
		results <- &Result{
			StatementID: i,
			Err:         ErrNotExecuted,
		}
	}
}

func (e *QueryExecutor) recover(query *Query, results chan *Result) {
	if err := recover(); err != nil {
		e.Logger.Printf("%s [panic:%s] %s", query.String(), err, debug.Stack())
		results <- &Result{
			StatementID: -1,
			Err:         fmt.Errorf("%s [panic:%s]", query.String(), err),
		}
	}
}

// QueryMonitorFunc is a function that will be called to check if a query
// is currently healthy. If the query needs to be interrupted for some reason,
// the error should be returned by this function.
type QueryMonitorFunc func(<-chan struct{}) error

// QueryTask is the internal data structure for managing queries.
// For the public use data structure that gets returned, see QueryTask.
type QueryTask struct {
	query     string
	database  string
	startTime time.Time
	closing   chan struct{}
	monitorCh chan error
	err       error
	mu        sync.Mutex
}

// Monitor starts a new goroutine that will monitor a query. The function
// will be passed in a channel to signal when the query has been finished
// normally. If the function returns with an error and the query is still
// running, the query will be terminated.
func (q *QueryTask) Monitor(fn QueryMonitorFunc) {
	go q.monitor(fn)
}

// Error returns any asynchronous error that may have occured while executing
// the query.
func (q *QueryTask) Error() error {
	q.mu.Lock()
	defer q.mu.Unlock()
	return q.err
}

func (q *QueryTask) setError(err error) {
	q.mu.Lock()
	q.err = err
	q.mu.Unlock()
}

func (q *QueryTask) monitor(fn QueryMonitorFunc) {
	if err := fn(q.closing); err != nil {
		select {
		case <-q.closing:
		case q.monitorCh <- err:
		}
	}
}
<commit_msg>Check in between query statements to see if the query was interrupted<commit_after>package influxql

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"runtime/debug"
	"sync"
	"sync/atomic"
	"time"

	"github.com/influxdata/influxdb/models"
)

var (
	// ErrInvalidQuery is returned when executing an unknown query type.
	ErrInvalidQuery = errors.New("invalid query")

	// ErrNotExecuted is returned when a statement is not executed in a query.
	// This can occur when a previous statement in the same query has errored.
	ErrNotExecuted = errors.New("not executed")

	// ErrQueryInterrupted is an error returned when the query is interrupted.
	ErrQueryInterrupted = errors.New("query interrupted")

	// ErrMaxConcurrentQueriesReached is an error when a query cannot be run
	// because the maximum number of queries has been reached.
	ErrMaxConcurrentQueriesReached = errors.New("max concurrent queries reached")

	// ErrQueryEngineShutdown is an error sent when the query cannot be
	// created because the query engine was shutdown.
	ErrQueryEngineShutdown = errors.New("query engine shutdown")

	// ErrMaxPointsReached is an error when a query hits the maximum number of
	// points.
	ErrMaxPointsReached = errors.New("max number of points reached")

	// ErrQueryTimeoutReached is an error when a query hits the timeout.
	ErrQueryTimeoutReached = errors.New("query timeout reached")
)

// Statistics for the QueryExecutor
const (
	statQueriesActive          = "queriesActive"   // Number of queries currently being executed
	statQueriesExecuted        = "queriesExecuted" // Number of queries that have been executed (started).
	statQueriesFinished        = "queriesFinished" // Number of queries that have finished.
	statQueryExecutionDuration = "queryDurationNs" // Total (wall) time spent executing queries
)

// ErrDatabaseNotFound returns a database not found error for the given database name.
func ErrDatabaseNotFound(name string) error { return fmt.Errorf("database not found: %s", name) }

// ErrMeasurementNotFound returns a measurement not found error for the given measurement name.
func ErrMeasurementNotFound(name string) error { return fmt.Errorf("measurement not found: %s", name) }

// ExecutionOptions contains the options for executing a query.
type ExecutionOptions struct {
	// The database the query is running against.
	Database string

	// The requested maximum number of points to return in each result.
	ChunkSize int

	// If this query is being executed in a read-only context.
	ReadOnly bool

	// Node to execute on.
	NodeID uint64

	// Quiet suppresses non-essential output from the query executor.
	Quiet bool
}

// ExecutionContext contains state that the query is currently executing with.
type ExecutionContext struct {
	// The statement ID of the executing query.
	StatementID int

	// The query ID of the executing query.
	QueryID uint64

	// The query task information available to the StatementExecutor.
	Query *QueryTask

	// Output channel where results and errors should be sent.
	Results chan *Result

	// Hold the query executor's logger.
	Log *log.Logger

	// A channel that is closed when the query is interrupted.
	InterruptCh <-chan struct{}

	// Options used to start this query.
	ExecutionOptions
}

// StatementExecutor executes a statement within the QueryExecutor.
type StatementExecutor interface {
	// ExecuteStatement executes a statement. Results should be sent to the
	// results channel in the ExecutionContext.
	ExecuteStatement(stmt Statement, ctx ExecutionContext) error
}

// StatementNormalizer normalizes a statement before it is executed.
type StatementNormalizer interface {
	// NormalizeStatement adds a default database and policy to the
	// measurements in the statement.
	NormalizeStatement(stmt Statement, database string) error
}

// QueryExecutor executes every statement in an Query.
type QueryExecutor struct {
	// Used for executing a statement in the query.
	StatementExecutor StatementExecutor

	// Used for tracking running queries.
	TaskManager *TaskManager

	// Logger to use for all logging.
	// Defaults to discarding all log output.
	Logger *log.Logger

	// expvar-based stats.
	stats *QueryStatistics
}

// NewQueryExecutor returns a new instance of QueryExecutor.
func NewQueryExecutor() *QueryExecutor {
	return &QueryExecutor{
		TaskManager: NewTaskManager(),
		Logger:      log.New(ioutil.Discard, "[query] ", log.LstdFlags),
		stats:       &QueryStatistics{},
	}
}

// QueryStatistics keeps statistics related to the QueryExecutor.
type QueryStatistics struct {
	ActiveQueries          int64
	ExecutedQueries        int64
	FinishedQueries        int64
	QueryExecutionDuration int64
}

// Statistics returns statistics for periodic monitoring.
func (e *QueryExecutor) Statistics(tags map[string]string) []models.Statistic {
	return []models.Statistic{{
		Name: "queryExecutor",
		Tags: tags,
		Values: map[string]interface{}{
			statQueriesActive:          atomic.LoadInt64(&e.stats.ActiveQueries),
			statQueriesExecuted:        atomic.LoadInt64(&e.stats.ExecutedQueries),
			statQueriesFinished:        atomic.LoadInt64(&e.stats.FinishedQueries),
			statQueryExecutionDuration: atomic.LoadInt64(&e.stats.QueryExecutionDuration),
		},
	}}
}

// Close kills all running queries and prevents new queries from being attached.
func (e *QueryExecutor) Close() error {
	return e.TaskManager.Close()
}

// SetLogOutput sets the writer to which all logs are written. It must not be
// called after Open is called.
func (e *QueryExecutor) SetLogOutput(w io.Writer) {
	e.Logger = log.New(w, "[query] ", log.LstdFlags)
	e.TaskManager.Logger = e.Logger
}

// ExecuteQuery executes each statement within a query.
func (e *QueryExecutor) ExecuteQuery(query *Query, opt ExecutionOptions, closing chan struct{}) <-chan *Result {
	results := make(chan *Result)
	go e.executeQuery(query, opt, closing, results)
	return results
}

func (e *QueryExecutor) executeQuery(query *Query, opt ExecutionOptions, closing <-chan struct{}, results chan *Result) {
	defer close(results)
	defer e.recover(query, results)

	atomic.AddInt64(&e.stats.ActiveQueries, 1)
	atomic.AddInt64(&e.stats.ExecutedQueries, 1)
	defer func(start time.Time) {
		atomic.AddInt64(&e.stats.ActiveQueries, -1)
		atomic.AddInt64(&e.stats.FinishedQueries, 1)
		atomic.AddInt64(&e.stats.QueryExecutionDuration, time.Since(start).Nanoseconds())
	}(time.Now())

	qid, task, err := e.TaskManager.AttachQuery(query, opt.Database, closing)
	if err != nil {
		results <- &Result{Err: err}
		return
	}
	defer e.TaskManager.KillQuery(qid)

	// Setup the execution context that will be used when executing statements.
	ctx := ExecutionContext{
		QueryID:          qid,
		Query:            task,
		Results:          results,
		Log:              e.Logger,
		InterruptCh:      task.closing,
		ExecutionOptions: opt,
	}

	var i int
	for ; i < len(query.Statements); i++ {
		ctx.StatementID = i
		stmt := query.Statements[i]

		// If a default database wasn't passed in by the caller, check the statement.
		defaultDB := opt.Database
		if defaultDB == "" {
			if s, ok := stmt.(HasDefaultDatabase); ok {
				defaultDB = s.DefaultDatabase()
			}
		}

		// Rewrite statements, if necessary.
		// This can occur on meta read statements which convert to SELECT statements.
		newStmt, err := RewriteStatement(stmt)
		if err != nil {
			results <- &Result{Err: err}
			break
		}
		stmt = newStmt

		// Normalize each statement if possible.
		if normalizer, ok := e.StatementExecutor.(StatementNormalizer); ok {
			if err := normalizer.NormalizeStatement(stmt, defaultDB); err != nil {
				results <- &Result{Err: err}
				break
			}
		}

		// Log each normalized statement.
		if !ctx.Quiet {
			e.Logger.Println(stmt.String())
		}

		// Send any other statements to the underlying statement executor.
		err = e.StatementExecutor.ExecuteStatement(stmt, ctx)
		if err == ErrQueryInterrupted {
			// Query was interrupted so retrieve the real interrupt error from
			// the query task if there is one.
			if qerr := task.Error(); qerr != nil {
				err = qerr
			}
		}

		// Send an error for this result if it failed for some reason.
		if err != nil {
			results <- &Result{
				StatementID: i,
				Err:         err,
			}
			// Stop after the first error.
			break
		}

		// Check if the query was interrupted during an uninterruptible statement.
		interrupted := false
		if ctx.InterruptCh != nil {
			select {
			case <-ctx.InterruptCh:
				interrupted = true
			default:
				// Query has not been interrupted.
			}
		}

		if interrupted {
			break
		}
	}

	// Send error results for any statements which were not executed.
	for ; i < len(query.Statements)-1; i++ {
		results <- &Result{
			StatementID: i,
			Err:         ErrNotExecuted,
		}
	}
}

func (e *QueryExecutor) recover(query *Query, results chan *Result) {
	if err := recover(); err != nil {
		e.Logger.Printf("%s [panic:%s] %s", query.String(), err, debug.Stack())
		results <- &Result{
			StatementID: -1,
			Err:         fmt.Errorf("%s [panic:%s]", query.String(), err),
		}
	}
}

// QueryMonitorFunc is a function that will be called to check if a query
// is currently healthy. If the query needs to be interrupted for some reason,
// the error should be returned by this function.
type QueryMonitorFunc func(<-chan struct{}) error

// QueryTask is the internal data structure for managing queries.
// For the public use data structure that gets returned, see QueryTask.
type QueryTask struct {
	query     string
	database  string
	startTime time.Time
	closing   chan struct{}
	monitorCh chan error
	err       error
	mu        sync.Mutex
}

// Monitor starts a new goroutine that will monitor a query. The function
// will be passed in a channel to signal when the query has been finished
// normally. If the function returns with an error and the query is still
// running, the query will be terminated.
func (q *QueryTask) Monitor(fn QueryMonitorFunc) {
	go q.monitor(fn)
}

// Error returns any asynchronous error that may have occured while executing
// the query.
func (q *QueryTask) Error() error {
	q.mu.Lock()
	defer q.mu.Unlock()
	return q.err
}

func (q *QueryTask) setError(err error) {
	q.mu.Lock()
	q.err = err
	q.mu.Unlock()
}

func (q *QueryTask) monitor(fn QueryMonitorFunc) {
	if err := fn(q.closing); err != nil {
		select {
		case <-q.closing:
		case q.monitorCh <- err:
		}
	}
}
<|endoftext|>


<commit_before>// +build acceptance

package v2

import (
	"fmt"
	"testing"

	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/acceptance/tools"
	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
	"github.com/rackspace/gophercloud/pagination"
)

func TestListServers(t *testing.T) {
	client, err := newClient()
	if err != nil {
		t.Fatalf("Unable to create a compute client: %v", err)
	}

	t.Logf("ID\tRegion\tName\tStatus\tIPv4\tIPv6")

	pager := servers.List(client)
	count, pages := 0, 0
	pager.EachPage(func(page pagination.Page) (bool, error) {
		pages++
		t.Logf("---")

		servers, err := servers.ExtractServers(page)
		if err != nil {
			return false, err
		}

		for _, s := range servers {
			t.Logf("%s\t%s\t%s\t%s\t%s\t\n", s.ID, s.Name, s.Status, s.AccessIPv4, s.AccessIPv6)
			count++
		}

		return true, nil
	})

	fmt.Printf("--------\n%d servers listed on %d pages.\n", count, pages)
}

func createServer(t *testing.T, client *gophercloud.ServiceClient, choices *ComputeChoices) (*servers.Server, error) {
	name := tools.RandomString("ACPTTEST", 16)
	t.Logf("Attempting to create server: %s\n", name)

	server, err := servers.Create(client, servers.CreateOpts{
		Name:      name,
		FlavorRef: choices.FlavorID,
		ImageRef:  choices.ImageID,
	}).Extract()
	if err != nil {
		t.Fatalf("Unable to create server: %v", err)
	}

	return server, err
}

func TestCreateDestroyServer(t *testing.T) {
	choices, err := ComputeChoicesFromEnv()
	if err != nil {
		t.Fatal(err)
	}

	client, err := newClient()
	if err != nil {
		t.Fatalf("Unable to create a compute client: %v", err)
	}

	name := tools.RandomString("ACPTTEST", 16)
	t.Logf("Attempting to create server: %s\n", name)

	server, err := createServer(t, client, choices)
	if err != nil {
		t.Fatalf("Unable to create server: %v", err)
	}
	defer func() {
		servers.Delete(client, server.ID)
		t.Logf("Server deleted.")
	}()

	if err = waitForStatus(client, server, "ACTIVE"); err != nil {
		t.Fatalf("Unable to wait for server: %v", err)
	}
}

func TestUpdateServer(t *testing.T) {
	client, err := newClient()
	if err != nil {
		t.Fatalf("Unable to create a compute client: %v", err)
	}

	choices, err := ComputeChoicesFromEnv()
	if err != nil {
		t.Fatal(err)
	}

	server, err := createServer(t, client, choices)
	if err != nil {
		t.Fatal(err)
	}
	defer servers.Delete(client, server.ID)

	if err = waitForStatus(client, server, "ACTIVE"); err != nil {
		t.Fatal(err)
	}

	alternateName := tools.RandomString("ACPTTEST", 16)
	for alternateName == server.Name {
		alternateName = tools.RandomString("ACPTTEST", 16)
	}

	t.Logf("Attempting to rename the server to %s.", alternateName)

	updated, err := servers.Update(client, server.ID, servers.UpdateOpts{Name: alternateName}).Extract()
	if err != nil {
		t.Fatalf("Unable to rename server: %v", err)
	}

	if updated.ID != server.ID {
		t.Errorf("Updated server ID [%s] didn't match original server ID [%s]!", updated.ID, server.ID)
	}

	err = tools.WaitFor(func() (bool, error) {
		latest, err := servers.Get(client, updated.ID).Extract()
		if err != nil {
			return false, err
		}

		return latest.Name == alternateName, nil
	})
}

func TestActionChangeAdminPassword(t *testing.T) {
	t.Parallel()

	client, err := newClient()
	if err != nil {
		t.Fatalf("Unable to create a compute client: %v", err)
	}

	choices, err := ComputeChoicesFromEnv()
	if err != nil {
		t.Fatal(err)
	}

	server, err := createServer(t, client, choices)
	if err != nil {
		t.Fatal(err)
	}
	defer servers.Delete(client, server.ID)

	if err = waitForStatus(client, server, "ACTIVE"); err != nil {
		t.Fatal(err)
	}

	randomPassword := tools.MakeNewPassword(server.AdminPass)
	err = servers.ChangeAdminPassword(client, server.ID, randomPassword)
	if err != nil {
		t.Fatal(err)
	}

	if err = waitForStatus(client, server, "PASSWORD"); err != nil {
		t.Fatal(err)
	}

	if err = waitForStatus(client, server, "ACTIVE"); err != nil {
		t.Fatal(err)
	}
}

func TestActionReboot(t *testing.T) {
	t.Parallel()

	client, err := newClient()
	if err != nil {
		t.Fatalf("Unable to create a compute client: %v", err)
	}

	choices, err := ComputeChoicesFromEnv()
	if err != nil {
		t.Fatal(err)
	}

	server, err := createServer(t, client, choices)
	if err != nil {
		t.Fatal(err)
	}
	defer servers.Delete(client, server.ID)

	if err = waitForStatus(client, server, "ACTIVE"); err != nil {
		t.Fatal(err)
	}

	err = servers.Reboot(client, server.ID, "aldhjflaskhjf")
	if err == nil {
		t.Fatal("Expected the SDK to provide an ArgumentError here")
	}

	t.Logf("Attempting reboot of server %s", server.ID)
	err = servers.Reboot(client, server.ID, servers.OSReboot)
	if err != nil {
		t.Fatalf("Unable to reboot server: %v", err)
	}

	if err = waitForStatus(client, server, "REBOOT"); err != nil {
		t.Fatal(err)
	}

	if err = waitForStatus(client, server, "ACTIVE"); err != nil {
		t.Fatal(err)
	}
}

func TestActionRebuild(t *testing.T) {
	t.Parallel()

	client, err := newClient()
	if err != nil {
		t.Fatalf("Unable to create a compute client: %v", err)
	}

	choices, err := ComputeChoicesFromEnv()
	if err != nil {
		t.Fatal(err)
	}

	server, err := createServer(t, client, choices)
	if err != nil {
		t.Fatal(err)
	}
	defer servers.Delete(client, server.ID)

	if err = waitForStatus(client, server, "ACTIVE"); err != nil {
		t.Fatal(err)
	}

	t.Logf("Attempting to rebuild server %s", server.ID)

	newPassword := tools.MakeNewPassword(server.AdminPass)
	newName := tools.RandomString("ACPTTEST", 16)
	rebuilt, err := servers.Rebuild(client, server.ID, newName, newPassword, choices.ImageID, nil).Extract()
	if err != nil {
		t.Fatal(err)
	}

	if rebuilt.ID != server.ID {
		t.Errorf("Expected rebuilt server ID of [%s]; got [%s]", server.ID, rebuilt.ID)
	}

	if err = waitForStatus(client, rebuilt, "REBUILD"); err != nil {
		t.Fatal(err)
	}

	if err = waitForStatus(client, rebuilt, "ACTIVE"); err != nil {
		t.Fatal(err)
	}
}

func resizeServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server, choices *ComputeChoices) {
	if err := waitForStatus(client, server, "ACTIVE"); err != nil {
		t.Fatal(err)
	}

	t.Logf("Attempting to resize server [%s]", server.ID)

	if err := servers.Resize(client, server.ID, choices.FlavorIDResize); err != nil {
		t.Fatal(err)
	}

	if err := waitForStatus(client, server, "VERIFY_RESIZE"); err != nil {
		t.Fatal(err)
	}
}

func TestActionResizeConfirm(t *testing.T) {
	t.Parallel()

	choices, err := ComputeChoicesFromEnv()
	if err != nil {
		t.Fatal(err)
	}

	client, err := newClient()
	if err != nil {
		t.Fatalf("Unable to create a compute client: %v", err)
	}

	server, err := createServer(t, client, choices)
	if err != nil {
		t.Fatal(err)
	}
	defer servers.Delete(client, server.ID)
	resizeServer(t, client, server, choices)

	t.Logf("Attempting to confirm resize for server %s", server.ID)

	if err = servers.ConfirmResize(client, server.ID); err != nil {
		t.Fatal(err)
	}

	if err = waitForStatus(client, server, "ACTIVE"); err != nil {
		t.Fatal(err)
	}
}

func TestActionResizeRevert(t *testing.T) {
	t.Parallel()

	choices, err := ComputeChoicesFromEnv()
	if err != nil {
		t.Fatal(err)
	}

	client, err := newClient()
	if err != nil {
		t.Fatalf("Unable to create a compute client: %v", err)
	}

	server, err := createServer(t, client, choices)
	if err != nil {
		t.Fatal(err)
	}
	defer servers.Delete(client, server.ID)
	resizeServer(t, client, server, choices)

	t.Logf("Attempting to revert resize for server %s", server.ID)

	if err := servers.RevertResize(client, server.ID); err != nil {
		t.Fatal(err)
	}

	if err = waitForStatus(client, server, "ACTIVE"); err != nil {
		t.Fatal(err)
	}
}
<commit_msg>Updating tests<commit_after>// +build acceptance

package v2

import (
	"fmt"
	"testing"

	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/acceptance/tools"
	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
	"github.com/rackspace/gophercloud/pagination"
)

func TestListServers(t *testing.T) {
	client, err := newClient()
	if err != nil {
		t.Fatalf("Unable to create a compute client: %v", err)
	}

	t.Logf("ID\tRegion\tName\tStatus\tIPv4\tIPv6")

	pager := servers.List(client)
	count, pages := 0, 0
	pager.EachPage(func(page pagination.Page) (bool, error) {
		pages++
		t.Logf("---")

		servers, err := servers.ExtractServers(page)
		if err != nil {
			return false, err
		}

		for _, s := range servers {
			t.Logf("%s\t%s\t%s\t%s\t%s\t\n", s.ID, s.Name, s.Status, s.AccessIPv4, s.AccessIPv6)
			count++
		}

		return true, nil
	})

	fmt.Printf("--------\n%d servers listed on %d pages.\n", count, pages)
}

func createServer(t *testing.T, client *gophercloud.ServiceClient, choices *ComputeChoices) (*servers.Server, error) {
	name := tools.RandomString("ACPTTEST", 16)
	t.Logf("Attempting to create server: %s\n", name)

	server, err := servers.Create(client, servers.CreateOpts{
		Name:      name,
		FlavorRef: choices.FlavorID,
		ImageRef:  choices.ImageID,
	}).Extract()
	if err != nil {
		t.Fatalf("Unable to create server: %v", err)
	}

	return server, err
}

func TestCreateDestroyServer(t *testing.T) {
	choices, err := ComputeChoicesFromEnv()
	if err != nil {
		t.Fatal(err)
	}

	client, err := newClient()
	if err != nil {
		t.Fatalf("Unable to create a compute client: %v", err)
	}

	name := tools.RandomString("ACPTTEST", 16)
	t.Logf("Attempting to create server: %s\n", name)

	server, err := createServer(t, client, choices)
	if err != nil {
		t.Fatalf("Unable to create server: %v", err)
	}
	defer func() {
		servers.Delete(client, server.ID)
		t.Logf("Server deleted.")
	}()

	if err = waitForStatus(client, server, "ACTIVE"); err != nil {
		t.Fatalf("Unable to wait for server: %v", err)
	}
}

func TestUpdateServer(t *testing.T) {
	client, err := newClient()
	if err != nil {
		t.Fatalf("Unable to create a compute client: %v", err)
	}

	choices, err := ComputeChoicesFromEnv()
	if err != nil {
		t.Fatal(err)
	}

	server, err := createServer(t, client, choices)
	if err != nil {
		t.Fatal(err)
	}
	defer servers.Delete(client, server.ID)

	if err = waitForStatus(client, server, "ACTIVE"); err != nil {
		t.Fatal(err)
	}

	alternateName := tools.RandomString("ACPTTEST", 16)
	for alternateName == server.Name {
		alternateName = tools.RandomString("ACPTTEST", 16)
	}

	t.Logf("Attempting to rename the server to %s.", alternateName)

	updated, err := servers.Update(client, server.ID, servers.UpdateOpts{Name: alternateName}).Extract()
	if err != nil {
		t.Fatalf("Unable to rename server: %v", err)
	}

	if updated.ID != server.ID {
		t.Errorf("Updated server ID [%s] didn't match original server ID [%s]!", updated.ID, server.ID)
	}

	err = tools.WaitFor(func() (bool, error) {
		latest, err := servers.Get(client, updated.ID).Extract()
		if err != nil {
			return false, err
		}

		return latest.Name == alternateName, nil
	})
}

func TestActionChangeAdminPassword(t *testing.T) {
	t.Parallel()

	client, err := newClient()
	if err != nil {
		t.Fatalf("Unable to create a compute client: %v", err)
	}

	choices, err := ComputeChoicesFromEnv()
	if err != nil {
		t.Fatal(err)
	}

	server, err := createServer(t, client, choices)
	if err != nil {
		t.Fatal(err)
	}
	defer servers.Delete(client, server.ID)

	if err = waitForStatus(client, server, "ACTIVE"); err != nil {
		t.Fatal(err)
	}

	randomPassword := tools.MakeNewPassword(server.AdminPass)
	err = servers.ChangeAdminPassword(client, server.ID, randomPassword)
	if err != nil {
		t.Fatal(err)
	}

	if err = waitForStatus(client, server, "PASSWORD"); err != nil {
		t.Fatal(err)
	}

	if err = waitForStatus(client, server, "ACTIVE"); err != nil {
		t.Fatal(err)
	}
}

func TestActionReboot(t *testing.T) {
	t.Parallel()

	client, err := newClient()
	if err != nil {
		t.Fatalf("Unable to create a compute client: %v", err)
	}

	choices, err := ComputeChoicesFromEnv()
	if err != nil {
		t.Fatal(err)
	}

	server, err := createServer(t, client, choices)
	if err != nil {
		t.Fatal(err)
	}
	defer servers.Delete(client, server.ID)

	if err = waitForStatus(client, server, "ACTIVE"); err != nil {
		t.Fatal(err)
	}

	err = servers.Reboot(client, server.ID, "aldhjflaskhjf")
	if err == nil {
		t.Fatal("Expected the SDK to provide an ArgumentError here")
	}

	t.Logf("Attempting reboot of server %s", server.ID)
	err = servers.Reboot(client, server.ID, servers.OSReboot)
	if err != nil {
		t.Fatalf("Unable to reboot server: %v", err)
	}

	if err = waitForStatus(client, server, "REBOOT"); err != nil {
		t.Fatal(err)
	}

	if err = waitForStatus(client, server, "ACTIVE"); err != nil {
		t.Fatal(err)
	}
}

func TestActionRebuild(t *testing.T) {
	t.Parallel()

	client, err := newClient()
	if err != nil {
		t.Fatalf("Unable to create a compute client: %v", err)
	}

	choices, err := ComputeChoicesFromEnv()
	if err != nil {
		t.Fatal(err)
	}

	server, err := createServer(t, client, choices)
	if err != nil {
		t.Fatal(err)
	}
	defer servers.Delete(client, server.ID)

	if err = waitForStatus(client, server, "ACTIVE"); err != nil {
		t.Fatal(err)
	}

	t.Logf("Attempting to rebuild server %s", server.ID)

	rebuildOpts := servers.RebuildOpts{
		Name:      tools.RandomString("ACPTTEST", 16),
		AdminPass: tools.MakeNewPassword(server.AdminPass),
		ImageID:   choices.ImageID,
	}

	rebuilt, err := servers.Rebuild(client, server.ID, rebuildOpts).Extract()
	if err != nil {
		t.Fatal(err)
	}

	if rebuilt.ID != server.ID {
		t.Errorf("Expected rebuilt server ID of [%s]; got [%s]", server.ID, rebuilt.ID)
	}

	if err = waitForStatus(client, rebuilt, "REBUILD"); err != nil {
		t.Fatal(err)
	}

	if err = waitForStatus(client, rebuilt, "ACTIVE"); err != nil {
		t.Fatal(err)
	}
}

func resizeServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server, choices *ComputeChoices) {
	if err := waitForStatus(client, server, "ACTIVE"); err != nil {
		t.Fatal(err)
	}

	t.Logf("Attempting to resize server [%s]", server.ID)

	if err := servers.Resize(client, server.ID, choices.FlavorIDResize); err != nil {
		t.Fatal(err)
	}

	if err := waitForStatus(client, server, "VERIFY_RESIZE"); err != nil {
		t.Fatal(err)
	}
}

func TestActionResizeConfirm(t *testing.T) {
	t.Parallel()

	choices, err := ComputeChoicesFromEnv()
	if err != nil {
		t.Fatal(err)
	}

	client, err := newClient()
	if err != nil {
		t.Fatalf("Unable to create a compute client: %v", err)
	}

	server, err := createServer(t, client, choices)
	if err != nil {
		t.Fatal(err)
	}
	defer servers.Delete(client, server.ID)
	resizeServer(t, client, server, choices)

	t.Logf("Attempting to confirm resize for server %s", server.ID)

	if err = servers.ConfirmResize(client, server.ID); err != nil {
		t.Fatal(err)
	}

	if err = waitForStatus(client, server, "ACTIVE"); err != nil {
		t.Fatal(err)
	}
}

func TestActionResizeRevert(t *testing.T) {
	t.Parallel()

	choices, err := ComputeChoicesFromEnv()
	if err != nil {
		t.Fatal(err)
	}

	client, err := newClient()
	if err != nil {
		t.Fatalf("Unable to create a compute client: %v", err)
	}

	server, err := createServer(t, client, choices)
	if err != nil {
		t.Fatal(err)
	}
	defer servers.Delete(client, server.ID)
	resizeServer(t, client, server, choices)

	t.Logf("Attempting to revert resize for server %s", server.ID)

	if err := servers.RevertResize(client, server.ID); err != nil {
		t.Fatal(err)
	}

	if err = waitForStatus(client, server, "ACTIVE"); err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>