<commit_before>package main

//
// VMWare VMDK Docker Data Volume plugin.
//
// Provide suport for --driver=vmdk in Docker, when Docker VM is running under ESX.
//
// Serves requests from Docker Engine related to VMDK volume operations.
// Depends on vmdk-opsd service to be running on hosting ESX
// (see ./vmdkops-esxsrv)
///

import (
	//	"encoding/json"
	//      "fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/docker/go-plugins-helpers/volume"
	"github.com/vmware/docker-vmdk-plugin/fs"
	"github.com/vmware/docker-vmdk-plugin/vmdkops"
	"path/filepath"
	"sync"
)

const (
	mountRoot = "/mnt/vmdk" // VMDK block devices are mounted here
)

type vmdkDriver struct {
	m       *sync.Mutex // create() serialization - for future use
	mockEsx bool
	ops     vmdkops.VmdkOps
}

func newVmdkDriver(mockEsx bool) vmdkDriver {
	var vmdkCmd vmdkops.VmdkCmdRunner
	if mockEsx {
		vmdkCmd = vmdkops.MockVmdkCmd{}
	} else {
		vmdkCmd = vmdkops.VmdkCmd{}
	}
	d := vmdkDriver{
		m:       &sync.Mutex{},
		mockEsx: mockEsx,
		ops:     vmdkops.VmdkOps{Cmd: vmdkCmd},
	}
	return d
}

func (d vmdkDriver) Get(r volume.Request) volume.Response {
	_, err := d.ops.Get(r.Name)
	if err != nil {
		return volume.Response{Err: err.Error()}
	}
	mountpoint := filepath.Join(mountRoot, r.Name)
	return volume.Response{Volume: &volume.Volume{Name: r.Name, Mountpoint: mountpoint}}
}

func (d vmdkDriver) List(r volume.Request) volume.Response {
	volumes, err := d.ops.List()
	if err != nil {
		return volume.Response{Err: err.Error()}
	}
	responseVolumes := make([]*volume.Volume, 0, len(volumes))
	for _, vol := range volumes {
		mountpoint := filepath.Join(mountRoot, vol.Name)
		responseVol := volume.Volume{Name: vol.Name, Mountpoint: mountpoint}
		responseVolumes = append(responseVolumes, &responseVol)
	}
	return volume.Response{Volumes: responseVolumes}
}

// request attach and them mounts the volume
// actual mount - send attach to ESX and do the in-guest magix
// TODO: this should actually be a goroutine , no need to block
//       SAME (and more) applies to unmount
func (d vmdkDriver) mountVolume(r volume.Request, path string) error {

	// First of all, have ESX attach the disk

	refCount := 0
	if refCount != 0 { // TODO: actual refcounting
		return nil
	}

	// TODO: Issue #28
	// - refcount  if the volume is already mounted (for other container) and
	// just return volume.Response{Mountpoint: m} in this case
	// -  save info abouf volume mount , in memory
	// d.volumes[m] = &volumeName{name: r.Name, connections: 1}
	if err := d.ops.Attach(r.Name, r.Options); err != nil {
		return err
	}

	mountpoint := filepath.Join(mountRoot, r.Name)
	if d.mockEsx {
		return fs.Mount(mountpoint, r.Name, "ext4")
	}
	return fs.Mount(mountpoint, r.Name, "ext2")
}

// Unmounts the volume and then requests detach
func (d vmdkDriver) unmountVolume(r volume.Request) error {
	mountpoint := filepath.Join(mountRoot, r.Name)
	err := fs.Unmount(mountpoint)
	if err != nil {
		log.WithFields(log.Fields{"mountpoint": mountpoint, "error": err}).Error("Failed to unmount ")
                // Do not return error. Continue with detach.
	}
	return d.ops.Detach(r.Name, r.Options)
}

// The user wants to create a volume.
// No need to actually manifest the volume on the filesystem yet
// (until Mount is called).
// Name and driver specific options passed through to the ESX host
func (d vmdkDriver) Create(r volume.Request) volume.Response {
	err := d.ops.Create(r.Name, r.Options)
	if err != nil {
                log.WithFields(log.Fields{"name": r.Name, "error": err}).Error("Create volume failed ")
		return volume.Response{Err: err.Error()}
	}
        log.WithFields(log.Fields{"name": r.Name}).Info("Volume created ")
	return volume.Response{Err: ""}
}

func (d vmdkDriver) Remove(r volume.Request) volume.Response {
	err := d.ops.Remove(r.Name, r.Options)
	if err != nil {
                log.WithFields(log.Fields{"name": r.Name, "error": err}).Error("Failed to remove volume ")
		return volume.Response{Err: err.Error()}
	}
        log.WithFields(log.Fields{"name": r.Name}).Info("Volume removed ")
	return volume.Response{Err: ""}
}

// give docker a reminder of the volume mount path
func (d vmdkDriver) Path(r volume.Request) volume.Response {
	m := filepath.Join(mountRoot, r.Name)
	return volume.Response{Mountpoint: m}
}

// Provide a volume to docker container - called once per container start.
// We need to keep refcount and unmount on refcount drop to 0
func (d vmdkDriver) Mount(r volume.Request) volume.Response {
	d.m.Lock()
	defer d.m.Unlock()

	refCount := 0 // TBD: get actual from d.volumes(r.name).refCount
	if refCount != 0 {
		return volume.Response{Err: ""}
	}

	// Get the mount point path and make sure it exists.
	m := filepath.Join(mountRoot, r.Name)
	log.WithFields(log.Fields{"name": r.Name, "mountpoint": m}).Info("Mounting Volume ")

	err := fs.Mkdir(m)
	if err != nil {
		log.WithFields(log.Fields{"dir": m}).Error("Failed to make directory ")
		return volume.Response{Err: err.Error()}
	}

	if err := d.mountVolume(r, m); err != nil {
                log.WithFields(log.Fields{"name": r.Name, "error": err.Error()}).Error("Failed to mount ")
		return volume.Response{Err: err.Error()}
	}
	log.WithFields(log.Fields{"name": r.Name}).Info("Mount Succeeded ")

	return volume.Response{Mountpoint: m}
}

//
func (d vmdkDriver) Unmount(r volume.Request) volume.Response {
	// make sure it's unmounted on guest side, then detach
	refCount := 0 // TBD: get actual from d.volumes(r.name).refCount
	if refCount > 0 {
		return volume.Response{Err: ""}
	}

	err := d.unmountVolume(r)
	log.WithFields(log.Fields{"name": r.Name}).Info("Unmounting Volume ")
	if err != nil {
		log.WithFields(log.Fields{"name": r.Name, "error": err.Error()}).Error("Failed to unmount ")
		return volume.Response{Err: err.Error()}
	}
	log.WithFields(log.Fields{"name": r.Name}).Info("Unmount Succeeded ")
	return volume.Response{Err: ""}
}
<commit_msg>In-memory mount refcounter implementation<commit_after>package main

//
// VMWare VMDK Docker Data Volume plugin.
//
// Provide suport for --driver=vmdk in Docker, when Docker VM is running under ESX.
//
// Serves requests from Docker Engine related to VMDK volume operations.
// Depends on vmdk-opsd service to be running on hosting ESX
// (see ./vmdkops-esxsrv)
///

import (
	//	"encoding/json"
	//      "fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/docker/go-plugins-helpers/volume"
	"github.com/vmware/docker-vmdk-plugin/fs"
	"github.com/vmware/docker-vmdk-plugin/vmdkops"
	"path/filepath"
	"sync"
)

const (
	mountRoot = "/mnt/vmdk" // VMDK block devices are mounted here
)

type vmdkDriver struct {
	m       *sync.Mutex // create() serialization - for future use
	mockEsx bool
	ops     vmdkops.VmdkOps
}

var (
	refcounts = make(map[string]int)
)

// creates vmdkDriver which may talk to real ESX (mockEsx=False) or
// real ESX.
func newVmdkDriver(mockEsx bool) vmdkDriver {
	var vmdkCmd vmdkops.VmdkCmdRunner
	if mockEsx {
		vmdkCmd = vmdkops.MockVmdkCmd{}
	} else {
		vmdkCmd = vmdkops.VmdkCmd{}
	}
	d := vmdkDriver{
		m:       &sync.Mutex{},
		mockEsx: mockEsx,
		ops:     vmdkops.VmdkOps{Cmd: vmdkCmd},
	}
	return d
}

func (d vmdkDriver) Get(r volume.Request) volume.Response {
	_, err := d.ops.Get(r.Name)
	if err != nil {
		return volume.Response{Err: err.Error()}
	}
	mountpoint := filepath.Join(mountRoot, r.Name)
	return volume.Response{Volume: &volume.Volume{Name: r.Name, Mountpoint: mountpoint}}
}

func (d vmdkDriver) List(r volume.Request) volume.Response {
	volumes, err := d.ops.List()
	if err != nil {
		return volume.Response{Err: err.Error()}
	}
	responseVolumes := make([]*volume.Volume, 0, len(volumes))
	for _, vol := range volumes {
		mountpoint := filepath.Join(mountRoot, vol.Name)
		responseVol := volume.Volume{Name: vol.Name, Mountpoint: mountpoint}
		responseVolumes = append(responseVolumes, &responseVol)
	}
	return volume.Response{Volumes: responseVolumes}
}

// request attach and them mounts the volume
// actual mount - send attach to ESX and do the in-guest magix
// TODO: this should actually be a goroutine , no need to block
//       SAME (and more) applies to unmount
func (d vmdkDriver) mountVolume(r volume.Request, path string) error {
	// First of all, have ESX attach the disk
	if err := d.ops.Attach(r.Name, r.Options); err != nil {
		return err
	}

	mountpoint := filepath.Join(mountRoot, r.Name)
	if d.mockEsx {
		return fs.Mount(mountpoint, r.Name, "ext4")
	}
	return fs.Mount(mountpoint, r.Name, "ext2")
}

// Unmounts the volume and then requests detach
func (d vmdkDriver) unmountVolume(r volume.Request) error {
	mountpoint := filepath.Join(mountRoot, r.Name)
	err := fs.Unmount(mountpoint)
	if err != nil {
		log.WithFields(log.Fields{"mountpoint": mountpoint, "error": err}).Error("Failed to unmount ")
		// Do not return error. Continue with detach.
	}
	return d.ops.Detach(r.Name, r.Options)
}

// The user wants to create a volume.
// No need to actually manifest the volume on the filesystem yet
// (until Mount is called).
// Name and driver specific options passed through to the ESX host
func (d vmdkDriver) Create(r volume.Request) volume.Response {
	err := d.ops.Create(r.Name, r.Options)
	if err != nil {
		log.WithFields(log.Fields{"name": r.Name, "error": err}).Error("Create volume failed ")
		return volume.Response{Err: err.Error()}
	}
	log.WithFields(log.Fields{"name": r.Name}).Info("Volume created ")
	return volume.Response{Err: ""}
}

func (d vmdkDriver) Remove(r volume.Request) volume.Response {
	err := d.ops.Remove(r.Name, r.Options)
	if err != nil {
		log.WithFields(log.Fields{"name": r.Name, "error": err}).Error("Failed to remove volume ")
		return volume.Response{Err: err.Error()}
	}
	log.WithFields(log.Fields{"name": r.Name}).Info("Volume removed ")
	return volume.Response{Err: ""}
}

// give docker a reminder of the volume mount path
func (d vmdkDriver) Path(r volume.Request) volume.Response {
	m := filepath.Join(mountRoot, r.Name)
	return volume.Response{Mountpoint: m}
}

// Provide a volume to docker container - called once per container start.
// We need to keep refcount and unmount on refcount drop to 0
func (d vmdkDriver) Mount(r volume.Request) volume.Response {
	d.m.Lock()
	defer d.m.Unlock()

	// Get the mount point path and make sure it exists.
	m := filepath.Join(mountRoot, r.Name)
	err := fs.Mkdir(m)
	if err != nil {
		log.WithFields(
			log.Fields{"volume": r.Name, "dir": m},
		).Error("Failed to make directory for volume mount")
		return volume.Response{Err: err.Error()}
	}

	// if the volume is already mounted (for other container)
	// just return volume.Response{Mountpoint: m}
	// note: for new keys, GO maps return zero value
	refcounts[r.Name]++
	if refcounts[r.Name] > 1 {
		log.WithFields(
			log.Fields{"volume": r.Name, "refcount": refcounts[r.Name]},
		).Debug("Already mounted, skipping mount request. ")
		return volume.Response{Mountpoint: m}
	}
	if refcounts[r.Name] != 1 {
		log.WithFields(
			log.Fields{"volume": r.Name, "refcount": refcounts[r.Name]},
		).Fatal("WRONG REFCOUNT COUNT in mount (should be 1) ")
	}

	// This is the first time we are asked to mount the volume, so comply
	log.WithFields(
		log.Fields{"name": r.Name, "mountpoint": m},
	).Info("Mounting Volume ")
	if err := d.mountVolume(r, m); err != nil {
		log.WithFields(
			log.Fields{"name": r.Name, "error": err.Error()},
		).Error("Failed to mount ")
		return volume.Response{Err: err.Error()}
	}
	log.WithFields(log.Fields{"name": r.Name}).Info("Mount Succeeded ")

	return volume.Response{Mountpoint: m}
}

