<commit_before>package driver

import (
	"encoding/json"
	"fmt"
	"log"
	"os/exec"
	"regexp"
	"strings"

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

	"github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/nomad/structs"
)

var (
	reDockerVersion = regexp.MustCompile("Docker version ([\\d\\.]+),.+")
	reDockerSha     = regexp.MustCompile("^[a-f0-9]{64}$")
)

type DockerDriver struct {
	DriverContext
}

type dockerPID struct {
	ImageID     string
	ContainerID string
}

type dockerHandle struct {
	logger      *log.Logger
	imageID     string
	containerID string
	waitCh      chan error
	doneCh      chan struct{}
}

func NewDockerDriver(ctx *DriverContext) Driver {
	return &DockerDriver{*ctx}
}

func (d *DockerDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) {
	outBytes, err := exec.Command("docker", "-v").Output()
	out := strings.TrimSpace(string(outBytes))
	if err != nil {
		return false, nil
	}

	matches := reDockerVersion.FindStringSubmatch(out)
	if len(matches) != 2 {
		return false, fmt.Errorf("Unable to parse docker version string: %#v", matches)
	}

	node.Attributes["driver.docker"] = "true"
	node.Attributes["driver.docker.version"] = matches[1]

	return true, nil
}

// containerOptionsForTask initializes a struct needed to call
// docker.client.CreateContainer()
func containerOptionsForTask(task *structs.Task, logger *log.Logger) docker.CreateContainerOptions {
	if task.Resources == nil {
		panic("task.Resources is nil and we can't constrain resource usage. We shouldn't have been able to schedule this in the first place.")
	}

	containerConfig := &docker.HostConfig{
		// Convert MB to bytes. This is an absolute value.
		//
		// This value represents the total amount of memory a process can use.
		// Swap is added to total memory and is managed by the OS, not docker.
		// Since this may cause other processes to swap and cause system
		// instability, we will simply not use swap.
		//
		// See: https://www.kernel.org/doc/Documentation/cgroups/memory.txt
		Memory:     int64(task.Resources.MemoryMB) * 1024 * 1024,
		MemorySwap: -1,
		// Convert Mhz to shares. This is a relative value.
		//
		// There are two types of CPU limiters available: Shares and Quotas. A
		// Share allows a particular process to have a proportion of CPU time
		// relative to other processes; 1024 by default. A CPU Quota is enforced
		// over a Period of time and is a HARD limit on the amount of CPU time a
		// process can use. Processes with quotas cannot burst, while processes
		// with shares can, so we'll use shares.
		//
		// The simplest scale is 1 share to 1 MHz so 1024 = 1GHz. This means any
		// given process will have at least that amount of resources, but likely
		// more since it is (probably) rare that the machine will run at 100%
		// CPU. This scale will cease to work if a node is overprovisioned.
		//
		// See:
		//  - https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
		//  - https://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt
		//
		// TODO push CPU share up to the task group level. We can retain the
		// docker-specific implementation for very fine-grained control but the
		// usage semantics will change once we have this capability in task
		// groups.
		CPUShares: int64(task.Resources.CPU),
	}

	logger.Printf("[DEBUG] driver.docker: using %d bytes memory for %s", containerConfig.Memory, task.Config["image"])
	logger.Printf("[DEBUG] driver.docker: using %d cpu shares for %s", containerConfig.CPUShares, task.Config["image"])

	return docker.CreateContainerOptions{
		Config: &docker.Config{
			Image: task.Config["image"],
		},
		HostConfig: containerConfig,
	}
}

func (d *DockerDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) {
	// Get the image from config
	image, ok := task.Config["image"]
	if !ok || image == "" {
		return nil, fmt.Errorf("Image not specified")
	}
	if task.Resources == nil {
		return nil, fmt.Errorf("Resources are not specified")
	}
	if task.Resources.MemoryMB == 0 {
		return nil, fmt.Errorf("Memory limit cannot be zero")
	}
	if task.Resources.CPU == 0 {
		return nil.fmt.Errorf("CPU limit cannot be zero")
	}

	// Initialize docker API client
	dockerEndpoint := d.config.ReadDefault("docker.endpoint", "unix:///var/run/docker.sock")
	client, err := docker.NewClient(dockerEndpoint)
	if err != nil {
		return nil, fmt.Errorf("Failed to connect to docker.endpoint (%s): %s", dockerEndpoint, err)
	}

	// Download the image
	pull, err := exec.Command("docker", "pull", image).CombinedOutput()
	if err != nil {
		d.logger.Printf("[ERROR] driver.docker: pulling container %s", pull)
		return nil, fmt.Errorf("Failed to pull `%s`: %s", image, err)
	}
	d.logger.Printf("[DEBUG] driver.docker: docker pull %s:\n%s", image, pull)

	// Get the image ID (sha256). We need to keep track of this in case another
	// process pulls down a newer version of the image.
	imageIDBytes, err := exec.Command("docker", "images", "-q", "--no-trunc", image).CombinedOutput()
	imageID := strings.TrimSpace(string(imageIDBytes))
	if err != nil || imageID == "" {
		d.logger.Printf("[ERROR] driver.docker: getting image id %s", imageID)
		return nil, fmt.Errorf("Failed to determine image id for `%s`: %s", image, err)
	}
	if !reDockerSha.MatchString(imageID) {
		return nil, fmt.Errorf("Image id not in expected format (sha256); found %s", imageID)
	}
	d.logger.Printf("[DEBUG] driver.docker: using image %s", imageID)
	d.logger.Printf("[INFO] driver.docker: downloaded image %s as %s", image, imageID)

	// Create a container
	container, err := client.CreateContainer(containerOptionsForTask(task, d.logger))
	if err != nil {
		d.logger.Printf("[ERROR] driver.docker: %s", err)
		return nil, fmt.Errorf("Failed to create container from image %s", image)
	}
	if !reDockerSha.MatchString(container.ID) {
		return nil, fmt.Errorf("Container id not in expected format (sha256); found %s", container.ID)
	}
	d.logger.Printf("[INFO] driver.docker: created container %s", container.ID)

	// Start the container
	startBytes, err := exec.Command("docker", "start", container.ID).CombinedOutput()
	if err != nil {
		d.logger.Printf("[ERROR] driver.docker: starting container %s", strings.TrimSpace(string(startBytes)))
		return nil, fmt.Errorf("Failed to start container %s", container.ID)
	}
	d.logger.Printf("[INFO] driver.docker: started container %s", container.ID)

	// Return a driver handle
	h := &dockerHandle{
		logger:      d.logger,
		imageID:     imageID,
		containerID: container.ID,
		doneCh:      make(chan struct{}),
		waitCh:      make(chan error, 1),
	}
	go h.run()
	return h, nil
}

func (d *DockerDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) {
	// Split the handle
	pidBytes := []byte(strings.TrimPrefix(handleID, "DOCKER:"))
	pid := &dockerPID{}
	err := json.Unmarshal(pidBytes, pid)
	if err != nil {
		return nil, fmt.Errorf("Failed to parse handle '%s': %v", handleID, err)
	}
	d.logger.Printf("[INFO] driver.docker: re-attaching to docker process: %s", handleID)

	// Look for a running container with this ID
	// docker ps does not return an exit code if there are no matching processes
	// so we have to read the output and compare it to our known containerID
	psBytes, err := exec.Command("docker", "ps", "-q", "--no-trunc",
		fmt.Sprintf("-f=id=%s", pid.ContainerID)).Output()
	ps := strings.TrimSpace(string(psBytes))
	if err != nil {
		return nil, fmt.Errorf("Failed to find container %s: %v", pid.ContainerID, err)
	} else if ps != pid.ContainerID {
		return nil, fmt.Errorf("Container ID does not match; expected %s found %s", pid.ContainerID, ps)
	}

	// Return a driver handle
	h := &dockerHandle{
		logger:      d.logger,
		imageID:     pid.ImageID,
		containerID: pid.ContainerID,
		doneCh:      make(chan struct{}),
		waitCh:      make(chan error, 1),
	}
	go h.run()
	return h, nil
}

func (h *dockerHandle) ID() string {
	// Return a handle to the PID
	pid := dockerPID{
		ImageID:     h.imageID,
		ContainerID: h.containerID,
	}
	data, err := json.Marshal(pid)
	if err != nil {
		d.logger.Printf("[ERROR] driver.docker: failed to marshal docker PID to JSON: %s", err)
	}
	return fmt.Sprintf("DOCKER:%s", string(data))
}

func (h *dockerHandle) WaitCh() chan error {
	return h.waitCh
}

func (h *dockerHandle) Update(task *structs.Task) error {
	// Update is not possible
	return nil
}

// Kill is used to terminate the task. This uses docker stop -t 5
func (h *dockerHandle) Kill() error {
	// Stop the container
	stop, err := exec.Command("docker", "stop", "-t", "5", h.containerID).CombinedOutput()
	if err != nil {
		log.Printf("[ERROR] driver.docker: stopping container %s", stop)
		return fmt.Errorf("Failed to stop container %s: %s", h.containerID, err)
	}
	log.Printf("[INFO] driver.docker: stopped container %s", h.containerID)

	// Cleanup container
	rmContainer, err := exec.Command("docker", "rm", h.containerID).CombinedOutput()
	if err != nil {
		log.Printf("[ERROR] driver.docker: removing container %s", rmContainer)
		return fmt.Errorf("Failed to remove container %s: %s", h.containerID, err)
	}
	log.Printf("[INFO] driver.docker: removed container %s", h.containerID)

	// Cleanup image. This operation may fail if the image is in use by another
	// job. That is OK. Will we log a message but continue.
	_, err = exec.Command("docker", "rmi", h.imageID).CombinedOutput()
	if err != nil {
		log.Printf("[WARN] driver.docker: failed to remove image %s; it may still be in use", h.imageID)
	} else {
		log.Printf("[INFO] driver.docker: removed image %s", h.imageID)
	}
	return nil
}

func (h *dockerHandle) run() {
	// Wait for it...
	waitBytes, err := exec.Command("docker", "wait", h.containerID).Output()
	if err != nil {
		h.logger.Printf("[ERROR] driver.docker: unable to wait for %s; container already terminated", h.containerID)
	}
	wait := strings.TrimSpace(string(waitBytes))

	// If the container failed, try to get the last 10 lines of logs for our
	// error message.
	if wait != "0" {
		var logsBytes []byte
		logsBytes, err = exec.Command("docker", "logs", "--tail=10", h.containerID).Output()
		logs := string(logsBytes)
		if err == nil {
			err = fmt.Errorf("%s", logs)
		}
	}

	close(h.doneCh)
	if err != nil {
		h.waitCh <- err
	}
	close(h.waitCh)
}
<commit_msg>Fix typos<commit_after>package driver

import (
	"encoding/json"
	"fmt"
	"log"
	"os/exec"
	"regexp"
	"strings"

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

	"github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/nomad/structs"
)

var (
	reDockerVersion = regexp.MustCompile("Docker version ([\\d\\.]+),.+")
	reDockerSha     = regexp.MustCompile("^[a-f0-9]{64}$")
)

type DockerDriver struct {
	DriverContext
}

type dockerPID struct {
	ImageID     string
	ContainerID string
}

type dockerHandle struct {
	logger      *log.Logger
	imageID     string
	containerID string
	waitCh      chan error
	doneCh      chan struct{}
}

func NewDockerDriver(ctx *DriverContext) Driver {
	return &DockerDriver{*ctx}
}

func (d *DockerDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) {
	outBytes, err := exec.Command("docker", "-v").Output()
	out := strings.TrimSpace(string(outBytes))
	if err != nil {
		return false, nil
	}

	matches := reDockerVersion.FindStringSubmatch(out)
	if len(matches) != 2 {
		return false, fmt.Errorf("Unable to parse docker version string: %#v", matches)
	}

	node.Attributes["driver.docker"] = "true"
	node.Attributes["driver.docker.version"] = matches[1]

	return true, nil
}

