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

// Package docker contains simple utilities for pulling a docker image, starting
// a container, and stoping a container. It assumes that docker is installed. If
// it is not, it will simply throw an error.
package docker

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	glog "github.com/google/shipshape/third_party/go-glog"
)

const (
	shipshapeWork = "/shipshape-workspace"
	shipshapeLogs = "/shipshape-output"
)

// TODO(ciera): Consider making these all use channels.
type CommandResult struct {
	Stdout string
	Stderr string
	Err    error
}

func trimResult(stdout, stderr *bytes.Buffer, err error) CommandResult {
	return CommandResult{strings.TrimSpace(stdout.String()), strings.TrimSpace(stderr.String()), err}
}

// ContainerExists checks if a container with the given name is in the list of running, or old, containers.
func ContainerExists(container string) (bool, error) {
	// Setup and run command
	// TODO(ciera): When Travis and other places we run this at support docker
	// 1.8, we can drastically reduce this code by using the flag --format={{.Names}}
	// below and removing the logic to parse out the name ourselves.
	stdout := bytes.NewBuffer(nil)
	cmd := exec.Command("docker", "ps", "-a")
	cmd.Stdout = stdout
	if err := cmd.Run(); err != nil {
		fmt.Printf("Problem running command, err: %v", err)
		return false, err
	}
	// Process output in search for container name match.
	// The 'docker ps' output lists the container names in the last column.
	// Prefixing the container name with a space to avoid substring matching.
	spacedName := fmt.Sprintf(" %s", container)
	for itr, line := range strings.Split(stdout.String(), "\n") {
		// Skip the title row
		if itr == 0 {
			continue
		}
		// Docker adds spaces to the end of each row
		trimmedLine := strings.Trim(line, " ")
		if strings.HasSuffix(trimmedLine, spacedName) {
			return true, nil
		}
	}
	return false, nil
}

// HasDocker determines whether docker is installed
// and included in PATH.
func HasDocker() bool {
	cmd := exec.Command("which", "docker")
	return cmd.Run() == nil
}

// FullImageName creates a full image name from a repository URI, an image name, and a tag.
func FullImageName(repo, image, tag string) string {
	fullImage := repo
	if fullImage != "" && !strings.HasSuffix(fullImage, "/") {
		fullImage += "/"
	}
	fullImage += image

	if tag != "" {
		fullImage += ":" + tag
	}
	return fullImage
}

// Pull makes a command line call to docker to pull the specified container.
// docker pull repository/name:tag.
// It returns stdout, stderr, and any errors from running.
// This is a blocking call, and should be wrapped in a go routine for asynchonous use.
func Pull(image string) CommandResult {
	cmd := exec.Command("docker", "pull", image)
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err := cmd.Run()
	return CommandResult{stdout.String(), stderr.String(), err}
}

func setupArgs(container string, portMap map[int]int, volumeMap map[string]string, linkContainers []string, environment map[string]string) []string {
	var linkArgs []string
	for _, container := range linkContainers {
		linkArgs = append(linkArgs, fmt.Sprintf("--link=%s:%s", container, container))
	}

	var environmentVars []string
	for ev, val := range environment {
		environmentVars = append(environmentVars, "-e="+strconv.Quote(ev+"="+val))
	}

	var volumeList []string
	for hostVolume, containerVolume := range volumeMap {
		volumeList = append(volumeList, fmt.Sprintf("-v=%s:%s", hostVolume, containerVolume))
	}

	var exposePorts []string
	for hostPort, containerPort := range portMap {
		exposePorts = append(exposePorts, fmt.Sprintf("-p=127.0.0.1:%d:%d", hostPort, containerPort))
	}

	args := exposePorts
	args = append(args, linkArgs...)
	args = append(args, volumeList...)
	args = append(args, environmentVars...)
	args = append(args, fmt.Sprintf("--name=%s", container))
	return args
}

// RunAnalyzer runs the analyzer image with container analyzerContainer. It runs it at port (mapped
// to internal port 10005), binds the volumes for the workspacePath and logsPath, and gives the privileged
// if dind (docker-in-docker) is true.
func RunAnalyzer(image, analyzerContainer, workspacePath, logsPath string, port int, dind bool) CommandResult {
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	if len(analyzerContainer) == 0 {
		return CommandResult{"", "", errors.New("need to provide a name for the container")}
	}

	volumeMap := map[string]string{
		workspacePath: shipshapeWork,
		logsPath:      shipshapeLogs,
	}
	args := []string{"run"}
	if dind {
		args = append(args, "--privileged")
	}
	args = append(args, setupArgs(analyzerContainer, map[int]int{port: 10005}, volumeMap, nil, nil)...)
	args = append(args, "-d", image)

	glog.Infof("Running 'docker %v'\n", args)

	cmd := exec.Command("docker", args...)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err := cmd.Run()
	return CommandResult{stdout.String(), stderr.String(), err}
}

// RunService runs the shipshape service at image, as the container named container. It binds the
// shipshape workspace and logs appropriately. It starts with the third-party analyzers already
// running at analyzerContainers. The service is started with the privileged flag if dind (docker-in-docker)
// is true.
func RunService(image, container, workspacePath, logsPath string, analyzerContainers []string, dind bool) CommandResult {
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	if len(container) == 0 {
		return CommandResult{"", "", errors.New("need to provide a name for the container")}
	}

	volumeMap := map[string]string{workspacePath: shipshapeWork, logsPath: shipshapeLogs}

	var locations []string
	for _, container := range analyzerContainers {
		locations = append(locations, fmt.Sprintf(`$%s_PORT_10005_TCP_ADDR:$%s_PORT_10005_TCP_PORT`, strings.ToUpper(container), strings.ToUpper(container)))
	}
	locations = append(locations, "localhost:10005", "localhost:10006", "localhost:10008")

	args := []string{"run"}
	if dind {
		args = append(args, "--privileged")
	}
	args = append(args, setupArgs(container, map[int]int{10007: 10007}, volumeMap, analyzerContainers, map[string]string{"START_SERVICE": "true", "ANALYZERS": strings.Join(locations, ",")})...)
	args = append(args, "-d", image)

	glog.Infof("Running 'docker %v'\n", args)

	cmd := exec.Command("docker", args...)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err := cmd.Run()
	return CommandResult{stdout.String(), stderr.String(), err}
}

// RunKythe runs the specified kythe docker image at the named container. It uses the
// source root and extractor specified, and gives the privileged flag if dind (docker-in-docker) is true.
// It returns stdout, stderr, and any errors from running.
// This is a blocking call, and should be wrapped in a go routine for asynchonous use.
func RunKythe(image, container, sourcePath, extractor string, dind bool) CommandResult {
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	if len(container) == 0 {
		return CommandResult{"", "", errors.New("need to provide a name for the container")}
	}

	volumeMap := map[string]string{
		filepath.Join(sourcePath, "compilations"): "/compilations",
		sourcePath: "/repo",
	}
	home := os.Getenv("HOME")
	if len(home) > 0 {
		volumeMap[filepath.Join(home, ".m2")] = "/root/.m2"
	}

	// TODO(ciera): Can we exclude files in the .shipshape ignore path?
	// TODO(ciera/emso): Can we use the same command for blaze extraction?
	args := []string{"run"}
	if dind {
		args = append(args, "--privileged")
	}
	args = append(args, setupArgs(container, nil, volumeMap, nil, nil)...)
	args = append(args, "-i", "-a", "stdin", "-a", "stderr", "-a", "stdout", image)
	args = append(args, "--extract", extractor)

	cmd := exec.Command("docker", args...)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err := cmd.Run()
	return CommandResult{stdout.String(), stderr.String(), err}
}

// Stop stops a running container.
// It returns stdout, stderr, and any errors from running.
// This is a blocking call, and should be wrapped in a go routine for asynchonous use.
// If requested, also remove the container.
func Stop(container string, waitTime time.Duration, remove bool) CommandResult {
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	if container == "" {
		return CommandResult{"", "", errors.New("need to provide a name for the container")}
	}

	cmd := exec.Command("docker", "stop", fmt.Sprintf("-t=%d", int(waitTime.Seconds())), container)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err := cmd.Run()

	if err == nil && remove {
		cmd := exec.Command("docker", "rm", container)
		cmd.Stdout = stdout
		cmd.Stderr = stderr
		err = cmd.Run()
	}

	return CommandResult{stdout.String(), stderr.String(), err}
}

// OutOfDate returns true if the image specified
// has not been pulled recently.
func OutOfDate(image string) bool {
	// TODO(ciera): Rather than always return true,
	// check a file that contains the last time this
	// image was updated, and only return true if it
	// is at least N days old.
	return true
}

// ImageMatches returns whether the container is running
// the current version of image.
func ImageMatches(image, container string) bool {
	imageHash, err := inspect(image, "{{.Id}}")
	if err != nil {
		return false
	}
	containerHash, err := inspect(container, "{{.Image}}")
	if err != nil {
		return false
	}
	return bytes.Equal(imageHash, containerHash)
}

// ContainerId returns the id of the requested container
func ContainerId(container string) (string, error) {
	res, err := inspect(container, "{{.Id}}")
	return string(res), err
}

// MappedVolume returns whether path is already mapped into the workspace
// of the shipshape service running at container. If it is, it returns the relative path
// of path within the mapped volume.
func MappedVolume(path, container string) (bool, string) {
	// Sigh.
	// In docker < 1.8.0, docker inspect has a Volumes that is a map of strings to strings. We can't
	// directly index into the map though, because go has limitations of what values are a valid index,
	// and of course we are using those values.
	// In docker 1.8+, docker CHANGED THE FORMAT. docker inspect now has a Mounts that is an array of
	// objects. We have to go through the array and look for the item of interest.
	// into the array that contains the Destination of interest.
	versionArray, err := version()
	if err != nil {
		return false, ""
	}
	mappedVolumeFormat := `{{range $k, $v := .Mounts}} {{if eq $v.Destination "/shipshape-workspace"}} {{$v.Source}} {{end}} {{end}}`

	if versionArray[0] == 1 && versionArray[1] < 8 {
		mappedVolumeFormat = `{{range $k, $v := .Volumes}} {{if eq $k "/shipshape-workspace"}} {{$v}} {{end}} {{end}}`
	}
	v, err := inspect(container, mappedVolumeFormat)
	if err != nil {
		return false, ""
	}
	volume := strings.TrimSpace(string(v))
	// Handle the equal case
	if path == volume {
		return true, ""
	}
	// Handle the subdirectory case by adding a trailing '/'
	// Want to rule out the case: volume='/a/b2' and path='/a/b'
	path += "/"
	// We want to return true if the path we need is a subpath
	// of the directory we have mounted. That is, the start of path
	// is our volume.
	return strings.HasPrefix(path, volume), strings.TrimPrefix(path, volume)
}

// ContainsLinks returns whether the given container has links to the given
// list of containers.
func ContainsLinks(container string, linkedContainers []string) bool {
	l, err := inspect(container, `{{.HostConfig.Links}}`)
	if err != nil {
		return false
	}
	links := strings.TrimSpace(string(l))
	for _, linkedContainer := range linkedContainers {
		if !strings.Contains(links, linkedContainer) {
			return false
		}
	}
	return true
}