// Unmount request from Docker. If mount refcount is drop to 0,
// unmount and detach from VM
func (d vmdkDriver) Unmount(r volume.Request) volume.Response {

	// if the volume is still used by other containers, just return OK
	refcounts[r.Name]--
	if refcounts[r.Name] >= 1 {
		log.WithFields(
			log.Fields{"volume": r.Name, "refcount": refcounts[r.Name]},
		).Debug("Still in use, skipping unmount request. ")
		return volume.Response{Err: ""}
	}
	if refcounts[r.Name] != 0 {
		log.WithFields(
			log.Fields{"volume": r.Name, "refcount": refcounts[r.Name]},
		).Fatal("WRONG REF COUNT in mount (should be 0) ")
	}
	delete(refcounts, r.Name)

	// and if nobody needs it, unmount and detach
	log.WithFields(log.Fields{"name": r.Name}).Info("Unmounting Volume ")
	err := d.unmountVolume(r)
	if err != nil {
		log.WithFields(
			log.Fields{"name": r.Name, "error": err.Error()},
		).Error("Failed to unmount ")
		return volume.Response{Err: err.Error()}
	}
	log.WithFields(log.Fields{"name": r.Name}).Info("Unmount Succeeded ")
	return volume.Response{Err: ""}
}
<|endoftext|>


<commit_before>package consul

import (
	"reflect"
	"testing"
	"time"
)

func TestCheckHeapOrder(t *testing.T) {
	h := NewConsulChecksHeap()

	c1 := ExecScriptCheck{id: "a"}
	c2 := ExecScriptCheck{id: "b"}
	c3 := ExecScriptCheck{id: "c"}

	lookup := map[Check]string{
		&c1: "c1",
		&c2: "c2",
		&c3: "c3",
	}

	h.Push(&c1, time.Time{})
	h.Push(&c2, time.Unix(10, 0))
	h.Push(&c3, time.Unix(11, 0))

	expected := []string{"c2", "c3", "c1"}
	var actual []string
	for i := 0; i < 3; i++ {
		cCheck := h.Pop()

		actual = append(actual, lookup[cCheck.check])
	}

	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("Wrong ordering; got %v; want %v", actual, expected)
	}
}
<commit_msg>Removing non relevant tests<commit_after><|endoftext|>


<commit_before>package btrfs

import "sort"

/*
#cgo LDFLAGS: -lbtrfs

#include <stddef.h>
#include <btrfs/ioctl.h>
#include <btrfs/btrfs-list.h>
#include "btrfs.h"

// Required because Go has struct casting rules for negative numbers
const __u64 u64_BTRFS_LAST_FREE_OBJECTID = (__u64)BTRFS_LAST_FREE_OBJECTID;
const __u64 negative_one = (__u64)-1;

static char* get_name_btrfs_ioctl_vol_args_v2(struct btrfs_ioctl_vol_args_v2* btrfs_struct) {
	return btrfs_struct->name;
}
*/
import "C"

import (
	"fmt"
	"os"
	"path/filepath"
	"syscall"
	"unsafe"

	"github.com/pkg/errors"
)

// IsSubvolume returns nil if the path is a valid subvolume. An error is
// returned if the path does not exist or the path is not a valid subvolume.
func IsSubvolume(path string) error {
	fi, err := os.Lstat(path)
	if err != nil {
		return err
	}

	if err := isFileInfoSubvol(fi); err != nil {
		return err
	}

	var statfs syscall.Statfs_t
	if err := syscall.Statfs(path, &statfs); err != nil {
		return err
	}

	return isStatfsSubvol(&statfs)
}

// SubvolInfo returns information about the subvolume at the provided path.
func SubvolInfo(path string) (info Info, err error) {
	path, err = filepath.EvalSymlinks(path)
	if err != nil {
		return info, err
	}

	mnt, err := findMountPoint(path)
	if err != nil {
		return info, err
	}

	fp, err := openSubvolDir(path)
	if err != nil {
		return info, err
	}
	defer fp.Close()

	id, err := subvolID(fp.Fd())
	if err != nil {
		return info, err
	}

	var ri C.struct_root_info
	ri.root_id = C.u64(id)
	if id != C.BTRFS_FS_TREE_OBJECTID {
		// TODO(stevvooe): Remove this call and replace with the approch in
		// SubvolList. Both aren't very efficient, as they query the whole
		// dataset to get just a single record.
		ret, errno := C.btrfs_get_subvol(C.int(fp.Fd()), &ri)
		if ret != 0 {
			fmt.Println("failed")
		}

		if errno != nil {
			return info, errno
		}
	} else {
		return info, errors.Errorf("%q is a toplevel subvolume", path)
	}

	info.ID = uint64(ri.root_id)
	info.ParentID = uint64(ri.ref_tree)
	info.TopLevelID = uint64(ri.top_id)
	info.DirID = uint64(ri.dir_id)

	info.Offset = uint64(ri.root_offset)
	info.Generation = uint64(ri.gen)
	info.OriginalGeneration = uint64(ri.ogen)

	info.Name = C.GoString(ri.name)
	info.Path = filepath.Join(mnt, C.GoString(ri.full_path))

	info.UUID = uuidString(&ri.uuid)
	info.ParentUUID = uuidString(&ri.puuid)
	info.ReceivedUUID = uuidString(&ri.ruuid)

	// For some reason, these are different flags for readonly depending on the
	// context. Beware. Must be BTRFS_ROOT_SUBVOL_RDONLY, not
	// BTRFS_SUBVOL_RDONLY, which is used for ioctls.
	if ri.flags&C.BTRFS_ROOT_SUBVOL_RDONLY != 0 {
		info.Readonly = true
	}

	return info, nil
}

// SubvolList will return the information for all subvolumes corresponding to
// the provided path.
func SubvolList(path string) ([]Info, error) {
	fp, err := openSubvolDir(path)
	if err != nil {
		return nil, err
	}
	defer fp.Close()

	var args C.struct_btrfs_ioctl_search_args

	args.key.tree_id = C.BTRFS_ROOT_TREE_OBJECTID
	args.key.min_type = C.BTRFS_ROOT_ITEM_KEY
	args.key.max_type = C.BTRFS_ROOT_BACKREF_KEY
	args.key.min_objectid = C.BTRFS_FS_TREE_OBJECTID
	args.key.max_objectid = C.u64_BTRFS_LAST_FREE_OBJECTID
	args.key.max_offset = C.negative_one
	args.key.max_transid = C.negative_one

	subvolsByID := map[uint64]Info{}

	for {
		args.key.nr_items = 4096
		if err := ioctl(fp.Fd(), C.BTRFS_IOC_TREE_SEARCH, uintptr(unsafe.Pointer(&args))); err != nil {
			return nil, err
		}

		if args.key.nr_items == 0 {
			break
		}

		var (
			sh     C.struct_btrfs_ioctl_search_header
			shSize = unsafe.Sizeof(sh)
			buf    = (*[1<<31 - 1]byte)(unsafe.Pointer(&args.buf[0]))[:C.BTRFS_SEARCH_ARGS_BUFSIZE]
		)

		for i := 0; i < int(args.key.nr_items); i++ {
			sh = (*(*C.struct_btrfs_ioctl_search_header)(unsafe.Pointer(&buf[0])))
			buf = buf[shSize:]

			info := subvolsByID[uint64(sh.objectid)]
			info.ID = uint64(sh.objectid)

			if sh._type == C.BTRFS_ROOT_BACKREF_KEY {
				rr := (*(*C.struct_btrfs_root_ref)(unsafe.Pointer(&buf[0])))

				// This branch processes the backrefs from the root object. We
				// get an entry of the objectid, with name, but the parent is
				// the offset.

				nname := C.btrfs_stack_root_ref_name_len(&rr)
				name := string(buf[C.sizeof_struct_btrfs_root_ref : C.sizeof_struct_btrfs_root_ref+uintptr(nname)])

				info.ID = uint64(sh.objectid)
				info.ParentID = uint64(sh.offset)
				info.Name = name
				info.DirID = uint64(C.btrfs_stack_root_ref_dirid(&rr))

				subvolsByID[uint64(sh.objectid)] = info
			} else if sh._type == C.BTRFS_ROOT_ITEM_KEY &&
				(sh.objectid >= C.BTRFS_ROOT_ITEM_KEY ||
					sh.objectid == C.BTRFS_FS_TREE_OBJECTID) {

				var (
					ri  = (*C.struct_btrfs_root_item)(unsafe.Pointer(&buf[0]))
					gri C.struct_gosafe_btrfs_root_item
				)

				C.unpack_root_item(&gri, ri)

				if gri.flags&C.BTRFS_ROOT_SUBVOL_RDONLY != 0 {
					info.Readonly = true
				}

				// in this case, the offset is the actual offset.
				info.Offset = uint64(sh.offset)

				info.UUID = uuidString(&gri.uuid)
				info.ParentUUID = uuidString(&gri.parent_uuid)
				info.ReceivedUUID = uuidString(&gri.received_uuid)

				info.Generation = uint64(gri.gen)
				info.OriginalGeneration = uint64(gri.ogen)

				subvolsByID[uint64(sh.objectid)] = info
			}

			args.key.min_objectid = sh.objectid
			args.key.min_offset = sh.offset
			args.key.min_type = sh._type //  this is very questionable.

			buf = buf[sh.len:]
		}

		args.key.min_offset++
		if args.key.min_offset == 0 {
			args.key.min_type++
		} else {
			continue
		}

		if args.key.min_type > C.BTRFS_ROOT_BACKREF_KEY {
			args.key.min_type = C.BTRFS_ROOT_ITEM_KEY
			args.key.min_objectid++
		} else {
			continue
		}

		if args.key.min_objectid > args.key.max_objectid {
			break
		}
	}

	mnt, err := findMountPoint(path)
	if err != nil {
		return nil, err
	}

	subvols := make([]Info, 0, len(subvolsByID))
	for _, sv := range subvolsByID {
		path := sv.Name
		parentID := sv.ParentID

		for parentID != 0 {
			parent, ok := subvolsByID[parentID]
			if !ok {
				break
			}

			parentID = parent.ParentID
			path = filepath.Join(parent.Name, path)
		}

		sv.Path = filepath.Join(mnt, path)
		subvols = append(subvols, sv)
	}
	sort.Sort(infosByID(subvols))

	return subvols, nil
}

// SubvolCreate creates a subvolume at the provided path.
func SubvolCreate(path string) error {
	dir, name := filepath.Split(path)

	fp, err := os.Open(dir)
	if err != nil {
		return err
	}
	defer fp.Close()

	var args C.struct_btrfs_ioctl_vol_args
	args.fd = C.__s64(fp.Fd())

	if len(name) > C.BTRFS_PATH_NAME_MAX {
		return errors.Errorf("%q too long for subvolume", name)
	}
	nameptr := (*[1<<31 - 1]byte)(unsafe.Pointer(&args.name[0]))
	copy(nameptr[:C.BTRFS_PATH_NAME_MAX], []byte(name))

	if err := ioctl(fp.Fd(), C.BTRFS_IOC_SUBVOL_CREATE, uintptr(unsafe.Pointer(&args))); err != nil {
		return errors.Wrap(err, "btrfs subvolume create failed")
	}

	return nil
}

// SubvolSnapshot creates a snapshot in dst from src. If readonly is true, the
// snapshot will be readonly.
func SubvolSnapshot(dst, src string, readonly bool) error {
	dstdir, dstname := filepath.Split(dst)

	dstfp, err := openSubvolDir(dstdir)
	if err != nil {
		return errors.Wrapf(err, "opening snapshot desination subvolume failed")
	}
	defer dstfp.Close()

	srcfp, err := openSubvolDir(src)
	if err != nil {
		return errors.Wrapf(err, "opening snapshot source subvolume failed")
	}

	// dstdir is the ioctl arg, wile srcdir gets set on the args
	var args C.struct_btrfs_ioctl_vol_args_v2
	args.fd = C.__s64(srcfp.Fd())
	name := C.get_name_btrfs_ioctl_vol_args_v2(&args)

	if len(dstname) > C.BTRFS_SUBVOL_NAME_MAX {
		return errors.Errorf("%q too long for subvolume", dstname)
	}

	nameptr := (*[1<<31 - 1]byte)(unsafe.Pointer(name))
	copy(nameptr[:C.BTRFS_SUBVOL_NAME_MAX], []byte(dstname))

	if readonly {
		args.flags |= C.BTRFS_SUBVOL_RDONLY
	}

	if err := ioctl(dstfp.Fd(), C.BTRFS_IOC_SNAP_CREATE_V2, uintptr(unsafe.Pointer(&args))); err != nil {
		return errors.Wrapf(err, "snapshot create failed")
	}

	return nil
}

// SubvolDelete deletes the subvolumes under the given path.
func SubvolDelete(path string) error {
	fmt.Println("delete", path)
	dir, name := filepath.Split(path)
	fp, err := openSubvolDir(dir)
	if err != nil {
		return errors.Wrapf(err, "failed opening %v", path)
	}
	defer fp.Close()

	// remove child subvolumes
	if err := filepath.Walk(path, func(p string, fi os.FileInfo, err error) error {
		if err != nil {
			if os.IsNotExist(err) || p == path {
				return nil
			}

			return errors.Wrapf(err, "failed walking subvolume %v", p)
		}

		if !fi.IsDir() {
			return nil // just ignore it!
		}

		if p == path {
			return nil
		}

		if err := isFileInfoSubvol(fi); err != nil {
			return err
		}

		if err := SubvolDelete(p); err != nil {
			return err
		}

		return filepath.SkipDir // children get walked by call above.
	}); err != nil {
		return err
	}

	var args C.struct_btrfs_ioctl_vol_args
	if len(name) > C.BTRFS_SUBVOL_NAME_MAX {
		return errors.Errorf("%q too long for subvolume", name)
	}

	nameptr := (*[1<<31 - 1]byte)(unsafe.Pointer(&args.name[0]))
	copy(nameptr[:C.BTRFS_SUBVOL_NAME_MAX], []byte(name))

	if err := ioctl(fp.Fd(), C.BTRFS_IOC_SNAP_DESTROY, uintptr(unsafe.Pointer(&args))); err != nil {
		return errors.Wrapf(err, "failed removing subvolume %v", path)
	}

	return nil
}