// containerOptionsForTask initializes a struct needed to call
// docker.client.CreateContainer()
func containerOptionsForTask(task *structs.Task, logger *log.Logger) docker.CreateContainerOptions {
	if task.Resources == nil {
		panic("task.Resources is nil and we can't constrain resource usage. We shouldn't have been able to schedule this in the first place.")
	}

	containerConfig := &docker.HostConfig{
		// Convert MB to bytes. This is an absolute value.
		//
		// This value represents the total amount of memory a process can use.
		// Swap is added to total memory and is managed by the OS, not docker.
		// Since this may cause other processes to swap and cause system
		// instability, we will simply not use swap.
		//
		// See: https://www.kernel.org/doc/Documentation/cgroups/memory.txt
		Memory:     int64(task.Resources.MemoryMB) * 1024 * 1024,
		MemorySwap: -1,
		// Convert Mhz to shares. This is a relative value.
		//
		// There are two types of CPU limiters available: Shares and Quotas. A
		// Share allows a particular process to have a proportion of CPU time
		// relative to other processes; 1024 by default. A CPU Quota is enforced
		// over a Period of time and is a HARD limit on the amount of CPU time a
		// process can use. Processes with quotas cannot burst, while processes
		// with shares can, so we'll use shares.
		//
		// The simplest scale is 1 share to 1 MHz so 1024 = 1GHz. This means any
		// given process will have at least that amount of resources, but likely
		// more since it is (probably) rare that the machine will run at 100%
		// CPU. This scale will cease to work if a node is overprovisioned.
		//
		// See:
		//  - https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
		//  - https://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt
		//
		// TODO push CPU share up to the task group level. We can retain the
		// docker-specific implementation for very fine-grained control but the
		// usage semantics will change once we have this capability in task
		// groups.
		CPUShares: int64(task.Resources.CPU),
	}

	logger.Printf("[DEBUG] driver.docker: using %d bytes memory for %s", containerConfig.Memory, task.Config["image"])
	logger.Printf("[DEBUG] driver.docker: using %d cpu shares for %s", containerConfig.CPUShares, task.Config["image"])

	return docker.CreateContainerOptions{
		Config: &docker.Config{
			Image: task.Config["image"],
		},
		HostConfig: containerConfig,
	}
}

func (d *DockerDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) {
	// Get the image from config
	image, ok := task.Config["image"]
	if !ok || image == "" {
		return nil, fmt.Errorf("Image not specified")
	}
	if task.Resources == nil {
		return nil, fmt.Errorf("Resources are not specified")
	}
	if task.Resources.MemoryMB == 0 {
		return nil, fmt.Errorf("Memory limit cannot be zero")
	}
	if task.Resources.CPU == 0 {
		return nil, fmt.Errorf("CPU limit cannot be zero")
	}

	// Initialize docker API client
	dockerEndpoint := d.config.ReadDefault("docker.endpoint", "unix:///var/run/docker.sock")
	client, err := docker.NewClient(dockerEndpoint)
	if err != nil {
		return nil, fmt.Errorf("Failed to connect to docker.endpoint (%s): %s", dockerEndpoint, err)
	}

	// Download the image
	pull, err := exec.Command("docker", "pull", image).CombinedOutput()
	if err != nil {
		d.logger.Printf("[ERROR] driver.docker: pulling container %s", pull)
		return nil, fmt.Errorf("Failed to pull `%s`: %s", image, err)
	}
	d.logger.Printf("[DEBUG] driver.docker: docker pull %s:\n%s", image, pull)

	// Get the image ID (sha256). We need to keep track of this in case another
	// process pulls down a newer version of the image.
	imageIDBytes, err := exec.Command("docker", "images", "-q", "--no-trunc", image).CombinedOutput()
	imageID := strings.TrimSpace(string(imageIDBytes))
	if err != nil || imageID == "" {
		d.logger.Printf("[ERROR] driver.docker: getting image id %s", imageID)
		return nil, fmt.Errorf("Failed to determine image id for `%s`: %s", image, err)
	}
	if !reDockerSha.MatchString(imageID) {
		return nil, fmt.Errorf("Image id not in expected format (sha256); found %s", imageID)
	}
	d.logger.Printf("[DEBUG] driver.docker: using image %s", imageID)
	d.logger.Printf("[INFO] driver.docker: downloaded image %s as %s", image, imageID)

	// Create a container
	container, err := client.CreateContainer(containerOptionsForTask(task, d.logger))
	if err != nil {
		d.logger.Printf("[ERROR] driver.docker: %s", err)
		return nil, fmt.Errorf("Failed to create container from image %s", image)
	}
	if !reDockerSha.MatchString(container.ID) {
		return nil, fmt.Errorf("Container id not in expected format (sha256); found %s", container.ID)
	}
	d.logger.Printf("[INFO] driver.docker: created container %s", container.ID)

	// Start the container
	startBytes, err := exec.Command("docker", "start", container.ID).CombinedOutput()
	if err != nil {
		d.logger.Printf("[ERROR] driver.docker: starting container %s", strings.TrimSpace(string(startBytes)))
		return nil, fmt.Errorf("Failed to start container %s", container.ID)
	}
	d.logger.Printf("[INFO] driver.docker: started container %s", container.ID)

	// Return a driver handle
	h := &dockerHandle{
		logger:      d.logger,
		imageID:     imageID,
		containerID: container.ID,
		doneCh:      make(chan struct{}),
		waitCh:      make(chan error, 1),
	}
	go h.run()
	return h, nil
}

func (d *DockerDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) {
	// Split the handle
	pidBytes := []byte(strings.TrimPrefix(handleID, "DOCKER:"))
	pid := &dockerPID{}
	err := json.Unmarshal(pidBytes, pid)
	if err != nil {
		return nil, fmt.Errorf("Failed to parse handle '%s': %v", handleID, err)
	}
	d.logger.Printf("[INFO] driver.docker: re-attaching to docker process: %s", handleID)

	// Look for a running container with this ID
	// docker ps does not return an exit code if there are no matching processes
	// so we have to read the output and compare it to our known containerID
	psBytes, err := exec.Command("docker", "ps", "-q", "--no-trunc",
		fmt.Sprintf("-f=id=%s", pid.ContainerID)).Output()
	ps := strings.TrimSpace(string(psBytes))
	if err != nil {
		return nil, fmt.Errorf("Failed to find container %s: %v", pid.ContainerID, err)
	} else if ps != pid.ContainerID {
		return nil, fmt.Errorf("Container ID does not match; expected %s found %s", pid.ContainerID, ps)
	}

	// Return a driver handle
	h := &dockerHandle{
		logger:      d.logger,
		imageID:     pid.ImageID,
		containerID: pid.ContainerID,
		doneCh:      make(chan struct{}),
		waitCh:      make(chan error, 1),
	}
	go h.run()
	return h, nil
}

func (h *dockerHandle) ID() string {
	// Return a handle to the PID
	pid := dockerPID{
		ImageID:     h.imageID,
		ContainerID: h.containerID,
	}
	data, err := json.Marshal(pid)
	if err != nil {
		h.logger.Printf("[ERROR] driver.docker: failed to marshal docker PID to JSON: %s", err)
	}
	return fmt.Sprintf("DOCKER:%s", string(data))
}

func (h *dockerHandle) WaitCh() chan error {
	return h.waitCh
}

func (h *dockerHandle) Update(task *structs.Task) error {
	// Update is not possible
	return nil
}

// Kill is used to terminate the task. This uses docker stop -t 5
func (h *dockerHandle) Kill() error {
	// Stop the container
	stop, err := exec.Command("docker", "stop", "-t", "5", h.containerID).CombinedOutput()
	if err != nil {
		log.Printf("[ERROR] driver.docker: stopping container %s", stop)
		return fmt.Errorf("Failed to stop container %s: %s", h.containerID, err)
	}
	log.Printf("[INFO] driver.docker: stopped container %s", h.containerID)

	// Cleanup container
	rmContainer, err := exec.Command("docker", "rm", h.containerID).CombinedOutput()
	if err != nil {
		log.Printf("[ERROR] driver.docker: removing container %s", rmContainer)
		return fmt.Errorf("Failed to remove container %s: %s", h.containerID, err)
	}
	log.Printf("[INFO] driver.docker: removed container %s", h.containerID)

	// Cleanup image. This operation may fail if the image is in use by another
	// job. That is OK. Will we log a message but continue.
	_, err = exec.Command("docker", "rmi", h.imageID).CombinedOutput()
	if err != nil {
		log.Printf("[WARN] driver.docker: failed to remove image %s; it may still be in use", h.imageID)
	} else {
		log.Printf("[INFO] driver.docker: removed image %s", h.imageID)
	}
	return nil
}

func (h *dockerHandle) run() {
	// Wait for it...
	waitBytes, err := exec.Command("docker", "wait", h.containerID).Output()
	if err != nil {
		h.logger.Printf("[ERROR] driver.docker: unable to wait for %s; container already terminated", h.containerID)
	}
	wait := strings.TrimSpace(string(waitBytes))

	// If the container failed, try to get the last 10 lines of logs for our
	// error message.
	if wait != "0" {
		var logsBytes []byte
		logsBytes, err = exec.Command("docker", "logs", "--tail=10", h.containerID).Output()
		logs := string(logsBytes)
		if err == nil {
			err = fmt.Errorf("%s", logs)
		}
	}

	close(h.doneCh)
	if err != nil {
		h.waitCh <- err
	}
	close(h.waitCh)
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"testing"
	"net/url"
)

func TestParseUrlQuery(t *testing.T) {
	request := "http://127.0.0.1:3000/announce?info_hash=QtA%C0%81%8D%C5GV%02%150%5D%2B%91%80a%BB%02%9A&peer_id=-lt0D20-s%081%8ER%D7%C9%15X%DB%DD%D2&key=602bcd6f&compact=1&port=6963&uploaded=0&downloaded=0&left=5448254&event=started"

	result := decodeQueryURL(request)
	if result["uploaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["uploaded"])
	}
	if result["port"][0] != "6963" {
		t.Fatalf("Expected 0, got %s", result["port"])
	}
	if result["downloaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["downloaded"])
	}
	if result["compact"][0] != "1" {
		t.Fatalf("Expected 0, got %s", result["compact"])
	}

}

func TestParseTorrentGetRequest(t *testing.T) {
	request := "http://127.0.0.1:3000/announce?info_hash=QtA%C0%81%8D%C5GV%02%150%5D%2B%91%80a%BB%02%9A&peer_id=-lt0D20-s%081%8ER%D7%C9%15X%DB%DD%D2&key=602bcd6f&compact=1&port=6963&uploaded=0&downloaded=0&left=5448254&event=started"

	result := decodeQueryURL(request)
	fmt.Println(result)
	if result["uploaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["uploaded"])
	}
	if result["port"][0] != "6963" {
		t.Fatalf("Expected 0, got %s", result["port"])
	}
	if result["downloaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["downloaded"])
	}
	if result["compact"][0] != "1" {
		t.Fatalf("Expected 0, got %s", result["compact"])
	}
}

func TestParseInfoHash(t *testing.T) {
	expectedResult := "4925623525306625313825326325633425313825396325383925316325396559732563382566346725376225623359253137"
	result := ParseInfoHash("I%b5%0f%18%2c%c4%18%9c%89%1c%9eYs%c8%f4g%7b%b3Y%17")

	if result != expectedResult {
		t.Fatalf("Expected %s, got %s", expectedResult, result)
	}
}

func TestGetIntFailEmptyKey(t *testing.T) {
	u, _ := url.Parse("http://google.com/")
	urlValues := u.Query()
	key := "testInt"

	expectedResult := 50
	result := GetInt(urlValues, key)

	if result == expectedResult {
		t.Fatalf("Expected %s, got %s", expectedResult, result)
	}
}

func TestGetInt(t *testing.T) {
	u, _ := url.Parse("http://google.com/?testInt=50")
	urlValues := u.Query()
	key := "testInt"

	expectedResult := 50
	result := GetInt(urlValues, key)

	if result != expectedResult {
		t.Fatalf("Expected %s, got %s", expectedResult, result)
	}
}

<commit_msg>Update server_test.go<commit_after>package server

import (
	"fmt"
	"testing"
	"net/url"
)

func TestParseUrlQuery(t *testing.T) {
	request := "http://127.0.0.1:3000/announce?info_hash=QtA%C0%81%8D%C5GV%02%150%5D%2B%91%80a%BB%02%9A&peer_id=-lt0D20-s%081%8ER%D7%C9%15X%DB%DD%D2&key=602bcd6f&compact=1&port=6963&uploaded=0&downloaded=0&left=5448254&event=started"

	result := decodeQueryURL(request)
	if result["uploaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["uploaded"])
	}
	if result["port"][0] != "6963" {
		t.Fatalf("Expected 0, got %s", result["port"])
	}
	if result["downloaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["downloaded"])
	}
	if result["compact"][0] != "1" {
		t.Fatalf("Expected 0, got %s", result["compact"])
	}

}

func TestParseTorrentGetRequest(t *testing.T) {
	request := "http://127.0.0.1:3000/announce?info_hash=QtA%C0%81%8D%C5GV%02%150%5D%2B%91%80a%BB%02%9A&peer_id=-lt0D20-s%081%8ER%D7%C9%15X%DB%DD%D2&key=602bcd6f&compact=1&port=6963&uploaded=0&downloaded=0&left=5448254&event=started"

	result := decodeQueryURL(request)
	fmt.Println(result)
	if result["uploaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["uploaded"])
	}
	if result["port"][0] != "6963" {
		t.Fatalf("Expected 0, got %s", result["port"])
	}
	if result["downloaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["downloaded"])
	}
	if result["compact"][0] != "1" {
		t.Fatalf("Expected 0, got %s", result["compact"])
	}
}

func TestParseInfoHash(t *testing.T) {
	expectedResult := "4925623525306625313825326325633425313825396325383925316325396559732563382566346725376225623359253137"
	result := ParseInfoHash("I%b5%0f%18%2c%c4%18%9c%89%1c%9eYs%c8%f4g%7b%b3Y%17")

	if result != expectedResult {
		t.Fatalf("Expected %s, got %s", expectedResult, result)
	}
}