// inspect runs docker inspect on name, which must be either an image or a container.
// If non-empty, it uses the specified format string.
// Returns the combined stdout/stderr from running docker inspect
func inspect(name, format string) ([]byte, error) {
	var formatter string
	if len(format) != 0 {
		formatter = fmt.Sprintf("--format='%s'", format)
	}
	cmd := exec.Command("docker", "inspect", formatter, name)
	return cmd.CombinedOutput()
}

// version returns the docker version, as a []int. So docker version 1.8.3
// will be [1, 8, 3]
func version() ([]int, error) {
	cmd := exec.Command("docker", "version", "--format='{{.Server.Version}}'")
	raw, err := cmd.CombinedOutput()
	if err != nil {
		return nil, err
	}
	strParts := strings.Split(strings.TrimSpace(string(raw)), ".")
	parts := make([]int, len(strParts))
	for i, p := range strParts {
		parts[i], err = strconv.Atoi(p)
		if err != nil {
			return nil, err
		}
	}
	return parts, nil
}
<commit_msg>collinwinter comments<commit_after>/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Package docker contains simple utilities for pulling a docker image, starting
// a container, and stoping a container. It assumes that docker is installed. If
// it is not, it will simply throw an error.
package docker

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	glog "github.com/google/shipshape/third_party/go-glog"
)

const (
	shipshapeWork = "/shipshape-workspace"
	shipshapeLogs = "/shipshape-output"
)

// TODO(ciera): Consider making these all use channels.
type CommandResult struct {
	Stdout string
	Stderr string
	Err    error
}

func trimResult(stdout, stderr *bytes.Buffer, err error) CommandResult {
	return CommandResult{strings.TrimSpace(stdout.String()), strings.TrimSpace(stderr.String()), err}
}

// ContainerExists checks if a container with the given name is in the list of running, or old, containers.
func ContainerExists(container string) (bool, error) {
	// Setup and run command
	// TODO(ciera): When Travis and other places we run this at support docker
	// 1.8, we can drastically reduce this code by using the flag --format={{.Names}}
	// below and removing the logic to parse out the name ourselves.
	stdout := bytes.NewBuffer(nil)
	cmd := exec.Command("docker", "ps", "-a")
	cmd.Stdout = stdout
	if err := cmd.Run(); err != nil {
		fmt.Printf("Problem running command, err: %v", err)
		return false, err
	}
	// Process output in search for container name match.
	// The 'docker ps' output lists the container names in the last column.
	// Prefixing the container name with a space to avoid substring matching.
	spacedName := fmt.Sprintf(" %s", container)
	for itr, line := range strings.Split(stdout.String(), "\n") {
		// Skip the title row
		if itr == 0 {
			continue
		}
		// Docker adds spaces to the end of each row
		trimmedLine := strings.Trim(line, " ")
		if strings.HasSuffix(trimmedLine, spacedName) {
			return true, nil
		}
	}
	return false, nil
}

// HasDocker determines whether docker is installed
// and included in PATH.
func HasDocker() bool {
	cmd := exec.Command("which", "docker")
	return cmd.Run() == nil
}

// FullImageName creates a full image name from a repository URI, an image name, and a tag.
func FullImageName(repo, image, tag string) string {
	fullImage := repo
	if fullImage != "" && !strings.HasSuffix(fullImage, "/") {
		fullImage += "/"
	}
	fullImage += image

	if tag != "" {
		fullImage += ":" + tag
	}
	return fullImage
}

// Pull makes a command line call to docker to pull the specified container.
// docker pull repository/name:tag.
// It returns stdout, stderr, and any errors from running.
// This is a blocking call, and should be wrapped in a go routine for asynchonous use.
func Pull(image string) CommandResult {
	cmd := exec.Command("docker", "pull", image)
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err := cmd.Run()
	return CommandResult{stdout.String(), stderr.String(), err}
}

func setupArgs(container string, portMap map[int]int, volumeMap map[string]string, linkContainers []string, environment map[string]string) []string {
	var linkArgs []string
	for _, container := range linkContainers {
		linkArgs = append(linkArgs, fmt.Sprintf("--link=%s:%s", container, container))
	}

	var environmentVars []string
	for ev, val := range environment {
		environmentVars = append(environmentVars, "-e="+strconv.Quote(ev+"="+val))
	}

	var volumeList []string
	for hostVolume, containerVolume := range volumeMap {
		volumeList = append(volumeList, fmt.Sprintf("-v=%s:%s", hostVolume, containerVolume))
	}

	var exposePorts []string
	for hostPort, containerPort := range portMap {
		exposePorts = append(exposePorts, fmt.Sprintf("-p=127.0.0.1:%d:%d", hostPort, containerPort))
	}

	args := exposePorts
	args = append(args, linkArgs...)
	args = append(args, volumeList...)
	args = append(args, environmentVars...)
	args = append(args, fmt.Sprintf("--name=%s", container))
	return args
}

// RunAnalyzer runs the analyzer image with container analyzerContainer. It runs it at port (mapped
// to internal port 10005), binds the volumes for the workspacePath and logsPath, and gives the privileged
// if dind (docker-in-docker) is true.
func RunAnalyzer(image, analyzerContainer, workspacePath, logsPath string, port int, dind bool) CommandResult {
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	if len(analyzerContainer) == 0 {
		return CommandResult{"", "", errors.New("need to provide a name for the container")}
	}

	volumeMap := map[string]string{
		workspacePath: shipshapeWork,
		logsPath:      shipshapeLogs,
	}
	args := []string{"run"}
	if dind {
		args = append(args, "--privileged")
	}
	args = append(args, setupArgs(analyzerContainer, map[int]int{port: 10005}, volumeMap, nil, nil)...)
	args = append(args, "-d", image)

	glog.Infof("Running 'docker %v'\n", args)

	cmd := exec.Command("docker", args...)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err := cmd.Run()
	return CommandResult{stdout.String(), stderr.String(), err}
}

// RunService runs the shipshape service at image, as the container named container. It binds the
// shipshape workspace and logs appropriately. It starts with the third-party analyzers already
// running at analyzerContainers. The service is started with the privileged flag if dind (docker-in-docker)
// is true.
func RunService(image, container, workspacePath, logsPath string, analyzerContainers []string, dind bool) CommandResult {
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	if len(container) == 0 {
		return CommandResult{"", "", errors.New("need to provide a name for the container")}
	}

	volumeMap := map[string]string{workspacePath: shipshapeWork, logsPath: shipshapeLogs}

	var locations []string
	for _, container := range analyzerContainers {
		locations = append(locations, fmt.Sprintf(`$%s_PORT_10005_TCP_ADDR:$%s_PORT_10005_TCP_PORT`, strings.ToUpper(container), strings.ToUpper(container)))
	}
	locations = append(locations, "localhost:10005", "localhost:10006", "localhost:10008")

	args := []string{"run"}
	if dind {
		args = append(args, "--privileged")
	}
	args = append(args, setupArgs(container, map[int]int{10007: 10007}, volumeMap, analyzerContainers, map[string]string{"START_SERVICE": "true", "ANALYZERS": strings.Join(locations, ",")})...)
	args = append(args, "-d", image)

	glog.Infof("Running 'docker %v'\n", args)

	cmd := exec.Command("docker", args...)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err := cmd.Run()
	return CommandResult{stdout.String(), stderr.String(), err}
}

// RunKythe runs the specified kythe docker image at the named container. It uses the
// source root and extractor specified, and gives the privileged flag if dind (docker-in-docker) is true.
// It returns stdout, stderr, and any errors from running.
// This is a blocking call, and should be wrapped in a go routine for asynchonous use.
func RunKythe(image, container, sourcePath, extractor string, dind bool) CommandResult {
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	if len(container) == 0 {
		return CommandResult{"", "", errors.New("need to provide a name for the container")}
	}

	volumeMap := map[string]string{
		filepath.Join(sourcePath, "compilations"): "/compilations",
		sourcePath: "/repo",
	}
	home := os.Getenv("HOME")
	if len(home) > 0 {
		volumeMap[filepath.Join(home, ".m2")] = "/root/.m2"
	}

	// TODO(ciera): Can we exclude files in the .shipshape ignore path?
	// TODO(ciera/emso): Can we use the same command for blaze extraction?
	args := []string{"run"}
	if dind {
		args = append(args, "--privileged")
	}
	args = append(args, setupArgs(container, nil, volumeMap, nil, nil)...)
	args = append(args, "-i", "-a", "stdin", "-a", "stderr", "-a", "stdout", image)
	args = append(args, "--extract", extractor)

	cmd := exec.Command("docker", args...)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err := cmd.Run()
	return CommandResult{stdout.String(), stderr.String(), err}
}

// Stop stops a running container.
// It returns stdout, stderr, and any errors from running.
// This is a blocking call, and should be wrapped in a go routine for asynchonous use.
// If requested, also remove the container.
func Stop(container string, waitTime time.Duration, remove bool) CommandResult {
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	if container == "" {
		return CommandResult{"", "", errors.New("need to provide a name for the container")}
	}

	cmd := exec.Command("docker", "stop", fmt.Sprintf("-t=%d", int(waitTime.Seconds())), container)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err := cmd.Run()

	if err == nil && remove {
		cmd := exec.Command("docker", "rm", container)
		cmd.Stdout = stdout
		cmd.Stderr = stderr
		err = cmd.Run()
	}

	return CommandResult{stdout.String(), stderr.String(), err}
}

// OutOfDate returns true if the image specified
// has not been pulled recently.
func OutOfDate(image string) bool {
	// TODO(ciera): Rather than always return true,
	// check a file that contains the last time this
	// image was updated, and only return true if it
	// is at least N days old.
	return true
}

// ImageMatches returns whether the container is running
// the current version of image.
func ImageMatches(image, container string) bool {
	imageHash, err := inspect(image, "{{.Id}}")
	if err != nil {
		return false
	}
	containerHash, err := inspect(container, "{{.Image}}")
	if err != nil {
		return false
	}
	return bytes.Equal(imageHash, containerHash)
}

// ContainerId returns the id of the requested container
func ContainerId(container string) (string, error) {
	res, err := inspect(container, "{{.Id}}")
	return string(res), err
}

// MappedVolume returns whether path is already mapped into the workspace
// of the shipshape service running at container. If it is, it returns the relative path
// of path within the mapped volume.
func MappedVolume(path, container string) (bool, string) {
	// Sigh.
	// In docker < 1.8.0, docker inspect has a Volumes entry that is a map of strings to strings. We can't
	// directly index into the map though, because go requires indicies to have alphanumberics only,
	// and ours have punctuation since they are a path name.
	// In docker 1.8+, docker CHANGED THE FORMAT. docker inspect now has a Mounts that is an array of
	// objects. We have to go through the array and look for the item of interest.
	// into the array that contains the Destination of interest.

	// mappedVolumeFormat is a go format string that will retrieve the path that is mapped to
	// /shipshape-workspace
	mappedVolumeFormat := `{{range $k, $v := .Mounts}} {{if eq $v.Destination "/shipshape-workspace"}} {{$v.Source}} {{end}} {{end}}`
	versionArray, err := version()
	// Oh, yeah. In docker <1.7, getting the version doesn't even work because it doesn't have a
	// --format flag. So let's just assume that if we get an error here, it's old.
	if err != nil || (versionArray[0] == 1 && versionArray[1] < 8) {
		mappedVolumeFormat = `{{range $k, $v := .Volumes}} {{if eq $k "/shipshape-workspace"}} {{$v}} {{end}} {{end}}`
	}

	v, err := inspect(container, mappedVolumeFormat)
	if err != nil {
		return false, ""
	}
	volume := strings.TrimSpace(string(v))
	// Handle the equal case
	if path == volume {
		return true, ""
	}
	// Handle the subdirectory case by adding a trailing '/'
	// Want to rule out the case: volume='/a/b2' and path='/a/b'
	path += "/"
	// We want to return true if the path we need is a subpath
	// of the directory we have mounted. That is, the start of path
	// is our volume.
	return strings.HasPrefix(path, volume), strings.TrimPrefix(path, volume)
}