func openSubvolDir(path string) (*os.File, error) {
	fp, err := os.Open(path)
	if err != nil {
		return nil, errors.Wrapf(err, "opening %v as subvolume failed", path)
	}

	return fp, nil
}

func isStatfsSubvol(statfs *syscall.Statfs_t) error {
	if statfs.Type != C.BTRFS_SUPER_MAGIC {
		return errors.Errorf("not a btrfs filesystem")
	}

	return nil
}

func isFileInfoSubvol(fi os.FileInfo) error {
	if !fi.IsDir() {
		errors.Errorf("must be a directory")
	}

	stat := fi.Sys().(*syscall.Stat_t)

	if stat.Ino != C.BTRFS_FIRST_FREE_OBJECTID {
		return errors.Errorf("incorrect inode type")
	}

	return nil
}
<commit_msg>C.btrfs_get_subvol into subvolMap call<commit_after>package btrfs

import "sort"

/*
#cgo LDFLAGS: -lbtrfs

#include <stddef.h>
#include <btrfs/ioctl.h>
#include "btrfs.h"

// Required because Go has struct casting rules for negative numbers
const __u64 u64_BTRFS_LAST_FREE_OBJECTID = (__u64)BTRFS_LAST_FREE_OBJECTID;
const __u64 negative_one = (__u64)-1;

static char* get_name_btrfs_ioctl_vol_args_v2(struct btrfs_ioctl_vol_args_v2* btrfs_struct) {
	return btrfs_struct->name;
}
*/
import "C"

import (
	"fmt"
	"os"
	"path/filepath"
	"syscall"
	"unsafe"

	"github.com/pkg/errors"
)

// IsSubvolume returns nil if the path is a valid subvolume. An error is
// returned if the path does not exist or the path is not a valid subvolume.
func IsSubvolume(path string) error {
	fi, err := os.Lstat(path)
	if err != nil {
		return err
	}

	if err := isFileInfoSubvol(fi); err != nil {
		return err
	}

	var statfs syscall.Statfs_t
	if err := syscall.Statfs(path, &statfs); err != nil {
		return err
	}

	return isStatfsSubvol(&statfs)
}

// SubvolInfo returns information about the subvolume at the provided path.
func SubvolInfo(path string) (info Info, err error) {
	path, err = filepath.EvalSymlinks(path)
	if err != nil {
		return info, err
	}

	fp, err := openSubvolDir(path)
	if err != nil {
		return info, err
	}
	defer fp.Close()

	id, err := subvolID(fp.Fd())
	if err != nil {
		return info, err
	}

	subvolsByID, err := subvolMap(path)
	if err != nil {
		return info, err
	}

	if info, ok := subvolsByID[id]; ok {
		return info, nil
	}

	return info, errors.Errorf("%q not found", path)
}

func subvolMap(path string) (map[uint64]Info, error) {
	fp, err := openSubvolDir(path)
	if err != nil {
		return nil, err
	}
	defer fp.Close()

	var args C.struct_btrfs_ioctl_search_args

	args.key.tree_id = C.BTRFS_ROOT_TREE_OBJECTID
	args.key.min_type = C.BTRFS_ROOT_ITEM_KEY
	args.key.max_type = C.BTRFS_ROOT_BACKREF_KEY
	args.key.min_objectid = C.BTRFS_FS_TREE_OBJECTID
	args.key.max_objectid = C.u64_BTRFS_LAST_FREE_OBJECTID
	args.key.max_offset = C.negative_one
	args.key.max_transid = C.negative_one

	subvolsByID := map[uint64]Info{}

	for {
		args.key.nr_items = 4096
		if err := ioctl(fp.Fd(), C.BTRFS_IOC_TREE_SEARCH, uintptr(unsafe.Pointer(&args))); err != nil {
			return nil, err
		}

		if args.key.nr_items == 0 {
			break
		}

		var (
			sh     C.struct_btrfs_ioctl_search_header
			shSize = unsafe.Sizeof(sh)
			buf    = (*[1<<31 - 1]byte)(unsafe.Pointer(&args.buf[0]))[:C.BTRFS_SEARCH_ARGS_BUFSIZE]
		)

		for i := 0; i < int(args.key.nr_items); i++ {
			sh = (*(*C.struct_btrfs_ioctl_search_header)(unsafe.Pointer(&buf[0])))
			buf = buf[shSize:]

			info := subvolsByID[uint64(sh.objectid)]
			info.ID = uint64(sh.objectid)

			if sh._type == C.BTRFS_ROOT_BACKREF_KEY {
				rr := (*(*C.struct_btrfs_root_ref)(unsafe.Pointer(&buf[0])))

				// This branch processes the backrefs from the root object. We
				// get an entry of the objectid, with name, but the parent is
				// the offset.

				nname := C.btrfs_stack_root_ref_name_len(&rr)
				name := string(buf[C.sizeof_struct_btrfs_root_ref : C.sizeof_struct_btrfs_root_ref+uintptr(nname)])

				info.ID = uint64(sh.objectid)
				info.ParentID = uint64(sh.offset)
				info.Name = name
				info.DirID = uint64(C.btrfs_stack_root_ref_dirid(&rr))

				subvolsByID[uint64(sh.objectid)] = info
			} else if sh._type == C.BTRFS_ROOT_ITEM_KEY &&
				(sh.objectid >= C.BTRFS_ROOT_ITEM_KEY ||
					sh.objectid == C.BTRFS_FS_TREE_OBJECTID) {

				var (
					ri  = (*C.struct_btrfs_root_item)(unsafe.Pointer(&buf[0]))
					gri C.struct_gosafe_btrfs_root_item
				)

				C.unpack_root_item(&gri, ri)

				if gri.flags&C.BTRFS_ROOT_SUBVOL_RDONLY != 0 {
					info.Readonly = true
				}

				// in this case, the offset is the actual offset.
				info.Offset = uint64(sh.offset)

				info.UUID = uuidString(&gri.uuid)
				info.ParentUUID = uuidString(&gri.parent_uuid)
				info.ReceivedUUID = uuidString(&gri.received_uuid)

				info.Generation = uint64(gri.gen)
				info.OriginalGeneration = uint64(gri.ogen)

				subvolsByID[uint64(sh.objectid)] = info
			}

			args.key.min_objectid = sh.objectid
			args.key.min_offset = sh.offset
			args.key.min_type = sh._type //  this is very questionable.

			buf = buf[sh.len:]
		}

		args.key.min_offset++
		if args.key.min_offset == 0 {
			args.key.min_type++
		} else {
			continue
		}

		if args.key.min_type > C.BTRFS_ROOT_BACKREF_KEY {
			args.key.min_type = C.BTRFS_ROOT_ITEM_KEY
			args.key.min_objectid++
		} else {
			continue
		}

		if args.key.min_objectid > args.key.max_objectid {
			break
		}
	}

	mnt, err := findMountPoint(path)
	if err != nil {
		return nil, err
	}

	for _, sv := range subvolsByID {
		path := sv.Name
		parentID := sv.ParentID

		for parentID != 0 {
			parent, ok := subvolsByID[parentID]
			if !ok {
				break
			}

			parentID = parent.ParentID
			path = filepath.Join(parent.Name, path)
		}

		sv.Path = filepath.Join(mnt, path)
	}
	return subvolsByID, nil
}

// SubvolList will return the information for all subvolumes corresponding to
// the provided path.
func SubvolList(path string) ([]Info, error) {
	subvolsByID, err := subvolMap(path)
	if err != nil {
		return nil, err
	}

	subvols := make([]Info, 0, len(subvolsByID))
	for _, sv := range subvolsByID {
		subvols = append(subvols, sv)
	}

	sort.Sort(infosByID(subvols))

	return subvols, nil
}

// SubvolCreate creates a subvolume at the provided path.
func SubvolCreate(path string) error {
	dir, name := filepath.Split(path)

	fp, err := os.Open(dir)
	if err != nil {
		return err
	}
	defer fp.Close()

	var args C.struct_btrfs_ioctl_vol_args
	args.fd = C.__s64(fp.Fd())

	if len(name) > C.BTRFS_PATH_NAME_MAX {
		return errors.Errorf("%q too long for subvolume", name)
	}
	nameptr := (*[1<<31 - 1]byte)(unsafe.Pointer(&args.name[0]))
	copy(nameptr[:C.BTRFS_PATH_NAME_MAX], []byte(name))

	if err := ioctl(fp.Fd(), C.BTRFS_IOC_SUBVOL_CREATE, uintptr(unsafe.Pointer(&args))); err != nil {
		return errors.Wrap(err, "btrfs subvolume create failed")
	}

	return nil
}

// SubvolSnapshot creates a snapshot in dst from src. If readonly is true, the
// snapshot will be readonly.
func SubvolSnapshot(dst, src string, readonly bool) error {
	dstdir, dstname := filepath.Split(dst)

	dstfp, err := openSubvolDir(dstdir)
	if err != nil {
		return errors.Wrapf(err, "opening snapshot desination subvolume failed")
	}
	defer dstfp.Close()

	srcfp, err := openSubvolDir(src)
	if err != nil {
		return errors.Wrapf(err, "opening snapshot source subvolume failed")
	}

	// dstdir is the ioctl arg, wile srcdir gets set on the args
	var args C.struct_btrfs_ioctl_vol_args_v2
	args.fd = C.__s64(srcfp.Fd())
	name := C.get_name_btrfs_ioctl_vol_args_v2(&args)

	if len(dstname) > C.BTRFS_SUBVOL_NAME_MAX {
		return errors.Errorf("%q too long for subvolume", dstname)
	}

	nameptr := (*[1<<31 - 1]byte)(unsafe.Pointer(name))
	copy(nameptr[:C.BTRFS_SUBVOL_NAME_MAX], []byte(dstname))

	if readonly {
		args.flags |= C.BTRFS_SUBVOL_RDONLY
	}

	if err := ioctl(dstfp.Fd(), C.BTRFS_IOC_SNAP_CREATE_V2, uintptr(unsafe.Pointer(&args))); err != nil {
		return errors.Wrapf(err, "snapshot create failed")
	}

	return nil
}

// SubvolDelete deletes the subvolumes under the given path.
func SubvolDelete(path string) error {
	fmt.Println("delete", path)
	dir, name := filepath.Split(path)
	fp, err := openSubvolDir(dir)
	if err != nil {
		return errors.Wrapf(err, "failed opening %v", path)
	}
	defer fp.Close()

	// remove child subvolumes
	if err := filepath.Walk(path, func(p string, fi os.FileInfo, err error) error {
		if err != nil {
			if os.IsNotExist(err) || p == path {
				return nil
			}

			return errors.Wrapf(err, "failed walking subvolume %v", p)
		}

		if !fi.IsDir() {
			return nil // just ignore it!
		}

		if p == path {
			return nil
		}

		if err := isFileInfoSubvol(fi); err != nil {
			return err
		}

		if err := SubvolDelete(p); err != nil {
			return err
		}

		return filepath.SkipDir // children get walked by call above.
	}); err != nil {
		return err
	}

	var args C.struct_btrfs_ioctl_vol_args
	if len(name) > C.BTRFS_SUBVOL_NAME_MAX {
		return errors.Errorf("%q too long for subvolume", name)
	}

	nameptr := (*[1<<31 - 1]byte)(unsafe.Pointer(&args.name[0]))
	copy(nameptr[:C.BTRFS_SUBVOL_NAME_MAX], []byte(name))

	if err := ioctl(fp.Fd(), C.BTRFS_IOC_SNAP_DESTROY, uintptr(unsafe.Pointer(&args))); err != nil {
		return errors.Wrapf(err, "failed removing subvolume %v", path)
	}

	return nil
}

func openSubvolDir(path string) (*os.File, error) {
	fp, err := os.Open(path)
	if err != nil {
		return nil, errors.Wrapf(err, "opening %v as subvolume failed", path)
	}

	return fp, nil
}

func isStatfsSubvol(statfs *syscall.Statfs_t) error {
	if statfs.Type != C.BTRFS_SUPER_MAGIC {
		return errors.Errorf("not a btrfs filesystem")
	}

	return nil
}

func isFileInfoSubvol(fi os.FileInfo) error {
	if !fi.IsDir() {
		errors.Errorf("must be a directory")
	}

	stat := fi.Sys().(*syscall.Stat_t)

	if stat.Ino != C.BTRFS_FIRST_FREE_OBJECTID {
		return errors.Errorf("incorrect inode type")
	}

	return nil
}
<|endoftext|>


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

package gojenkins

import (
	"errors"
	"regexp"
	"strconv"
	"time"
)

type Build struct {
	Raw     *buildResponse
	Job     *Job
	Jenkins *Jenkins
	Base    string
	Depth   int
}

type parameter struct {
	Name  string
	Value string
}

type branch struct {
	SHA1 string
	Name string
}

type buildRevision struct {
	SHA1   string   `json:"SHA1"`
	Branch []branch `json:"branch"`
}

type builds struct {
	BuildNumber int64         `json:"buildNumber"`
	BuildResult interface{}   `json:"buildResult"`
	Marked      buildRevision `json:"marked"`
	Revision    buildRevision `json:"revision"`
}

type culprit struct {
	AbsoluteUrl string
	FullName    string
}

type generalObj struct {
	Parameters              []parameter              `json:"parameters"`
	Causes                  []map[string]interface{} `json:"causes"`
	BuildsByBranchName      map[string]builds        `json:"buildsByBranchName"`
	LastBuiltRevision       buildRevision            `json:"lastBuiltRevision"`
	RemoteUrls              []string                 `json:"remoteUrls"`
	ScmName                 string                   `json:"scmName"`
	MercurialNodeName       string                   `json:"mercurialNodeName"`
	MercurialRevisionNumber string                   `json:"mercurialRevisionNumber"`
	Subdir                  interface{}              `json:"subdir"`
	TotalCount              int64
	UrlName                 string
}