func TestGetIntFailEmptyKey(t *testing.T) {
	u, _ := url.Parse("http://google.com/")
	urlValues := u.Query()
	key := "testInt"

	expectedResult := 50
	result, err := GetInt(urlValues, key)
	if err != nil {
		t.Fatalf("Failed to GetInt() with %v", err)
	}

	if result == expectedResult {
		t.Fatalf("Expected %s, got %s", expectedResult, result)
	}
}

func TestGetInt(t *testing.T) {
	u, _ := url.Parse("http://google.com/?testInt=50")
	urlValues := u.Query()
	key := "testInt"

	expectedResult := 50
	result, err := GetInt(urlValues, key)
	if err != nil {
		t.Fatalf("Failed to GetInt() with %v", err)
	}

	if result != expectedResult || err != nil {
		t.Fatalf("Expected %s, got %s", expectedResult, result)
	}
}

<|endoftext|>


<commit_before>package driver

import (
	"fmt"
	"log"
	"strings"
	"sync"

	"github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/client/fingerprint"
	"github.com/hashicorp/nomad/nomad/structs"
)

// BuiltinDrivers contains the built in registered drivers
// which are available for allocation handling
var BuiltinDrivers = map[string]Factory{
	"docker": NewDockerDriver,
	"exec":   NewExecDriver,
	"java":   NewJavaDriver,
	"qemu":   NewQemuDriver,
}

// NewDriver is used to instantiate and return a new driver
// given the name and a logger
func NewDriver(name string, ctx *DriverContext) (Driver, error) {
	// Lookup the factory function
	factory, ok := BuiltinDrivers[name]
	if !ok {
		return nil, fmt.Errorf("unknown driver '%s'", name)
	}

	// Instantiate the driver
	f := factory(ctx)
	return f, nil
}

// Factory is used to instantiate a new Driver
type Factory func(*DriverContext) Driver

// Driver is used for execution of tasks. This allows Nomad
// to support many pluggable implementations of task drivers.
// Examples could include LXC, Docker, Qemu, etc.
type Driver interface {
	// Drivers must support the fingerprint interface for detection
	fingerprint.Fingerprint

	// Start is used to being task execution
	Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error)

	// Open is used to re-open a handle to a task
	Open(ctx *ExecContext, handleID string) (DriverHandle, error)
}

// DriverContext is a means to inject dependencies such as loggers, configs, and
// node attributes into a Driver without having to change the Driver interface
// each time we do it. Used in conjection with Factory, above.
type DriverContext struct {
	config *config.Config
	logger *log.Logger
	node   *structs.Node
}

// NewDriverContext initializes a new DriverContext with the specified fields.
// This enables other packages to create DriverContexts but keeps the fields
// private to the driver. If we want to change this later we can gorename all of
// the fields in DriverContext.
func NewDriverContext(config *config.Config, node *structs.Node, logger *log.Logger) *DriverContext {
	return &DriverContext{
		config: config,
		node:   node,
		logger: logger,
	}
}

// DriverHandle is an opaque handle into a driver used for task
// manipulation
type DriverHandle interface {
	// Returns an opaque handle that can be used to re-open the handle
	ID() string

	// WaitCh is used to return a channel used wait for task completion
	WaitCh() chan error

	// Update is used to update the task if possible
	Update(task *structs.Task) error

	// Kill is used to stop the task
	Kill() error
}

// ExecContext is shared between drivers within an allocation
type ExecContext struct {
	sync.Mutex

	// AllocDir is the directory used for storing any state
	// of this allocation. It will be purged on alloc destroy.
	AllocDir string
}

// NewExecContext is used to create a new execution context
func NewExecContext() *ExecContext {
	ctx := &ExecContext{}
	return ctx
}

// PopulateEnvironment converts exec context and task configuration into
// environment variables so they can be passed along to a driver.
//
// The output is a list of strings with NAME=value pairs.
func PopulateEnvironment(ctx *ExecContext, task *structs.Task) []string {
	env := []string{}

	env = append(env, fmt.Sprintf("NOMAD_ALLOC_DIR=%s", ctx.AllocDir))

	if task.Resources != nil {
		env = append(env, fmt.Sprintf("NOMAD_MEMORY_LIMIT=%d", task.Resources.MemoryMB))
		env = append(env, fmt.Sprintf("NOMAD_CPU_LIMIT=%d", task.Resources.CPU))

		// Named Ports
		if len(task.Resources.Networks) > 0 {
			network := task.Resources.Networks[0]

			env = append(env, fmt.Sprintf("NOMAD_HOST_IP=%d", network.IP))
			for idx, port := range network.ListDynamicPorts() {
				env = append(env, fmt.Sprintf("NOMAD_PORT_%s=%d", strings.ToUpper(network.DynamicPorts[idx]), port))
			}
		}
	}

	// Meta values
	for key, value := range task.Meta {
		env = append(env, fmt.Sprintf("NOMAD_META_%s=%s", strings.ToUpper(key), value))
	}

	return env
}
<commit_msg>Fix some printf tokens<commit_after>package driver

import (
	"fmt"
	"log"
	"strings"
	"sync"

	"github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/client/fingerprint"
	"github.com/hashicorp/nomad/nomad/structs"
)

// BuiltinDrivers contains the built in registered drivers
// which are available for allocation handling
var BuiltinDrivers = map[string]Factory{
	"docker": NewDockerDriver,
	"exec":   NewExecDriver,
	"java":   NewJavaDriver,
	"qemu":   NewQemuDriver,
}

// NewDriver is used to instantiate and return a new driver
// given the name and a logger
func NewDriver(name string, ctx *DriverContext) (Driver, error) {
	// Lookup the factory function
	factory, ok := BuiltinDrivers[name]
	if !ok {
		return nil, fmt.Errorf("unknown driver '%s'", name)
	}

	// Instantiate the driver
	f := factory(ctx)
	return f, nil
}

// Factory is used to instantiate a new Driver
type Factory func(*DriverContext) Driver

// Driver is used for execution of tasks. This allows Nomad
// to support many pluggable implementations of task drivers.
// Examples could include LXC, Docker, Qemu, etc.
type Driver interface {
	// Drivers must support the fingerprint interface for detection
	fingerprint.Fingerprint

	// Start is used to being task execution
	Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error)

	// Open is used to re-open a handle to a task
	Open(ctx *ExecContext, handleID string) (DriverHandle, error)
}

// DriverContext is a means to inject dependencies such as loggers, configs, and
// node attributes into a Driver without having to change the Driver interface
// each time we do it. Used in conjection with Factory, above.
type DriverContext struct {
	config *config.Config
	logger *log.Logger
	node   *structs.Node
}

// NewDriverContext initializes a new DriverContext with the specified fields.
// This enables other packages to create DriverContexts but keeps the fields
// private to the driver. If we want to change this later we can gorename all of
// the fields in DriverContext.
func NewDriverContext(config *config.Config, node *structs.Node, logger *log.Logger) *DriverContext {
	return &DriverContext{
		config: config,
		node:   node,
		logger: logger,
	}
}

// DriverHandle is an opaque handle into a driver used for task
// manipulation
type DriverHandle interface {
	// Returns an opaque handle that can be used to re-open the handle
	ID() string

	// WaitCh is used to return a channel used wait for task completion
	WaitCh() chan error

	// Update is used to update the task if possible
	Update(task *structs.Task) error

	// Kill is used to stop the task
	Kill() error
}

// ExecContext is shared between drivers within an allocation
type ExecContext struct {
	sync.Mutex

	// AllocDir is the directory used for storing any state
	// of this allocation. It will be purged on alloc destroy.
	AllocDir string
}

// NewExecContext is used to create a new execution context
func NewExecContext() *ExecContext {
	ctx := &ExecContext{}
	return ctx
}

// PopulateEnvironment converts exec context and task configuration into
// environment variables so they can be passed along to a driver.
//
// The output is a list of strings with NAME=value pairs.
func PopulateEnvironment(ctx *ExecContext, task *structs.Task) []string {
	env := []string{}

	env = append(env, fmt.Sprintf("NOMAD_ALLOC_DIR=%s", ctx.AllocDir))

	if task.Resources != nil {
		env = append(env, fmt.Sprintf("NOMAD_MEMORY_LIMIT=%d", task.Resources.MemoryMB))
		env = append(env, fmt.Sprintf("NOMAD_CPU_LIMIT=%f", task.Resources.CPU))

		// Named Ports
		if len(task.Resources.Networks) > 0 {
			network := task.Resources.Networks[0]

			env = append(env, fmt.Sprintf("NOMAD_HOST_IP=%s", network.IP))
			for idx, port := range network.ListDynamicPorts() {
				env = append(env, fmt.Sprintf("NOMAD_PORT_%s=%d", strings.ToUpper(network.DynamicPorts[idx]), port))
			}
		}
	}

	// Meta values
	for key, value := range task.Meta {
		env = append(env, fmt.Sprintf("NOMAD_META_%s=%s", strings.ToUpper(key), value))
	}

	return env
}
<|endoftext|>


<commit_before>package server

import (
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/lukaszbudnik/migrator/config"
	"github.com/lukaszbudnik/migrator/coordinator"
	"github.com/lukaszbudnik/migrator/data"
	"github.com/lukaszbudnik/migrator/types"
	"github.com/stretchr/testify/assert"
)

var (
	configFile          = "../test/migrator-postgresql.yaml"
	configFileOverrides = "../test/migrator-overrides.yaml"
)

func newTestRequestV1(method, url string, body io.Reader) (*http.Request, error) {
	versionURL := "/v1" + url
	return http.NewRequest(method, versionURL, body)
}

func newTestRequestV2(method, url string, body io.Reader) (*http.Request, error) {
	versionURL := "/v2" + url
	return http.NewRequest(method, versionURL, body)
}

func testSetupRouter(config *config.Config, newCoordinator coordinator.Factory) *gin.Engine {
	versionInfo := &types.VersionInfo{Release: "GitRef", Sha: "GitSha", APIVersions: []types.APIVersion{types.APIV2}}
	gin.SetMode(gin.ReleaseMode)
	r := gin.New()
	return SetupRouter(r, versionInfo, config, newNoopMetrics(), newCoordinator)
}

func TestGetDefaultPort(t *testing.T) {
	config, err := config.FromFile(configFile)
	assert.Nil(t, err)
	assert.Equal(t, "8080", GetPort(config))
}

func TestGetDefaultPortOverrides(t *testing.T) {
	config, err := config.FromFile(configFileOverrides)
	assert.Nil(t, err)
	assert.Equal(t, "8811", GetPort(config))
}

// section /

func TestRoot(t *testing.T) {
	config, err := config.FromFile(configFile)
	assert.Nil(t, err)

	router := testSetupRouter(config, nil)

	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/", nil)
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	assert.Equal(t, "application/json; charset=utf-8", w.Result().Header.Get("Content-Type"))
	assert.Equal(t, `{"release":"GitRef","sha":"GitSha","apiVersions":["v2"]}`, strings.TrimSpace(w.Body.String()))
}

func TestRootWithPathPrefix(t *testing.T) {
	config, err := config.FromFile(configFile)
	assert.Nil(t, err)

	config.PathPrefix = "/migrator"

	router := testSetupRouter(config, nil)

	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/migrator/", nil)
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	assert.Equal(t, "application/json; charset=utf-8", w.Result().Header.Get("Content-Type"))
	assert.Equal(t, `{"release":"GitRef","sha":"GitSha","apiVersions":["v2"]}`, strings.TrimSpace(w.Body.String()))
}

// /v1 API

func TestConfigRoute(t *testing.T) {
	config, err := config.FromFile(configFile)
	assert.Nil(t, err)

	router := testSetupRouter(config, nil)

	w := httptest.NewRecorder()
	req, _ := newTestRequestV1("GET", "/config", nil)
	router.ServeHTTP(w, req)

	// v1 is now removed
	assert.Equal(t, http.StatusNotFound, w.Code)
}

// /v2 API

func TestGraphQLSchema(t *testing.T) {
	config, err := config.FromFile(configFile)
	assert.Nil(t, err)

	router := testSetupRouter(config, newMockedCoordinator)

	w := httptest.NewRecorder()
	req, _ := newTestRequestV2("GET", "/schema", nil)
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	assert.Equal(t, "text/plain; charset=utf-8", w.Result().Header.Get("Content-Type"))
	assert.Equal(t, strings.TrimSpace(data.SchemaDefinition), strings.TrimSpace(w.Body.String()))
}