// ContainsLinks returns whether the given container has links to the given
// list of containers.
func ContainsLinks(container string, linkedContainers []string) bool {
	l, err := inspect(container, `{{.HostConfig.Links}}`)
	if err != nil {
		return false
	}
	links := strings.TrimSpace(string(l))
	for _, linkedContainer := range linkedContainers {
		if !strings.Contains(links, linkedContainer) {
			return false
		}
	}
	return true
}

// inspect runs docker inspect on name, which must be either an image or a container.
// If non-empty, it uses the specified format string.
// Returns the combined stdout/stderr from running docker inspect
func inspect(name, format string) ([]byte, error) {
	var formatter string
	if len(format) != 0 {
		formatter = fmt.Sprintf("--format='%s'", format)
	}
	cmd := exec.Command("docker", "inspect", formatter, name)
	return cmd.CombinedOutput()
}

// version returns the docker version, as a []int. So docker version 1.8.3
// will be [1, 8, 3]
func version() ([]int, error) {
	cmd := exec.Command("docker", "version", "--format='{{.Server.Version}}'")
	raw, err := cmd.CombinedOutput()
	if err != nil {
		return nil, err
	}
	strParts := strings.Split(strings.TrimSpace(string(raw)), ".")
	parts := make([]int, len(strParts))
	for i, p := range strParts {
		parts[i], err = strconv.Atoi(p)
		if err != nil {
			return nil, err
		}
	}
	return parts, nil
}
<|endoftext|>


<commit_before>package rrs

import (
	"os"
	"strconv"
)

// -----------------------------------------------------------------------------

var testingRRServers []*RRServer
var testingRRServerAddrs []string

func init() {
	// use SEQ_SERVERS envar to dynamically specify number of test servers
	var nbServers int64 = 5
	if n, err := strconv.ParseInt(os.Getenv("SEQ_SERVERS"), 10, 64); err == nil {
		nbServers = n
	}

	testingRRServers = make([]*RRServer, nbServers)

	var err error
	for i := int64(0); i < nbServers; i++ {
		testingRRServers[i], err = NewRRServer(":0", "") // warning: 0 peer
		if err != nil {
			panic(err)
		}
	}
	for i, s := range testingRRServers {
		for j := int64(0); j < nbServers; j++ {
			if j != int64(i) {
				// /!\ dynamically adding peers directly to the pool
				if err := s.cp.Add(testingRRServers[j].Addr().String()); err != nil {
					panic(err)
				}
			}
		}
	}

	testingRRServerAddrs = make([]string, nbServers)
	for i := range testingRRServerAddrs {
		testingRRServerAddrs[i] = testingRRServers[i].Addr().String()
	}
}
<commit_msg>RRSeq/RRServer tests: added SEQ_FSYNC env var<commit_after>package rrs

import (
	"io/ioutil"
	"os"
	"strconv"
)

// ENV_VARS:
//
// SEQ_SERVERS=<int> -> specifies the number N of nodes in the cluster (default: 5)
// SEQ_FSYNC={0,1}   -> enables or disables disk persistence (default: disabled)

// -----------------------------------------------------------------------------

var testingRRServers []*RRServer
var testingRRServerAddrs []string

func init() {
	var nbServers int64 = 5
	if n, err := strconv.ParseInt(os.Getenv("SEQ_SERVERS"), 10, 64); err == nil {
		nbServers = n
	}

	var path string
	fsync := false
	if os.Getenv("SEQ_FSYNC") == "1" {
		fsync = true
	}
	if fsync {
		f, err := ioutil.TempFile("", "seq_tests")
		if err != nil {
			panic(err)
		}
		path = f.Name()
		_ = f.Close()
	}

	testingRRServers = make([]*RRServer, nbServers)

	var err error
	for i := int64(0); i < nbServers; i++ {
		testingRRServers[i], err = NewRRServer(":0", path) // warning "0 peer" expected
		if err != nil {
			panic(err)
		}
	}
	for i, s := range testingRRServers {
		for j := int64(0); j < nbServers; j++ {
			if j != int64(i) {
				// /!\ dynamically adding peers directly to the pool
				if err := s.cp.Add(testingRRServers[j].Addr().String()); err != nil {
					panic(err)
				}
			}
		}
	}

	testingRRServerAddrs = make([]string, nbServers)
	for i := range testingRRServerAddrs {
		testingRRServerAddrs[i] = testingRRServers[i].Addr().String()
	}
}
<|endoftext|>


<commit_before>// Package auto implements an on-the-fly loading file backend.
package auto

import (
	"context"
	"regexp"
	"time"

	"github.com/coredns/coredns/plugin"
	"github.com/coredns/coredns/plugin/file"
	"github.com/coredns/coredns/plugin/metrics"
	"github.com/coredns/coredns/plugin/pkg/upstream"
	"github.com/coredns/coredns/plugin/transfer"
	"github.com/coredns/coredns/request"

	"github.com/miekg/dns"
)

type (
	// Auto holds the zones and the loader configuration for automatically loading zones.
	Auto struct {
		Next plugin.Handler
		*Zones

		metrics  *metrics.Metrics
		transfer *transfer.Transfer
		loader
	}

	loader struct {
		directory string
		template  string
		re        *regexp.Regexp

		ReloadInterval time.Duration
		upstream       *upstream.Upstream // Upstream for looking up names during the resolution process.
	}
)

// ServeDNS implements the plugin.Handler interface.
func (a Auto) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
	state := request.Request{W: w, Req: r}
	qname := state.Name()

	// Precheck with the origins, i.e. are we allowed to look here?
	zone := plugin.Zones(a.Zones.Origins()).Matches(qname)
	if zone == "" {
		return plugin.NextOrFailure(a.Name(), a.Next, ctx, w, r)
	}

	// Now the real zone.
	zone = plugin.Zones(a.Zones.Names()).Matches(qname)

	a.Zones.RLock()
	z, ok := a.Zones.Z[zone]
	a.Zones.RUnlock()

	if !ok || z == nil {
		return dns.RcodeServerFailure, nil
	}

	answer, ns, extra, result := z.Lookup(ctx, state, qname)

	m := new(dns.Msg)
	m.SetReply(r)
	m.Authoritative = true
	m.Answer, m.Ns, m.Extra = answer, ns, extra

	switch result {
	case file.Success:
	case file.NoData:
	case file.NameError:
		m.Rcode = dns.RcodeNameError
	case file.Delegation:
		m.Authoritative = false
	case file.ServerFailure:
		return dns.RcodeServerFailure, nil
	}

	w.WriteMsg(m)
	return dns.RcodeSuccess, nil
}

// Name implements the Handler interface.
func (a Auto) Name() string { return "auto" }
<commit_msg>plugins/auto: allow fallthrough if no zone match (#4166)<commit_after>// Package auto implements an on-the-fly loading file backend.
package auto

import (
	"context"
	"regexp"
	"time"

	"github.com/coredns/coredns/plugin"
	"github.com/coredns/coredns/plugin/file"
	"github.com/coredns/coredns/plugin/metrics"
	"github.com/coredns/coredns/plugin/pkg/upstream"
	"github.com/coredns/coredns/plugin/transfer"
	"github.com/coredns/coredns/request"

	"github.com/miekg/dns"
)

type (
	// Auto holds the zones and the loader configuration for automatically loading zones.
	Auto struct {
		Next plugin.Handler
		*Zones

		metrics  *metrics.Metrics
		transfer *transfer.Transfer
		loader
	}

	loader struct {
		directory string
		template  string
		re        *regexp.Regexp

		ReloadInterval time.Duration
		upstream       *upstream.Upstream // Upstream for looking up names during the resolution process.
	}
)

// ServeDNS implements the plugin.Handler interface.
func (a Auto) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
	state := request.Request{W: w, Req: r}
	qname := state.Name()

	// Precheck with the origins, i.e. are we allowed to look here?
	zone := plugin.Zones(a.Zones.Origins()).Matches(qname)
	if zone == "" {
		return plugin.NextOrFailure(a.Name(), a.Next, ctx, w, r)
	}

	// Now the real zone.
	zone = plugin.Zones(a.Zones.Names()).Matches(qname)
	if zone == "" {
		return plugin.NextOrFailure(a.Name(), a.Next, ctx, w, r)
	}

	a.Zones.RLock()
	z, ok := a.Zones.Z[zone]
	a.Zones.RUnlock()

	if !ok || z == nil {
		return dns.RcodeServerFailure, nil
	}

	answer, ns, extra, result := z.Lookup(ctx, state, qname)

	m := new(dns.Msg)
	m.SetReply(r)
	m.Authoritative = true
	m.Answer, m.Ns, m.Extra = answer, ns, extra

	switch result {
	case file.Success:
	case file.NoData:
	case file.NameError:
		m.Rcode = dns.RcodeNameError
	case file.Delegation:
		m.Authoritative = false
	case file.ServerFailure:
		return dns.RcodeServerFailure, nil
	}

	w.WriteMsg(m)
	return dns.RcodeSuccess, nil
}

// Name implements the Handler interface.
func (a Auto) Name() string { return "auto" }
<|endoftext|>