type testResult struct {
	Duration  int64 `json:"duration"`
	Empty     bool  `json:"empty"`
	FailCount int64 `json:"failCount"`
	PassCount int64 `json:"passCount"`
	SkipCount int64 `json:"skipCount"`
	Suites    []struct {
		Cases []struct {
			Age             int64       `json:"age"`
			ClassName       string      `json:"className"`
			Duration        int64       `json:"duration"`
			ErrorDetails    interface{} `json:"errorDetails"`
			ErrorStackTrace interface{} `json:"errorStackTrace"`
			FailedSince     int64       `json:"failedSince"`
			Name            string      `json:"name"`
			Skipped         bool        `json:"skipped"`
			SkippedMessage  interface{} `json:"skippedMessage"`
			Status          string      `json:"status"`
			Stderr          interface{} `json:"stderr"`
			Stdout          interface{} `json:"stdout"`
		} `json:"cases"`
		Duration  int64       `json:"duration"`
		ID        interface{} `json:"id"`
		Name      string      `json:"name"`
		Stderr    interface{} `json:"stderr"`
		Stdout    interface{} `json:"stdout"`
		Timestamp interface{} `json:"timestamp"`
	} `json:"suites"`
}

type buildResponse struct {
	Actions   []generalObj
	Artifacts []struct {
		DisplayPath  string `json:"displayPath"`
		FileName     string `json:"fileName"`
		RelativePath string `json:"relativePath"`
	} `json:"artifacts"`
	Building  bool   `json:"building"`
	BuiltOn   string `json:"builtOn"`
	ChangeSet struct {
		Items []struct {
			AffectedPaths []string `json:"affectedPaths"`
			Author        struct {
				AbsoluteUrl string `json:"absoluteUrl"`
				FullName    string `json:"fullName"`
			} `json:"author"`
			Comment  string `json:"comment"`
			CommitId string `json:"commitId"`
			Date     string `json:"date"`
			ID       string `json:"id"`
			Msg      string `json:"msg"`
			Paths    []struct {
				EditType string `json:"editType"`
				File     string `json:"file"`
			} `json:"paths"`
			Timestamp int64 `json:"timestamp"`
		} `json:"items"`
		Kind      string `json:"kind"`
		Revisions []struct {
			Module   string
			Revision int
		} `json:"revision"`
	} `json:"changeSet"`
	Culprits          []culprit   `json:"culprits"`
	Description       interface{} `json:"description"`
	Duration          int64       `json:"duration"`
	EstimatedDuration int64       `json:"estimatedDuration"`
	Executor          interface{} `json:"executor"`
	FullDisplayName   string      `json:"fullDisplayName"`
	ID                string      `json:"id"`
	KeepLog           bool        `json:"keepLog"`
	Number            int64       `json:"number"`
	Result            string      `json:"result"`
	Timestamp         int64       `json:"timestamp"`
	URL               string      `json:"url"`
	MavenArtifacts    interface{} `json:"mavenArtifacts"`
	MavenVersionUsed  string      `json:"mavenVersionUsed"`
	Fingerprint       []fingerPrintResponse
	Runs              []struct {
		Number int64
		Url    string
	} `json:"runs"`
}

// Builds
func (b *Build) Info() *buildResponse {
	return b.Raw
}

func (b *Build) GetActions() []generalObj {
	return b.Raw.Actions
}

func (b *Build) GetUrl() string {
	return b.Raw.URL
}

func (b *Build) GetBuildNumber() int64 {
	return b.Raw.Number
}
func (b *Build) GetResult() string {
	return b.Raw.Result
}

func (b *Build) GetArtifacts() []Artifact {
	artifacts := make([]Artifact, len(b.Raw.Artifacts))
	for i, artifact := range b.Raw.Artifacts {
		artifacts[i] = Artifact{
			Jenkins:  b.Jenkins,
			Build:    b,
			FileName: artifact.FileName,
			Path:     b.Base + "/artifact/" + artifact.RelativePath,
		}
	}
	return artifacts
}

func (b *Build) GetCulprits() []culprit {
	return b.Raw.Culprits
}

func (b *Build) Stop() (bool, error) {
	if b.IsRunning() {
		response, err := b.Jenkins.Requester.Post(b.Base+"/stop", nil, nil, nil)
		if err != nil {
			return false, err
		}
		return response.StatusCode == 200, nil
	}
	return true, nil
}

func (b *Build) GetConsoleOutput() string {
	url := b.Base + "/consoleText"
	var content string
	b.Jenkins.Requester.GetXML(url, &content, nil)
	return content
}

func (b *Build) GetCauses() ([]map[string]interface{}, error) {
	_, err := b.Poll()
	if err != nil {
		return nil, err
	}
	for _, a := range b.Raw.Actions {
		if a.Causes != nil {
			return a.Causes, nil
		}
	}
	return nil, errors.New("No Causes")
}

func (b *Build) GetParameters() []parameter {
	for _, a := range b.Raw.Actions {
		if a.Parameters != nil {
			return a.Parameters
		}
	}
	return nil
}

func (b *Build) GetInjectedEnvVars() (map[string]string, error) {
	var envVars struct {
		EnvMap map[string]string `json:"envMap"`
	}
	endpoint := b.Base + "/injectedEnvVars"
	_, err := b.Jenkins.Requester.GetJSON(endpoint, &envVars, nil)
	if err != nil {
		return envVars.EnvMap, err
	}
	return envVars.EnvMap, nil
}

func (b *Build) GetDownstreamBuilds() ([]*Build, error) {
	downstreamJobs := b.GetDownstreamJobNames()
	fingerprints := b.GetAllFingerprints()
	result := make([]*Build, 0)
	for _, fingerprint := range fingerprints {
		for _, usage := range fingerprint.Raw.Usage {
			if inSlice(usage.Name, downstreamJobs) {
				job, err := b.Jenkins.GetJob(usage.Name)
				if err != nil {
					return nil, err
				}
				for _, ranges := range usage.Ranges.Ranges {
					for i := ranges.Start; i <= ranges.End; i++ {
						build, err := job.GetBuild(i)
						if err != nil {
							return nil, err
						}
						result = append(result, build)
					}
				}
			}
		}
	}
	return result, nil
}

func (b *Build) GetDownstreamJobNames() []string {
	result := make([]string, 0)
	downstreamJobs := b.Job.GetDownstreamJobsMetadata()
	fingerprints := b.GetAllFingerprints()
	for _, fingerprint := range fingerprints {
		for _, usage := range fingerprint.Raw.Usage {
			for _, job := range downstreamJobs {
				if job.Name == usage.Name {
					result = append(result, job.Name)
				}
			}
		}
	}
	return result
}

func (b *Build) GetAllFingerprints() []*Fingerprint {
	b.Poll(3)
	result := make([]*Fingerprint, len(b.Raw.Fingerprint))
	for i, f := range b.Raw.Fingerprint {
		result[i] = &Fingerprint{Jenkins: b.Jenkins, Base: "/fingerprint/", Id: f.Hash, Raw: &f}
	}
	return result
}

func (b *Build) GetUpstreamJob() (*Job, error) {
	causes, err := b.GetCauses()
	if err != nil {
		return nil, err
	}
	if len(causes) > 0 {
		if job, ok := causes[0]["upstreamProject"]; ok {
			return b.Jenkins.GetJob(job.(string))
		}
	}
	return nil, errors.New("Unable to get Upstream Job")
}

func (b *Build) GetUpstreamBuildNumber() (int64, error) {
	causes, err := b.GetCauses()
	if err != nil {
		return 0, err
	}
	if len(causes) > 0 {
		if build, ok := causes[0]["upstreamBuild"]; ok {
			switch t := build.(type) {
			default:
				return t.(int64), nil
			case float64:
				return int64(t), nil
			}
		}
	}
	return 0, nil
}

func (b *Build) GetUpstreamBuild() (*Build, error) {
	job, err := b.GetUpstreamJob()
	if err != nil {
		return nil, err
	}
	if job != nil {
		buildNumber, err := b.GetUpstreamBuildNumber()
		if err == nil {
			return job.GetBuild(buildNumber)
		}
	}
	return nil, errors.New("Build not found")
}

func (b *Build) GetMatrixRuns() ([]*Build, error) {
	_, err := b.Poll(0)
	if err != nil {
		return nil, err
	}
	runs := b.Raw.Runs
	result := make([]*Build, len(b.Raw.Runs))
	r, _ := regexp.Compile("job/(.*?)/(.*?)/(\\d+)/")

	for i, run := range runs {
		result[i] = &Build{Jenkins: b.Jenkins, Job: b.Job, Raw: new(buildResponse), Depth: 1, Base: "/" + r.FindString(run.Url)}
		result[i].Poll()
	}
	return result, nil
}

func (b *Build) GetResultSet() (*testResult, error) {

	url := b.Base + "/testReport"
	var report testResult

	_, err := b.Jenkins.Requester.GetJSON(url, &report, nil)
	if err != nil {
		return nil, err
	}

	return &report, nil

}

func (b *Build) GetTimestamp() time.Time {
	msInt := int64(b.Raw.Timestamp)
	return time.Unix(0, msInt*int64(time.Millisecond))
}

func (b *Build) GetDuration() int64 {
	return b.Raw.Duration
}

func (b *Build) GetRevision() string {
	vcs := b.Raw.ChangeSet.Kind

	if vcs == "git" || vcs == "hg" {
		for _, a := range b.Raw.Actions {
			if a.LastBuiltRevision.SHA1 != "" {
				return a.LastBuiltRevision.SHA1
			}
			if a.MercurialRevisionNumber != "" {
				return a.MercurialRevisionNumber
			}
		}
	} else if vcs == "svn" {
		return strconv.Itoa(b.Raw.ChangeSet.Revisions[0].Revision)
	}
	return ""
}

func (b *Build) GetRevisionBranch() string {
	vcs := b.Raw.ChangeSet.Kind
	if vcs == "git" {
		for _, a := range b.Raw.Actions {
			if len(a.LastBuiltRevision.Branch) > 0 && a.LastBuiltRevision.Branch[0].SHA1 != "" {
				return a.LastBuiltRevision.Branch[0].SHA1
			}
		}
	} else {
		panic("Not implemented")
	}
	return ""
}

func (b *Build) IsGood() bool {
	return (!b.IsRunning() && b.Raw.Result == STATUS_SUCCESS)
}

func (b *Build) IsRunning() bool {
	_, err := b.Poll()
	if err != nil {
		return false
	}
	return b.Raw.Building
}

// Poll for current data. Optional parameter - depth.
// More about depth here: https://wiki.jenkins-ci.org/display/JENKINS/Remote+access+API
func (b *Build) Poll(options ...interface{}) (int, error) {
	depth := "-1"

	for _, o := range options {
		switch v := o.(type) {
		case string:
			depth = v
		case int:
			depth = strconv.Itoa(v)
		case int64:
			depth = strconv.FormatInt(v, 10)
		}
	}
	if depth == "-1" {
		depth = strconv.Itoa(b.Depth)
	}

	qr := map[string]string{
		"depth": depth,
	}
	response, err := b.Jenkins.Requester.GetJSON(b.Base, b.Raw, qr)
	if err != nil {
		return 0, err
	}
	return response.StatusCode, nil
}
<commit_msg>Update build.go<commit_after>// Copyright 2015 Vadim Kravcenko
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package gojenkins

import (
	"bytes"
	"errors"
	"net/url"
	"regexp"
	"strconv"
	"time"
)

type Build struct {
	Raw     *buildResponse
	Job     *Job
	Jenkins *Jenkins
	Base    string
	Depth   int
}

type parameter struct {
	Name  string
	Value string
}

type branch struct {
	SHA1 string
	Name string
}

type buildRevision struct {
	SHA1   string   `json:"SHA1"`
	Branch []branch `json:"branch"`
}

type builds struct {
	BuildNumber int64         `json:"buildNumber"`
	BuildResult interface{}   `json:"buildResult"`
	Marked      buildRevision `json:"marked"`
	Revision    buildRevision `json:"revision"`
}

type culprit struct {
	AbsoluteUrl string
	FullName    string
}

type generalObj struct {
	Parameters              []parameter              `json:"parameters"`
	Causes                  []map[string]interface{} `json:"causes"`
	BuildsByBranchName      map[string]builds        `json:"buildsByBranchName"`
	LastBuiltRevision       buildRevision            `json:"lastBuiltRevision"`
	RemoteUrls              []string                 `json:"remoteUrls"`
	ScmName                 string                   `json:"scmName"`
	MercurialNodeName       string                   `json:"mercurialNodeName"`
	MercurialRevisionNumber string                   `json:"mercurialRevisionNumber"`
	Subdir                  interface{}              `json:"subdir"`
	TotalCount              int64
	UrlName                 string
}

type testResult struct {
	Duration  int64 `json:"duration"`
	Empty     bool  `json:"empty"`
	FailCount int64 `json:"failCount"`
	PassCount int64 `json:"passCount"`
	SkipCount int64 `json:"skipCount"`
	Suites    []struct {
		Cases []struct {
			Age             int64       `json:"age"`
			ClassName       string      `json:"className"`
			Duration        int64       `json:"duration"`
			ErrorDetails    interface{} `json:"errorDetails"`
			ErrorStackTrace interface{} `json:"errorStackTrace"`
			FailedSince     int64       `json:"failedSince"`
			Name            string      `json:"name"`
			Skipped         bool        `json:"skipped"`
			SkippedMessage  interface{} `json:"skippedMessage"`
			Status          string      `json:"status"`
			Stderr          interface{} `json:"stderr"`
			Stdout          interface{} `json:"stdout"`
		} `json:"cases"`
		Duration  int64       `json:"duration"`
		ID        interface{} `json:"id"`
		Name      string      `json:"name"`
		Stderr    interface{} `json:"stderr"`
		Stdout    interface{} `json:"stdout"`
		Timestamp interface{} `json:"timestamp"`
	} `json:"suites"`
}