func TestGraphQLQueryWithVariables(t *testing.T) {
	config, err := config.FromFile(configFile)
	assert.Nil(t, err)

	router := testSetupRouter(config, newMockedCoordinator)

	w := httptest.NewRecorder()
	req, _ := newTestRequestV2("POST", "/service", strings.NewReader(`
    {
      "query": "query SourceMigration($file: String!) { sourceMigration(file: $file) { name, migrationType, sourceDir, file } }",
      "operationName": "SourceMigration",
      "variables": { "file": "source/201602220001.sql" }
    }
  `))
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	assert.Equal(t, "application/json; charset=utf-8", w.Result().Header.Get("Content-Type"))
	assert.Equal(t, `{"data":{"sourceMigration":{"name":"201602220001.sql","migrationType":"SingleMigration","sourceDir":"source","file":"source/201602220001.sql"}}}`, strings.TrimSpace(w.Body.String()))
}

func TestGraphQLQueryError(t *testing.T) {
	config, err := config.FromFile(configFile)
	assert.Nil(t, err)

	router := testSetupRouter(config, newMockedCoordinator)

	w := httptest.NewRecorder()
	req, _ := newTestRequestV2("POST", "/service", strings.NewReader(`
    {
      "query": "query SourceMigration($file: String!) { sourceMigration(file: $file) { name, migrationType, sourceDir, file } }",
      "operationName": "SourceMigration",
    }
  `))
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusBadRequest, w.Code)
	assert.Equal(t, "application/json; charset=utf-8", w.Result().Header.Get("Content-Type"))
	assert.Equal(t, `{"error":"Invalid request, please see documentation for valid JSON payload"}`, strings.TrimSpace(w.Body.String()))
}
<commit_msg>added test to cover CreateRouterAndPrometheus method<commit_after>package server

import (
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/lukaszbudnik/migrator/config"
	"github.com/lukaszbudnik/migrator/coordinator"
	"github.com/lukaszbudnik/migrator/data"
	"github.com/lukaszbudnik/migrator/types"
	"github.com/stretchr/testify/assert"
)

var (
	configFile          = "../test/migrator-postgresql.yaml"
	configFileOverrides = "../test/migrator-overrides.yaml"
)

func newTestRequestV1(method, url string, body io.Reader) (*http.Request, error) {
	versionURL := "/v1" + url
	return http.NewRequest(method, versionURL, body)
}

func newTestRequestV2(method, url string, body io.Reader) (*http.Request, error) {
	versionURL := "/v2" + url
	return http.NewRequest(method, versionURL, body)
}

func testSetupRouter(config *config.Config, newCoordinator coordinator.Factory) *gin.Engine {
	versionInfo := &types.VersionInfo{Release: "GitRef", Sha: "GitSha", APIVersions: []types.APIVersion{types.APIV2}}
	gin.SetMode(gin.ReleaseMode)
	r := gin.New()
	return SetupRouter(r, versionInfo, config, newNoopMetrics(), newCoordinator)
}

func TestGetDefaultPort(t *testing.T) {
	config, err := config.FromFile(configFile)
	assert.Nil(t, err)
	assert.Equal(t, "8080", GetPort(config))
}

func TestGetDefaultPortOverrides(t *testing.T) {
	config, err := config.FromFile(configFileOverrides)
	assert.Nil(t, err)
	assert.Equal(t, "8811", GetPort(config))
}

func TestCreateRouterAndPrometheus(t *testing.T) {
	config, err := config.FromFile(configFile)
	assert.Nil(t, err)
	versionInfo := &types.VersionInfo{Release: "GitRef", Sha: "GitSha", APIVersions: []types.APIVersion{types.APIV2}}
	gin.SetMode(gin.ReleaseMode)
	r := CreateRouterAndPrometheus(versionInfo, config, newMockedCoordinator)
	assert.NotNil(t, r)
}

// section /

func TestRoot(t *testing.T) {
	config, err := config.FromFile(configFile)
	assert.Nil(t, err)

	router := testSetupRouter(config, nil)

	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/", nil)
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	assert.Equal(t, "application/json; charset=utf-8", w.Result().Header.Get("Content-Type"))
	assert.Equal(t, `{"release":"GitRef","sha":"GitSha","apiVersions":["v2"]}`, strings.TrimSpace(w.Body.String()))
}

func TestRootWithPathPrefix(t *testing.T) {
	config, err := config.FromFile(configFile)
	assert.Nil(t, err)

	config.PathPrefix = "/migrator"

	router := testSetupRouter(config, nil)

	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/migrator/", nil)
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	assert.Equal(t, "application/json; charset=utf-8", w.Result().Header.Get("Content-Type"))
	assert.Equal(t, `{"release":"GitRef","sha":"GitSha","apiVersions":["v2"]}`, strings.TrimSpace(w.Body.String()))
}

// /v1 API

func TestConfigRoute(t *testing.T) {
	config, err := config.FromFile(configFile)
	assert.Nil(t, err)

	router := testSetupRouter(config, nil)

	w := httptest.NewRecorder()
	req, _ := newTestRequestV1("GET", "/config", nil)
	router.ServeHTTP(w, req)

	// v1 is now removed
	assert.Equal(t, http.StatusNotFound, w.Code)
}

// /v2 API

func TestGraphQLSchema(t *testing.T) {
	config, err := config.FromFile(configFile)
	assert.Nil(t, err)

	router := testSetupRouter(config, newMockedCoordinator)

	w := httptest.NewRecorder()
	req, _ := newTestRequestV2("GET", "/schema", nil)
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	assert.Equal(t, "text/plain; charset=utf-8", w.Result().Header.Get("Content-Type"))
	assert.Equal(t, strings.TrimSpace(data.SchemaDefinition), strings.TrimSpace(w.Body.String()))
}

func TestGraphQLQueryWithVariables(t *testing.T) {
	config, err := config.FromFile(configFile)
	assert.Nil(t, err)

	router := testSetupRouter(config, newMockedCoordinator)

	w := httptest.NewRecorder()
	req, _ := newTestRequestV2("POST", "/service", strings.NewReader(`
    {
      "query": "query SourceMigration($file: String!) { sourceMigration(file: $file) { name, migrationType, sourceDir, file } }",
      "operationName": "SourceMigration",
      "variables": { "file": "source/201602220001.sql" }
    }
  `))
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	assert.Equal(t, "application/json; charset=utf-8", w.Result().Header.Get("Content-Type"))
	assert.Equal(t, `{"data":{"sourceMigration":{"name":"201602220001.sql","migrationType":"SingleMigration","sourceDir":"source","file":"source/201602220001.sql"}}}`, strings.TrimSpace(w.Body.String()))
}

func TestGraphQLQueryError(t *testing.T) {
	config, err := config.FromFile(configFile)
	assert.Nil(t, err)

	router := testSetupRouter(config, newMockedCoordinator)

	w := httptest.NewRecorder()
	req, _ := newTestRequestV2("POST", "/service", strings.NewReader(`
    {
      "query": "query SourceMigration($file: String!) { sourceMigration(file: $file) { name, migrationType, sourceDir, file } }",
      "operationName": "SourceMigration",
    }
  `))
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusBadRequest, w.Code)
	assert.Equal(t, "application/json; charset=utf-8", w.Result().Header.Get("Content-Type"))
	assert.Equal(t, `{"error":"Invalid request, please see documentation for valid JSON payload"}`, strings.TrimSpace(w.Body.String()))
}
<|endoftext|>


<commit_before>package client

import (
	"context"
	"github.com/imdario/mergo"
	"github.com/ory/fosite"
	"github.com/pborman/uuid"
	"github.com/pkg/errors"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

// MongoManager cares for the managing of the Mongo Session instance of a Client
type MongoManager struct {
	DB     *mgo.Database
	Hasher fosite.Hasher
}

// GetClient returns a Client if found by an ID lookup.
func (m *MongoManager) GetClient(ctx context.Context, id string) (fosite.Client, error) {
	return m.GetConcreteClient(id)
}

// UpdateClient updates an OAuth 2.0 Client record. This is done using the equivalent of an object replace.
func (m *MongoManager) UpdateClient(c *Client) error {
	o, err := m.GetConcreteClient(c.ID)
	if err != nil {
		return errors.WithStack(err)
	}

	// If the password isn't updated, grab it from the stored object
	if c.Secret == "" {
		c.Secret = string(c.GetHashedSecret())
	} else {
		h, err := m.Hasher.Hash([]byte(c.Secret))
		if err != nil {
			return errors.WithStack(err)
		}
		c.Secret = string(h)
	}

	// Otherwise, update the object with the new updates
	if err := mergo.Merge(c, o); err != nil {
		return errors.WithStack(err)
	}

	// Update Mongo reference with the updated object
	collection := m.DB.C("clients").With(m.DB.Session.Copy())
	defer collection.Database.Session.Close()
	selector := bson.M{"_id": c.ID}
	if err := collection.Update(selector, c); err != nil {
		return errors.WithStack(err)
	}
	return nil
}

// GetConcreteClient finds a Client based on ID and returns it, if found in Mongo.
func (m *MongoManager) GetConcreteClient(id string) (*Client, error) {
	collection := m.DB.C("clients").With(m.DB.Session.Copy())
	defer collection.Database.Session.Close()

	client := &Client{}
	err := collection.Find(bson.M{"_id": id}).One(client)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return client, nil
}

// Authenticate compares a client secret with the client's stored hashed secret
func (m *MongoManager) Authenticate(id string, secret []byte) (*Client, error) {
	c, err := m.GetConcreteClient(id)
	if err != nil {
		return nil, errors.WithStack(err)
	}

	if err := m.Hasher.Compare(c.GetHashedSecret(), secret); err != nil {
		return nil, errors.WithStack(err)
	}
	return c, nil
}

// CreateClient adds a new OAuth2.0 Client to the client store.
func (m *MongoManager) CreateClient(c *Client) error {
	if c.ID == "" {
		c.ID = uuid.New()
	}

	// Hash incoming secret
	h, err := m.Hasher.Hash([]byte(c.Secret))
	if err != nil {
		return errors.WithStack(err)
	}
	c.Secret = string(h)

	// Insert to Mongo
	collection := m.DB.C("clients").With(m.DB.Session.Copy())
	defer collection.Database.Session.Close()
	if err := collection.Insert(c); err != nil {
		return errors.WithStack(err)
	}
	return nil
}

// DeleteClient removes an OAuth 2.0 Client from the client store
func (m *MongoManager) DeleteClient(id string) error {
	collection := m.DB.C("clients").With(m.DB.Session.Copy())
	defer collection.Database.Session.Close()
	if err := collection.Remove(bson.M{"_id": id}); err != nil {
		return errors.WithStack(err)
	}
	return nil
}
<commit_msg>:arrow_up: Client: Add GetClients method<commit_after>package client

import (
	"context"
	"github.com/imdario/mergo"
	"github.com/ory/fosite"
	"github.com/pborman/uuid"
	"github.com/pkg/errors"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

// MongoManager cares for the managing of the Mongo Session instance of a Client
type MongoManager struct {
	DB     *mgo.Database
	Hasher fosite.Hasher
}

// GetClient returns a Client if found by an ID lookup.
func (m *MongoManager) GetClient(ctx context.Context, id string) (fosite.Client, error) {
	return m.GetConcreteClient(id)
}

// UpdateClient updates an OAuth 2.0 Client record. This is done using the equivalent of an object replace.
func (m *MongoManager) UpdateClient(c *Client) error {
	o, err := m.GetConcreteClient(c.ID)
	if err != nil {
		return errors.WithStack(err)
	}

	// If the password isn't updated, grab it from the stored object
	if c.Secret == "" {
		c.Secret = string(c.GetHashedSecret())
	} else {
		h, err := m.Hasher.Hash([]byte(c.Secret))
		if err != nil {
			return errors.WithStack(err)
		}
		c.Secret = string(h)
	}

	// Otherwise, update the object with the new updates
	if err := mergo.Merge(c, o); err != nil {
		return errors.WithStack(err)
	}

	// Update Mongo reference with the updated object
	collection := m.DB.C("clients").With(m.DB.Session.Copy())
	defer collection.Database.Session.Close()
	selector := bson.M{"_id": c.ID}
	if err := collection.Update(selector, c); err != nil {
		return errors.WithStack(err)
	}
	return nil
}

// GetConcreteClient finds a Client based on ID and returns it, if found in Mongo.
func (m *MongoManager) GetConcreteClient(id string) (*Client, error) {
	collection := m.DB.C("clients").With(m.DB.Session.Copy())
	defer collection.Database.Session.Close()

	client := &Client{}
	err := collection.Find(bson.M{"_id": id}).One(client)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return client, nil
}

// Authenticate compares a client secret with the client's stored hashed secret
func (m *MongoManager) Authenticate(id string, secret []byte) (*Client, error) {
	c, err := m.GetConcreteClient(id)
	if err != nil {
		return nil, errors.WithStack(err)
	}

	if err := m.Hasher.Compare(c.GetHashedSecret(), secret); err != nil {
		return nil, errors.WithStack(err)
	}
	return c, nil
}

// CreateClient adds a new OAuth2.0 Client to the client store.
func (m *MongoManager) CreateClient(c *Client) error {
	if c.ID == "" {
		c.ID = uuid.New()
	}

	// Hash incoming secret
	h, err := m.Hasher.Hash([]byte(c.Secret))
	if err != nil {
		return errors.WithStack(err)
	}
	c.Secret = string(h)

	// Insert to Mongo
	collection := m.DB.C("clients").With(m.DB.Session.Copy())
	defer collection.Database.Session.Close()
	if err := collection.Insert(c); err != nil {
		return errors.WithStack(err)
	}
	return nil
}

// DeleteClient removes an OAuth 2.0 Client from the client store
func (m *MongoManager) DeleteClient(id string) error {
	collection := m.DB.C("clients").With(m.DB.Session.Copy())
	defer collection.Database.Session.Close()
	if err := collection.Remove(bson.M{"_id": id}); err != nil {
		return errors.WithStack(err)
	}
	return nil
}

// GetClients returns a map of clients mapped by client ID
func (m *MongoManager) GetClients() (clients map[string]Client, err error) {
	clients = make(map[string]Client)
	collection := m.DB.C("clients").With(m.DB.Session.Copy())
	defer collection.Database.Session.Close()

	var result *Client
	iter := collection.Find(bson.M{}).Limit(100).Iter()
	for iter.Next(&result) {
		clients[result.ID] = *result
	}
	if iter.Err() != nil {
		return nil, iter.Err()
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"
	"os"

	"github.com/codegangsta/cli"
	"github.com/marcinwyszynski/tcproxy"
)

func handleConn(local net.Conn, remoteAddr string) {
	remote, err := net.Dial("tcp", remoteAddr)
	if err != nil {
		fmt.Fprintf(
			os.Stderr,
			"error connecting to remote host %s: %v\n",
			remoteAddr,
			err,
		)
		os.Exit(3)
	}
	tcproxy.TCProxy(remote.(*net.TCPConn), local.(*net.TCPConn))
}

func serve(ctx *cli.Context) {
	remoteAddr, localPort := ctx.String("remote"), ctx.Int("port")
	fmt.Fprintf(
		os.Stderr,
		"Creating a tunnel between %q and localhost:%d\n",
		remoteAddr,
		localPort,
	)
	listener, err := net.Listen(
		"tcp",
		fmt.Sprintf("127.0.0.1:%d", localPort),
	)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error starting local server: %v\n", err)
		os.Exit(1)
	}
	defer listener.Close()
	for {
		local, err := listener.Accept()
		if err != nil {
			fmt.Fprintf(
				os.Stderr,
				"error accepting incoming connection: %v\n",
				err,
			)
			os.Exit(2)
		}
		go handleConn(local, remoteAddr)
	}
}

func main() {
	app := cli.NewApp()
	app.Name = "teleport"
	app.Usage = "Like ngrok, but reverse - tunnel a remote endpoint to your localhost"
	app.Version = "0.0.1alpha"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "remote, r",
			Usage: "remote endpoint - eg. 0.tcp.ngrok.io:42644",
			Value: "",
		},
		cli.IntFlag{
			Name:  "port, p",
			Usage: "local port to expose",
			Value: 3000,
		},
	}
	app.Action = serve
	app.Run(os.Args)
}
<commit_msg>Finishing touches.<commit_after>package main