<commit_before><commit_msg>Ub-1734: added wwn to log message (#277)<commit_after><|endoftext|>


<commit_before>package quickselect

import (
  "testing"
)

type TestData struct {
  Array []int
}

func (t TestData) Len() (int) {
  return len(t.Array)
}

func (t TestData) Less(i, j int) bool {
  return t.Array[i] < t.Array[j]
}

func (t TestData) Swap(i, j int) {
  t.Array[i], t.Array[j] = t.Array[j], t.Array[i]
}

func TestQuickSelectWithSimpleArray(t *testing.T) {
  fixture := TestData{[]int{50, 20, 30, 25, 45, 2, 6, 10, 3, 4, 5}}
  QuickSelect(fixture, 5)

  smallestK := fixture.Array[:5]
  expectedK := []int{2,3,4,5,6}
  if !hasSameElements(smallestK, expectedK) {
    t.Errorf("Expected smallest K elements to be '%s', but got '%s'", expectedK, smallestK)
  }
}

func hasSameElements(array1, array2 []int) (bool) {
  elements := make(map[int]int)

  for _, elem1 := range array1 {
    elements[elem1]++
  }

  for _, elem2 := range array2 {
    elements[elem2]--
  }

  for _, count := range elements {
    if count != 0 {
      return false
    }
  }
  return true
}
<commit_msg>Adding test for repeated elements.<commit_after>package quickselect

import (
  "testing"
)

type TestData struct {
  Array []int
}

func (t TestData) Len() (int) {
  return len(t.Array)
}

func (t TestData) Less(i, j int) bool {
  return t.Array[i] < t.Array[j]
}

func (t TestData) Swap(i, j int) {
  t.Array[i], t.Array[j] = t.Array[j], t.Array[i]
}

func TestQuickSelectWithSimpleArray(t *testing.T) {
  fixture := TestData{[]int{50, 20, 30, 25, 45, 2, 6, 10, 3, 4, 5}}
  QuickSelect(fixture, 5)

  smallestK := fixture.Array[:5]
  expectedK := []int{2,3,4,5,6}
  if !hasSameElements(smallestK, expectedK) {
    t.Errorf("Expected smallest K elements to be '%s', but got '%s'", expectedK, smallestK)
  }
}

func TestQuickSelectWithRepeatedElements(t *testing.T) {
  fixture := TestData{[]int{2, 10, 5, 3, 2, 6, 2, 6, 10, 3, 4, 5}}
  QuickSelect(fixture, 5)

  smallestK := fixture.Array[:5]
  expectedK := []int{2,2,2,3,3}
  if !hasSameElements(smallestK, expectedK) {
    t.Errorf("Expected smallest K elements to be '%s', but got '%s'", expectedK, smallestK)
  }
}

func hasSameElements(array1, array2 []int) (bool) {
  elements := make(map[int]int)

  for _, elem1 := range array1 {
    elements[elem1]++
  }

  for _, elem2 := range array2 {
    elements[elem2]--
  }

  for _, count := range elements {
    if count != 0 {
      return false
    }
  }
  return true
}
<|endoftext|>


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

// +build darwin linux

package main

import (
	"log"
	"time"

	"github.com/rakyll/sensors"
	"golang.org/x/mobile/app"
)

func main() {
	app.Main(func(a app.App) {
		sensor.Enable(a, sensor.Accelerometer, 100*time.Millisecond)
		sensor.Enable(a, sensor.Gyroscope, time.Second)

		go func() {
			<-time.Tick(time.Second)
			sensor.Disable(sensor.Gyroscope)
		}()

		for e := range a.Events() {
			log.Println(e)
		}
	})
}
<commit_msg>Import gl, so the app doesn't stall.<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build darwin linux

// An app that draws a green triangle on a red background.
//
// Note: This demo is an early preview of Go 1.5. In order to build this
// program as an Android APK using the gomobile tool.
//
// See http://godoc.org/golang.org/x/mobile/cmd/gomobile to install gomobile.
//
// Get the basic example and use gomobile to build or install it on your device.
//
//   $ go get -d golang.org/x/mobile/example/basic
//   $ gomobile build golang.org/x/mobile/example/basic # will build an APK
//
//   # plug your Android device to your computer or start an Android emulator.
//   # if you have adb installed on your machine, use gomobile install to
//   # build and deploy the APK to an Android target.
//   $ gomobile install golang.org/x/mobile/example/basic
//
// Switch to your device or emulator to start the Basic application from
// the launcher.
// You can also run the application on your desktop by running the command
// below. (Note: It currently doesn't work on Windows.)
//   $ go install golang.org/x/mobile/example/basic && basic
package main

import (
	"encoding/binary"
	"log"
	"time"

	"github.com/rakyll/sensors"
	"golang.org/x/mobile/app"
	"golang.org/x/mobile/event/lifecycle"
	"golang.org/x/mobile/event/paint"
	"golang.org/x/mobile/event/size"
	"golang.org/x/mobile/event/touch"
	"golang.org/x/mobile/exp/app/debug"
	"golang.org/x/mobile/exp/f32"
	"golang.org/x/mobile/exp/gl/glutil"
	"golang.org/x/mobile/gl"
)

var (
	program  gl.Program
	position gl.Attrib
	offset   gl.Uniform
	color    gl.Uniform
	buf      gl.Buffer

	green  float32
	touchX float32
	touchY float32
)

func main() {
	app.Main(func(a app.App) {
		sensor.Enable(a, sensor.Accelerometer, 100*time.Millisecond)

		var sz size.Event
		for e := range a.Events() {
			switch e := app.Filter(e).(type) {
			case lifecycle.Event:
				switch e.Crosses(lifecycle.StageVisible) {
				case lifecycle.CrossOn:
					onStart()
				case lifecycle.CrossOff:
					onStop()
				}
			case size.Event:
				sz = e
				touchX = float32(sz.WidthPx / 2)
				touchY = float32(sz.HeightPx / 2)
			case paint.Event:
				onPaint(sz)
				a.EndPaint(e)
			case touch.Event:
				touchX = e.X
				touchY = e.Y
			case sensor.Event:
				// do nothing
			}
		}
	})
}

func onStart() {
	var err error
	program, err = glutil.CreateProgram(vertexShader, fragmentShader)
	if err != nil {
		log.Printf("error creating GL program: %v", err)
		return
	}

	buf = gl.CreateBuffer()
	gl.BindBuffer(gl.ARRAY_BUFFER, buf)
	gl.BufferData(gl.ARRAY_BUFFER, triangleData, gl.STATIC_DRAW)

	position = gl.GetAttribLocation(program, "position")
	color = gl.GetUniformLocation(program, "color")
	offset = gl.GetUniformLocation(program, "offset")

	// TODO(crawshaw): the debug package needs to put GL state init here
	// Can this be an app.RegisterFilter call now??
}

func onStop() {
	gl.DeleteProgram(program)
	gl.DeleteBuffer(buf)
}

func onPaint(sz size.Event) {
	gl.ClearColor(1, 0, 0, 1)
	gl.Clear(gl.COLOR_BUFFER_BIT)

	gl.UseProgram(program)

	green += 0.01
	if green > 1 {
		green = 0
	}
	gl.Uniform4f(color, 0, green, 0, 1)

	gl.Uniform2f(offset, touchX/float32(sz.WidthPx), touchY/float32(sz.HeightPx))

	gl.BindBuffer(gl.ARRAY_BUFFER, buf)
	gl.EnableVertexAttribArray(position)
	gl.VertexAttribPointer(position, coordsPerVertex, gl.FLOAT, false, 0, 0)
	gl.DrawArrays(gl.TRIANGLES, 0, vertexCount)
	gl.DisableVertexAttribArray(position)

	debug.DrawFPS(sz)
}

var triangleData = f32.Bytes(binary.LittleEndian,
	0.0, 0.4, 0.0, // top left
	0.0, 0.0, 0.0, // bottom left
	0.4, 0.0, 0.0, // bottom right
)

const (
	coordsPerVertex = 3
	vertexCount     = 3
)

const vertexShader = `#version 100
uniform vec2 offset;

attribute vec4 position;
void main() {
	// offset comes in with x/y values between 0 and 1.
	// position bounds are -1 to 1.
	vec4 offset4 = vec4(2.0*offset.x-1.0, 1.0-2.0*offset.y, 0, 0);
	gl_Position = position + offset4;
}`

const fragmentShader = `#version 100
precision mediump float;
uniform vec4 color;
void main() {
	gl_FragColor = color;
}`
<|endoftext|>


<commit_before>package digraph

import (
	"bytes"
	"strings"
	"testing"
)

func TestWriteDot(t *testing.T) {
	nodes := ParseBasic(`a -> b ; foo
a -> c
b -> d
b -> e
`)
	var nlist []Node
	for _, n := range nodes {
		nlist = append(nlist, n)
	}

	buf := bytes.NewBuffer(nil)
	if err := WriteDot(buf, nlist); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(string(buf.Bytes()))
	expected := strings.TrimSpace(writeDotStr)
	if actual != expected {
		t.Fatalf("bad: %s", actual)
	}
}

const writeDotStr = `
digraph {
	"a";
	"a" -> "b" [label="foo"];
	"a" -> "c" [label="Edge"];
	"b";
	"b" -> "d" [label="Edge"];
	"b" -> "e" [label="Edge"];
	"c";
	"d";
	"e";
}
`
<commit_msg>Fix TestWriteDot random order error<commit_after>package digraph

import (
	"bytes"
	"strings"
	"testing"
)

func TestWriteDot(t *testing.T) {
	nodes := ParseBasic(`a -> b ; foo
a -> c
b -> d
b -> e
`)
	var nlist []Node
	for _, n := range nodes {
		nlist = append(nlist, n)
	}

	buf := bytes.NewBuffer(nil)
	if err := WriteDot(buf, nlist); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(string(buf.Bytes()))
	expected := strings.TrimSpace(writeDotStr)

	actualLines := strings.Split(actual, "\n")
	expectedLines := strings.Split(expected, "\n")

	if actualLines[0] != expectedLines[0] ||
		actualLines[len(actualLines)-1] != expectedLines[len(expectedLines)-1] ||
		len(actualLines) != len(expectedLines) {
		t.Fatalf("bad: %s", actual)
	}

	count := 0
	for _, el := range expectedLines[1 : len(expectedLines)-1] {
		for _, al := range actualLines[1 : len(actualLines)-1] {
			if el == al {
				count++
				break
			}
		}
	}

	if count != len(expectedLines)-2 {
		t.Fatalf("bad: %s", actual)
	}
}

const writeDotStr = `
digraph {
	"a";
	"a" -> "b" [label="foo"];
	"a" -> "c" [label="Edge"];
	"b";
	"b" -> "d" [label="Edge"];
	"b" -> "e" [label="Edge"];
	"c";
	"d";
	"e";
}
`
<|endoftext|>


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

package service

import (
	stderrors "errors"
	"io"
	"net/http"
	"sync"

	"github.com/tsuru/tsuru/action"
	"github.com/tsuru/tsuru/app/bind"
	"github.com/tsuru/tsuru/db"
	"github.com/tsuru/tsuru/errors"
	"github.com/tsuru/tsuru/log"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

// createServiceInstance is an action that calls the service endpoint
// to create a service instance.
//
// The first argument in the context must be a Service.
// The second argument in the context must be a ServiceInstance.
var createServiceInstance = action.Action{
	Name: "create-service-instance",
	Forward: func(ctx action.FWContext) (action.Result, error) {
		service, ok := ctx.Params[0].(Service)
		if !ok {
			return nil, stderrors.New("First parameter must be a Service.")
		}
		endpoint, err := service.getClient("production")
		if err != nil {
			return nil, err
		}
		instance, ok := ctx.Params[1].(ServiceInstance)
		if !ok {
			return nil, stderrors.New("Second parameter must be a ServiceInstance.")
		}
		user, ok := ctx.Params[2].(string)
		if !ok {
			return nil, stderrors.New("Third parameter must be a string.")
		}
		err = endpoint.Create(&instance, user)
		if err != nil {
			return nil, err
		}
		return instance, nil
	},
	Backward: func(ctx action.BWContext) {
		service, ok := ctx.Params[0].(Service)
		if !ok {
			return
		}
		endpoint, err := service.getClient("production")
		if err != nil {
			return
		}
		instance, ok := ctx.Params[1].(ServiceInstance)
		if !ok {
			return
		}
		endpoint.Destroy(&instance)
	},
	MinParams: 2,
}

// insertServiceInstance is an action that inserts an instance in the database.
//
// The first argument in the context must be a Service Instance.
var insertServiceInstance = action.Action{
	Name: "insert-service-instance",
	Forward: func(ctx action.FWContext) (action.Result, error) {
		instance, ok := ctx.Params[1].(ServiceInstance)
		if !ok {
			return nil, stderrors.New("Second parameter must be a ServiceInstance.")
		}
		conn, err := db.Conn()
		if err != nil {
			return nil, err
		}
		defer conn.Close()
		err = conn.ServiceInstances().Insert(&instance)
		if err != nil {
			return nil, err
		}
		return nil, nil
	},
	Backward: func(ctx action.BWContext) {
		instance, ok := ctx.Params[1].(ServiceInstance)
		if !ok {
			return
		}
		conn, err := db.Conn()
		if err != nil {
			return
		}
		defer conn.Close()
		conn.ServiceInstances().Remove(bson.M{"name": instance.Name})
	},
	MinParams: 2,
}

type bindPipelineArgs struct {
	app             bind.App
	writer          io.Writer
	serviceInstance *ServiceInstance
}

var bindAppDBAction = action.Action{
	Name: "bind-app-db",
	Forward: func(ctx action.FWContext) (action.Result, error) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		if args == nil {
			return nil, stderrors.New("invalid arguments for pipeline, expected *bindPipelineArgs")
		}
		conn, err := db.Conn()
		if err != nil {
			return nil, err
		}
		defer conn.Close()
		si := args.serviceInstance
		updateOp := bson.M{"$addToSet": bson.M{"apps": args.app.GetName()}}
		err = conn.ServiceInstances().Update(bson.M{"name": si.Name, "apps": bson.M{"$ne": args.app.GetName()}}, updateOp)
		if err != nil {
			if err == mgo.ErrNotFound {
				return nil, &errors.HTTP{Code: http.StatusConflict, Message: "This app is already bound to this service instance."}
			}
			return nil, err
		}
		return nil, nil
	},
	Backward: func(ctx action.BWContext) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		if err := args.serviceInstance.update(bson.M{"$pull": bson.M{"apps": args.app.GetName()}}); err != nil {
			log.Errorf("[bind-app-db backward] could not remove app from service instance: %s", err)
		}
	},
	MinParams: 1,
}