type buildResponse struct {
	Actions   []generalObj
	Artifacts []struct {
		DisplayPath  string `json:"displayPath"`
		FileName     string `json:"fileName"`
		RelativePath string `json:"relativePath"`
	} `json:"artifacts"`
	Building  bool   `json:"building"`
	BuiltOn   string `json:"builtOn"`
	ChangeSet struct {
		Items []struct {
			AffectedPaths []string `json:"affectedPaths"`
			Author        struct {
				AbsoluteUrl string `json:"absoluteUrl"`
				FullName    string `json:"fullName"`
			} `json:"author"`
			Comment  string `json:"comment"`
			CommitId string `json:"commitId"`
			Date     string `json:"date"`
			ID       string `json:"id"`
			Msg      string `json:"msg"`
			Paths    []struct {
				EditType string `json:"editType"`
				File     string `json:"file"`
			} `json:"paths"`
			Timestamp int64 `json:"timestamp"`
		} `json:"items"`
		Kind      string `json:"kind"`
		Revisions []struct {
			Module   string
			Revision int
		} `json:"revision"`
	} `json:"changeSet"`
	Culprits          []culprit   `json:"culprits"`
	Description       interface{} `json:"description"`
	Duration          int64       `json:"duration"`
	EstimatedDuration int64       `json:"estimatedDuration"`
	Executor          interface{} `json:"executor"`
	FullDisplayName   string      `json:"fullDisplayName"`
	ID                string      `json:"id"`
	KeepLog           bool        `json:"keepLog"`
	Number            int64       `json:"number"`
	Result            string      `json:"result"`
	Timestamp         int64       `json:"timestamp"`
	URL               string      `json:"url"`
	MavenArtifacts    interface{} `json:"mavenArtifacts"`
	MavenVersionUsed  string      `json:"mavenVersionUsed"`
	Fingerprint       []fingerPrintResponse
	Runs              []struct {
		Number int64
		Url    string
	} `json:"runs"`
}

// Builds
func (b *Build) Info() *buildResponse {
	return b.Raw
}

func (b *Build) GetActions() []generalObj {
	return b.Raw.Actions
}

func (b *Build) GetUrl() string {
	return b.Raw.URL
}

func (b *Build) GetBuildNumber() int64 {
	return b.Raw.Number
}
func (b *Build) GetResult() string {
	return b.Raw.Result
}

func (b *Build) GetArtifacts() []Artifact {
	artifacts := make([]Artifact, len(b.Raw.Artifacts))
	for i, artifact := range b.Raw.Artifacts {
		artifacts[i] = Artifact{
			Jenkins:  b.Jenkins,
			Build:    b,
			FileName: artifact.FileName,
			Path:     b.Base + "/artifact/" + artifact.RelativePath,
		}
	}
	return artifacts
}

func (b *Build) GetCulprits() []culprit {
	return b.Raw.Culprits
}

func (b *Build) Stop() (bool, error) {
	if b.IsRunning() {
		response, err := b.Jenkins.Requester.Post(b.Base+"/stop", nil, nil, nil)
		if err != nil {
			return false, err
		}
		return response.StatusCode == 200, nil
	}
	return true, nil
}

func (b *Build) GetConsoleOutput() string {
	url := b.Base + "/consoleText"
	var content string
	b.Jenkins.Requester.GetXML(url, &content, nil)
	return content
}

func (b *Build) GetCauses() ([]map[string]interface{}, error) {
	_, err := b.Poll()
	if err != nil {
		return nil, err
	}
	for _, a := range b.Raw.Actions {
		if a.Causes != nil {
			return a.Causes, nil
		}
	}
	return nil, errors.New("No Causes")
}

func (b *Build) GetParameters() []parameter {
	for _, a := range b.Raw.Actions {
		if a.Parameters != nil {
			return a.Parameters
		}
	}
	return nil
}

func (b *Build) GetInjectedEnvVars() (map[string]string, error) {
	var envVars struct {
		EnvMap map[string]string `json:"envMap"`
	}
	endpoint := b.Base + "/injectedEnvVars"
	_, err := b.Jenkins.Requester.GetJSON(endpoint, &envVars, nil)
	if err != nil {
		return envVars.EnvMap, err
	}
	return envVars.EnvMap, nil
}

func (b *Build) GetDownstreamBuilds() ([]*Build, error) {
	downstreamJobs := b.GetDownstreamJobNames()
	fingerprints := b.GetAllFingerprints()
	result := make([]*Build, 0)
	for _, fingerprint := range fingerprints {
		for _, usage := range fingerprint.Raw.Usage {
			if inSlice(usage.Name, downstreamJobs) {
				job, err := b.Jenkins.GetJob(usage.Name)
				if err != nil {
					return nil, err
				}
				for _, ranges := range usage.Ranges.Ranges {
					for i := ranges.Start; i <= ranges.End; i++ {
						build, err := job.GetBuild(i)
						if err != nil {
							return nil, err
						}
						result = append(result, build)
					}
				}
			}
		}
	}
	return result, nil
}

func (b *Build) GetDownstreamJobNames() []string {
	result := make([]string, 0)
	downstreamJobs := b.Job.GetDownstreamJobsMetadata()
	fingerprints := b.GetAllFingerprints()
	for _, fingerprint := range fingerprints {
		for _, usage := range fingerprint.Raw.Usage {
			for _, job := range downstreamJobs {
				if job.Name == usage.Name {
					result = append(result, job.Name)
				}
			}
		}
	}
	return result
}

func (b *Build) GetAllFingerprints() []*Fingerprint {
	b.Poll(3)
	result := make([]*Fingerprint, len(b.Raw.Fingerprint))
	for i, f := range b.Raw.Fingerprint {
		result[i] = &Fingerprint{Jenkins: b.Jenkins, Base: "/fingerprint/", Id: f.Hash, Raw: &f}
	}
	return result
}

func (b *Build) GetUpstreamJob() (*Job, error) {
	causes, err := b.GetCauses()
	if err != nil {
		return nil, err
	}
	if len(causes) > 0 {
		if job, ok := causes[0]["upstreamProject"]; ok {
			return b.Jenkins.GetJob(job.(string))
		}
	}
	return nil, errors.New("Unable to get Upstream Job")
}

func (b *Build) GetUpstreamBuildNumber() (int64, error) {
	causes, err := b.GetCauses()
	if err != nil {
		return 0, err
	}
	if len(causes) > 0 {
		if build, ok := causes[0]["upstreamBuild"]; ok {
			switch t := build.(type) {
			default:
				return t.(int64), nil
			case float64:
				return int64(t), nil
			}
		}
	}
	return 0, nil
}

func (b *Build) GetUpstreamBuild() (*Build, error) {
	job, err := b.GetUpstreamJob()
	if err != nil {
		return nil, err
	}
	if job != nil {
		buildNumber, err := b.GetUpstreamBuildNumber()
		if err == nil {
			return job.GetBuild(buildNumber)
		}
	}
	return nil, errors.New("Build not found")
}

func (b *Build) GetMatrixRuns() ([]*Build, error) {
	_, err := b.Poll(0)
	if err != nil {
		return nil, err
	}
	runs := b.Raw.Runs
	result := make([]*Build, len(b.Raw.Runs))
	r, _ := regexp.Compile("job/(.*?)/(.*?)/(\\d+)/")

	for i, run := range runs {
		result[i] = &Build{Jenkins: b.Jenkins, Job: b.Job, Raw: new(buildResponse), Depth: 1, Base: "/" + r.FindString(run.Url)}
		result[i].Poll()
	}
	return result, nil
}

func (b *Build) GetResultSet() (*testResult, error) {

	url := b.Base + "/testReport"
	var report testResult

	_, err := b.Jenkins.Requester.GetJSON(url, &report, nil)
	if err != nil {
		return nil, err
	}

	return &report, nil

}

func (b *Build) GetTimestamp() time.Time {
	msInt := int64(b.Raw.Timestamp)
	return time.Unix(0, msInt*int64(time.Millisecond))
}

func (b *Build) GetDuration() int64 {
	return b.Raw.Duration
}

func (b *Build) GetRevision() string {
	vcs := b.Raw.ChangeSet.Kind

	if vcs == "git" || vcs == "hg" {
		for _, a := range b.Raw.Actions {
			if a.LastBuiltRevision.SHA1 != "" {
				return a.LastBuiltRevision.SHA1
			}
			if a.MercurialRevisionNumber != "" {
				return a.MercurialRevisionNumber
			}
		}
	} else if vcs == "svn" {
		return strconv.Itoa(b.Raw.ChangeSet.Revisions[0].Revision)
	}
	return ""
}

func (b *Build) GetRevisionBranch() string {
	vcs := b.Raw.ChangeSet.Kind
	if vcs == "git" {
		for _, a := range b.Raw.Actions {
			if len(a.LastBuiltRevision.Branch) > 0 && a.LastBuiltRevision.Branch[0].SHA1 != "" {
				return a.LastBuiltRevision.Branch[0].SHA1
			}
		}
	} else {
		panic("Not implemented")
	}
	return ""
}

func (b *Build) IsGood() bool {
	return (!b.IsRunning() && b.Raw.Result == STATUS_SUCCESS)
}

func (b *Build) IsRunning() bool {
	_, err := b.Poll()
	if err != nil {
		return false
	}
	return b.Raw.Building
}

func (b *Build) SetDescription(description string) error {
	data := url.Values{}
	data.Set("description", description)
	if _, err := b.Jenkins.Requester.Post(b.Base+"/submitDescription", bytes.NewBufferString(data.Encode()), nil, nil); err != nil {
		return err
	}

	return nil
}

// Poll for current data. Optional parameter - depth.
// More about depth here: https://wiki.jenkins-ci.org/display/JENKINS/Remote+access+API
func (b *Build) Poll(options ...interface{}) (int, error) {
	depth := "-1"

	for _, o := range options {
		switch v := o.(type) {
		case string:
			depth = v
		case int:
			depth = strconv.Itoa(v)
		case int64:
			depth = strconv.FormatInt(v, 10)
		}
	}
	if depth == "-1" {
		depth = strconv.Itoa(b.Depth)
	}

	qr := map[string]string{
		"depth": depth,
	}
	response, err := b.Jenkins.Requester.GetJSON(b.Base, b.Raw, qr)
	if err != nil {
		return 0, err
	}
	return response.StatusCode, nil
}
<|endoftext|>


<commit_before>package ringbuffer

import (
	"fmt"
	"sync"
	"sync/atomic"
)

type RingBuffer struct {
	readIndex  int64      //读序号
	writeIndex int64      //写序号
	buf        []*[]byte  //环形buffer指针数组
	bufSize    int64      //初始化环形buffer指针数组大小
	mask       int64      //初始化环形buffer指针数组大小
	pcond      *sync.Cond //生产者
	ccond      *sync.Cond //消费者
	done       int64      //is done? 1=done; 0=doing
	pWaitTimes int64      //生产者wait次数
	cWaitTimes int64      //消费者wait次数
}

func powerOfTwo64(n int64) bool {
	return n != 0 && (n&(n-1)) == 0
}

/**
初始化ringbuffer
参数bufferSize：初始化环形buffer指针数组大小
*/
func NewRingBuffer(size int64) (*RingBuffer, error) {
	if !powerOfTwo64(size) {
		return nil, fmt.Errorf("This size is not able to used")
	}
	buffer := RingBuffer{
		readIndex:  int64(0),
		writeIndex: int64(0),
		buf:        make([]*[]byte, size),
		bufSize:    size,
		mask:       size - int64(1),
		pcond:      sync.NewCond(new(sync.Mutex)),
		ccond:      sync.NewCond(new(sync.Mutex)),
		done:       int64(0),
		pWaitTimes: int64(0),
		cWaitTimes: int64(0),
	}
	for i := int64(0); i < size; i++ {
		buffer.buf[i] = nil
	}
	return &buffer, nil
}

func (this *RingBuffer) GetpWaitTimes() int64 {
	return atomic.LoadInt64(&this.pWaitTimes)
}

func (this *RingBuffer) GetcWaitTimes() int64 {
	return atomic.LoadInt64(&this.cWaitTimes)
}

func (this *RingBuffer) ReSetpWaitTimes() {
	return atomic.StoreInt64(&this.pWaitTimes, int64(0))
}

func (this *RingBuffer) ReSetcWaitTimes() {
	return atomic.StoreInt64(&this.cWaitTimes, int64(0))
}

func (this *RingBuffer) AddpWaitTimes() int64 {
	return atomic.AddInt64(&this.pWaitTimes, int64(1))
}

func (this *RingBuffer) AddcWaitTimes() int64 {
	return atomic.AddInt64(&this.cWaitTimes, int64(1))
}

/**
获取当前读序号
*/
func (this *RingBuffer) GetCurrentReadIndex() int64 {
	return atomic.LoadInt64(&this.readIndex)
}

/**
获取当前写序号
*/
func (this *RingBuffer) GetCurrentWriteIndex() int64 {
	return atomic.LoadInt64(&this.writeIndex)
}

/**
读取ringbuffer指定的buffer指针，返回该指针并清空ringbuffer该位置存在的指针内容，以及将读序号加1
*/
func (this *RingBuffer) ReadBuffer() (p *[]byte, ok bool) {
	this.ccond.L.Lock()
	defer func() {
		if this.GetpWaitTimes() > int64(0) {
			this.ReSetpWaitTimes()
			this.pcond.Broadcast()
		}
		this.ccond.L.Unlock()
	}()
	ok = false
	p = nil
	readIndex := this.GetCurrentReadIndex()
	writeIndex := this.GetCurrentWriteIndex()
	for {
		if this.isDone() {
			return nil, false
		}
		writeIndex = this.GetCurrentWriteIndex()
		if readIndex >= writeIndex {
			if this.GetpWaitTimes() > int64(0) {
				this.ReSetpWaitTimes()
				this.pcond.Broadcast()
			}
			this.AddcWaitTimes()
			this.ccond.Wait()
		} else {
			break
		}
		//time.Sleep(1 * time.Millisecond)
		//time.Sleep(500 * time.Microsecond)
	}
	index := readIndex & this.mask //替代求模
	p = this.buf[index]
	this.buf[index] = nil
	atomic.AddInt64(&this.readIndex, int64(1))
	if p != nil {
		ok = true
	}
	return p, ok
}