import (
	"fmt"
	"net"
	"os"

	"github.com/codegangsta/cli"
	"github.com/marcinwyszynski/tcproxy"
	"github.com/mgutz/ansi"
)

func handleConn(local net.Conn, remoteAddr string) {
	remote, err := net.Dial("tcp", remoteAddr)
	if err != nil {
		fmt.Fprintf(
			os.Stderr,
			"Error connecting to remote host %s: %v\n",
			remoteAddr,
			err,
		)
		os.Exit(3)
	}
	tcproxy.TCProxy(remote.(*net.TCPConn), local.(*net.TCPConn))
}

func serve(ctx *cli.Context) {
	fmt.Fprintln(
		os.Stderr,
		"Made with",
		ansi.Color("<3", "red"),
		"by codequest.com",
	)
	remoteAddr, localPort := ctx.String("remote"), ctx.Int("port")
	fmt.Fprintf(
		os.Stderr,
		"Creating a tunnel between %q and localhost:%d\n",
		remoteAddr,
		localPort,
	)
	listener, err := net.Listen(
		"tcp",
		fmt.Sprintf("127.0.0.1:%d", localPort),
	)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error starting local server: %v\n", err)
		os.Exit(1)
	}
	defer listener.Close()
	for {
		local, err := listener.Accept()
		if err != nil {
			fmt.Fprintf(
				os.Stderr,
				"Error accepting incoming connection: %v\n",
				err,
			)
			os.Exit(2)
		}
		go handleConn(local, remoteAddr)
	}
}

func main() {
	app := cli.NewApp()
	app.Name = "teleport"
	app.Usage = "Like ngrok, but reverse - tunnel a remote endpoint to your localhost"
	app.Version = "0.0.1alpha"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "remote, r",
			Usage: "remote endpoint - eg. 0.tcp.ngrok.io:42644",
			Value: "",
		},
		cli.IntFlag{
			Name:  "port, p",
			Usage: "local port to expose",
			Value: 3000,
		},
	}
	app.Action = serve
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, Mitchell Cooper
package main