var bindAppEndpointAction = action.Action{
	Name: "bind-app-endpoint",
	Forward: func(ctx action.FWContext) (action.Result, error) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		if args == nil {
			return nil, stderrors.New("invalid arguments for pipeline, expected *bindPipelineArgs")
		}
		endpoint, err := args.serviceInstance.Service().getClient("production")
		if err != nil {
			return nil, err
		}
		return endpoint.BindApp(args.serviceInstance, args.app)
	},
	Backward: func(ctx action.BWContext) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		endpoint, err := args.serviceInstance.Service().getClient("production")
		if err != nil {
			log.Errorf("[bind-app-endpoint backward] could not get endpoint: %s", err)
			return
		}
		err = endpoint.UnbindApp(args.serviceInstance, args.app)
		if err != nil {
			log.Errorf("[bind-app-endpoint backward] failed to unbind unit: %s", err)
		}
	},
	MinParams: 1,
}

var setBindedEnvsAction = action.Action{
	Name: "set-binded-envs",
	Forward: func(ctx action.FWContext) (action.Result, error) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		if args == nil {
			return nil, stderrors.New("invalid arguments for pipeline, expected *bindPipelineArgs")
		}
		instance := bind.ServiceInstance{
			Name: args.serviceInstance.Name,
			Envs: ctx.Previous.(map[string]string),
		}
		return instance, args.app.AddInstance(args.serviceInstance.ServiceName, instance, args.writer)
	},
	Backward: func(ctx action.BWContext) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		instance := ctx.FWResult.(bind.ServiceInstance)
		err := args.app.RemoveInstance(args.serviceInstance.ServiceName, instance, args.writer)
		if err != nil {
			log.Errorf("[set-binded-envs backward] failed to remove instance: %s", err)
		}
	},
}

var bindUnitsAction = action.Action{
	Name: "bind-units",
	Forward: func(ctx action.FWContext) (action.Result, error) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		if args == nil {
			return ctx.Previous, stderrors.New("invalid arguments for pipeline, expected *bindPipelineArgs")
		}
		var wg sync.WaitGroup
		si := args.serviceInstance
		units, err := args.app.GetUnits()
		if err != nil {
			return nil, err
		}
		errCh := make(chan error, len(units))
		unbindedCh := make(chan bind.Unit, len(units))
		for i := range units {
			wg.Add(1)
			go func(i int) {
				defer wg.Done()
				unit := units[i]
				err := si.BindUnit(args.app, unit)
				if err == nil || err == ErrUnitAlreadyBound {
					unbindedCh <- unit
				} else {
					errCh <- err
				}
			}(i)
		}
		wg.Wait()
		close(errCh)
		close(unbindedCh)
		if err := <-errCh; err != nil {
			for unit := range unbindedCh {
				unbindErr := si.UnbindUnit(args.app, unit)
				if unbindErr != nil {
					log.Errorf("[bind-units forward] failed to unbind unit after error: %s", unbindErr)
				}
			}
			return ctx.Previous, err
		}
		return ctx.Previous, nil
	},
	Backward: func(ctx action.BWContext) {
	},
}

var unbindUnits = action.Action{
	Name: "unbind-units",
	Forward: func(ctx action.FWContext) (action.Result, error) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		if args == nil {
			return nil, stderrors.New("invalid arguments for pipeline, expected *bindPipelineArgs")
		}
		var wg sync.WaitGroup
		si := args.serviceInstance
		units, err := args.app.GetUnits()
		if err != nil {
			return nil, err
		}
		errCh := make(chan error, len(units))
		unbindedCh := make(chan bind.Unit, len(units))
		for i := range units {
			wg.Add(1)
			go func(i int) {
				defer wg.Done()
				unit := units[i]
				err := si.UnbindUnit(args.app, unit)
				if err == nil || err == ErrUnitNotBound {
					unbindedCh <- unit
				} else {
					errCh <- err
				}
			}(i)
		}
		wg.Wait()
		close(errCh)
		close(unbindedCh)
		if err := <-errCh; err != nil {
			for unit := range unbindedCh {
				rebindErr := si.BindUnit(args.app, unit)
				if rebindErr != nil {
					log.Errorf("[unbind-units forward] failed to rebind unit after error: %s", rebindErr)
				}
			}
			return nil, err
		}
		return nil, nil
	},
	Backward: func(ctx action.BWContext) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		units, err := args.app.GetUnits()
		if err != nil {
			log.Errorf("[unbind-units backward] failed get units to rebind in rollback: %s", err)
		}
		for _, unit := range units {
			err := args.serviceInstance.BindUnit(args.app, unit)
			if err != nil {
				log.Errorf("[unbind-units backward] failed to rebind unit in rollback: %s", err)
			}
		}
	},
	MinParams: 1,
}

var unbindAppDB = action.Action{
	Name: "unbind-app-db",
	Forward: func(ctx action.FWContext) (action.Result, error) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		if args == nil {
			return nil, stderrors.New("invalid arguments for pipeline, expected *bindPipelineArgs")
		}
		err := args.serviceInstance.update(bson.M{"$pull": bson.M{"apps": args.app.GetName()}})
		if err != nil {
			return nil, err
		}
		return nil, err
	},
	Backward: func(ctx action.BWContext) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		err := args.serviceInstance.update(bson.M{"$addToSet": bson.M{"apps": args.app.GetName()}})
		if err != nil {
			log.Errorf("[unbind-app-db backward] failed to rebind app in db: %s", err)
		}
	},
	MinParams: 1,
}

var unbindAppEndpoint = action.Action{
	Name: "unbind-app-endpoint",
	Forward: func(ctx action.FWContext) (action.Result, error) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		if args == nil {
			return nil, stderrors.New("invalid arguments for pipeline, expected *bindPipelineArgs")
		}
		if endpoint, err := args.serviceInstance.Service().getClient("production"); err == nil {
			err := endpoint.UnbindApp(args.serviceInstance, args.app)
			if err != nil {
				return nil, err
			}
		}
		return nil, nil
	},
	Backward: func(ctx action.BWContext) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		if endpoint, err := args.serviceInstance.Service().getClient("production"); err == nil {
			_, err := endpoint.BindApp(args.serviceInstance, args.app)
			if err != nil {
				log.Errorf("[unbind-app-endpoint backward] failed to rebind app in endpoint: %s", err)
			}
		}
	},
	MinParams: 1,
}

var removeBindedEnvs = action.Action{
	Name: "remove-binded-envs",
	Forward: func(ctx action.FWContext) (action.Result, error) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		if args == nil {
			return nil, stderrors.New("invalid arguments for pipeline, expected *bindPipelineArgs")
		}
		si := args.serviceInstance
		instance := bind.ServiceInstance{Name: si.Name, Envs: make(map[string]string)}
		for k, envVar := range args.app.InstanceEnv(si.Name) {
			instance.Envs[k] = envVar.Value
		}
		return nil, args.app.RemoveInstance(si.ServiceName, instance, args.writer)
	},
	Backward: func(ctx action.BWContext) {
	},
	MinParams: 1,
}<commit_msg>Actions: file formatted<commit_after>// Copyright 2015 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package service

import (
	stderrors "errors"
	"io"
	"net/http"
	"sync"

	"github.com/tsuru/tsuru/action"
	"github.com/tsuru/tsuru/app/bind"
	"github.com/tsuru/tsuru/db"
	"github.com/tsuru/tsuru/errors"
	"github.com/tsuru/tsuru/log"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

// createServiceInstance is an action that calls the service endpoint
// to create a service instance.
//
// The first argument in the context must be a Service.
// The second argument in the context must be a ServiceInstance.
var createServiceInstance = action.Action{
	Name: "create-service-instance",
	Forward: func(ctx action.FWContext) (action.Result, error) {
		service, ok := ctx.Params[0].(Service)
		if !ok {
			return nil, stderrors.New("First parameter must be a Service.")
		}
		endpoint, err := service.getClient("production")
		if err != nil {
			return nil, err
		}
		instance, ok := ctx.Params[1].(ServiceInstance)
		if !ok {
			return nil, stderrors.New("Second parameter must be a ServiceInstance.")
		}
		user, ok := ctx.Params[2].(string)
		if !ok {
			return nil, stderrors.New("Third parameter must be a string.")
		}
		err = endpoint.Create(&instance, user)
		if err != nil {
			return nil, err
		}
		return instance, nil
	},
	Backward: func(ctx action.BWContext) {
		service, ok := ctx.Params[0].(Service)
		if !ok {
			return
		}
		endpoint, err := service.getClient("production")
		if err != nil {
			return
		}
		instance, ok := ctx.Params[1].(ServiceInstance)
		if !ok {
			return
		}
		endpoint.Destroy(&instance)
	},
	MinParams: 2,
}

// insertServiceInstance is an action that inserts an instance in the database.
//
// The first argument in the context must be a Service Instance.
var insertServiceInstance = action.Action{
	Name: "insert-service-instance",
	Forward: func(ctx action.FWContext) (action.Result, error) {
		instance, ok := ctx.Params[1].(ServiceInstance)
		if !ok {
			return nil, stderrors.New("Second parameter must be a ServiceInstance.")
		}
		conn, err := db.Conn()
		if err != nil {
			return nil, err
		}
		defer conn.Close()
		err = conn.ServiceInstances().Insert(&instance)
		if err != nil {
			return nil, err
		}
		return nil, nil
	},
	Backward: func(ctx action.BWContext) {
		instance, ok := ctx.Params[1].(ServiceInstance)
		if !ok {
			return
		}
		conn, err := db.Conn()
		if err != nil {
			return
		}
		defer conn.Close()
		conn.ServiceInstances().Remove(bson.M{"name": instance.Name})
	},
	MinParams: 2,
}

type bindPipelineArgs struct {
	app             bind.App
	writer          io.Writer
	serviceInstance *ServiceInstance
}

var bindAppDBAction = action.Action{
	Name: "bind-app-db",
	Forward: func(ctx action.FWContext) (action.Result, error) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		if args == nil {
			return nil, stderrors.New("invalid arguments for pipeline, expected *bindPipelineArgs")
		}
		conn, err := db.Conn()
		if err != nil {
			return nil, err
		}
		defer conn.Close()
		si := args.serviceInstance
		updateOp := bson.M{"$addToSet": bson.M{"apps": args.app.GetName()}}
		err = conn.ServiceInstances().Update(bson.M{"name": si.Name, "apps": bson.M{"$ne": args.app.GetName()}}, updateOp)
		if err != nil {
			if err == mgo.ErrNotFound {
				return nil, &errors.HTTP{Code: http.StatusConflict, Message: "This app is already bound to this service instance."}
			}
			return nil, err
		}
		return nil, nil
	},
	Backward: func(ctx action.BWContext) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		if err := args.serviceInstance.update(bson.M{"$pull": bson.M{"apps": args.app.GetName()}}); err != nil {
			log.Errorf("[bind-app-db backward] could not remove app from service instance: %s", err)
		}
	},
	MinParams: 1,
}