/**
写入ringbuffer指针，以及将写序号加1
*/
func (this *RingBuffer) WriteBuffer(in *[]byte) (ok bool) {
	this.pcond.L.Lock()
	defer func() {
		if this.GetcWaitTimes() > int64(0) {
			this.ReSetcWaitTimes()
			this.ccond.Broadcast()
		}
		this.pcond.L.Unlock()
	}()
	ok = false
	readIndex := this.GetCurrentReadIndex()
	writeIndex := this.GetCurrentWriteIndex()
	for {
		if this.isDone() {
			return false
		}
		readIndex = this.GetCurrentReadIndex()
		if writeIndex >= readIndex && writeIndex-readIndex >= this.bufSize {
			if this.GetcWaitTimes() > int64(0) {
				this.ReSetcWaitTimes()
				this.ccond.Broadcast()
			}
			this.AddpWaitTimes()
			this.pcond.Wait()
			//time.Sleep(1 * time.Millisecond)
		} else {
			break
		}
		//time.Sleep(1 * time.Millisecond)
		//time.Sleep(500 * time.Microsecond)
	}
	index := writeIndex & this.mask //替代求模
	this.buf[index] = in
	atomic.AddInt64(&this.writeIndex, int64(1))
	ok = true
	return ok
}

func (this *RingBuffer) Close() error {
	atomic.StoreInt64(&this.done, 1)

	this.pcond.L.Lock()
	this.ccond.Broadcast()
	this.pcond.L.Unlock()

	this.ccond.L.Lock()
	this.pcond.Broadcast()
	this.ccond.L.Unlock()

	return nil
}

func (this *RingBuffer) isDone() bool {
	if atomic.LoadInt64(&this.done) == 1 {
		return true
	}

	return false
}
<commit_msg>修改ringbuffer锁<commit_after>package ringbuffer

import (
	"fmt"
	"sync"
	"sync/atomic"
)

type RingBuffer struct {
	readIndex  int64      //读序号
	writeIndex int64      //写序号
	buf        []*[]byte  //环形buffer指针数组
	bufSize    int64      //初始化环形buffer指针数组大小
	mask       int64      //初始化环形buffer指针数组大小
	pcond      *sync.Cond //生产者
	ccond      *sync.Cond //消费者
	done       int64      //is done? 1=done; 0=doing
	pWaitTimes int64      //生产者wait次数
	cWaitTimes int64      //消费者wait次数
}

func powerOfTwo64(n int64) bool {
	return n != 0 && (n&(n-1)) == 0
}

/**
初始化ringbuffer
参数bufferSize：初始化环形buffer指针数组大小
*/
func NewRingBuffer(size int64) (*RingBuffer, error) {
	if !powerOfTwo64(size) {
		return nil, fmt.Errorf("This size is not able to used")
	}
	buffer := RingBuffer{
		readIndex:  int64(0),
		writeIndex: int64(0),
		buf:        make([]*[]byte, size),
		bufSize:    size,
		mask:       size - int64(1),
		pcond:      sync.NewCond(new(sync.Mutex)),
		ccond:      sync.NewCond(new(sync.Mutex)),
		done:       int64(0),
		pWaitTimes: int64(0),
		cWaitTimes: int64(0),
	}
	for i := int64(0); i < size; i++ {
		buffer.buf[i] = nil
	}
	return &buffer, nil
}

func (this *RingBuffer) GetpWaitTimes() int64 {
	return atomic.LoadInt64(&this.pWaitTimes)
}

func (this *RingBuffer) GetcWaitTimes() int64 {
	return atomic.LoadInt64(&this.cWaitTimes)
}

func (this *RingBuffer) ReSetpWaitTimes() {
	atomic.StoreInt64(&this.pWaitTimes, int64(0))
}

func (this *RingBuffer) ReSetcWaitTimes() {
	atomic.StoreInt64(&this.cWaitTimes, int64(0))
}

func (this *RingBuffer) AddpWaitTimes() int64 {
	return atomic.AddInt64(&this.pWaitTimes, int64(1))
}

func (this *RingBuffer) AddcWaitTimes() int64 {
	return atomic.AddInt64(&this.cWaitTimes, int64(1))
}

/**
获取当前读序号
*/
func (this *RingBuffer) GetCurrentReadIndex() int64 {
	return atomic.LoadInt64(&this.readIndex)
}

/**
获取当前写序号
*/
func (this *RingBuffer) GetCurrentWriteIndex() int64 {
	return atomic.LoadInt64(&this.writeIndex)
}

/**
读取ringbuffer指定的buffer指针，返回该指针并清空ringbuffer该位置存在的指针内容，以及将读序号加1
*/
func (this *RingBuffer) ReadBuffer() (p *[]byte, ok bool) {
	this.ccond.L.Lock()
	defer func() {
		if this.GetpWaitTimes() > int64(0) {
			this.ReSetpWaitTimes()
			this.pcond.Broadcast()
		}
		this.ccond.L.Unlock()
	}()
	ok = false
	p = nil
	readIndex := this.GetCurrentReadIndex()
	writeIndex := this.GetCurrentWriteIndex()
	for {
		if this.isDone() {
			return nil, false
		}
		writeIndex = this.GetCurrentWriteIndex()
		if readIndex >= writeIndex {
			if this.GetpWaitTimes() > int64(0) {
				this.ReSetpWaitTimes()
				this.pcond.Broadcast()
			}
			this.AddcWaitTimes()
			this.ccond.Wait()
		} else {
			break
		}
		//time.Sleep(1 * time.Millisecond)
		//time.Sleep(500 * time.Microsecond)
	}
	index := readIndex & this.mask //替代求模
	p = this.buf[index]
	this.buf[index] = nil
	atomic.AddInt64(&this.readIndex, int64(1))
	if p != nil {
		ok = true
	}
	return p, ok
}

/**
写入ringbuffer指针，以及将写序号加1
*/
func (this *RingBuffer) WriteBuffer(in *[]byte) (ok bool) {
	this.pcond.L.Lock()
	defer func() {
		if this.GetcWaitTimes() > int64(0) {
			this.ReSetcWaitTimes()
			this.ccond.Broadcast()
		}
		this.pcond.L.Unlock()
	}()
	ok = false
	readIndex := this.GetCurrentReadIndex()
	writeIndex := this.GetCurrentWriteIndex()
	for {
		if this.isDone() {
			return false
		}
		readIndex = this.GetCurrentReadIndex()
		if writeIndex >= readIndex && writeIndex-readIndex >= this.bufSize {
			if this.GetcWaitTimes() > int64(0) {
				this.ReSetcWaitTimes()
				this.ccond.Broadcast()
			}
			this.AddpWaitTimes()
			this.pcond.Wait()
			//time.Sleep(1 * time.Millisecond)
		} else {
			break
		}
		//time.Sleep(1 * time.Millisecond)
		//time.Sleep(500 * time.Microsecond)
	}
	index := writeIndex & this.mask //替代求模
	this.buf[index] = in
	atomic.AddInt64(&this.writeIndex, int64(1))
	ok = true
	return ok
}

func (this *RingBuffer) Close() error {
	atomic.StoreInt64(&this.done, 1)

	this.pcond.L.Lock()
	this.ccond.Broadcast()
	this.pcond.L.Unlock()

	this.ccond.L.Lock()
	this.pcond.Broadcast()
	this.ccond.L.Unlock()

	return nil
}

func (this *RingBuffer) isDone() bool {
	if atomic.LoadInt64(&this.done) == 1 {
		return true
	}

	return false
}
<|endoftext|>


<commit_before>package wiki

import (
	"path/filepath"

	"github.com/cooper/quiki/wikifier"
)

func (w *Wiki) checkDirectories() {
	// TODO
	panic("unimplemented")
}

func (w *Wiki) allPageFiles() []string {
	files, _ := wikifier.UniqueFilesInDir(w.Opt.Dir.Page, []string{"page"}, false)
	return files
}

func (w *Wiki) allCategoryFiles(catType string) []string {
	dir := w.Opt.Dir.Category
	if catType != "" {
		dir += "/" + catType
	}
	files, _ := wikifier.UniqueFilesInDir(dir, []string{"cat"}, false)
	return files
}

func (w *Wiki) allModelFiles() []string {
	files, _ := wikifier.UniqueFilesInDir(w.Opt.Dir.Model, []string{"model"}, false)
	return files
}

func (w *Wiki) allImageFiles() []string {
	files, _ := wikifier.UniqueFilesInDir(w.Opt.Dir.Image, []string{"png", "jpg", "jpeg"}, false)
	return files
}

func (w *Wiki) allMarkdownFiles() []string {
	files, _ := wikifier.UniqueFilesInDir(w.Opt.Dir.Markdown, []string{"md"}, false)
	return files
}

// pathForPage returns the absolute path for a page. If necessary, it creates
// diretories for the path components that do not exist.
func (w *Wiki) pathForPage(pageName string, createOK bool, dirPage string) string {
	if dirPage == "" {
		dirPage = w.Opt.Dir.Page
	}
	pageName = wikifier.PageName(pageName)
	if createOK {
		makeDir(dirPage, pageName)
	}
	path, _ := filepath.Abs(dirPage + "/" + pageName)
	return path
}

// pathForCategory returns the absolute path for a category. If necessary, it
// creates directories for the path components that do not exist.
func (w *Wiki) pathForCategory(catName, catType string, createOK bool) string {
	catName = wikifier.CategoryName(catName, false)
	if catType != "" {
		catType += "/"
	}
	dir := w.Opt.Dir.Cache + "/category"
	if createOK {
		makeDir(dir, catType+catName)
	}
	path, _ := filepath.Abs(dir + "/" + catType + catName)
	return path
}

// pathForImage returns the absolute path for an image.
func (w *Wiki) pathForImage(imageName string) string {
	path, _ := filepath.Abs(w.Opt.Dir.Image + "/" + imageName)
	return path
}

// pathForModel returns the absolute path for a model.
func (w *Wiki) pathForModel(modelName string) string {
	modelName = wikifier.PageNameExt(modelName, ".model")
	path, _ := filepath.Abs(w.Opt.Dir.Model + "/" + modelName)
	return path
}

func makeDir(dir, name string) {

}
<commit_msg>makeDir<commit_after>package wiki

import (
	"os"
	"path/filepath"

	"github.com/cooper/quiki/wikifier"
)

func (w *Wiki) checkDirectories() {
	// TODO
	panic("unimplemented")
}

func (w *Wiki) allPageFiles() []string {
	files, _ := wikifier.UniqueFilesInDir(w.Opt.Dir.Page, []string{"page"}, false)
	return files
}

func (w *Wiki) allCategoryFiles(catType string) []string {
	dir := w.Opt.Dir.Category
	if catType != "" {
		dir += "/" + catType
	}
	files, _ := wikifier.UniqueFilesInDir(dir, []string{"cat"}, false)
	return files
}

func (w *Wiki) allModelFiles() []string {
	files, _ := wikifier.UniqueFilesInDir(w.Opt.Dir.Model, []string{"model"}, false)
	return files
}

func (w *Wiki) allImageFiles() []string {
	files, _ := wikifier.UniqueFilesInDir(w.Opt.Dir.Image, []string{"png", "jpg", "jpeg"}, false)
	return files
}

func (w *Wiki) allMarkdownFiles() []string {
	files, _ := wikifier.UniqueFilesInDir(w.Opt.Dir.Markdown, []string{"md"}, false)
	return files
}

// pathForPage returns the absolute path for a page. If necessary, it creates
// diretories for the path components that do not exist.
func (w *Wiki) pathForPage(pageName string, createOK bool, dirPage string) string {
	if dirPage == "" {
		dirPage = w.Opt.Dir.Page
	}
	pageName = wikifier.PageName(pageName)
	if createOK {
		makeDir(dirPage, pageName)
	}
	path, _ := filepath.Abs(dirPage + "/" + pageName)
	return path
}

// pathForCategory returns the absolute path for a category. If necessary, it
// creates directories for the path components that do not exist.
func (w *Wiki) pathForCategory(catName, catType string, createOK bool) string {
	catName = wikifier.CategoryName(catName, false)
	if catType != "" {
		catType += "/"
	}
	dir := w.Opt.Dir.Cache + "/category"
	if createOK {
		makeDir(dir, catType+catName)
	}
	path, _ := filepath.Abs(dir + "/" + catType + catName)
	return path
}

// pathForImage returns the absolute path for an image.
func (w *Wiki) pathForImage(imageName string) string {
	path, _ := filepath.Abs(w.Opt.Dir.Image + "/" + imageName)
	return path
}

// pathForModel returns the absolute path for a model.
func (w *Wiki) pathForModel(modelName string) string {
	modelName = wikifier.PageNameExt(modelName, ".model")
	path, _ := filepath.Abs(w.Opt.Dir.Model + "/" + modelName)
	return path
}

func makeDir(dir, name string) {
	pfx := filepath.Dir(name)
	if pfx == "." || pfx == "./" {
		return
	}
	os.MkdirAll(dir+"/"+pfx, 0755)
}
<|endoftext|>


<commit_before>package workflow

import (
	"encoding/json"
	"time"
)

// Runner is a stateful representation of a Running Workflow
type Runner struct {
	EndTime      time.Time
	ErrorMessage string
	Last         string
	StartTime    time.Time
	State        string
	UUID         string
	Variables    map[string]interface{}
	Workflow     Workflow
}