import (
	"github.com/cooper/quiki/wikiclient"
	"html/template"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

var templateDir string
var templates = make(map[string]wikiTemplate)

type wikiTemplate struct {
	path       string             // template directory path
	template   *template.Template // master HTML template
	staticPath string             // static file directory path, if any
	staticRoot string             // static file directory HTTP root, if any
}

func getTemplate(name string) (wikiTemplate, error) {
	var t wikiTemplate
	path := templateDir + "/" + name

	// template is already cached
	if t, ok := templates[path]; ok {
		return t, nil
	}

	// parse HTML templates
	tmpl := template.New("")
	if err := filepath.Walk(path, func(filePath string, info os.FileInfo, err error) error {

		// a template
		if strings.HasSuffix(filePath, ".tpl") {
			if _, err := tmpl.ParseFiles(filePath); err != nil {
				return err
			}
		}

		// static content directory
		if info.IsDir() && info.Name() == "static" {
			t.staticPath = path
			t.staticRoot = "/tmpl/" + name
			fileServer := http.FileServer(http.Dir(path))
			http.Handle(t.staticRoot+"/", fileServer)
		}

		return err
	}); err != nil {
		return t, err
	}

	// cache the template
	t.path = path
	t.template = tmpl
	templates[path] = t
	return t, nil
}

type wikiPage struct {

	// titles -
	// need to provide either WholeTitle (by itself)
	// or Title (the page title) and WikiTitle (the wiki name) together
	WholeTitle string
	Title      string
	WikiTitle  string

	// response
	Res wikiclient.Message

	// path to static/ directory within the template
	StaticRoot string
}

func (p wikiPage) VisibleTitle() string {
	if p.WholeTitle != "" {
		return p.WholeTitle
	}
	return p.Title + " - " + p.WikiTitle
}

func (p wikiPage) PageCSS() string {
	return ""
}

func (p wikiPage) HTMLContent() template.HTML {
	return template.HTML(p.Res.String("content"))
}
<commit_msg>stripPrefix?<commit_after>// Copyright (c) 2017, Mitchell Cooper
package main

import (
	"github.com/cooper/quiki/wikiclient"
	"html/template"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

var templateDir string
var templates = make(map[string]wikiTemplate)

type wikiTemplate struct {
	path       string             // template directory path
	template   *template.Template // master HTML template
	staticPath string             // static file directory path, if any
	staticRoot string             // static file directory HTTP root, if any
}

func getTemplate(name string) (wikiTemplate, error) {
	var t wikiTemplate
	path := templateDir + "/" + name

	// template is already cached
	if t, ok := templates[path]; ok {
		return t, nil
	}

	// parse HTML templates
	tmpl := template.New("")
	if err := filepath.Walk(path, func(filePath string, info os.FileInfo, err error) error {

		// a template
		if strings.HasSuffix(filePath, ".tpl") {
			if _, err := tmpl.ParseFiles(filePath); err != nil {
				return err
			}
		}

		// static content directory
		if info.IsDir() && info.Name() == "static" {
			t.staticPath = path
			t.staticRoot = "/tmpl/" + name
			fileServer := http.FileServer(http.Dir(path))
			pfx := t.staticRoot + "/"
			http.Handle(pfx, http.StripPrefix(pfx, fileServer))
		}

		return err
	}); err != nil {
		return t, err
	}

	// cache the template
	t.path = path
	t.template = tmpl
	templates[path] = t
	return t, nil
}

type wikiPage struct {

	// titles -
	// need to provide either WholeTitle (by itself)
	// or Title (the page title) and WikiTitle (the wiki name) together
	WholeTitle string
	Title      string
	WikiTitle  string

	// response
	Res wikiclient.Message

	// path to static/ directory within the template
	StaticRoot string
}

func (p wikiPage) VisibleTitle() string {
	if p.WholeTitle != "" {
		return p.WholeTitle
	}
	return p.Title + " - " + p.WikiTitle
}

func (p wikiPage) PageCSS() string {
	return ""
}

func (p wikiPage) HTMLContent() template.HTML {
	return template.HTML(p.Res.String("content"))
}
<|endoftext|>


<commit_before>package dashboard

import (
	"html/template"
)

type templateInfo struct {
	Projects []*Project
}

var (
	indexTmpl = template.Must(template.New("index.html").Parse(`
<!DOCTYPE html>

  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta content="origin-when-cross-origin" name="referrer" />
  <link crossorigin="anonymous" media="all" integrity="sha512-R+Vpkv86him5JZcqAEuQRUGOKqH897w6q7uJ1P65tQR+9Hxar5vU4wpEd4uvcXT8ooRZ7zsNftrjnCemEt2u2Q==" rel="stylesheet" href="https://github.githubassets.com/assets/frameworks-f4557b27209914aa4705202b188165b5.css" />
  <link crossorigin="anonymous" media="all" integrity="sha512-KY4UdRxVuu2IR/1+5BfVjOAci/NTaEiyg0NelgnXevWPbhUU4YLcpNeWyVh6kVHzLwOF75XT+iW9BwA4zURVaw==" rel="stylesheet" href="https://github.githubassets.com/assets/github-af00f93db15422dc4aa5207a2f2ee52c.css" />
  <title>Dashboard</title>

Jekyll At-a-Glance Dashboard

      Repo
      Gem
      Travis
      AppVeyor
      Downloads
      Pull Requests
      Issues
      Unreleased commits

    {{range .Projects}}

    {{end}}

	Commits are as of this week. Issues and pull requests are total open.
	Source Code.

	Look wrong? <form action="/reset.json" method="post"><input type="Submit" value="Reset the cache."></form>

`))
)
<commit_msg>template: colorize appveyor success/failure<commit_after>package dashboard

import (
	"html/template"
)

type templateInfo struct {
	Projects []*Project
}

var (
	indexTmpl = template.Must(template.New("index.html").Parse(`
<!DOCTYPE html>

  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta content="origin-when-cross-origin" name="referrer" />
  <link crossorigin="anonymous" media="all" integrity="sha512-R+Vpkv86him5JZcqAEuQRUGOKqH897w6q7uJ1P65tQR+9Hxar5vU4wpEd4uvcXT8ooRZ7zsNftrjnCemEt2u2Q==" rel="stylesheet" href="https://github.githubassets.com/assets/frameworks-f4557b27209914aa4705202b188165b5.css" />
  <link crossorigin="anonymous" media="all" integrity="sha512-KY4UdRxVuu2IR/1+5BfVjOAci/NTaEiyg0NelgnXevWPbhUU4YLcpNeWyVh6kVHzLwOF75XT+iW9BwA4zURVaw==" rel="stylesheet" href="https://github.githubassets.com/assets/github-af00f93db15422dc4aa5207a2f2ee52c.css" />
  <title>Dashboard</title>

Jekyll At-a-Glance Dashboard

      Repo
      Gem
      Travis
      AppVeyor
      Downloads
      Pull Requests
      Issues
      Unreleased commits

    {{range .Projects}}

    {{end}}

	Commits are as of this week. Issues and pull requests are total open.
	Source Code.

	Look wrong? <form action="/reset.json" method="post"><input type="Submit" value="Reset the cache."></form>

`))
)
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"sync"
	"time"
)

const commandMessage = "Specify: n-clients n-requests n-replicas[just for logging] ip:port (optional: path)"

const requestBody = "BODY"

var (
	wg sync.WaitGroup

	mutex  sync.Mutex
	file   *os.File
	writer *bufio.Writer

	nClients, nRequests, nReplicas int

	targetIP, path string

	timeInit time.Time
)

func main() {

	var err error

	if len(os.Args) < 5 {
		fmt.Println(commandMessage)
		os.Exit(1)
	}

	if len(os.Args) == 6 {
		path = os.Args[5]
	}

	nClients, err = strconv.Atoi(os.Args[1])

	if err != nil {
		fmt.Println(commandMessage, ": ", err.Error())
		os.Exit(1)
	}

	nRequests, err = strconv.Atoi(os.Args[2])

	if err != nil {
		fmt.Println(commandMessage, ": ", err.Error())
		os.Exit(1)
	}

	nReplicas, err = strconv.Atoi(os.Args[3])

	if err != nil {
		fmt.Println(commandMessage, ": ", err.Error())
		os.Exit(1)
	}

	targetIP = "http://" + os.Args[4]

	file, err = os.Create(fmt.Sprintf(path+"raft_test_c%dreq%drep%d.csv", nClients, nRequests, nReplicas))

	if err != nil {
		fmt.Println("Can't create file")
		os.Exit(1)
	}

	writer = bufio.NewWriter(file)

	wg.Add(nClients)

	timeInit = time.Now()

	for c := 1; c <= nClients; c++ {
		go client(c)
	}

	wg.Wait()
	file.Sync()
}

func client(clientID int) {
	var t0, t1 time.Time

	for r := 0; r < nRequests; r++ {

		requestID := 1000*clientID + r

		target := fmt.Sprintf("%s/request/%d/%s", targetIP, requestID, requestBody)

		t0 = time.Now()

		err := errors.New("")

		var resp *http.Response

		for err != nil {
			resp, err = http.Get(target)
		}

		// if err != nil {
		// 	go writeToFile(fmt.Sprintf("Error on HTTP/GET! Client: %d, Request %d", clientID, requestID))
		// 	continue
		// }

		defer resp.Body.Close()

		var leader int

		if resp.StatusCode == 290 {
			leader = 0
		} else if resp.StatusCode == 291 {
			leader = 1
		} else {
			go writeToFile(fmt.Sprintf("Error on command! Status code: %d Client: %d Request %d", resp.StatusCode, clientID, requestID))
			return
		}

		t1 = time.Now()

		diff := t1.Sub(t0).Nanoseconds()

		elapsed := time.Now().Sub(timeInit).Nanoseconds()

		// client;request;time(ns);time since start;requestBody;total clients;requests;replicas
		go writeToFile(fmt.Sprintf("%d;%d;%d;%d;%d;%s;%d;%d;%d", clientID, requestID, diff, elapsed, leader, requestBody, nClients, nRequests, nReplicas))

	}

	wg.Done()
}

func writeToFile(s string) {
	mutex.Lock()

	_, err := writer.WriteString(s + "\n")

	writer.Flush()

	fmt.Println(s)

	mutex.Unlock()

	if err != nil {
		os.Exit(1)
	}
}
<commit_msg>Request now fails if http.Get returns an error more than 10 times<commit_after>package main

import (
	"bufio"
	"errors"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"sync"
	"time"
)

const commandMessage = "Specify: n-clients n-requests n-replicas[just for logging] ip:port (optional: path)"

const requestBody = "BODY"

var (
	wg sync.WaitGroup

	mutex  sync.Mutex
	file   *os.File
	writer *bufio.Writer

	nClients, nRequests, nReplicas int

	targetIP, path string

	timeInit time.Time
)

func main() {

	var err error

	if len(os.Args) < 5 {
		fmt.Println(commandMessage)
		os.Exit(1)
	}

	if len(os.Args) == 6 {
		path = os.Args[5]
	}

	nClients, err = strconv.Atoi(os.Args[1])

	if err != nil {
		fmt.Println(commandMessage, ": ", err.Error())
		os.Exit(1)
	}

	nRequests, err = strconv.Atoi(os.Args[2])

	if err != nil {
		fmt.Println(commandMessage, ": ", err.Error())
		os.Exit(1)
	}

	nReplicas, err = strconv.Atoi(os.Args[3])

	if err != nil {
		fmt.Println(commandMessage, ": ", err.Error())
		os.Exit(1)
	}

	targetIP = "http://" + os.Args[4]

	file, err = os.Create(fmt.Sprintf(path+"raft_test_c%dreq%drep%d.csv", nClients, nRequests, nReplicas))

	if err != nil {
		fmt.Println("Can't create file")
		os.Exit(1)
	}

	writer = bufio.NewWriter(file)

	wg.Add(nClients)

	timeInit = time.Now()

	for c := 1; c <= nClients; c++ {
		go client(c)
	}

	wg.Wait()
	file.Sync()
}

func client(clientID int) {
	var t0, t1 time.Time

	for r := 0; r < nRequests; r++ {

		requestID := 1000*clientID + r

		target := fmt.Sprintf("%s/request/%d/%s", targetIP, requestID, requestBody)

		t0 = time.Now()

		var resp *http.Response

		err := errors.New("")

		for try := 0; try < 10; try++ {
			resp, err = http.Get(target)

			if err == nil {
				break
			}
		}

		// if err != nil {
		// 	go writeToFile(fmt.Sprintf("Error on HTTP/GET! Client: %d, Request %d", clientID, requestID))
		// 	continue
		// }

		defer resp.Body.Close()

		var leader int

		if resp.StatusCode == 290 {
			leader = 0
		} else if resp.StatusCode == 291 {
			leader = 1
		} else {
			go writeToFile(fmt.Sprintf("Error on command! Status code: %d Client: %d Request %d", resp.StatusCode, clientID, requestID))
			return
		}

		t1 = time.Now()

		diff := t1.Sub(t0).Nanoseconds()

		elapsed := time.Now().Sub(timeInit).Nanoseconds()

		// client;request;time(ns);time since start;requestBody;total clients;requests;replicas
		go writeToFile(fmt.Sprintf("%d;%d;%d;%d;%d;%s;%d;%d;%d", clientID, requestID, diff, elapsed, leader, requestBody, nClients, nRequests, nReplicas))

	}

	wg.Done()
}

func writeToFile(s string) {
	mutex.Lock()

	_, err := writer.WriteString(s + "\n")

	writer.Flush()

	fmt.Println(s)

	mutex.Unlock()

	if err != nil {
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>// Copyright 2013-present Greg Hurrell. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"os/user"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"syscall"
)

type Options struct {
	Address    string
	Config     string
	Logfile    string
	Executable string
	Flags      string
	Port       int
}

var version = "unknown"

var config Options   // Options read from disk.
var defaults Options // Default options.
var flags Options    // Options set via commandline flags.
var settings Options // Result of merging: flags > config > defaults.
var showHelp bool
var showVersion bool

func printVersion() {
	fmt.Fprintf(os.Stderr, "clipper version: %s (%s)\n", version, runtime.GOOS)
}

func initFlags() {
	const (
		flagsUsage      = "arguments passed to clipboard executable"
		configFileUsage = "path to (JSON) config file"
		executableUsage = "program called to write to clipboard"
		helpUsage       = "show usage information"
		listenAddrUsage = "address to bind to (default loopback interface)"
		listenPortUsage = "port to listen on"
		logFileUsage    = "path to logfile"
		versionUsage    = "show version information"
	)

	flag.BoolVar(&showHelp, "h", false, helpUsage)
	flag.BoolVar(&showHelp, "help", false, helpUsage)
	flag.IntVar(&flags.Port, "p", defaults.Port, listenPortUsage)
	flag.IntVar(&flags.Port, "port", defaults.Port, listenPortUsage)
	flag.StringVar(&flags.Address, "a", defaults.Address, listenAddrUsage)
	flag.StringVar(&flags.Address, "address", defaults.Address, listenAddrUsage)
	flag.StringVar(&flags.Config, "c", defaults.Config, configFileUsage)
	flag.StringVar(&flags.Config, "config", defaults.Config, configFileUsage)
	flag.StringVar(&flags.Executable, "e", defaults.Executable, executableUsage)
	flag.StringVar(&flags.Executable, "executable", defaults.Executable, executableUsage)
	flag.StringVar(&flags.Flags, "f", defaults.Flags, flagsUsage)
	flag.StringVar(&flags.Flags, "flags", defaults.Flags, flagsUsage)
	flag.StringVar(&flags.Logfile, "l", defaults.Logfile, logFileUsage)
	flag.StringVar(&flags.Logfile, "logfile", defaults.Logfile, logFileUsage)
	flag.BoolVar(&showVersion, "v", false, versionUsage)
	flag.BoolVar(&showVersion, "version", false, versionUsage)
}

func setDefaults() {
	defaults.Address = "" // IPv4/IPv6 loopback.
	defaults.Port = 8377

	if runtime.GOOS == "linux" {
		defaults.Config = "~/.config/clipper/clipper.json"
		defaults.Logfile = "~/.config/clipper/logs/clipper.log"
		defaults.Executable = "xclip"
		defaults.Flags = "-selection clipboard"
	} else {
		defaults.Config = "~/.clipper.json"
		defaults.Logfile = "~/Library/Logs/com.wincent.clipper.log"
		defaults.Executable = "pbcopy"
		defaults.Flags = ""
	}
}

func mergeSettings() {
	// Detect which flags were passed in explicitly, and set them immediately.
	// This is used below to determine response to a missing config file.
	visitor := func(f *flag.Flag) {
		if f.Name == "address" || f.Name == "a" {
			settings.Address = flags.Address
		} else if f.Name == "config" || f.Name == "c" {
			settings.Config = flags.Config
		} else if f.Name == "executable" || f.Name == "e" {
			settings.Executable = flags.Executable
		} else if f.Name == "flags" || f.Name == "f" {
			settings.Flags = flags.Flags
		} else if f.Name == "port" || f.Name == "p" {
			settings.Port = flags.Port
		} else if f.Name == "logfile" || f.Name == "l" {
			settings.Logfile = flags.Logfile
		}
	}
	flag.Visit(visitor)

	expandedPath := expandPath(flags.Config)

	if configData, err := ioutil.ReadFile(expandedPath); err != nil {
		if settings.Config != "" {
			// User explicitly asked for a config file and it wasn't there; fail hard.
			log.Fatal(err)
		} else {
			// Default config file missing; just warn.
			log.Print(err)
		}
	} else {
		if err = json.Unmarshal(configData, &config); err != nil {
			log.Fatal(err)
		}
	}

	// Final merge into settings object.
	if settings.Address == "" {
		if config.Address != "" {
			settings.Address = config.Address
		} else {
			settings.Address = defaults.Address
		}
	}
	if settings.Logfile == "" {
		if config.Logfile != "" {
			settings.Logfile = config.Logfile
		} else {
			settings.Logfile = defaults.Logfile
		}
	}
	if settings.Port != 0 || config.Port != 0 {
		if isPath(settings.Address) {
			log.Print("--port option ignored when listening on UNIX domain socket")
		}
	}
	if settings.Port == 0 {
		if config.Port != 0 {
			settings.Port = config.Port
		} else {
			settings.Port = defaults.Port
		}
	}
	if settings.Executable == "" {
		if config.Executable != "" {
			settings.Executable = config.Executable
		} else {
			settings.Executable = defaults.Executable
		}
	}
	if settings.Flags == "" {
		if config.Flags != "" {
			settings.Flags = config.Flags
		} else {
			settings.Flags = defaults.Flags
		}
	}
}

func main() {
	syscall.Umask(0077)
	// Set this up before we even know where our logfile is, in case we have to
	// bail early and print something to stderr.
	log.SetPrefix("clipper: ")
	// Set default values per GOOS.
	setDefaults()
	// Setup flags subsystem.
	initFlags()

	flag.Parse()
	if flag.NArg() != 0 {
		// Additional command-line options not supported.
		flag.Usage()
		os.Exit(1)
	}
	if showHelp {
		printVersion()
		flag.Usage()
		os.Exit(0)
	}
	if showVersion {
		printVersion()
		os.Exit(0)
	}

	// Merge flags -> config -> defaults.
	mergeSettings()

	expandedPath := expandPath(settings.Logfile)
	outfile, err := os.OpenFile(expandedPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
	if err != nil {
		log.Fatal(err)
	}
	defer outfile.Close()
	log.SetOutput(outfile)

	if _, err := exec.LookPath(settings.Executable); err != nil {
		log.Fatal(err)
	}

	var addr string
	var listeners []net.Listener
	if isPath(settings.Address) {
		addr = expandPath(settings.Address)
	} else {
		addr = settings.Address
	}
	if strings.HasPrefix(addr, "/") {
		log.Print("Starting UNIX domain socket server at ", addr)
		listener, err := net.Listen("unix", addr)
		if err != nil {
			log.Fatal(err)
		}
		listeners = append(listeners, listener)
	} else {
		if addr == "" {
			log.Print("Starting TCP server on loopback interface")
			addr = fmt.Sprintf("%s:%d", "127.0.0.1", settings.Port)
			listener, err := net.Listen("tcp4", addr)
			if err != nil {
				log.Fatal(err)
			}
			listeners = append(listeners, listener)
			addr = fmt.Sprintf("%s:%d", "[::1]", settings.Port)
			listener, err = net.Listen("tcp6", addr)
			if err != nil {
				log.Fatal(err)
			}
			listeners = append(listeners, listener)
		} else {
			log.Print("Starting TCP server on ", addr)
			addr = fmt.Sprintf("%s:%d", settings.Address, settings.Port)
			listener, err := net.Listen("tcp", addr)
			if err != nil {
				log.Fatal(err)
			}
			listeners = append(listeners, listener)
		}
	}
	for i := range listeners {
		defer listeners[i].Close()
	}

	go func() {
		for i := range listeners {
			go func(listener net.Listener) {
				for {
					conn, err := listener.Accept()
					if err != nil {
						log.Print(err)
						return
					}

					go handleConnection(conn)
				}
			}(listeners[i])
		}
	}()

	// Need to catch signals in order for `defer`-ed clean-up items to run.
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGTERM)
	sig := <-c
	log.Print("Got signal ", sig)
}

// Returns true for things which look like paths (start with "~", "." or "/").
func isPath(path string) bool {
	return strings.HasPrefix(path, "~") ||
		strings.HasPrefix(path, ".") ||
		strings.HasPrefix(path, "/")
}

func expandPath(path string) string {
	expanded := pathByExpandingTildeInPath(path)
	result, err := filepath.Abs(expanded)
	if err != nil {
		log.Fatal(err)
	}
	return result
}

func pathByExpandingTildeInPath(path string) string {
	if strings.HasPrefix(path, "~") {
		user, err := user.Current()
		if err != nil {
			log.Fatal(err)
		}
		path = user.HomeDir + path[1:]
	}
	return path
}

func handleConnection(conn net.Conn) {
	defer log.Print("Connection closed")
	defer conn.Close()

	var args []string
	if settings.Flags != "" {
		whitespace := regexp.MustCompile("\\s+")
		args = whitespace.Split(strings.TrimSpace(settings.Flags), -1)
	}
	cmd := exec.Command(settings.Executable, args...)
	stdin, err := cmd.StdinPipe()
	if err != nil {
		log.Printf("[ERROR] pipe init: %v\n", err)
		return
	}

	if err = cmd.Start(); err != nil {
		log.Printf("[ERROR] process start: %v\n", err)
		return
	}

	if copied, err := io.Copy(stdin, conn); err != nil {
		log.Printf("[ERROR] pipe copy: %v\n", err)
	} else {
		log.Print("Echoed ", copied, " bytes")
	}
	stdin.Close()

	if err = cmd.Wait(); err != nil {
		log.Printf("[ERROR] wait: %v\n", err)
	}
}
<commit_msg>Remove unnecessary anonymous goroutine<commit_after>// Copyright 2013-present Greg Hurrell. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"os/user"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"syscall"
)

type Options struct {
	Address    string
	Config     string
	Logfile    string
	Executable string
	Flags      string
	Port       int
}

var version = "unknown"

var config Options   // Options read from disk.
var defaults Options // Default options.
var flags Options    // Options set via commandline flags.
var settings Options // Result of merging: flags > config > defaults.
var showHelp bool
var showVersion bool

func printVersion() {
	fmt.Fprintf(os.Stderr, "clipper version: %s (%s)\n", version, runtime.GOOS)
}

func initFlags() {
	const (
		flagsUsage      = "arguments passed to clipboard executable"
		configFileUsage = "path to (JSON) config file"
		executableUsage = "program called to write to clipboard"
		helpUsage       = "show usage information"
		listenAddrUsage = "address to bind to (default loopback interface)"
		listenPortUsage = "port to listen on"
		logFileUsage    = "path to logfile"
		versionUsage    = "show version information"
	)

	flag.BoolVar(&showHelp, "h", false, helpUsage)
	flag.BoolVar(&showHelp, "help", false, helpUsage)
	flag.IntVar(&flags.Port, "p", defaults.Port, listenPortUsage)
	flag.IntVar(&flags.Port, "port", defaults.Port, listenPortUsage)
	flag.StringVar(&flags.Address, "a", defaults.Address, listenAddrUsage)
	flag.StringVar(&flags.Address, "address", defaults.Address, listenAddrUsage)
	flag.StringVar(&flags.Config, "c", defaults.Config, configFileUsage)
	flag.StringVar(&flags.Config, "config", defaults.Config, configFileUsage)
	flag.StringVar(&flags.Executable, "e", defaults.Executable, executableUsage)
	flag.StringVar(&flags.Executable, "executable", defaults.Executable, executableUsage)
	flag.StringVar(&flags.Flags, "f", defaults.Flags, flagsUsage)
	flag.StringVar(&flags.Flags, "flags", defaults.Flags, flagsUsage)
	flag.StringVar(&flags.Logfile, "l", defaults.Logfile, logFileUsage)
	flag.StringVar(&flags.Logfile, "logfile", defaults.Logfile, logFileUsage)
	flag.BoolVar(&showVersion, "v", false, versionUsage)
	flag.BoolVar(&showVersion, "version", false, versionUsage)
}

func setDefaults() {
	defaults.Address = "" // IPv4/IPv6 loopback.
	defaults.Port = 8377

	if runtime.GOOS == "linux" {
		defaults.Config = "~/.config/clipper/clipper.json"
		defaults.Logfile = "~/.config/clipper/logs/clipper.log"
		defaults.Executable = "xclip"
		defaults.Flags = "-selection clipboard"
	} else {
		defaults.Config = "~/.clipper.json"
		defaults.Logfile = "~/Library/Logs/com.wincent.clipper.log"
		defaults.Executable = "pbcopy"
		defaults.Flags = ""
	}
}

func mergeSettings() {
	// Detect which flags were passed in explicitly, and set them immediately.
	// This is used below to determine response to a missing config file.
	visitor := func(f *flag.Flag) {
		if f.Name == "address" || f.Name == "a" {
			settings.Address = flags.Address
		} else if f.Name == "config" || f.Name == "c" {
			settings.Config = flags.Config
		} else if f.Name == "executable" || f.Name == "e" {
			settings.Executable = flags.Executable
		} else if f.Name == "flags" || f.Name == "f" {
			settings.Flags = flags.Flags
		} else if f.Name == "port" || f.Name == "p" {
			settings.Port = flags.Port
		} else if f.Name == "logfile" || f.Name == "l" {
			settings.Logfile = flags.Logfile
		}
	}
	flag.Visit(visitor)

	expandedPath := expandPath(flags.Config)

	if configData, err := ioutil.ReadFile(expandedPath); err != nil {
		if settings.Config != "" {
			// User explicitly asked for a config file and it wasn't there; fail hard.
			log.Fatal(err)
		} else {
			// Default config file missing; just warn.
			log.Print(err)
		}
	} else {
		if err = json.Unmarshal(configData, &config); err != nil {
			log.Fatal(err)
		}
	}

	// Final merge into settings object.
	if settings.Address == "" {
		if config.Address != "" {
			settings.Address = config.Address
		} else {
			settings.Address = defaults.Address
		}
	}
	if settings.Logfile == "" {
		if config.Logfile != "" {
			settings.Logfile = config.Logfile
		} else {
			settings.Logfile = defaults.Logfile
		}
	}
	if settings.Port != 0 || config.Port != 0 {
		if isPath(settings.Address) {
			log.Print("--port option ignored when listening on UNIX domain socket")
		}
	}
	if settings.Port == 0 {
		if config.Port != 0 {
			settings.Port = config.Port
		} else {
			settings.Port = defaults.Port
		}
	}
	if settings.Executable == "" {
		if config.Executable != "" {
			settings.Executable = config.Executable
		} else {
			settings.Executable = defaults.Executable
		}
	}
	if settings.Flags == "" {
		if config.Flags != "" {
			settings.Flags = config.Flags
		} else {
			settings.Flags = defaults.Flags
		}
	}
}

func main() {
	syscall.Umask(0077)
	// Set this up before we even know where our logfile is, in case we have to
	// bail early and print something to stderr.
	log.SetPrefix("clipper: ")
	// Set default values per GOOS.
	setDefaults()
	// Setup flags subsystem.
	initFlags()

	flag.Parse()
	if flag.NArg() != 0 {
		// Additional command-line options not supported.
		flag.Usage()
		os.Exit(1)
	}
	if showHelp {
		printVersion()
		flag.Usage()
		os.Exit(0)
	}
	if showVersion {
		printVersion()
		os.Exit(0)
	}

	// Merge flags -> config -> defaults.
	mergeSettings()

	expandedPath := expandPath(settings.Logfile)
	outfile, err := os.OpenFile(expandedPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
	if err != nil {
		log.Fatal(err)
	}
	defer outfile.Close()
	log.SetOutput(outfile)

	if _, err := exec.LookPath(settings.Executable); err != nil {
		log.Fatal(err)
	}

	var addr string
	var listeners []net.Listener
	if isPath(settings.Address) {
		addr = expandPath(settings.Address)
	} else {
		addr = settings.Address
	}
	if strings.HasPrefix(addr, "/") {
		log.Print("Starting UNIX domain socket server at ", addr)
		listener, err := net.Listen("unix", addr)
		if err != nil {
			log.Fatal(err)
		}
		listeners = append(listeners, listener)
	} else {
		if addr == "" {
			log.Print("Starting TCP server on loopback interface")
			addr = fmt.Sprintf("%s:%d", "127.0.0.1", settings.Port)
			listener, err := net.Listen("tcp4", addr)
			if err != nil {
				log.Fatal(err)
			}
			listeners = append(listeners, listener)
			addr = fmt.Sprintf("%s:%d", "[::1]", settings.Port)
			listener, err = net.Listen("tcp6", addr)
			if err != nil {
				log.Fatal(err)
			}
			listeners = append(listeners, listener)
		} else {
			log.Print("Starting TCP server on ", addr)
			addr = fmt.Sprintf("%s:%d", settings.Address, settings.Port)
			listener, err := net.Listen("tcp", addr)
			if err != nil {
				log.Fatal(err)
			}
			listeners = append(listeners, listener)
		}
	}
	for i := range listeners {
		defer listeners[i].Close()
	}

	for i := range listeners {
		go func(listener net.Listener) {
			for {
				conn, err := listener.Accept()
				if err != nil {
					log.Print(err)
					return
				}

				go handleConnection(conn)
			}
		}(listeners[i])
	}

	// Need to catch signals in order for `defer`-ed clean-up items to run.
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGTERM)
	sig := <-c
	log.Print("Got signal ", sig)
}

// Returns true for things which look like paths (start with "~", "." or "/").
func isPath(path string) bool {
	return strings.HasPrefix(path, "~") ||
		strings.HasPrefix(path, ".") ||
		strings.HasPrefix(path, "/")
}

func expandPath(path string) string {
	expanded := pathByExpandingTildeInPath(path)
	result, err := filepath.Abs(expanded)
	if err != nil {
		log.Fatal(err)
	}
	return result
}

func pathByExpandingTildeInPath(path string) string {
	if strings.HasPrefix(path, "~") {
		user, err := user.Current()
		if err != nil {
			log.Fatal(err)
		}
		path = user.HomeDir + path[1:]
	}
	return path
}

func handleConnection(conn net.Conn) {
	defer log.Print("Connection closed")
	defer conn.Close()

	var args []string
	if settings.Flags != "" {
		whitespace := regexp.MustCompile("\\s+")
		args = whitespace.Split(strings.TrimSpace(settings.Flags), -1)
	}
	cmd := exec.Command(settings.Executable, args...)
	stdin, err := cmd.StdinPipe()
	if err != nil {
		log.Printf("[ERROR] pipe init: %v\n", err)
		return
	}

	if err = cmd.Start(); err != nil {
		log.Printf("[ERROR] process start: %v\n", err)
		return
	}

	if copied, err := io.Copy(stdin, conn); err != nil {
		log.Printf("[ERROR] pipe copy: %v\n", err)
	} else {
		log.Print("Echoed ", copied, " bytes")
	}
	stdin.Close()

	if err = cmd.Wait(); err != nil {
		log.Printf("[ERROR] wait: %v\n", err)
	}
}
<|endoftext|>


<commit_before>// Ogo

package ogo

import (
	"fmt"
	"time"

	//"gopkg.in/redis.v3"
)

/* {{{ func CacheSet(key, value string, expire int) error
 *
 */
func CacheSet(key, value string, expire int) error {
	if cc := ClusterClient(); cc != nil {
		return cc.Set(key, value, time.Duration(expire)*time.Second).Err()
	} else {
		return fmt.Errorf("not found cache client")
	}
}

/* }}} */

/* {{{ func CacheGet(key) error
 *
 */
func CacheGet(key string) (string, error) {
	if cc := ClusterClient(); cc != nil {
		return cc.Get(key).Result()
	} else {
		return "", fmt.Errorf("not found cache client")
	}
}

/* }}} */

/* {{{ func GetLock(tag string) string
 * 获取锁
 */
func GetLock(key string) string {
	if cc := ClusterClient(); cc != nil {
		multi := client.Multi()
		defer multi.Close()
		val, err := multi.Watch(key).Result()
	}
	return ""
}

/* }}} */
<commit_msg>add lock/release function(cluster)<commit_after>// Ogo

package ogo

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/Odinman/ogo/utils"
	//"gopkg.in/redis.v3"
)