var bindAppEndpointAction = action.Action{
	Name: "bind-app-endpoint",
	Forward: func(ctx action.FWContext) (action.Result, error) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		if args == nil {
			return nil, stderrors.New("invalid arguments for pipeline, expected *bindPipelineArgs")
		}
		endpoint, err := args.serviceInstance.Service().getClient("production")
		if err != nil {
			return nil, err
		}
		return endpoint.BindApp(args.serviceInstance, args.app)
	},
	Backward: func(ctx action.BWContext) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		endpoint, err := args.serviceInstance.Service().getClient("production")
		if err != nil {
			log.Errorf("[bind-app-endpoint backward] could not get endpoint: %s", err)
			return
		}
		err = endpoint.UnbindApp(args.serviceInstance, args.app)
		if err != nil {
			log.Errorf("[bind-app-endpoint backward] failed to unbind unit: %s", err)
		}
	},
	MinParams: 1,
}

var setBindedEnvsAction = action.Action{
	Name: "set-binded-envs",
	Forward: func(ctx action.FWContext) (action.Result, error) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		if args == nil {
			return nil, stderrors.New("invalid arguments for pipeline, expected *bindPipelineArgs")
		}
		instance := bind.ServiceInstance{
			Name: args.serviceInstance.Name,
			Envs: ctx.Previous.(map[string]string),
		}
		return instance, args.app.AddInstance(args.serviceInstance.ServiceName, instance, args.writer)
	},
	Backward: func(ctx action.BWContext) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		instance := ctx.FWResult.(bind.ServiceInstance)
		err := args.app.RemoveInstance(args.serviceInstance.ServiceName, instance, args.writer)
		if err != nil {
			log.Errorf("[set-binded-envs backward] failed to remove instance: %s", err)
		}
	},
}

var bindUnitsAction = action.Action{
	Name: "bind-units",
	Forward: func(ctx action.FWContext) (action.Result, error) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		if args == nil {
			return ctx.Previous, stderrors.New("invalid arguments for pipeline, expected *bindPipelineArgs")
		}
		var wg sync.WaitGroup
		si := args.serviceInstance
		units, err := args.app.GetUnits()
		if err != nil {
			return nil, err
		}
		errCh := make(chan error, len(units))
		unbindedCh := make(chan bind.Unit, len(units))
		for i := range units {
			wg.Add(1)
			go func(i int) {
				defer wg.Done()
				unit := units[i]
				err := si.BindUnit(args.app, unit)
				if err == nil || err == ErrUnitAlreadyBound {
					unbindedCh <- unit
				} else {
					errCh <- err
				}
			}(i)
		}
		wg.Wait()
		close(errCh)
		close(unbindedCh)
		if err := <-errCh; err != nil {
			for unit := range unbindedCh {
				unbindErr := si.UnbindUnit(args.app, unit)
				if unbindErr != nil {
					log.Errorf("[bind-units forward] failed to unbind unit after error: %s", unbindErr)
				}
			}
			return ctx.Previous, err
		}
		return ctx.Previous, nil
	},
	Backward: func(ctx action.BWContext) {
	},
}

var unbindUnits = action.Action{
	Name: "unbind-units",
	Forward: func(ctx action.FWContext) (action.Result, error) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		if args == nil {
			return nil, stderrors.New("invalid arguments for pipeline, expected *bindPipelineArgs")
		}
		var wg sync.WaitGroup
		si := args.serviceInstance
		units, err := args.app.GetUnits()
		if err != nil {
			return nil, err
		}
		errCh := make(chan error, len(units))
		unbindedCh := make(chan bind.Unit, len(units))
		for i := range units {
			wg.Add(1)
			go func(i int) {
				defer wg.Done()
				unit := units[i]
				err := si.UnbindUnit(args.app, unit)
				if err == nil || err == ErrUnitNotBound {
					unbindedCh <- unit
				} else {
					errCh <- err
				}
			}(i)
		}
		wg.Wait()
		close(errCh)
		close(unbindedCh)
		if err := <-errCh; err != nil {
			for unit := range unbindedCh {
				rebindErr := si.BindUnit(args.app, unit)
				if rebindErr != nil {
					log.Errorf("[unbind-units forward] failed to rebind unit after error: %s", rebindErr)
				}
			}
			return nil, err
		}
		return nil, nil
	},
	Backward: func(ctx action.BWContext) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		units, err := args.app.GetUnits()
		if err != nil {
			log.Errorf("[unbind-units backward] failed get units to rebind in rollback: %s", err)
		}
		for _, unit := range units {
			err := args.serviceInstance.BindUnit(args.app, unit)
			if err != nil {
				log.Errorf("[unbind-units backward] failed to rebind unit in rollback: %s", err)
			}
		}
	},
	MinParams: 1,
}

var unbindAppDB = action.Action{
	Name: "unbind-app-db",
	Forward: func(ctx action.FWContext) (action.Result, error) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		if args == nil {
			return nil, stderrors.New("invalid arguments for pipeline, expected *bindPipelineArgs")
		}
		err := args.serviceInstance.update(bson.M{"$pull": bson.M{"apps": args.app.GetName()}})
		if err != nil {
			return nil, err
		}
		return nil, err
	},
	Backward: func(ctx action.BWContext) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		err := args.serviceInstance.update(bson.M{"$addToSet": bson.M{"apps": args.app.GetName()}})
		if err != nil {
			log.Errorf("[unbind-app-db backward] failed to rebind app in db: %s", err)
		}
	},
	MinParams: 1,
}

var unbindAppEndpoint = action.Action{
	Name: "unbind-app-endpoint",
	Forward: func(ctx action.FWContext) (action.Result, error) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		if args == nil {
			return nil, stderrors.New("invalid arguments for pipeline, expected *bindPipelineArgs")
		}
		if endpoint, err := args.serviceInstance.Service().getClient("production"); err == nil {
			err := endpoint.UnbindApp(args.serviceInstance, args.app)
			if err != nil {
				return nil, err
			}
		}
		return nil, nil
	},
	Backward: func(ctx action.BWContext) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		if endpoint, err := args.serviceInstance.Service().getClient("production"); err == nil {
			_, err := endpoint.BindApp(args.serviceInstance, args.app)
			if err != nil {
				log.Errorf("[unbind-app-endpoint backward] failed to rebind app in endpoint: %s", err)
			}
		}
	},
	MinParams: 1,
}

var removeBindedEnvs = action.Action{
	Name: "remove-binded-envs",
	Forward: func(ctx action.FWContext) (action.Result, error) {
		args, _ := ctx.Params[0].(*bindPipelineArgs)
		if args == nil {
			return nil, stderrors.New("invalid arguments for pipeline, expected *bindPipelineArgs")
		}
		si := args.serviceInstance
		instance := bind.ServiceInstance{Name: si.Name, Envs: make(map[string]string)}
		for k, envVar := range args.app.InstanceEnv(si.Name) {
			instance.Envs[k] = envVar.Value
		}
		return nil, args.app.RemoveInstance(si.ServiceName, instance, args.writer)
	},
	Backward: func(ctx action.BWContext) {
	},
	MinParams: 1,
}
<|endoftext|>


<commit_before>package service

import (
	"io"
	"time"

	"github.com/gogo/protobuf/proto"
	"github.com/tracerun/tracerun/lg"
	"go.uber.org/zap"
)

const (
	bufferCount   = 200
	tickerSeconds = 60
)

var (
	// actionChan is used to handle actions
	actionChan = make(chan *act, bufferCount)
)

type act struct {
	target string
	ts     uint32
}

func receiveActions() {
	for {
		a := <-actionChan
		addOneAction(a)
	Remaining:
		for i := 0; i < bufferCount-1; i++ {
			select {
			case a := <-actionChan:
				addOneAction(a)
			default:
				break Remaining
			}
		}
	}
}

func addOneAction(a *act) {
	lg.L.Debug("action from Q", zap.Any("target", a.target), zap.Uint32("ts", a.ts))
	if err := db.AddAction(a.target, a.ts); err != nil {
		lg.L.Error("error add action", zap.Error(err))
	}
}

func checkActions() {
	for _ = range time.Tick(tickerSeconds * time.Second) {
		if err := db.CheckExpirations(); err != nil {
			lg.L.Error("error while checking actions", zap.Error(err))
		}
	}
}

// exit uint8(0) to stop the server
func exit(b []byte, w io.Writer) {
	stopChan <- true
}

// ping uint8(1) used to extend readtimeout
func ping(b []byte, w io.Writer) {}

// action uint8(2) to receive action income.
func action(b []byte, w io.Writer) {
	// enqueue
	go func() {
		actionChan <- &act{
			target: string(b),
			ts:     uint32(time.Now().Unix()),
		}
	}()
}

// getActions uint8(3) to get all actions
func getActions(b []byte, w io.Writer) {
	var all AllActions
	targets, starts, lasts, err := db.GetActions()
	if err != nil {
		lg.L.Error("error getting actions", zap.Error(err))
		WriteErrorMessage(err, w)
		return
	}

	for i := 0; i < len(targets); i++ {
		all.Actions = append(all.Actions, &AllActions_Act{
			Target: targets[i],
			Start:  starts[i],
			Last:   lasts[i],
		})
	}

	buf, err := proto.Marshal(&all)
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}

	headerBuf := GenerateHeaderBuf(uint16(len(buf)), uint8(3))
	if _, err := w.Write(append(headerBuf, buf...)); err != nil {
		lg.L.Error("error writing", zap.Error(err))
	}
}

// getTargets uint8(4) to get all targets
func getTargets(b []byte, w io.Writer) {
	targets := db.GetTargets()

	var all Targets
	all.Target = targets

	buf, err := proto.Marshal(&all)
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}

	headerBuf := GenerateHeaderBuf(uint16(len(buf)), uint8(4))
	if _, err := w.Write(append(headerBuf, buf...)); err != nil {
		lg.L.Error("error writing", zap.Error(err))
	}
}