// NewRunner initialises and Return a Runner
func NewRunner(uuid string, wf Workflow) (wfr Runner) {
	wfr.UUID = uuid
	wfr.Workflow = wf
	wfr.Variables = make(map[string]interface{})
	wfr.Variables["Defaults"] = wf.Variables

	return
}

// ParseRunner returns a parsed Runner from a string
func ParseRunner(data string) (wfr Runner, err error) {
	if data == "" {
		return
	}
	err = json.Unmarshal([]byte(data), &wfr)

	return
}

// Start puts a Running Workflow into a started state
func (wfr *Runner) Start() {
	wfr.StartTime = time.Now()
	wfr.State = "started"
}

// Next returns the next Step of a Runner
func (wfr *Runner) Next() (s Step, done bool) {
	var idx int
	wfr.State = "running"

	if wfr.Last == "" {
		return wfr.Workflow.Steps[0], false
	}

	for idx, s = range wfr.Workflow.Steps {
		if s.Name == wfr.Last {
			break
		}
	}

	if idx+1 >= len(wfr.Workflow.Steps) {
		return s, true
	}

	return wfr.Workflow.Steps[idx+1], false
}

// Current returns the current step. It is used, mainly,
// after a step has returned to add extra data
func (wfr *Runner) Current() (i int, s Step) {
	for i, s = range wfr.Workflow.Steps {
		if s.Name == wfr.Last {
			return
		}
	}

	return
}

// Fail will set state to "failed" and end the workflow runner
func (wfr *Runner) Fail(msg string) {
	wfr.ErrorMessage = msg
	wfr.endWithState("failed")
}

// End will set state to "ended" and end the workflow runner
func (wfr *Runner) End() {
	wfr.endWithState("ended")
}

func (wfr *Runner) endWithState(state string) {
	wfr.EndTime = time.Now()
	wfr.State = state
}
<commit_msg>Handle workflows with no steps<commit_after>package workflow

import (
	"encoding/json"
	"time"
)

// Runner is a stateful representation of a Running Workflow
type Runner struct {
	EndTime      time.Time
	ErrorMessage string
	Last         string
	StartTime    time.Time
	State        string
	UUID         string
	Variables    map[string]interface{}
	Workflow     Workflow
}

// NewRunner initialises and Return a Runner
func NewRunner(uuid string, wf Workflow) (wfr Runner) {
	wfr.UUID = uuid
	wfr.Workflow = wf
	wfr.Variables = make(map[string]interface{})
	wfr.Variables["Defaults"] = wf.Variables

	return
}

// ParseRunner returns a parsed Runner from a string
func ParseRunner(data string) (wfr Runner, err error) {
	if data == "" {
		return
	}
	err = json.Unmarshal([]byte(data), &wfr)

	return
}

// Start puts a Running Workflow into a started state
func (wfr *Runner) Start() {
	wfr.StartTime = time.Now()
	wfr.State = "started"
}

// Next returns the next Step of a Runner
func (wfr *Runner) Next() (s Step, done bool) {
	var idx int
	wfr.State = "running"

	if len(wfr.Workflow.Steps) == 0 {
		return s, true
	}

	if wfr.Last == "" {
		return wfr.Workflow.Steps[0], false
	}

	for idx, s = range wfr.Workflow.Steps {
		if s.Name == wfr.Last {
			break
		}
	}

	if idx+1 >= len(wfr.Workflow.Steps) {
		return s, true
	}

	return wfr.Workflow.Steps[idx+1], false
}

// Current returns the current step. It is used, mainly,
// after a step has returned to add extra data
func (wfr *Runner) Current() (i int, s Step) {
	for i, s = range wfr.Workflow.Steps {
		if s.Name == wfr.Last {
			return
		}
	}

	return 0, wfr.Workflow.Steps[0]
}

// Fail will set state to "failed" and end the workflow runner
func (wfr *Runner) Fail(msg string) {
	wfr.ErrorMessage = msg
	wfr.endWithState("failed")
}

// End will set state to "ended" and end the workflow runner
func (wfr *Runner) End() {
	wfr.endWithState("ended")
}

func (wfr *Runner) endWithState(state string) {
	wfr.EndTime = time.Now()
	wfr.State = state
}
<|endoftext|>


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

package cpkg

//#include <stdio.h>
//#include <string.h>
//#include <stdlib.h>
//void cpkg_printf(const char *str) {
//	fprintf(stdout, str);
//}
import "C"

import (
	"fmt"
	"unsafe"
)

// Hi prints hi from Go (via C's stdio)
func Hi() {
	cstr := C.CString("hi from go\n")
	defer C.free(unsafe.Pointer(cstr))
	C.cpkg_printf(cstr)
}

// Hello prints a string via C's stdio
func Hello(s string) {
	if s == "" {
		s = "you"
	}
	cstr := C.CString(fmt.Sprintf("hello %s from go\n", s))
	defer C.free(unsafe.Pointer(cstr))
	C.cpkg_printf(cstr)
}

// Printf prints a string via C's stdio
func Printf(format string, args ...interface{}) {
	str := fmt.Sprintf(format, args...)
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	C.cpkg_printf(cstr)
}
<commit_msg>examples: make sure stdout is flushed<commit_after>// Copyright 2015 The go-python Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cpkg

//#include <stdio.h>
//#include <string.h>
//#include <stdlib.h>
//void cpkg_printf(const char *str) {
//	fprintf(stdout, str);
//	fflush(stdout);
//}
import "C"

import (
	"fmt"
	"unsafe"
)

// Hi prints hi from Go (via C's stdio)
func Hi() {
	cstr := C.CString("hi from go\n")
	defer C.free(unsafe.Pointer(cstr))
	C.cpkg_printf(cstr)
}

// Hello prints a string via C's stdio
func Hello(s string) {
	if s == "" {
		s = "you"
	}
	cstr := C.CString(fmt.Sprintf("hello %s from go\n", s))
	defer C.free(unsafe.Pointer(cstr))
	C.cpkg_printf(cstr)
}

// Printf prints a string via C's stdio
func Printf(format string, args ...interface{}) {
	str := fmt.Sprintf(format, args...)
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	C.cpkg_printf(cstr)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"gopkg.in/src-d/go-git.v4"
	. "gopkg.in/src-d/go-git.v4/_examples"
	"gopkg.in/src-d/go-git.v4/plumbing/object"
)

// Open an existing repository in a specific folder.
func main() {
	CheckArgs("<path>")
	path := os.Args[1]

	// We instanciate a new repository targeting the given path (the .git folder)
	r, err := git.PlainOpen(path)
	CheckIfError(err)

	// Length of the HEAD history
	Info("git rev-list HEAD --count")

	// ... retrieving the HEAD reference
	ref, err := r.Head()
	CheckIfError(err)

	// ... retrieves the commit history
	cIter, err := r.Log(&git.LogOptions{From: ref.Hash()})
	CheckIfError(err)

	// ... just iterates over the commits
	var cCount int
	err = cIter.ForEach(func(c *object.Commit) error {
		cCount++

		return nil
	})
	CheckIfError(err)

	fmt.Println(cCount)
}
<commit_msg>Spelling mistake<commit_after>package main

import (
	"fmt"
	"os"

	"gopkg.in/src-d/go-git.v4"
	. "gopkg.in/src-d/go-git.v4/_examples"
	"gopkg.in/src-d/go-git.v4/plumbing/object"
)

// Open an existing repository in a specific folder.
func main() {
	CheckArgs("<path>")
	path := os.Args[1]

	// We instantiate a new repository targeting the given path (the .git folder)
	r, err := git.PlainOpen(path)
	CheckIfError(err)

	// Length of the HEAD history
	Info("git rev-list HEAD --count")

	// ... retrieving the HEAD reference
	ref, err := r.Head()
	CheckIfError(err)

	// ... retrieves the commit history
	cIter, err := r.Log(&git.LogOptions{From: ref.Hash()})
	CheckIfError(err)

	// ... just iterates over the commits
	var cCount int
	err = cIter.ForEach(func(c *object.Commit) error {
		cCount++

		return nil
	})
	CheckIfError(err)

	fmt.Println(cCount)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"
)

const youtube = `{"jsonrpc": "2.0", "method": "Player.Open", "params":{` +
	`"item": {"file":"plugin://plugin.video.youtube/?action=` +
	`play_video&videoid=%s"}}, "id" : 1}`
const youtubeAndSeek = `{"jsonrpc":"2.0","method":"Player.Open","params":{` +
	`"item":{"file":"plugin://plugin.video.youtube/?action=` +
	`play_video&videoid=%s"},` +
	`"options":` +
	`{"resume":{"hours":%d,"minutes":%d,"seconds":%d}}` +
	`},"id":1},`
const genericURL = `{"jsonrpc": "2.0", "method": "Player.open", "params": ` +
	`{"item": {"file": "%s"}}, "id": 1}`

func divmod(value int64, modulo int64) (int64, int64) {
	mod := (value % modulo)
	return (value - mod) / modulo, mod
}

func generatePayload(video string) string {
	videoID := ""

	if !strings.HasPrefix(video, "http") {
		video = "http://" + video
	}

	// Parse the url
	videoURL, err := url.Parse(video)
	if err != nil {
		panic(err)
	}

	// Extract video id for youtube
	if regexp.MustCompile("[/\\.]youtube\\.com/").FindString(video) != "" {
		videoID = videoURL.Query().Get("v")
	} else if regexp.MustCompile("[/\\.]youtu\\.be/").FindString(video) != "" {
		videoID = videoURL.EscapedPath()[1:]
	} else {
		return fmt.Sprintf(genericURL, video)
	}

	if videoURL.Query().Get("t") != "" {
		seek := 0 * time.Second
		timestr := videoURL.Query().Get("t")

		if value, err := strconv.ParseInt(timestr, 10, 64); err == nil {
			seek = time.Duration(value) * time.Second
		} else {
			seek, _ = time.ParseDuration(videoURL.Query().Get("t"))
		}

		// go back 15s for viewing convenience
		seek -= 15 * time.Second
		if seek < 0*time.Second {
			seek = 0 * time.Second
		}

		minutes, seconds := divmod(int64(seek.Seconds()), 60)
		hours, minutes := divmod(minutes, 60)

		return fmt.Sprintf(youtubeAndSeek, videoID, hours, minutes, seconds)
	}

	return fmt.Sprintf(youtube, videoID)
}
<commit_msg>Fix request<commit_after>package main

import (
	"fmt"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"
)

const youtube = `{"jsonrpc": "2.0", "method": "Player.Open", "params": {` +
	`"item": {"file": "plugin://plugin.video.youtube/?action=` +
	`play_video&videoid=%s"}}, "id" : 1}`
const youtubeAndSeek = `{"jsonrpc": "2.0","method": "Player.Open","params": {` +
	`"item": {"file": "plugin://plugin.video.youtube/?action=` +
	`play_video&videoid=%s"}, ` +
	`"options": ` +
	`{"resume": {"hours": %d, "minutes": %d, "seconds": %d}}` +
	`},"id": 1}`
const genericURL = `{"jsonrpc": "2.0", "method": "Player.open", "params": ` +
	`{"item": {"file": "%s"}}, "id": 1}`

func divmod(value int64, modulo int64) (int64, int64) {
	mod := (value % modulo)
	return (value - mod) / modulo, mod
}

func generatePayload(video string) string {
	videoID := ""

	if !strings.HasPrefix(video, "http") {
		video = "http://" + video
	}

	// Parse the url
	videoURL, err := url.Parse(video)
	if err != nil {
		panic(err)
	}

	// Extract video id for youtube
	if regexp.MustCompile("[/\\.]youtube\\.com/").FindString(video) != "" {
		videoID = videoURL.Query().Get("v")
	} else if regexp.MustCompile("[/\\.]youtu\\.be/").FindString(video) != "" {
		videoID = videoURL.EscapedPath()[1:]
	} else {
		return fmt.Sprintf(genericURL, video)
	}

	if videoURL.Query().Get("t") != "" {
		seek := 0 * time.Second
		timestr := videoURL.Query().Get("t")

		if value, err := strconv.ParseInt(timestr, 10, 64); err == nil {
			seek = time.Duration(value) * time.Second
		} else {
			seek, _ = time.ParseDuration(videoURL.Query().Get("t"))
		}

		// go back 15s for viewing convenience
		seek -= 15 * time.Second
		if seek < 0*time.Second {
			seek = 0 * time.Second
		}

		minutes, seconds := divmod(int64(seek.Seconds()), 60)
		hours, minutes := divmod(minutes, 60)

		return fmt.Sprintf(youtubeAndSeek, videoID, hours, minutes, seconds)
	}

	return fmt.Sprintf(youtube, videoID)
}
<|endoftext|>


<commit_before>package sudoku

import (
	"math/rand"
)

type instructionType int

const (
	INSERT = iota
	GET
	DECREMENT_ACTIVE
)

type instruction struct {
	result          chan interface{}
	instructionType instructionType
	item            interface{}
	probability     float32
}

type stackItem struct {
	item interface{}
	next *stackItem
}

type ChanSyncedStack struct {
	*SyncedStack
	DefaultProbability float32
	Output             chan interface{}
	doneChan           chan bool
}

type SyncedStack struct {
	closed         bool
	instructions   chan instruction
	numItems       int
	numActiveItems int
	firstItem      *stackItem
}

func NewChanSyncedStack(doneChan chan bool) *ChanSyncedStack {
	//WARNING: doneChan should be a buffered channel or else you cloud get deadlock!
	result := &ChanSyncedStack{&SyncedStack{false, make(chan instruction), 0, 0, nil}, 1.0, make(chan interface{}, 1), doneChan}
	go result.workLoop()
	return result
}

func NewSyncedStack() *SyncedStack {
	stack := &SyncedStack{false, make(chan instruction), 0, 0, nil}
	go stack.workLoop()
	return stack
}