/* {{{ func CacheSet(key, value string, expire int) error
 *
 */
func CacheSet(key, value string, expire int) error {
	if cc := ClusterClient(); cc != nil {
		return cc.Set(key, value, time.Duration(expire)*time.Second).Err()
	} else {
		return fmt.Errorf("not found cache client")
	}
}

/* }}} */

/* {{{ func CacheGet(key) error
 *
 */
func CacheGet(key string) (string, error) {
	if cc := ClusterClient(); cc != nil {
		return cc.Get(key).Result()
	} else {
		return "", fmt.Errorf("not found cache client")
	}
}

/* }}} */

/* {{{ func GetLock(key string) string
 * 获取锁
 */
func GetLock(key string) string {
	if cc := ClusterClient(); cc != nil {
		cs := utils.NewUUID()
		ts := time.Now().Unix()
		val := fmt.Sprint(ts, ",", cs)
		if err := cc.SetNX(key, val, 600*time.Second).Err(); err == nil { //10分钟自动消失
			// lock exist
			if old, err := cc.Get(key).Result(); err == nil {
				vs := strings.SplitN(old, ",", 2)
				if ots, err := strconv.Atoi(vs[0]); err == nil && int(ts)-ots > 60 { //1分钟过期
					//过期了,抢
					if getSet := cc.GetSet(key, val); getSet.Err() == nil {
						gl := getSet.Val()
						if gl == old {
							//抢到了,返回checksum
							return cs
						} else {
							//没抢到,并且还覆盖了人家抢到的锁,(可能会产生问题)
							//cc.Set(key, gl, 600*time.Second)
						}
					}
				}
			}
		}
	}
	return ""
}