// getSlots uint8(5) to get slots of a target in a range
func getSlots(b []byte, w io.Writer) {
	var rang SlotRange
	if err := proto.Unmarshal(b, &rang); err != nil {
		WriteErrorMessage(err, w)
		return
	}

	startsResult, slotsResult, err := db.GetSlots(rang.Target, rang.Start, rang.End)
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}
	var slots []*Slot
	for i := 0; i < len(startsResult); i++ {
		oneStarts, oneSlots := startsResult[i], slotsResult[i]
		for j := 0; j < len(oneStarts); j++ {
			slots = append(slots, &Slot{
				Start: oneStarts[j],
				Slot:  oneSlots[j],
			})
		}
	}

	var all Slots
	all.Slots = slots

	buf, err := proto.Marshal(&all)
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}

	headerBuf := GenerateHeaderBuf(uint16(len(buf)), uint8(5))
	if _, err := w.Write(append(headerBuf, buf...)); err != nil {
		lg.L.Error("error writing", zap.Error(err))
	}
}

// getMeta uint8(6) to get meta information
func getMeta(b []byte, w io.Writer) {
	var meta Meta

	v, err := db.Version()
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}
	meta.Version = uint32(v)

	tag, err := db.Tag()
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}
	meta.Tag = tag

	cre, err := db.CreateAt()
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}
	meta.CreateAt = cre

	host, err := db.Host()
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}
	meta.Host = host

	username, err := db.Username()
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}
	meta.Username = username

	arch, err := db.Arch()
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}
	meta.Arch = arch

	osInfo, err := db.OS()
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}
	meta.Os = osInfo

	offset, err := db.ZoneOffset()
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}
	meta.ZoneOffset = offset

	buf, err := proto.Marshal(&meta)
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}

	headerBuf := GenerateHeaderBuf(uint16(len(buf)), uint8(6))
	if _, err := w.Write(append(headerBuf, buf...)); err != nil {
		lg.L.Error("error writing", zap.Error(err))
	}
}

func getRouter() map[uint8]RouteFunc {
	m := make(map[uint8]RouteFunc)

	m[uint8(0)] = exit
	m[uint8(1)] = ping
	m[uint8(2)] = action
	m[uint8(3)] = getActions
	m[uint8(4)] = getTargets
	m[uint8(5)] = getSlots
	m[uint8(6)] = getMeta

	return m
}
<commit_msg>Change route and add get meta method.<commit_after>package service

import (
	"io"
	"time"

	"github.com/gogo/protobuf/proto"
	"github.com/tracerun/tracerun/lg"
	"go.uber.org/zap"
)

const (
	bufferCount   = 200
	tickerSeconds = 60
)

var (
	// actionChan is used to handle actions
	actionChan = make(chan *act, bufferCount)
)

type act struct {
	target string
	ts     uint32
}

func receiveActions() {
	for {
		a := <-actionChan
		addOneAction(a)
	Remaining:
		for i := 0; i < bufferCount-1; i++ {
			select {
			case a := <-actionChan:
				addOneAction(a)
			default:
				break Remaining
			}
		}
	}
}

func addOneAction(a *act) {
	lg.L.Debug("action from Q", zap.Any("target", a.target), zap.Uint32("ts", a.ts))
	if err := db.AddAction(a.target, a.ts); err != nil {
		lg.L.Error("error add action", zap.Error(err))
	}
}

func checkActions() {
	for _ = range time.Tick(tickerSeconds * time.Second) {
		if err := db.CheckExpirations(); err != nil {
			lg.L.Error("error while checking actions", zap.Error(err))
		}
	}
}

// exit uint8(0) to stop the server
func exit(b []byte, w io.Writer) {
	stopChan <- true
}

// ping uint8(1) used to extend readtimeout
func ping(b []byte, w io.Writer) {}

// getMeta uint8(2) to get meta information
func getMeta(b []byte, w io.Writer) {
	thisRoute := uint8(2)

	var meta Meta

	v, err := db.Version()
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}
	meta.Version = uint32(v)

	tag, err := db.Tag()
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}
	meta.Tag = tag

	cre, err := db.CreateAt()
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}
	meta.CreateAt = cre

	host, err := db.Host()
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}
	meta.Host = host

	username, err := db.Username()
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}
	meta.Username = username

	arch, err := db.Arch()
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}
	meta.Arch = arch

	osInfo, err := db.OS()
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}
	meta.Os = osInfo

	offset, err := db.ZoneOffset()
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}
	meta.ZoneOffset = offset

	buf, err := proto.Marshal(&meta)
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}

	headerBuf := GenerateHeaderBuf(uint16(len(buf)), thisRoute)
	if _, err := w.Write(append(headerBuf, buf...)); err != nil {
		lg.L.Error("error writing", zap.Error(err))
	}
}

// action uint8(10) to receive action income.
func action(b []byte, w io.Writer) {
	// enqueue
	go func() {
		actionChan <- &act{
			target: string(b),
			ts:     uint32(time.Now().Unix()),
		}
	}()
}

// getActions uint8(11) to get all actions
func getActions(b []byte, w io.Writer) {
	thisRoute := uint8(11)

	var all AllActions
	targets, starts, lasts, err := db.GetActions()
	if err != nil {
		lg.L.Error("error getting actions", zap.Error(err))
		WriteErrorMessage(err, w)
		return
	}

	for i := 0; i < len(targets); i++ {
		all.Actions = append(all.Actions, &AllActions_Act{
			Target: targets[i],
			Start:  starts[i],
			Last:   lasts[i],
		})
	}

	buf, err := proto.Marshal(&all)
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}

	headerBuf := GenerateHeaderBuf(uint16(len(buf)), thisRoute)
	if _, err := w.Write(append(headerBuf, buf...)); err != nil {
		lg.L.Error("error writing", zap.Error(err))
	}
}

// getTargets uint8(20) to get all targets
func getTargets(b []byte, w io.Writer) {
	thisRoute := uint8(20)

	targets := db.GetTargets()

	var all Targets
	all.Target = targets

	buf, err := proto.Marshal(&all)
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}

	headerBuf := GenerateHeaderBuf(uint16(len(buf)), thisRoute)
	if _, err := w.Write(append(headerBuf, buf...)); err != nil {
		lg.L.Error("error writing", zap.Error(err))
	}
}

// getSlots uint8(21) to get slots of a target in a range
func getSlots(b []byte, w io.Writer) {
	thisRoute := uint8(21)

	var rang SlotRange
	if err := proto.Unmarshal(b, &rang); err != nil {
		WriteErrorMessage(err, w)
		return
	}

	startsResult, slotsResult, err := db.GetSlots(rang.Target, rang.Start, rang.End)
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}
	var slots []*Slot
	for i := 0; i < len(startsResult); i++ {
		oneStarts, oneSlots := startsResult[i], slotsResult[i]
		for j := 0; j < len(oneStarts); j++ {
			slots = append(slots, &Slot{
				Start: oneStarts[j],
				Slot:  oneSlots[j],
			})
		}
	}

	var all Slots
	all.Slots = slots

	buf, err := proto.Marshal(&all)
	if err != nil {
		WriteErrorMessage(err, w)
		return
	}

	headerBuf := GenerateHeaderBuf(uint16(len(buf)), thisRoute)
	if _, err := w.Write(append(headerBuf, buf...)); err != nil {
		lg.L.Error("error writing", zap.Error(err))
	}
}

func getRouter() map[uint8]RouteFunc {
	m := make(map[uint8]RouteFunc)

	m[uint8(0)] = exit
	m[uint8(1)] = ping
	m[uint8(2)] = getMeta
	m[uint8(10)] = action
	m[uint8(11)] = getActions
	m[uint8(20)] = getTargets
	m[uint8(21)] = getSlots

	return m
}
<|endoftext|>


<commit_before>package code

import (
	"net/http"
)

type (
	Code         int
	TextHTTPCode struct {
		Text     string
		HTTPCode int
	}
	CodeMap map[Code]TextHTTPCode
)

const (
	// - 操作状态

	RequestTimeout   Code = -204  //提交超时
	AbnormalResponse Code = -203  //响应异常
	OperationTimeout Code = -202  //操作超时
	Unsupported      Code = -201  //不支持的操作
	RepeatOperation  Code = -200  //重复操作

	// - 数据状态

	DataNotFound     Code = -100  //数据未找到

	// - 用户状态

	BalanceNoEnough  Code = -5  //余额不足
	UserDisabled     Code = -4  //用户被禁用
	UserNotFound     Code = -3  //用户未找到
	NonPrivileged    Code = -2  //无权限
	Unauthenticated  Code = -1  //未登录

	// - 通用

	Failure          Code = 0   //操作失败
	Success          Code = 1   //操作成功
)

// CodeDict 状态码字典
var CodeDict = CodeMap{
	BalanceNoEnough:  {"BalanceNoEnough", http.StatusOK},
	RequestTimeout:   {"RequestTimeout", http.StatusOK},
	AbnormalResponse: {"AbnormalResponse", http.StatusOK},
	OperationTimeout: {"OperationTimeout", http.StatusOK},
	Unsupported:      {"Unsupported", http.StatusOK},
	RepeatOperation:  {"RepeatOperation", http.StatusOK},
	DataNotFound:     {"DataNotFound", http.StatusOK},
	UserNotFound:     {"UserNotFound", http.StatusOK},
	NonPrivileged:    {"NonPrivileged", http.StatusOK},
	Unauthenticated:  {"Unauthenticated", http.StatusOK},
	Failure:          {"Failure", http.StatusOK},
	Success:          {"Success", http.StatusOK},
}

func (c Code) String() string {
	if v, y := CodeDict[c]; y {
		return v.Text
	}
	return `Undefined`
}

// Int 返回int类型的自定义状态码
func (c Code) Int() int {
	return int(c)
}

// HTTPCode 返回HTTP状态码
func (c Code) HTTPCode() int {
	if v, y := CodeDict[c]; y {
		return v.HTTPCode
	}
	return http.StatusOK
}

func (s CodeMap) Get(code int) TextHTTPCode {
	v, _ := s[Code(code)]
	return v
}
<commit_msg>improved<commit_after>package code

import (
	"net/http"
)

type (
	Code         int
	TextHTTPCode struct {
		Text     string
		HTTPCode int
	}
	CodeMap map[Code]TextHTTPCode
)

const (
	// - 操作状态

	RequestFailure   Code = -205  //提交失败
	RequestTimeout   Code = -204  //提交超时
	AbnormalResponse Code = -203  //响应异常
	OperationTimeout Code = -202  //操作超时
	Unsupported      Code = -201  //不支持的操作
	RepeatOperation  Code = -200  //重复操作

	// - 数据状态

	DataNotFound     Code = -100  //数据未找到

	// - 用户状态

	BalanceNoEnough  Code = -5  //余额不足
	UserDisabled     Code = -4  //用户被禁用
	UserNotFound     Code = -3  //用户未找到
	NonPrivileged    Code = -2  //无权限
	Unauthenticated  Code = -1  //未登录

	// - 通用

	Failure          Code = 0   //操作失败
	Success          Code = 1   //操作成功
)

// CodeDict 状态码字典
var CodeDict = CodeMap{
	BalanceNoEnough:  {"BalanceNoEnough", http.StatusOK},
	RequestTimeout:   {"RequestTimeout", http.StatusOK},
	AbnormalResponse: {"AbnormalResponse", http.StatusOK},
	OperationTimeout: {"OperationTimeout", http.StatusOK},
	Unsupported:      {"Unsupported", http.StatusOK},
	RepeatOperation:  {"RepeatOperation", http.StatusOK},
	DataNotFound:     {"DataNotFound", http.StatusOK},
	UserNotFound:     {"UserNotFound", http.StatusOK},
	NonPrivileged:    {"NonPrivileged", http.StatusOK},
	Unauthenticated:  {"Unauthenticated", http.StatusOK},
	Failure:          {"Failure", http.StatusOK},
	Success:          {"Success", http.StatusOK},
}