func (self *SyncedStack) Length() int {
	return self.numItems
}

func (self *SyncedStack) NumActiveItems() int {
	return self.numActiveItems
}

func (self *SyncedStack) IsDone() bool {
	return self.numItems == 0 && self.numActiveItems == 0
}

func (self *ChanSyncedStack) Dispose() {
	self.SyncedStack.Dispose()
	close(self.Output)
	self.doneChan <- true
}

func (self *SyncedStack) Dispose() {
	//TODO: do we need to close out anything else here?
	//TODO: the work loops, when they notice the channel is closed, should probably exit.
	self.closed = true
	close(self.instructions)
}

func (self *SyncedStack) workLoop() {
	for {
		instruction, ok := <-self.instructions
		if ok {
			self.processInstruction(instruction)
		}
	}
}

func (self *ChanSyncedStack) workLoop() {

	//This workloop is complicated.
	//If we ahve an item and there's room in the Output channel, we ALWAYS want to do that.
	// But if there's not room in the channel, we don't want to just wait for an instruction--
	// because it's possible that the Output is emptied before we get another instruction.
	// So try first to fill output, then try either.

	var instruction instruction
	var ok bool
	for {
		if self.numItems > 0 {
			wrappedItem, previous := self.doSelect(self.DefaultProbability)
			select {
			case self.Output <- wrappedItem.item:
				self.doExtract(wrappedItem, previous)
			default:
				select {
				case self.Output <- wrappedItem.item:
					self.doExtract(wrappedItem, previous)
				case instruction, ok = <-self.instructions:
					if ok {
						self.processInstruction(instruction)
					}
				}
			}
		} else {
			instruction, ok = <-self.instructions
			if ok {
				self.processInstruction(instruction)
			}
		}
	}
}

func (self *SyncedStack) processInstruction(instruction instruction) {
	switch instruction.instructionType {
	case INSERT:
		self.doInsert(instruction.item)
		instruction.result <- nil
	case GET:
		wrappedItem, previous := self.doSelect(instruction.probability)
		instruction.result <- self.doExtract(wrappedItem, previous)
	case DECREMENT_ACTIVE:
		self.doDecrementActive()
		instruction.result <- nil
	}
	//Drop other instructions on the floor for now.
}

func (self *ChanSyncedStack) ItemDone() {
	self.SyncedStack.ItemDone()
	if self.IsDone() {
		self.Dispose()
	}
}

func (self *SyncedStack) ItemDone() {
	if self.closed {
		return
	}
	result := make(chan interface{})
	self.instructions <- instruction{result, DECREMENT_ACTIVE, nil, 0.0}
	<-result
	return
}

func (self *SyncedStack) Insert(item interface{}) {
	if self.closed {
		return
	}
	result := make(chan interface{})
	self.instructions <- instruction{result, INSERT, item, 0.0}
	<-result
	return
}

func (self *ChanSyncedStack) Pop() interface{} {
	//You must use output for a ChanSyncedStack
	return nil
}

func (self *SyncedStack) Pop() interface{} {
	//Gets the last item on the stack.
	return self.Get(1.0)
}

func (self *ChanSyncedStack) Get(probability float32) interface{} {
	//You must use output for a ChanSyncedStack
	return nil
}

func (self *SyncedStack) Get(probability float32) interface{} {
	if self.closed {
		return nil
	}
	//Working from the back, will take each item with probability probability, else move to the next item in the stack.
	result := make(chan interface{})
	self.instructions <- instruction{result, GET, nil, probability}
	return <-result
}

func (self *SyncedStack) doDecrementActive() {
	//May only be called from workLoop.
	if self.numActiveItems > 0 {
		self.numActiveItems--
	}
}

func (self *SyncedStack) doInsert(item interface{}) {
	//May only be called from workLoop
	wrappedItem := &stackItem{item, self.firstItem}
	self.firstItem = wrappedItem
	self.numItems++
}

func (self *SyncedStack) doSelect(probability float32) (item *stackItem, previous *stackItem) {

	var lastItem *stackItem
	var lastLastItem *stackItem

	wrappedItem := self.firstItem

	for wrappedItem != nil {
		if rand.Float32() < probability {
			return wrappedItem, lastItem
		}
		lastLastItem = lastItem
		lastItem = wrappedItem
		wrappedItem = wrappedItem.next
	}

	return lastItem, lastLastItem
}

func (self *SyncedStack) doExtract(item *stackItem, previous *stackItem) interface{} {
	//may only be called from within workLoop
	//Called when we've decided we ware going to take the item.
	if item == nil {
		return nil
	}
	self.numActiveItems++
	self.numItems--
	if previous != nil {
		previous.next = item.next
	} else {
		self.firstItem = item.next
	}
	return item.item
}
<commit_msg>TESTS FAIL. Jiggered how we exit work loops when the instruction queue is closed.<commit_after>package sudoku

import (
	"math/rand"
)

type instructionType int

const (
	INSERT = iota
	GET
	DECREMENT_ACTIVE
)

type instruction struct {
	result          chan interface{}
	instructionType instructionType
	item            interface{}
	probability     float32
}

type stackItem struct {
	item interface{}
	next *stackItem
}

type ChanSyncedStack struct {
	*SyncedStack
	DefaultProbability float32
	Output             chan interface{}
	doneChan           chan bool
}

type SyncedStack struct {
	closed         bool
	instructions   chan instruction
	numItems       int
	numActiveItems int
	firstItem      *stackItem
}

func NewChanSyncedStack(doneChan chan bool) *ChanSyncedStack {
	//WARNING: doneChan should be a buffered channel or else you cloud get deadlock!
	result := &ChanSyncedStack{&SyncedStack{false, make(chan instruction), 0, 0, nil}, 1.0, make(chan interface{}, 1), doneChan}
	go result.workLoop()
	return result
}

func NewSyncedStack() *SyncedStack {
	stack := &SyncedStack{false, make(chan instruction), 0, 0, nil}
	go stack.workLoop()
	return stack
}

func (self *SyncedStack) Length() int {
	return self.numItems
}

func (self *SyncedStack) NumActiveItems() int {
	return self.numActiveItems
}

func (self *SyncedStack) IsDone() bool {
	return self.numItems == 0 && self.numActiveItems == 0
}

func (self *ChanSyncedStack) Dispose() {
	self.SyncedStack.Dispose()
	close(self.Output)
	self.doneChan <- true
}

func (self *SyncedStack) Dispose() {
	//TODO: do we need to close out anything else here?
	//TODO: the work loops, when they notice the channel is closed, should probably exit.
	self.closed = true
	close(self.instructions)
}

func (self *SyncedStack) workLoop() {
	for {
		instruction, ok := <-self.instructions
		if !ok {
			return
		}
		self.processInstruction(instruction)
	}
}

func (self *ChanSyncedStack) workLoop() {

	//This workloop is complicated.
	//If we ahve an item and there's room in the Output channel, we ALWAYS want to do that.
	// But if there's not room in the channel, we don't want to just wait for an instruction--
	// because it's possible that the Output is emptied before we get another instruction.
	// So try first to fill output, then try either.

	var instruction instruction
	var ok bool
	for {
		if self.numItems > 0 {
			wrappedItem, previous := self.doSelect(self.DefaultProbability)
			select {
			case self.Output <- wrappedItem.item:
				self.doExtract(wrappedItem, previous)
			default:
				select {
				case self.Output <- wrappedItem.item:
					self.doExtract(wrappedItem, previous)
				case instruction, ok = <-self.instructions:
					if !ok {
						return
					}
					self.processInstruction(instruction)
				}
			}
		} else {
			instruction, ok = <-self.instructions
			if !ok {
				return
			}
			self.processInstruction(instruction)
		}
	}
}

func (self *SyncedStack) processInstruction(instruction instruction) {
	switch instruction.instructionType {
	case INSERT:
		self.doInsert(instruction.item)
		instruction.result <- nil
	case GET:
		wrappedItem, previous := self.doSelect(instruction.probability)
		instruction.result <- self.doExtract(wrappedItem, previous)
	case DECREMENT_ACTIVE:
		self.doDecrementActive()
		instruction.result <- nil
	}
	//Drop other instructions on the floor for now.
}

func (self *ChanSyncedStack) ItemDone() {
	self.SyncedStack.ItemDone()
	if self.IsDone() {
		self.Dispose()
	}
}

func (self *SyncedStack) ItemDone() {
	if self.closed {
		return
	}
	result := make(chan interface{})
	self.instructions <- instruction{result, DECREMENT_ACTIVE, nil, 0.0}
	<-result
	return
}

func (self *SyncedStack) Insert(item interface{}) {
	if self.closed {
		return
	}
	result := make(chan interface{})
	self.instructions <- instruction{result, INSERT, item, 0.0}
	<-result
	return
}

func (self *ChanSyncedStack) Pop() interface{} {
	//You must use output for a ChanSyncedStack
	return nil
}

func (self *SyncedStack) Pop() interface{} {
	//Gets the last item on the stack.
	return self.Get(1.0)
}

func (self *ChanSyncedStack) Get(probability float32) interface{} {
	//You must use output for a ChanSyncedStack
	return nil
}

func (self *SyncedStack) Get(probability float32) interface{} {
	if self.closed {
		return nil
	}
	//Working from the back, will take each item with probability probability, else move to the next item in the stack.
	result := make(chan interface{})
	self.instructions <- instruction{result, GET, nil, probability}
	return <-result
}

func (self *SyncedStack) doDecrementActive() {
	//May only be called from workLoop.
	if self.numActiveItems > 0 {
		self.numActiveItems--
	}
}

func (self *SyncedStack) doInsert(item interface{}) {
	//May only be called from workLoop
	wrappedItem := &stackItem{item, self.firstItem}
	self.firstItem = wrappedItem
	self.numItems++
}

func (self *SyncedStack) doSelect(probability float32) (item *stackItem, previous *stackItem) {

	var lastItem *stackItem
	var lastLastItem *stackItem

	wrappedItem := self.firstItem

	for wrappedItem != nil {
		if rand.Float32() < probability {
			return wrappedItem, lastItem
		}
		lastLastItem = lastItem
		lastItem = wrappedItem
		wrappedItem = wrappedItem.next
	}

	return lastItem, lastLastItem
}

func (self *SyncedStack) doExtract(item *stackItem, previous *stackItem) interface{} {
	//may only be called from within workLoop
	//Called when we've decided we ware going to take the item.
	if item == nil {
		return nil
	}
	self.numActiveItems++
	self.numItems--
	if previous != nil {
		previous.next = item.next
	} else {
		self.firstItem = item.next
	}
	return item.item
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"strconv"
	"strings"
)

func main() {
	port := fmt.Sprintf(":%s", os.Getenv("PORT"))
	log.Printf("Starting on port %s ....", port)
	http.ListenAndServe(port, proxy())
}

func proxy() *httputil.ReverseProxy {
	proxyUrl, _ := url.Parse(os.Getenv("PROXY_URL"))
	proxy := httputil.NewSingleHostReverseProxy(proxyUrl)
	director := proxy.Director
	proxy.Director = func(req *http.Request) {
		log.Printf("%+v", req)
		req.Header.Set("X-Proxy-Host", req.Host)
		req.Host = proxyUrl.Host
		req.SetBasicAuth(os.Getenv("USER"), os.Getenv("PASS"))
		director(req)
		log.Printf("%s -> %s", req.RequestURI, req.URL)
	}
	proxy.Transport = &proxyTransport{}
	return proxy
}

type proxyTransport struct{}

func (t *proxyTransport) RoundTrip(request *http.Request) (*http.Response, error) {
	response, err := http.DefaultTransport.RoundTrip(request)

	if response.Header.Get("Content-Type") != "audio/mpeg" {
		body := new(bytes.Buffer)
		body.ReadFrom(response.Body)

		bod := strings.Replace(body.String(), request.Host, request.Header.Get("X-Proxy-Host"), -1)
		buf := bytes.NewBufferString(bod)
		contentLength := strconv.Itoa(buf.Len())

		response.Body = ioutil.NopCloser(buf)
		response.Header.Set("Content-Length", contentLength)
	}

	return response, err
}
<commit_msg>additional debug logging<commit_after>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"strconv"
	"strings"
)

func main() {
	port := fmt.Sprintf(":%s", os.Getenv("PORT"))
	log.Printf("Starting on port %s ....", port)
	http.ListenAndServe(port, proxy())
}

func proxy() *httputil.ReverseProxy {
	proxyUrl, _ := url.Parse(os.Getenv("PROXY_URL"))
	proxy := httputil.NewSingleHostReverseProxy(proxyUrl)
	director := proxy.Director
	proxy.Director = func(req *http.Request) {
		log.Printf("%+v", req)
		req.Header.Set("X-Proxy-Host", req.Host)
		req.Host = proxyUrl.Host
		req.SetBasicAuth(os.Getenv("USER"), os.Getenv("PASS"))
		director(req)
		log.Printf("%s -> %s", req.RequestURI, req.URL)
	}
	proxy.Transport = &proxyTransport{}
	return proxy
}

type proxyTransport struct{}

func (t *proxyTransport) RoundTrip(request *http.Request) (*http.Response, error) {
	response, err := http.DefaultTransport.RoundTrip(request)

	if response.Header.Get("Content-Type") != "audio/mpeg" {
		body := new(bytes.Buffer)
		body.ReadFrom(response.Body)

		log.Printf("%+v", request.Header)

		bod := strings.Replace(body.String(), request.Host, request.Header.Get("X-Proxy-Host"), -1)
		buf := bytes.NewBufferString(bod)
		contentLength := strconv.Itoa(buf.Len())

		response.Body = ioutil.NopCloser(buf)
		response.Header.Set("Content-Length", contentLength)
	}

	return response, err
}
<|endoftext|>