/* }}} */

/* {{{ func ReleaseLock(key,cs string) error
 * 释放锁
 */
func ReleaseLock(key, cs string) error {
	if cc := ClusterClient(); cc != nil {
		if cur, err := cc.Get(key).Result(); err == nil {
			vs := strings.SplitN(cur, ",", 2)
			if vs[1] != cs {
				Warn("[self: %s][lock: %s][diff]", cs, vs[1])
			}
			return cc.Del(key).Err()
		}
	}
	return fmt.Errorf("release wrong")
}

/* }}} */
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"net/url"
)

type Cmd interface {
	Run(name string, args ...string) (string, string, error)
	Start(name string, args ...string) (Cmd, error)
	Wait() error
	Output(name string, args ...string) (string, string, error)
	URL(path string) *url.URL
}

func Scp(src, dest *url.URL) error {
	scpCmd := NewLocal()
	_, _, err := scpCmd.Run("scp", formatCopyURL(src), formatCopyURL(dest))

	return err
}

func formatCopyURL(u *url.URL) string {
	if u.Host == "" {
		return u.String()
	}
	return fmt.Sprintf("%s@%s:%s", u.User.Username(), u.Host, u.Path)
}
<commit_msg>Use SCP gateway when copying file between hosts<commit_after>package cmd

import (
	"fmt"
	"net/url"
)

type Cmd interface {
	Run(name string, args ...string) (string, string, error)
	Start(name string, args ...string) (Cmd, error)
	Wait() error
	Output(name string, args ...string) (string, string, error)
	URL(path string) *url.URL
}

func Scp(src, dest *url.URL) error {
	scpCmd := NewLocal()
	_, _, err := scpCmd.Run("scp", "-3", formatCopyURL(src), formatCopyURL(dest))

	return err
}

func formatCopyURL(u *url.URL) string {
	if u.Host == "" {
		return u.String()
	}
	return fmt.Sprintf("%s@%s:%s", u.User.Username(), u.Host, u.Path)
}
<|endoftext|>


<commit_before>package cmd

import (
	"os"
	"os/exec"
	"fmt"
	"strings"
	"regexp"
)

type GitVCS struct {}

// GitGet implements the getting logic for Git.
func (g *GitVCS) Get(dep *Dependency) error {
	dest := fmt.Sprintf("%s/src/%s", os.Getenv("GOPATH"), dep.Name)
	//Info("Cloning %s into %s\n", dep.Repository, dest)
	Info("Git: ")
	out, err := exec.Command("git", "clone", dep.Repository, dest).CombinedOutput()
	fmt.Print(string(out))
	return err
}

func (g *GitVCS) Update(dep *Dependency) error {
	dest := fmt.Sprintf("%s/src/%s", os.Getenv("GOPATH"), dep.Name)

	if _, err := os.Stat(dest); err != nil {
		// Assume a new package?
		Info("Looks like %s is a new package. Cloning.", dep.Name)
		return exec.Command("git", "clone", dep.Repository, dest).Run()
	}

	oldDir, err := os.Getwd()
	if err != nil {
		return err
	}
	os.Chdir(dest)
	defer os.Chdir(oldDir)

	// Because we can't predict which branch we want to be on, and since
	// we want to set checkouts explicitly, we should probably fetch.
	//out, err :=  exec.Command("git", "pull", "--ff-only").CombinedOutput()
	Info("Git: ")
	out, err :=  exec.Command("git", "fetch", "--all").CombinedOutput()
	fmt.Print(string(out))
	return err
}

func (g *GitVCS) Version(dep *Dependency) error {
	dest := fmt.Sprintf("%s/src/%s", os.Getenv("GOPATH"), dep.Name)

	oldDir, err := os.Getwd()
	if err != nil {
		return err
	}
	os.Chdir(dest)
	defer os.Chdir(oldDir)

	//Info("Setting %s with 'git checkout'\n", dep.Name)

	// Now get to the right reference.
	if out, err := exec.Command("git", "checkout", dep.Reference).CombinedOutput(); err != nil {
		fmt.Println(string(out))
		return err
	} else {
		updatedTo := "the latest"
		if dep.Reference != "" {
			updatedTo = dep.Reference
		}
		Info("Settting version of %s to %s\n", dep.Name, updatedTo)
		//fmt.Print(string(out))
	}

	// EXPERIMENTAL: This will keep the repo up to date according to the
	// master branch on Git. Since 'master' is convention only, this isn't
	// a 100% reliable way to do things.
	if dep.Reference == "" {
		Info("No Git reference set. Trying to update 'master'...\n")
		dep.Reference = "master"
	}

	branchref := fmt.Sprintf("origin/%s", dep.Reference)
	//err = exec.Command("git", "show-ref", "-q", branchref).Run()
	out, err := exec.Command("git", "show-ref", branchref).CombinedOutput()
	if err == nil {
		//Info("Git: Found branch %s", string(out))
		//Debug("Reference %s is to a branch.", dep.Reference)
		// git merge --ff-only origin $VERSION
		out, err := exec.Command("git", "merge", "--ff-only", "origin", dep.Reference).CombinedOutput()
		Info("Git: %s", string(out))
		if err != nil {
			return err
		}

	}

	// EXPERIMENTAL: Show how far behind we are.
	out, err = exec.Command("git", "rev-list", "--count", "HEAD..origin").CombinedOutput()
	if err == nil {
		count := strings.TrimSpace(string(out))
		if count != "0" {
			var c string
			switch len(count) {
			// 0-9, not that bad
			case 1:
				c = Yellow
			// 10-99, we're getting behind
			case 2:
				c = Red
			// Whoa! We're falling way behind!
			default:
				c = BoldRed
			}
			Info(Color(c, fmt.Sprintf("Git: %s is %s behind origin.\n", dep.Name, count)))
		}
	}

	return nil
}

func (g *GitVCS) LastCommit(dep *Dependency) (string, error) {
	dest := fmt.Sprintf("%s/src/%s", os.Getenv("GOPATH"), dep.Name)

	oldDir, err := os.Getwd()
	if err != nil {
		return "", err
	}
	os.Chdir(dest)
	defer os.Chdir(oldDir)

	out, err := exec.Command("git", "log", "-n", "1", "--pretty=format:%h%d").CombinedOutput()
	if err != nil {
		return "", err
	}
	parts := strings.SplitN(string(out), " ", 2)

	sha := parts[0]

	// Send back a tag if a tag matches.
	if len(parts) > 1 && strings.Contains(parts[1], "tag: ") {
		re := regexp.MustCompile("tag: ([^,)]*)")
		subs := re.FindStringSubmatch(parts[1])
		if len(subs) > 1 {
			return subs[1], nil
		}
	}

	return sha, nil
}

<commit_msg>Experimenting with using git pull instead of git merge.<commit_after>package cmd

import (
	"os"
	"os/exec"
	"fmt"
	"strings"
	"regexp"
)

type GitVCS struct {}

// GitGet implements the getting logic for Git.
func (g *GitVCS) Get(dep *Dependency) error {
	dest := fmt.Sprintf("%s/src/%s", os.Getenv("GOPATH"), dep.Name)
	//Info("Cloning %s into %s\n", dep.Repository, dest)
	Info("Git: ")
	out, err := exec.Command("git", "clone", dep.Repository, dest).CombinedOutput()
	fmt.Print(string(out))
	return err
}

func (g *GitVCS) Update(dep *Dependency) error {
	dest := fmt.Sprintf("%s/src/%s", os.Getenv("GOPATH"), dep.Name)

	if _, err := os.Stat(dest); err != nil {
		// Assume a new package?
		Info("Looks like %s is a new package. Cloning.", dep.Name)
		return exec.Command("git", "clone", dep.Repository, dest).Run()
	}

	oldDir, err := os.Getwd()
	if err != nil {
		return err
	}
	os.Chdir(dest)
	defer os.Chdir(oldDir)

	// Because we can't predict which branch we want to be on, and since
	// we want to set checkouts explicitly, we should probably fetch.
	//out, err :=  exec.Command("git", "pull", "--ff-only").CombinedOutput()
	Info("Git: ")
	out, err :=  exec.Command("git", "fetch", "--all").CombinedOutput()
	fmt.Print(string(out))
	return err
}

func (g *GitVCS) Version(dep *Dependency) error {
	dest := fmt.Sprintf("%s/src/%s", os.Getenv("GOPATH"), dep.Name)

	oldDir, err := os.Getwd()
	if err != nil {
		return err
	}
	os.Chdir(dest)
	defer os.Chdir(oldDir)

	//Info("Setting %s with 'git checkout'\n", dep.Name)

	// Now get to the right reference.
	if out, err := exec.Command("git", "checkout", dep.Reference).CombinedOutput(); err != nil {
		fmt.Println(string(out))
		return err
	} else {
		updatedTo := "the latest"
		if dep.Reference != "" {
			updatedTo = dep.Reference
		}
		Info("Settting version of %s to %s\n", dep.Name, updatedTo)
		//fmt.Print(string(out))
	}

	// EXPERIMENTAL: This will keep the repo up to date according to the
	// master branch on Git. Since 'master' is convention only, this isn't
	// a 100% reliable way to do things.
	if dep.Reference == "" {
		Info("No Git reference set. Trying to update 'master'...\n")
		dep.Reference = "master"
	}

	branchref := fmt.Sprintf("origin/%s", dep.Reference)
	//err = exec.Command("git", "show-ref", "-q", branchref).Run()
	out, err := exec.Command("git", "show-ref", branchref).CombinedOutput()
	if err == nil {
		//Info("Git: Found branch %s", string(out))
		//Debug("Reference %s is to a branch.", dep.Reference)
		// git merge --ff-only origin $VERSION
		out, err := exec.Command("git", "pull", "--ff-only", "origin", dep.Reference).CombinedOutput()
		Info("Git: (merge) %s", string(out))
		if err != nil {
			return err
		}

	}

	// EXPERIMENTAL: Show how far behind we are.
	out, err = exec.Command("git", "rev-list", "--count", "HEAD..origin").CombinedOutput()
	if err == nil {
		count := strings.TrimSpace(string(out))
		if count != "0" {
			var c string
			switch len(count) {
			// 0-9, not that bad
			case 1:
				c = Yellow
			// 10-99, we're getting behind
			case 2:
				c = Red
			// Whoa! We're falling way behind!
			default:
				c = BoldRed
			}
			Info(Color(c, fmt.Sprintf("Git: %s is %s behind origin.\n", dep.Name, count)))
		}
	}

	return nil
}

func (g *GitVCS) LastCommit(dep *Dependency) (string, error) {
	dest := fmt.Sprintf("%s/src/%s", os.Getenv("GOPATH"), dep.Name)

	oldDir, err := os.Getwd()
	if err != nil {
		return "", err
	}
	os.Chdir(dest)
	defer os.Chdir(oldDir)

	out, err := exec.Command("git", "log", "-n", "1", "--pretty=format:%h%d").CombinedOutput()
	if err != nil {
		return "", err
	}
	parts := strings.SplitN(string(out), " ", 2)

	sha := parts[0]

	// Send back a tag if a tag matches.
	if len(parts) > 1 && strings.Contains(parts[1], "tag: ") {
		re := regexp.MustCompile("tag: ([^,)]*)")
		subs := re.FindStringSubmatch(parts[1])
		if len(subs) > 1 {
			return subs[1], nil
		}
	}

	return sha, nil
}

<|endoftext|>