func (c Code) String() string {
	if v, y := CodeDict[c]; y {
		return v.Text
	}
	return `Undefined`
}

// Int 返回int类型的自定义状态码
func (c Code) Int() int {
	return int(c)
}

// HTTPCode 返回HTTP状态码
func (c Code) HTTPCode() int {
	if v, y := CodeDict[c]; y {
		return v.HTTPCode
	}
	return http.StatusOK
}

func (s CodeMap) Get(code int) TextHTTPCode {
	v, _ := s[Code(code)]
	return v
}
<|endoftext|>


<commit_before>package udf

import (
	"encoding/json"
	"github.com/pkg/errors"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/data"
	"io/ioutil"
)
//LoadJSON loads new line delimited or regular JSON into data structure
func LoadJSON(source interface{}, state data.Map) (interface{}, error) {
	location := toolbox.AsString(source)
	if location == "" {
		return nil, errors.New("location was empty at LoadJSON")
	}
	data, err := ioutil.ReadFile(location)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to load: %v", location)
	}
	JSON := string(data)
	if toolbox.IsNewLineDelimitedJSON(JSON) {
		return toolbox.NewLineDelimitedJSON(JSON)
	}
	var result interface{}
	err = json.Unmarshal(data, &result)
	return result, err
}
<commit_msg>updated udf<commit_after>package udf

import (
	"encoding/json"
	"github.com/pkg/errors"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/data"
	"io/ioutil"
)
//LoadJSON loads new line delimited or regular JSON into data structure
func LoadJSON(source interface{}, state data.Map) (interface{}, error) {
	location := toolbox.AsString(source)
	if location == "" {
		return nil, errors.New("location was empty at LoadJSON")
	}
	data, err := ioutil.ReadFile(location)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to load: %v", location)
	}
	JSON := string(data)
	if toolbox.IsNewLineDelimitedJSON(JSON) {
		slice, err :=  toolbox.NewLineDelimitedJSON(JSON)
		if err != nil {
			return nil, err
		}
		var result = make([]interface{}, 0)
		toolbox.ProcessSlice(slice, func(item interface{}) bool {
			if item == nil {
				return true
			}
			if toolbox.IsMap(item) && len(toolbox.AsMap(item)) == 0 {
				return true
			}
			result = append(result, item)
			return true
		})
		return result, nil
	}
	var result interface{}
	err = json.Unmarshal(data, &result)
	return result, err
}
<|endoftext|>


<commit_before>package somaproto

type DeploymentDetails struct {
	Repository         string                `json:"repository"`
	Environment        string                `json:"environment"`
	Bucket             string                `json:"bucket"`
	ObjectType         string                `json:"object_type"`
	View               string                `json:"view"`
	Task               string                `json:"task"`
	Datacenter         string                `json:"datacenter"`
	Capability         *ProtoCapability      `json:"capability"`
	Monitoring         *ProtoMonitoring      `json:"monitoring_system"`
	Metric             *ProtoMetric          `json:"metric"`
	Team               *ProtoTeam            `json:"organizational_team"`
	Oncall             *ProtoOncall          `json:"oncall,omitempty"`
	Service            *TreePropertyService  `json:"service,omitempty"`
	Properties         *[]TreePropertySystem `json:"properties,omitempty"`
	CustomProperties   *[]TreePropertyCustom `json:"custom_properties,omitempty"`
	Group              *ProtoGroup           `json:"group,omitempty"`
	Cluster            *ProtoCluster         `json:"cluster,omitempty"`
	Node               *ProtoNode            `json:"node,omitempty"`
	Server             *ProtoServer          `json:"server,omitempty"`
	CheckConfiguration *CheckConfiguration   `json:"check_configuration"`
	Check              *TreeCheck            `json:"check"`
	CheckInstance      *TreeCheckInstance    `json:"check_instance"`
}

func (dd *DeploymentDetails) DeepCompare(alternate *DeploymentDetails) bool {
	if dd.Repository != alternate.Repository {
		return false
	}
	if dd.Environment != alternate.Environment {
		return false
	}
	if dd.Bucket != alternate.Bucket {
		return false
	}
	if dd.ObjectType != alternate.ObjectType {
		return false
	}
	if dd.View != alternate.View {
		return false
	}
	if dd.Task != alternate.Task {
		return false
	}
	if dd.Datacenter != alternate.Datacenter {
		return false
	}
	//if !dd.Capability.DeepCompare(alternate.Capability) {
	//	return false
	//}
	return true
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Add unit to deployment details<commit_after>package somaproto

type DeploymentDetails struct {
	Repository         string                `json:"repository"`
	Environment        string                `json:"environment"`
	Bucket             string                `json:"bucket"`
	ObjectType         string                `json:"object_type"`
	View               string                `json:"view"`
	Task               string                `json:"task"`
	Datacenter         string                `json:"datacenter"`
	Capability         *ProtoCapability      `json:"capability"`
	Monitoring         *ProtoMonitoring      `json:"monitoring_system"`
	Metric             *ProtoMetric          `json:"metric"`
	Unit               *ProtoUnit            `json:"unit"`
	Team               *ProtoTeam            `json:"organizational_team"`
	Oncall             *ProtoOncall          `json:"oncall,omitempty"`
	Service            *TreePropertyService  `json:"service,omitempty"`
	Properties         *[]TreePropertySystem `json:"properties,omitempty"`
	CustomProperties   *[]TreePropertyCustom `json:"custom_properties,omitempty"`
	Group              *ProtoGroup           `json:"group,omitempty"`
	Cluster            *ProtoCluster         `json:"cluster,omitempty"`
	Node               *ProtoNode            `json:"node,omitempty"`
	Server             *ProtoServer          `json:"server,omitempty"`
	CheckConfiguration *CheckConfiguration   `json:"check_configuration"`
	Check              *TreeCheck            `json:"check"`
	CheckInstance      *TreeCheckInstance    `json:"check_instance"`
}

func (dd *DeploymentDetails) DeepCompare(alternate *DeploymentDetails) bool {
	if dd.Repository != alternate.Repository {
		return false
	}
	if dd.Environment != alternate.Environment {
		return false
	}
	if dd.Bucket != alternate.Bucket {
		return false
	}
	if dd.ObjectType != alternate.ObjectType {
		return false
	}
	if dd.View != alternate.View {
		return false
	}
	if dd.Task != alternate.Task {
		return false
	}
	if dd.Datacenter != alternate.Datacenter {
		return false
	}
	//if !dd.Capability.DeepCompare(alternate.Capability) {
	//	return false
	//}
	return true
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>package main

import (
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
)

func main() {
	router := gin.Default()
	router.Use(gin.Logger())

	// temp storage into map for testing
	linkMap := make(map[string]string)
	linkMap["goog"] = "http://www.google.com"
	linkMap["fb"] = "http://www.facebook.com"

	router.GET("/:short", func(c *gin.Context) {
		// TODO: create db, look up in db, if found redirect user to correct one
		short := c.Param("short")

		val, ok := linkMap[short]

		if ok {
			log.Println(val)
			c.Redirect(http.StatusMovedPermanently, val)
		} else {
			// TODO: redirect to 404 for now. eventually
			// sendthem to page to create the link
			c.String(http.StatusNotFound, "Page not found.")
		}
	})

	// TODO: implement post request to add things to db
	// test later
	router.POST("/add", func(c *gin.Context) {
		short := c.PostForm("short")
		url := c.PostForm("url")

		val, ok := linkMap[short]
		// check if key already exists
		if ok {
			c.String(http.StatusForbidden, "Already exists.\n"+short+"="+val)
		} else {
			linkMap[short] = url
			c.String(http.StatusOK, "Added succesfully.\n"+short+"="+linkMap[short])
		}
	})

	// TODO: implement post request to add things to db
	// test later
	router.POST("/edit", func(c *gin.Context) {
		short := c.PostForm("short")
		url := c.PostForm("url")

		val, ok := linkMap[short]
		// check if key already exists
		if ok {
			linkMap[short] = url
			c.String(http.StatusOK, "Changed succesfully.\n"+short+"="+linkMap[short])

		} else {
			c.String(http.StatusForbidden, "Does not exist"+val)
		}
	})

	router.Run(":8080") // listen and serve on 0.0.0.0:8080
}
<commit_msg>db working probably, with bolt db<commit_after>package main

import (
	"fmt"
	"github.com/boltdb/bolt"
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
)

func main() {
	const (
		DB_FILE     = "GOLINKS.DB"
		DB_BUCKET   = "GOLINKS"
		DB_KEY_NAME = "short"
		DB_VAL_NAME = "url"
	)

	router := gin.Default()
	router.Use(gin.Logger())

	// Open the my.db data file in your current directory.
	// It will be created if it doesn't exist.
	db, err := bolt.Open(DB_FILE, 0600, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// create a bucket
	db.Update(func(tx *bolt.Tx) error {
		_, err := tx.CreateBucket([]byte(DB_BUCKET))
		if err != nil {
			return fmt.Errorf("create bucket: %s", err)
		}
		return nil
	})

	router.GET("/:short", func(c *gin.Context) {
		// TODO: create db, look up in db, if found redirect user to correct one
		short := c.Param(DB_KEY_NAME)

		// reading from db
		db.View(func(tx *bolt.Tx) error {
			b := tx.Bucket([]byte(DB_BUCKET))
			v := b.Get([]byte(short))

			if v == nil {
				// TODO: redirect to 404 for now. eventually
				// sendthem to page to create the link
				c.String(http.StatusNotFound, "Page not found.")
			} else {
				log.Println(v)
				c.Redirect(http.StatusMovedPermanently, string(v))
			}

			return nil
		})

	})

	router.POST("/add", func(c *gin.Context) {
		short := c.PostForm(DB_KEY_NAME)
		url := c.PostForm(DB_VAL_NAME)

		// writing to db
		db.Update(func(tx *bolt.Tx) error {
			b := tx.Bucket([]byte(DB_BUCKET))
			// check if it already exists
			if b.Get([]byte(short)) == nil {
				b.Put([]byte(short), []byte(url))
				c.String(http.StatusOK, "Added succesfully.\n"+short+"="+url)
			} else {
				c.String(http.StatusForbidden, "Already exists.\n"+short+"="+url)
			}

			return err
		})
	})

	router.POST("/edit", func(c *gin.Context) {
		short := c.PostForm(DB_KEY_NAME)
		url := c.PostForm(DB_VAL_NAME)

		// writing to db
		db.Update(func(tx *bolt.Tx) error {
			b := tx.Bucket([]byte(DB_BUCKET))
			// check if it already exists

			if b.Get([]byte(short)) == nil {
				c.String(http.StatusForbidden, "Does not exist.\n"+short+"="+url)
			} else {
				b.Put([]byte(short), []byte(url))
				c.String(http.StatusOK, "Changed succesfully.\n"+short+"="+url)
			}

			return nil
		})
	})

	router.Run(":8000") // listen and serve on 0.0.0.0:8000
}
<|endoftext|>