<commit_before>package main

import (
	"bufio"
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"time"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/network"
	"github.com/docker/docker/client"
	manifest "github.com/estafette/estafette-ci-manifest"

	"github.com/rs/zerolog/log"
)

// DockerRunner pulls and runs docker containers
type DockerRunner interface {
	isDockerImagePulled(manifest.EstafettePipeline) bool
	runDockerPull(manifest.EstafettePipeline) error
	getDockerImageSize(manifest.EstafettePipeline) (int64, error)
	runDockerRun(string, map[string]string, manifest.EstafettePipeline) error
	startDockerDaemon() error
	waitForDockerDaemon()
}

type dockerRunnerImpl struct {
	envvarHelper EnvvarHelper
}

// NewDockerRunner returns a new DockerRunner
func NewDockerRunner(envvarHelper EnvvarHelper) DockerRunner {
	return &dockerRunnerImpl{
		envvarHelper: envvarHelper,
	}
}

func (dr *dockerRunnerImpl) isDockerImagePulled(p manifest.EstafettePipeline) bool {

	log.Info().Msgf("Checking if docker image '%v' exists locally...", p.ContainerImage)

	cli, err := client.NewEnvClient()
	if err != nil {
		return false
	}

	imageSummaries, err := cli.ImageList(context.Background(), types.ImageListOptions{})

	for _, summary := range imageSummaries {
		if contains(summary.RepoTags, p.ContainerImage) {
			return true
		}
	}

	return false
}

func (dr *dockerRunnerImpl) runDockerPull(p manifest.EstafettePipeline) (err error) {

	log.Info().Msgf("Pulling docker image '%v'", p.ContainerImage)

	cli, err := client.NewEnvClient()
	if err != nil {
		return err
	}

	rc, err := cli.ImagePull(context.Background(), p.ContainerImage, types.ImagePullOptions{})
	if err != nil {
		return err
	}
	defer rc.Close()

	// wait for image pull to finish
	_, err = ioutil.ReadAll(rc)
	if err != nil {
		return err
	}

	return
}

func (dr *dockerRunnerImpl) getDockerImageSize(p manifest.EstafettePipeline) (totalSize int64, err error) {

	cli, err := client.NewEnvClient()
	if err != nil {
		return totalSize, err
	}

	items, err := cli.ImageHistory(context.Background(), p.ContainerImage)
	if err != nil {
		return totalSize, err
	}

	for _, item := range items {
		totalSize += item.Size
	}

	return totalSize, nil
}

func (dr *dockerRunnerImpl) runDockerRun(dir string, envvars map[string]string, p manifest.EstafettePipeline) (err error) {

	// run docker with image and commands from yaml
	ctx := context.Background()
	cli, err := client.NewEnvClient()
	if err != nil {
		return err
	}

	// define commands
	cmdSlice := make([]string, 0)
	cmdSlice = append(cmdSlice, "set -e;"+strings.Join(p.Commands, ";"))

	// add custom properties as ESTAFETTE_EXTENSION_... envvar
	extensionEnvVars := map[string]string{}
	if p.CustomProperties != nil && len(p.CustomProperties) > 0 {
		for k, v := range p.CustomProperties {
			// if custom property is of type string add the envvar
			if s, isString := v.(string); isString {
				extensionkey := dr.envvarHelper.getEstafetteEnvvarName(fmt.Sprintf("ESTAFETTE_EXTENSION_%v", dr.envvarHelper.toUpperSnake(k)))
				extensionEnvVars[extensionkey] = os.Expand(s, dr.envvarHelper.getEstafetteEnv)
			} else if i, isInterfaceArray := v.([]interface{}); isInterfaceArray {
				// check whether all array items are of type string
				valid := true
				stringValues := []string{}
				for _, iv := range i {
					if s, isString := iv.(string); isString {
						stringValues = append(stringValues, s)
					} else {
						valid = false
						break
					}
				}

				if valid {
					// if all array items are string, pass as comma-separated list to extension
					extensionkey := dr.envvarHelper.getEstafetteEnvvarName(fmt.Sprintf("ESTAFETTE_EXTENSION_%v", dr.envvarHelper.toUpperSnake(k)))
					extensionEnvVars[extensionkey] = os.Expand(strings.Join(stringValues, ","), dr.envvarHelper.getEstafetteEnv)
				} else {
					log.Warn().Interface("customProperty", v).Msgf("Cannot turn custom property %v into extension envvar", k)
				}
			} else {
				log.Warn().Interface("customProperty", v).Msgf("Cannot turn custom property %v into extension envvar", k)
			}
		}
	}

	// combine and override envvars
	combinedEnvVars := dr.envvarHelper.overrideEnvvars(envvars, p.EnvVars, extensionEnvVars)

	// define docker envvars
	dockerEnvVars := make([]string, 0)
	if combinedEnvVars != nil && len(combinedEnvVars) > 0 {
		for k, v := range combinedEnvVars {
			dockerEnvVars = append(dockerEnvVars, fmt.Sprintf("%v=%v", k, v))
		}
	}

	// define entrypoint
	entrypoint := make([]string, 0)
	entrypoint = append(entrypoint, p.Shell)
	entrypoint = append(entrypoint, "-c")

	// define binds
	binds := make([]string, 0)
	if runtime.GOOS != "windows" {
		binds = append(binds, fmt.Sprintf("%v:%v", dir, os.Expand(p.WorkingDirectory, dr.envvarHelper.getEstafetteEnv)))
	}
	if ok, _ := pathExists("/var/run/docker.sock"); ok {
		binds = append(binds, "/var/run/docker.sock:/var/run/docker.sock")
	}
	if ok, _ := pathExists("/var/run/secrets/kubernetes.io/serviceaccount"); ok {
		binds = append(binds, "/var/run/secrets/kubernetes.io/serviceaccount:/var/run/secrets/kubernetes.io/serviceaccount")
	}

	// define config
	config := container.Config{
		AttachStdout: true,
		AttachStderr: true,
		Env:          dockerEnvVars,
		Image:        p.ContainerImage,
		WorkingDir:   os.Expand(p.WorkingDirectory, dr.envvarHelper.getEstafetteEnv),
	}
	if len(p.Commands) > 0 {
		// only pass commands when they are set, so extensions can work without
		config.Cmd = cmdSlice
		// only override entrypoint when commands are set, so extensions can work without commands
		config.Entrypoint = entrypoint
	}

	// create container
	resp, err := cli.ContainerCreate(ctx, &config, &container.HostConfig{
		Binds:      binds,
		AutoRemove: true,
		Privileged: true,
	}, &network.NetworkingConfig{}, "")
	if err != nil {
		return err
	}

	// start container
	if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
		return err
	}

	// follow logs
	rc, err := cli.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{
		ShowStdout: true,
		ShowStderr: true,
		Follow:     true,
	})
	defer rc.Close()
	if err != nil {
		return err
	}

	// stream logs to stdout with buffering
	in := bufio.NewScanner(rc)
	for in.Scan() {

		// strip first 8 bytes, they contain docker control characters (https://github.com/docker/docker/issues/7375)
		logLine := in.Text()
		logType := "stdout"
		if len(logLine) > 8 {

			headers := []byte(logLine[0:8])

			// first byte contains the streamType
			// -   0: stdin (will be written on stdout)
			// -   1: stdout
			// -   2: stderr
			streamType := headers[0]

			if streamType == 2 {
				logType = "stderr"
			}

			logLine = logLine[8:]
		}

		if logType == "stderr" {
			log.Warn().Msgf("[%v] %v", p.Name, logLine)
		} else {
			log.Info().Msgf("[%v] %v", p.Name, logLine)
		}
	}
	if err := in.Err(); err != nil {
		log.Error().Msgf("[%v] Error: %v", p.Name, err)
		return err
	}

	// wait for container to stop run
	exitCode, err := cli.ContainerWait(ctx, resp.ID)
	if err != nil {
		return err
	}

	if exitCode > 0 {
		return fmt.Errorf("Failed with exit code: %v", exitCode)
	}

	return
}

func (dr *dockerRunnerImpl) startDockerDaemon() error {

	// dockerd --host=unix:///var/run/docker.sock --host=tcp://0.0.0.0:2375 --storage-driver=$STORAGE_DRIVER &
	log.Debug().Msg("Starting docker daemon...")
	args := []string{"--host=unix:///var/run/docker.sock", "--host=tcp://0.0.0.0:2375", "--storage-driver=overlay2"}
	dockerDaemonCommand := exec.Command("dockerd", args...)
	dockerDaemonCommand.Stdout = log.Logger
	dockerDaemonCommand.Stderr = log.Logger
	err := dockerDaemonCommand.Start()
	if err != nil {
		return err
	}

	return nil
}

func (dr *dockerRunnerImpl) waitForDockerDaemon() {

	// wait until /var/run/docker.sock exists
	log.Debug().Msg("Waiting for docker daemon to be ready for use...")
	for {
		if _, err := os.Stat("/var/run/docker.sock"); os.IsNotExist(err) {
			// does not exist
			time.Sleep(1000 * time.Millisecond)
		} else {
			// file exists, break out of for loop
			break
		}
	}
	log.Debug().Msg("Docker daemon is ready for use")
}
<commit_msg>decrypt secrets<commit_after>package main

import (
	"bufio"
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"time"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/network"
	"github.com/docker/docker/client"
	manifest "github.com/estafette/estafette-ci-manifest"

	"github.com/rs/zerolog/log"
)

// DockerRunner pulls and runs docker containers
type DockerRunner interface {
	isDockerImagePulled(manifest.EstafettePipeline) bool
	runDockerPull(manifest.EstafettePipeline) error
	getDockerImageSize(manifest.EstafettePipeline) (int64, error)
	runDockerRun(string, map[string]string, manifest.EstafettePipeline) error
	startDockerDaemon() error
	waitForDockerDaemon()
}

type dockerRunnerImpl struct {
	envvarHelper EnvvarHelper
}

// NewDockerRunner returns a new DockerRunner
func NewDockerRunner(envvarHelper EnvvarHelper) DockerRunner {
	return &dockerRunnerImpl{
		envvarHelper: envvarHelper,
	}
}

func (dr *dockerRunnerImpl) isDockerImagePulled(p manifest.EstafettePipeline) bool {

	log.Info().Msgf("Checking if docker image '%v' exists locally...", p.ContainerImage)

	cli, err := client.NewEnvClient()
	if err != nil {
		return false
	}

	imageSummaries, err := cli.ImageList(context.Background(), types.ImageListOptions{})

	for _, summary := range imageSummaries {
		if contains(summary.RepoTags, p.ContainerImage) {
			return true
		}
	}

	return false
}

func (dr *dockerRunnerImpl) runDockerPull(p manifest.EstafettePipeline) (err error) {

	log.Info().Msgf("Pulling docker image '%v'", p.ContainerImage)

	cli, err := client.NewEnvClient()
	if err != nil {
		return err
	}

	rc, err := cli.ImagePull(context.Background(), p.ContainerImage, types.ImagePullOptions{})
	if err != nil {
		return err
	}
	defer rc.Close()

	// wait for image pull to finish
	_, err = ioutil.ReadAll(rc)
	if err != nil {
		return err
	}

	return
}

func (dr *dockerRunnerImpl) getDockerImageSize(p manifest.EstafettePipeline) (totalSize int64, err error) {

	cli, err := client.NewEnvClient()
	if err != nil {
		return totalSize, err
	}

	items, err := cli.ImageHistory(context.Background(), p.ContainerImage)
	if err != nil {
		return totalSize, err
	}

	for _, item := range items {
		totalSize += item.Size
	}

	return totalSize, nil
}

func (dr *dockerRunnerImpl) runDockerRun(dir string, envvars map[string]string, p manifest.EstafettePipeline) (err error) {

	// run docker with image and commands from yaml
	ctx := context.Background()
	cli, err := client.NewEnvClient()
	if err != nil {
		return err
	}

	// define commands
	cmdSlice := make([]string, 0)
	cmdSlice = append(cmdSlice, "set -e;"+strings.Join(p.Commands, ";"))

	// add custom properties as ESTAFETTE_EXTENSION_... envvar
	extensionEnvVars := map[string]string{}
	if p.CustomProperties != nil && len(p.CustomProperties) > 0 {
		for k, v := range p.CustomProperties {
			// if custom property is of type string add the envvar
			if s, isString := v.(string); isString {
				extensionkey := dr.envvarHelper.getEstafetteEnvvarName(fmt.Sprintf("ESTAFETTE_EXTENSION_%v", dr.envvarHelper.toUpperSnake(k)))
				extensionEnvVars[extensionkey] = os.Expand(s, dr.envvarHelper.getEstafetteEnv)
			} else if i, isInterfaceArray := v.([]interface{}); isInterfaceArray {
				// check whether all array items are of type string
				valid := true
				stringValues := []string{}
				for _, iv := range i {
					if s, isString := iv.(string); isString {
						stringValues = append(stringValues, s)
					} else {
						valid = false
						break
					}
				}

				if valid {
					// if all array items are string, pass as comma-separated list to extension
					extensionkey := dr.envvarHelper.getEstafetteEnvvarName(fmt.Sprintf("ESTAFETTE_EXTENSION_%v", dr.envvarHelper.toUpperSnake(k)))
					extensionEnvVars[extensionkey] = os.Expand(strings.Join(stringValues, ","), dr.envvarHelper.getEstafetteEnv)
				} else {
					log.Warn().Interface("customProperty", v).Msgf("Cannot turn custom property %v into extension envvar", k)
				}
			} else {
				log.Warn().Interface("customProperty", v).Msgf("Cannot turn custom property %v into extension envvar", k)
			}
		}
	}

	// combine and override envvars
	combinedEnvVars := dr.envvarHelper.overrideEnvvars(envvars, p.EnvVars, extensionEnvVars)

	// decrypt secrets
	combinedEnvVars = dr.envvarHelper.decryptSecrets(combinedEnvVars)

	// define docker envvars
	dockerEnvVars := make([]string, 0)
	if combinedEnvVars != nil && len(combinedEnvVars) > 0 {
		for k, v := range combinedEnvVars {
			dockerEnvVars = append(dockerEnvVars, fmt.Sprintf("%v=%v", k, v))
		}
	}

	// define entrypoint
	entrypoint := make([]string, 0)
	entrypoint = append(entrypoint, p.Shell)
	entrypoint = append(entrypoint, "-c")

	// define binds
	binds := make([]string, 0)
	if runtime.GOOS != "windows" {
		binds = append(binds, fmt.Sprintf("%v:%v", dir, os.Expand(p.WorkingDirectory, dr.envvarHelper.getEstafetteEnv)))
	}
	if ok, _ := pathExists("/var/run/docker.sock"); ok {
		binds = append(binds, "/var/run/docker.sock:/var/run/docker.sock")
	}
	if ok, _ := pathExists("/var/run/secrets/kubernetes.io/serviceaccount"); ok {
		binds = append(binds, "/var/run/secrets/kubernetes.io/serviceaccount:/var/run/secrets/kubernetes.io/serviceaccount")
	}

	// define config
	config := container.Config{
		AttachStdout: true,
		AttachStderr: true,
		Env:          dockerEnvVars,
		Image:        p.ContainerImage,
		WorkingDir:   os.Expand(p.WorkingDirectory, dr.envvarHelper.getEstafetteEnv),
	}
	if len(p.Commands) > 0 {
		// only pass commands when they are set, so extensions can work without
		config.Cmd = cmdSlice
		// only override entrypoint when commands are set, so extensions can work without commands
		config.Entrypoint = entrypoint
	}

	// create container
	resp, err := cli.ContainerCreate(ctx, &config, &container.HostConfig{
		Binds:      binds,
		AutoRemove: true,
		Privileged: true,
	}, &network.NetworkingConfig{}, "")
	if err != nil {
		return err
	}

	// start container
	if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
		return err
	}

	// follow logs
	rc, err := cli.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{
		ShowStdout: true,
		ShowStderr: true,
		Follow:     true,
	})
	defer rc.Close()
	if err != nil {
		return err
	}

	// stream logs to stdout with buffering
	in := bufio.NewScanner(rc)
	for in.Scan() {

		// strip first 8 bytes, they contain docker control characters (https://github.com/docker/docker/issues/7375)
		logLine := in.Text()
		logType := "stdout"
		if len(logLine) > 8 {

			headers := []byte(logLine[0:8])

			// first byte contains the streamType
			// -   0: stdin (will be written on stdout)
			// -   1: stdout
			// -   2: stderr
			streamType := headers[0]

			if streamType == 2 {
				logType = "stderr"
			}

			logLine = logLine[8:]
		}

		if logType == "stderr" {
			log.Warn().Msgf("[%v] %v", p.Name, logLine)
		} else {
			log.Info().Msgf("[%v] %v", p.Name, logLine)
		}
	}
	if err := in.Err(); err != nil {
		log.Error().Msgf("[%v] Error: %v", p.Name, err)
		return err
	}

	// wait for container to stop run
	exitCode, err := cli.ContainerWait(ctx, resp.ID)
	if err != nil {
		return err
	}

	if exitCode > 0 {
		return fmt.Errorf("Failed with exit code: %v", exitCode)
	}

	return
}

func (dr *dockerRunnerImpl) startDockerDaemon() error {

	// dockerd --host=unix:///var/run/docker.sock --host=tcp://0.0.0.0:2375 --storage-driver=$STORAGE_DRIVER &
	log.Debug().Msg("Starting docker daemon...")
	args := []string{"--host=unix:///var/run/docker.sock", "--host=tcp://0.0.0.0:2375", "--storage-driver=overlay2"}
	dockerDaemonCommand := exec.Command("dockerd", args...)
	dockerDaemonCommand.Stdout = log.Logger
	dockerDaemonCommand.Stderr = log.Logger
	err := dockerDaemonCommand.Start()
	if err != nil {
		return err
	}

	return nil
}

func (dr *dockerRunnerImpl) waitForDockerDaemon() {

	// wait until /var/run/docker.sock exists
	log.Debug().Msg("Waiting for docker daemon to be ready for use...")
	for {
		if _, err := os.Stat("/var/run/docker.sock"); os.IsNotExist(err) {
			// does not exist
			time.Sleep(1000 * time.Millisecond)
		} else {
			// file exists, break out of for loop
			break
		}
	}
	log.Debug().Msg("Docker daemon is ready for use")
}
<|endoftext|>


<commit_before>package handlers

import (
    "encoding/json"
    "io/ioutil"
    "net/http"

    "github.com/cloudfoundry-incubator/notifications/metrics"
    "github.com/cloudfoundry-incubator/notifications/models"
    "github.com/cloudfoundry-incubator/notifications/web/params"
    "github.com/cloudfoundry-incubator/notifications/web/services"
    "github.com/dgrijalva/jwt-go"
    "github.com/ryanmoran/stack"
)

type UpdatePreferences struct {
    preferenceUpdater services.PreferenceUpdaterInterface
    errorWriter       ErrorWriterInterface
}

func NewUpdatePreferences(preferenceUpdater services.PreferenceUpdaterInterface, errorWriter ErrorWriterInterface) UpdatePreferences {
    return UpdatePreferences{
        preferenceUpdater: preferenceUpdater,
        errorWriter:       errorWriter,
    }
}

func (handler UpdatePreferences) ServeHTTP(w http.ResponseWriter, req *http.Request, context stack.Context) {
    connection := models.Database().Connection()
    handler.Execute(w, req, connection, context)

    metrics.NewMetric("counter", map[string]interface{}{
        "name": "notifications.web.preferences.update",
    }).Log()
}

func (handler UpdatePreferences) Execute(w http.ResponseWriter, req *http.Request, connection models.ConnectionInterface, context stack.Context) {
    token := context.Get("token").(*jwt.Token)
    userID := token.Claims["user_id"].(string)

    body, err := ioutil.ReadAll(req.Body)
    if err != nil {
        panic(err)
    }

    builder, err := handler.ParsePreferences(body)
    if err != nil {
        handler.errorWriter.Write(w, params.ParseError{})
        return
    }
    preferences, err := builder.ToPreferences()
    if err != nil {
        handler.errorWriter.Write(w, params.ValidationError{})
        return
    }

    transaction := connection.Transaction()
    transaction.Begin()
    err = handler.preferenceUpdater.Execute(transaction, preferences, userID)
    if err != nil {
        transaction.Rollback()

        switch err.(type) {
        case services.MissingKindOrClientError, services.CriticalKindError:
            handler.errorWriter.Write(w, params.ValidationError([]string{err.Error()}))
        default:
            handler.errorWriter.Write(w, err)
        }
        return
    }

    transaction.Commit()
    w.WriteHeader(http.StatusNoContent)
}

func (handler UpdatePreferences) ParsePreferences(body []byte) (services.PreferencesBuilder, error) {
    builder := services.NewPreferencesBuilder()
    err := json.Unmarshal(body, &builder)
    if err != nil {
        return builder, err
    }
    return builder, nil
}
<commit_msg>Update preference returns validation and parse errors<commit_after>package handlers

import (
    "encoding/json"
    "io/ioutil"
    "net/http"

    "github.com/cloudfoundry-incubator/notifications/metrics"
    "github.com/cloudfoundry-incubator/notifications/models"
    "github.com/cloudfoundry-incubator/notifications/web/params"
    "github.com/cloudfoundry-incubator/notifications/web/services"
    "github.com/dgrijalva/jwt-go"
    "github.com/ryanmoran/stack"
)

type UpdatePreferences struct {
    preferenceUpdater services.PreferenceUpdaterInterface
    errorWriter       ErrorWriterInterface
}

func NewUpdatePreferences(preferenceUpdater services.PreferenceUpdaterInterface, errorWriter ErrorWriterInterface) UpdatePreferences {
    return UpdatePreferences{
        preferenceUpdater: preferenceUpdater,
        errorWriter:       errorWriter,
    }
}

func (handler UpdatePreferences) ServeHTTP(w http.ResponseWriter, req *http.Request, context stack.Context) {
    connection := models.Database().Connection()
    handler.Execute(w, req, connection, context)

    metrics.NewMetric("counter", map[string]interface{}{
        "name": "notifications.web.preferences.update",
    }).Log()
}

func (handler UpdatePreferences) Execute(w http.ResponseWriter, req *http.Request, connection models.ConnectionInterface, context stack.Context) {
    token := context.Get("token").(*jwt.Token)
    userID := token.Claims["user_id"].(string)

    body, err := ioutil.ReadAll(req.Body)
    if err != nil {
        panic(err)
    }

    builder, err := handler.ParsePreferences(body)
    if err != nil {
        handler.errorWriter.Write(w, params.ParseError{})
        return
    }
    preferences, err := builder.ToPreferences()
    if err != nil {
        handler.errorWriter.Write(w, params.ValidationError([]string{err.Error()}))
        return
    }

    transaction := connection.Transaction()
    transaction.Begin()
    err = handler.preferenceUpdater.Execute(transaction, preferences, userID)
    if err != nil {
        transaction.Rollback()

        switch err.(type) {
        case services.MissingKindOrClientError, services.CriticalKindError:
            handler.errorWriter.Write(w, params.ValidationError([]string{err.Error()}))
        default:
            handler.errorWriter.Write(w, err)
        }
        return
    }

    transaction.Commit()
    w.WriteHeader(http.StatusNoContent)
}

func (handler UpdatePreferences) ParsePreferences(body []byte) (services.PreferencesBuilder, error) {
    builder := services.NewPreferencesBuilder()
    err := json.Unmarshal(body, &builder)
    if err != nil {
        return builder, err
    }
    return builder, nil
}
<|endoftext|>


<commit_before>// +build !go1.12

package proxy

import (
	"runtime"
	"stringstrings"
)

func findCaller(f Filter) int {
	// skip starts 5. 0: Callers, 1: findCaller, 2: hooks, 3: proxy-funcs, 4: database/sql, and equals or greater than 5: user-funcs
	skip := 5
	for {
		var rpc [8]uintptr
		n := runtime.Callers(skip, rpc[:])

		for i, pc := range rpc[:n] {
			// http://stackoverflow.com/questions/25262754/how-to-get-name-of-current-package-in-go
			name := runtime.FuncForPC(pc).Name()
			if name == "" || strings.HasPrefix(name, "runtime.") {
				continue
			}
			dotIdx := 0
			for j := len(name) - 1; j >= 0; j-- {
				if name[j] == '.' {
					dotIdx = j
				} else if name[j] == '/' {
					break
				}
			}
			packageName := name[:dotIdx]
			if f.DoOutput(packageName) {
				return skip + i + 1
			}
		}
		if n < len(rpc) {
			break
		}
		skip += n
	}
	// fallback to the caller
	// 1: Outputter.Output, 2: the caller
	return 2
}
<commit_msg>fix compile error<commit_after>// +build !go1.12

package proxy

import (
	"runtime"
	"strings"
)

func findCaller(f Filter) int {
	// skip starts 5. 0: Callers, 1: findCaller, 2: hooks, 3: proxy-funcs, 4: database/sql, and equals or greater than 5: user-funcs
	skip := 5
	for {
		var rpc [8]uintptr
		n := runtime.Callers(skip, rpc[:])

		for i, pc := range rpc[:n] {
			// http://stackoverflow.com/questions/25262754/how-to-get-name-of-current-package-in-go
			name := runtime.FuncForPC(pc).Name()
			if name == "" || strings.HasPrefix(name, "runtime.") {
				continue
			}
			dotIdx := 0
			for j := len(name) - 1; j >= 0; j-- {
				if name[j] == '.' {
					dotIdx = j
				} else if name[j] == '/' {
					break
				}
			}
			packageName := name[:dotIdx]
			if f.DoOutput(packageName) {
				return skip + i + 1
			}
		}
		if n < len(rpc) {
			break
		}
		skip += n
	}
	// fallback to the caller
	// 1: Outputter.Output, 2: the caller
	return 2
}
<|endoftext|>


<commit_before>package tengo

import (
	"database/sql"
	"fmt"
	"net/url"
	"reflect"
	"strings"
	"sync"

	"github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

// Instance represents a single database server running on a specific host or address.
type Instance struct {
	BaseDSN        string // DSN ending in trailing slash; i.e. no schema name or params
	Driver         string
	User           string
	Password       string
	Host           string
	Port           int
	SocketPath     string
	defaultParams  map[string]string
	schemas        []*Schema
	connectionPool map[string]*sqlx.DB // key is in format "schema?params" or just "schema" if no params
	*sync.RWMutex                      // protects internal state
}

var allInstances struct {
	sync.Mutex
	byDSN map[string]*Instance
}

func init() {
	allInstances.byDSN = make(map[string]*Instance)
}

// NewInstance returns a pointer to a new Instance corresponding to the
// supplied driver and dsn. Currently only "mysql" driver is supported.
// dsn should be formatted according to driver specifications. If it contains
// a schema name, it will be ignored. If it contains any params, they will be
// applied as default params to all connections (in addition to whatever is
// supplied in Connect).
// If an Instance with the supplied dsn has already been created previously,
// it will be returned instead of a new Instance being created. This
// deduplication is necessary in order for Instance's internal caching to work
// properly.
func NewInstance(driver, dsn string) (*Instance, error) {
	if driver != "mysql" {
		return nil, fmt.Errorf("Unsupported driver \"%s\"", driver)
	}

	base := baseDSN(dsn)
	params := paramMap(dsn)
	parsedConfig, err := mysql.ParseDSN(dsn)
	if err != nil {
		return nil, err
	}

	// See if an instance with the supplied dsn already exists. Note that we forbid
	// creating a duplicate instance that has a different user, pass, or default
	// params; having multiple Instances that refer to the same underlying DB
	// server instance would break caching logic.
	// TODO: permit changing the username, password, and/or params of an existing
	// instance through another set of methods
	allInstances.Lock()
	defer allInstances.Unlock()
	instance, already := allInstances.byDSN[base]
	if already {
		if instance.User != parsedConfig.User {
			return nil, fmt.Errorf("Instance already exists, but with different username")
		} else if instance.Password != parsedConfig.Passwd {
			return nil, fmt.Errorf("Instance already exists, but with different password")
		} else if !reflect.DeepEqual(instance.defaultParams, params) {
			return nil, fmt.Errorf("Instance already exists, but with different default params")
		}
		return instance, nil
	}

	instance = &Instance{
		BaseDSN:        base,
		Driver:         driver,
		User:           parsedConfig.User,
		Password:       parsedConfig.Passwd,
		defaultParams:  params,
		connectionPool: make(map[string]*sqlx.DB),
		RWMutex:        new(sync.RWMutex),
	}

	switch parsedConfig.Net {
	case "unix":
		instance.Host = "localhost"
		instance.SocketPath = parsedConfig.Addr
	case "cloudsql":
		instance.Host = parsedConfig.Addr
	default:
		instance.Host, instance.Port, err = SplitHostOptionalPort(parsedConfig.Addr)
		if err != nil {
			return nil, err
		}
	}

	allInstances.byDSN[base] = instance
	return instance, nil
}

// String for an instance returns a "host:port" string (or "localhost:/path/to/socket"
// if using UNIX domain socket)
func (instance *Instance) String() string {
	if instance.SocketPath != "" {
		return fmt.Sprintf("%s:%s", instance.Host, instance.SocketPath)
	} else if instance.Port == 0 {
		return instance.Host
	} else {
		return fmt.Sprintf("%s:%d", instance.Host, instance.Port)
	}
}

// HostAndOptionalPort is like String(), but omits the port if default
func (instance *Instance) HostAndOptionalPort() string {
	if instance.Port == 3306 || instance.SocketPath != "" {
		return instance.Host
	}
	return instance.String()
}

func (instance *Instance) buildParamString(params string) string {
	v := url.Values{}
	for defName, defValue := range instance.defaultParams {
		v.Set(defName, defValue)
	}
	overrides, _ := url.ParseQuery(params)
	for name := range overrides {
		v.Set(name, overrides.Get(name))
	}
	return v.Encode()
}

// Connect returns a connection pool (sql.DB) for this instance's host/port/
// user/pass with the supplied default schema and params string. If a connection
// pool already exists for this combination, it will be returned; otherwise, one
// will be initialized and a connection attempt is made to confirm access.
// defaultSchema may be "" if it is not relevant.
// params should be supplied in format "foo=bar&fizz=buzz" with URL escaping
// already applied. Do not include a prefix of "?". params will be merged with
// instance.defaultParams, with params supplied here taking precedence.
func (instance *Instance) Connect(defaultSchema string, params string) (*sqlx.DB, error) {
	key := fmt.Sprintf("%s?%s", defaultSchema, instance.buildParamString(params))

	instance.RLock()
	pool, ok := instance.connectionPool[key]
	instance.RUnlock()

	if ok {
		return pool, nil
	}

	fullDSN := instance.BaseDSN + key
	db, err := sqlx.Connect(instance.Driver, fullDSN)
	if err != nil {
		return nil, err
	}

	instance.Lock()
	defer instance.Unlock()
	instance.connectionPool[key] = db.Unsafe()
	return instance.connectionPool[key], nil
}

// CanConnect verifies that the Instance can be connected to
func (instance *Instance) CanConnect() (bool, error) {
	instance.Lock()
	delete(instance.connectionPool, "?") // ensure we're initializing a new conn pool for schemalass, paramless use
	instance.Unlock()

	_, err := instance.Connect("", "")
	return err == nil, err
}

// Schemas returns a slice of all schemas on the instance visible to the user.
func (instance *Instance) Schemas() ([]*Schema, error) {
	instance.RLock()
	ret := instance.schemas
	instance.RUnlock()
	if ret != nil {
		return ret, nil
	}

	db, err := instance.Connect("information_schema", "")
	if err != nil {
		return nil, err
	}

	instance.Lock()
	defer instance.Unlock()

	var rawSchemas []struct {
		Name             string `db:"schema_name"`
		DefaultCharSet   string `db:"default_character_set_name"`
		DefaultCollation string `db:"default_collation_name"`
	}
	query := `
		SELECT schema_name, default_character_set_name, default_collation_name
		FROM   schemata
		WHERE  schema_name NOT IN ('information_schema', 'performance_schema', 'mysql', 'test', 'sys')`
	if err := db.Select(&rawSchemas, query); err != nil {
		return nil, err
	}

	instance.schemas = make([]*Schema, len(rawSchemas))
	for n, rawSchema := range rawSchemas {
		instance.schemas[n] = &Schema{
			Name:             rawSchema.Name,
			DefaultCharSet:   rawSchema.DefaultCharSet,
			DefaultCollation: rawSchema.DefaultCollation,
			instance:         instance,
		}
	}
	return instance.schemas, nil
}

// Schema returns a single schema by name.
func (instance *Instance) Schema(name string) (*Schema, error) {
	schemas, err := instance.Schemas()
	if err != nil {
		return nil, err
	}
	for _, s := range schemas {
		if s.Name == name {
			return s, nil
		}
	}
	return nil, nil
}

// HasSchema returns true if this instance has a schema with the supplied name
// visible to the user, or false otherwise.
func (instance *Instance) HasSchema(name string) bool {
	s, _ := instance.Schema(name)
	return s != nil
}

// ShowCreateTable returns a string with a CREATE TABLE statement, representing
// how the instance views the specified table as having been created.
func (instance *Instance) ShowCreateTable(schema *Schema, table *Table) (string, error) {
	db, err := instance.Connect(schema.Name, "")
	if err != nil {
		return "", err
	}

	var createRows []struct {
		TableName       string `db:"Table"`
		CreateStatement string `db:"Create Table"`
	}
	query := fmt.Sprintf("SHOW CREATE TABLE %s", EscapeIdentifier(table.Name))
	if err := db.Select(&createRows, query); err != nil {
		return "", err
	}
	if len(createRows) != 1 {
		return "", sql.ErrNoRows
	}

	return createRows[0].CreateStatement, nil
}

// TableSize returns an estimate of the table's size on-disk, based on data in
// information_schema. If the table or schema does not exist on this instance,
// the error will be sql.ErrNoRows.
// Please note that use of innodb_stats_persistent may negatively impact the
// accuracy. For example, see https://bugs.mysql.com/bug.php?id=75428.
func (instance *Instance) TableSize(schema *Schema, table *Table) (int64, error) {
	var result int64
	db, err := instance.Connect("information_schema", "")
	if err != nil {
		return 0, err
	}
	err = db.Get(&result, `
		SELECT  data_length + index_length + data_free
		FROM    tables
		WHERE   table_schema = ? and table_name = ?`,
		schema.Name, table.Name)
	return result, err
}

// TableHasRows returns true if the table has at least one row. If an error
// occurs in querying, also returns true (along with the error) since a false
// positive is generally less dangerous in this case than a false negative.
func (instance *Instance) TableHasRows(schema *Schema, table *Table) (bool, error) {
	db, err := instance.Connect(schema.Name, "")
	if err != nil {
		return true, err
	}
	var result []int
	query := fmt.Sprintf("SELECT 1 FROM %s LIMIT 1", EscapeIdentifier(table.Name))
	if err := db.Select(&result, query); err != nil {
		return true, err
	}
	return len(result) != 0, nil
}

func (instance *Instance) purgeSchemaCache() {
	instance.Lock()
	instance.schemas = nil
	instance.Unlock()
}

// CreateSchema creates a new database schema with the supplied name.
func (instance *Instance) CreateSchema(name string) (*Schema, error) {
	db, err := instance.Connect("", "")
	if err != nil {
		return nil, err
	}
	// TODO: support DEFAULT CHARACTER SET and DEFAULT COLLATE
	schema := Schema{Name: name}
	_, err = db.Exec(schema.CreateStatement())
	if err != nil {
		return nil, err
	}

	// Purge schema cache; next call to Schema will repopulate
	instance.purgeSchemaCache()
	return instance.Schema(name)
}

// DropSchema first drops all tables in the schema, and then drops the database
// schema itself. If onlyIfEmpty==true, returns an error if any of the tables
// have any rows.
func (instance *Instance) DropSchema(schema *Schema, onlyIfEmpty bool) error {
	err := instance.DropTablesInSchema(schema, onlyIfEmpty)
	if err != nil {
		return err
	}

	db, err := instance.Connect(schema.Name, "")
	if err != nil {
		return err
	}
	_, err = db.Exec(schema.DropStatement())
	if err != nil {
		return err
	}

	prefix := fmt.Sprintf("%s?", schema.Name)
	instance.Lock()
	for key, connPool := range instance.connectionPool {
		if strings.HasPrefix(key, prefix) {
			connPool.Close()
			delete(instance.connectionPool, key)
		}
	}
	instance.Unlock()

	// Purge schema cache; next call to Schema will repopulate
	instance.purgeSchemaCache()
	return nil
}

// DropTablesInSchema drops all tables in a schema. If onlyIfEmpty==true,
// returns an error if any of the tables have any rows.
func (instance *Instance) DropTablesInSchema(schema *Schema, onlyIfEmpty bool) error {
	db, err := instance.Connect(schema.Name, "foreign_key_checks=0")
	if err != nil {
		return err
	}
	tables, err := schema.Tables()
	if err != nil {
		return err
	}

	if onlyIfEmpty {
		for _, t := range tables {
			hasRows, err := instance.TableHasRows(schema, t)
			if err != nil {
				return err
			}
			if hasRows {
				return fmt.Errorf("DropTablesInSchema: table %s.%s has at least one row", EscapeIdentifier(schema.Name), EscapeIdentifier(t.Name))
			}
		}
	}

	for _, t := range tables {
		_, err := db.Exec(t.DropStatement())
		if err != nil {
			return err
		}
	}

	schema.PurgeTableCache()
	return nil
}

// CloneSchema copies all tables (just definitions, not data) from src to dest.
// Ideally dest should be an empty schema, or at least be pre-verified for not
// having existing tables with conflicting names, but this is the caller's
// responsibility to confirm.
func (instance *Instance) CloneSchema(src, dest *Schema) error {
	db, err := instance.Connect(dest.Name, "foreign_key_checks=0")
	if err != nil {
		return err
	}
	tables, err := src.Tables()
	if err != nil {
		return err
	}
	for _, t := range tables {
		_, err := db.Exec(t.CreateStatement())
		if err != nil {
			return err
		}
	}
	dest.PurgeTableCache()
	return nil
}

// baseDSN returns a DSN with the database (schema) name and params stripped.
// Currently only supports MySQL, via go-sql-driver/mysql's DSN format.
func baseDSN(dsn string) string {
	tokens := strings.SplitAfter(dsn, "/")
	return strings.Join(tokens[0:len(tokens)-1], "")
}

// paramMap builds a map representing all params in the DSN.
// This does not rely on mysql.ParseDSN because that handles some vars
// separately; i.e. mysql.Config's params field does NOT include all
// params that are passed in!
func paramMap(dsn string) map[string]string {
	parts := strings.Split(dsn, "?")
	if len(parts) == 1 {
		return make(map[string]string)
	}
	params := parts[len(parts)-1]
	values, _ := url.ParseQuery(params)

	// Convert values, which is map[string][]string, to single-valued map[string]string
	// i.e. if a param is present multiple times, we only keep the first value
	result := make(map[string]string, len(values))
	for key := range values {
		result[key] = values.Get(key)
	}
	return result
}
<commit_msg>Add new method Instance.SchemasByName() for convenience<commit_after>package tengo

import (
	"database/sql"
	"fmt"
	"net/url"
	"reflect"
	"strings"
	"sync"

	"github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

// Instance represents a single database server running on a specific host or address.
type Instance struct {
	BaseDSN        string // DSN ending in trailing slash; i.e. no schema name or params
	Driver         string
	User           string
	Password       string
	Host           string
	Port           int
	SocketPath     string
	defaultParams  map[string]string
	schemas        []*Schema
	connectionPool map[string]*sqlx.DB // key is in format "schema?params" or just "schema" if no params
	*sync.RWMutex                      // protects internal state
}

var allInstances struct {
	sync.Mutex
	byDSN map[string]*Instance
}

func init() {
	allInstances.byDSN = make(map[string]*Instance)
}

// NewInstance returns a pointer to a new Instance corresponding to the
// supplied driver and dsn. Currently only "mysql" driver is supported.
// dsn should be formatted according to driver specifications. If it contains
// a schema name, it will be ignored. If it contains any params, they will be
// applied as default params to all connections (in addition to whatever is
// supplied in Connect).
// If an Instance with the supplied dsn has already been created previously,
// it will be returned instead of a new Instance being created. This
// deduplication is necessary in order for Instance's internal caching to work
// properly.
func NewInstance(driver, dsn string) (*Instance, error) {
	if driver != "mysql" {
		return nil, fmt.Errorf("Unsupported driver \"%s\"", driver)
	}

	base := baseDSN(dsn)
	params := paramMap(dsn)
	parsedConfig, err := mysql.ParseDSN(dsn)
	if err != nil {
		return nil, err
	}

	// See if an instance with the supplied dsn already exists. Note that we forbid
	// creating a duplicate instance that has a different user, pass, or default
	// params; having multiple Instances that refer to the same underlying DB
	// server instance would break caching logic.
	// TODO: permit changing the username, password, and/or params of an existing
	// instance through another set of methods
	allInstances.Lock()
	defer allInstances.Unlock()
	instance, already := allInstances.byDSN[base]
	if already {
		if instance.User != parsedConfig.User {
			return nil, fmt.Errorf("Instance already exists, but with different username")
		} else if instance.Password != parsedConfig.Passwd {
			return nil, fmt.Errorf("Instance already exists, but with different password")
		} else if !reflect.DeepEqual(instance.defaultParams, params) {
			return nil, fmt.Errorf("Instance already exists, but with different default params")
		}
		return instance, nil
	}

	instance = &Instance{
		BaseDSN:        base,
		Driver:         driver,
		User:           parsedConfig.User,
		Password:       parsedConfig.Passwd,
		defaultParams:  params,
		connectionPool: make(map[string]*sqlx.DB),
		RWMutex:        new(sync.RWMutex),
	}

	switch parsedConfig.Net {
	case "unix":
		instance.Host = "localhost"
		instance.SocketPath = parsedConfig.Addr
	case "cloudsql":
		instance.Host = parsedConfig.Addr
	default:
		instance.Host, instance.Port, err = SplitHostOptionalPort(parsedConfig.Addr)
		if err != nil {
			return nil, err
		}
	}

	allInstances.byDSN[base] = instance
	return instance, nil
}

// String for an instance returns a "host:port" string (or "localhost:/path/to/socket"
// if using UNIX domain socket)
func (instance *Instance) String() string {
	if instance.SocketPath != "" {
		return fmt.Sprintf("%s:%s", instance.Host, instance.SocketPath)
	} else if instance.Port == 0 {
		return instance.Host
	} else {
		return fmt.Sprintf("%s:%d", instance.Host, instance.Port)
	}
}

// HostAndOptionalPort is like String(), but omits the port if default
func (instance *Instance) HostAndOptionalPort() string {
	if instance.Port == 3306 || instance.SocketPath != "" {
		return instance.Host
	}
	return instance.String()
}

func (instance *Instance) buildParamString(params string) string {
	v := url.Values{}
	for defName, defValue := range instance.defaultParams {
		v.Set(defName, defValue)
	}
	overrides, _ := url.ParseQuery(params)
	for name := range overrides {
		v.Set(name, overrides.Get(name))
	}
	return v.Encode()
}

// Connect returns a connection pool (sql.DB) for this instance's host/port/
// user/pass with the supplied default schema and params string. If a connection
// pool already exists for this combination, it will be returned; otherwise, one
// will be initialized and a connection attempt is made to confirm access.
// defaultSchema may be "" if it is not relevant.
// params should be supplied in format "foo=bar&fizz=buzz" with URL escaping
// already applied. Do not include a prefix of "?". params will be merged with
// instance.defaultParams, with params supplied here taking precedence.
func (instance *Instance) Connect(defaultSchema string, params string) (*sqlx.DB, error) {
	key := fmt.Sprintf("%s?%s", defaultSchema, instance.buildParamString(params))

	instance.RLock()
	pool, ok := instance.connectionPool[key]
	instance.RUnlock()

	if ok {
		return pool, nil
	}

	fullDSN := instance.BaseDSN + key
	db, err := sqlx.Connect(instance.Driver, fullDSN)
	if err != nil {
		return nil, err
	}

	instance.Lock()
	defer instance.Unlock()
	instance.connectionPool[key] = db.Unsafe()
	return instance.connectionPool[key], nil
}

// CanConnect verifies that the Instance can be connected to
func (instance *Instance) CanConnect() (bool, error) {
	instance.Lock()
	delete(instance.connectionPool, "?") // ensure we're initializing a new conn pool for schemalass, paramless use
	instance.Unlock()

	_, err := instance.Connect("", "")
	return err == nil, err
}

// Schemas returns a slice of all schemas on the instance visible to the user.
func (instance *Instance) Schemas() ([]*Schema, error) {
	instance.RLock()
	ret := instance.schemas
	instance.RUnlock()
	if ret != nil {
		return ret, nil
	}

	db, err := instance.Connect("information_schema", "")
	if err != nil {
		return nil, err
	}

	instance.Lock()
	defer instance.Unlock()

	var rawSchemas []struct {
		Name             string `db:"schema_name"`
		DefaultCharSet   string `db:"default_character_set_name"`
		DefaultCollation string `db:"default_collation_name"`
	}
	query := `
		SELECT schema_name, default_character_set_name, default_collation_name
		FROM   schemata
		WHERE  schema_name NOT IN ('information_schema', 'performance_schema', 'mysql', 'test', 'sys')`
	if err := db.Select(&rawSchemas, query); err != nil {
		return nil, err
	}

	instance.schemas = make([]*Schema, len(rawSchemas))
	for n, rawSchema := range rawSchemas {
		instance.schemas[n] = &Schema{
			Name:             rawSchema.Name,
			DefaultCharSet:   rawSchema.DefaultCharSet,
			DefaultCollation: rawSchema.DefaultCollation,
			instance:         instance,
		}
	}
	return instance.schemas, nil
}

// SchemasByName returns a map of schema name string to *Schema, for all schemas
// that exist on the instance.
func (instance *Instance) SchemasByName() (map[string]*Schema, error) {
	schemas, err := instance.Schemas()
	if err != nil {
		return nil, err
	}
	result := make(map[string]*Schema, len(schemas))
	for _, s := range schemas {
		result[s.Name] = s
	}
	return result, nil
}

// Schema returns a single schema by name.
func (instance *Instance) Schema(name string) (*Schema, error) {
	byName, err := instance.SchemasByName()
	if err != nil {
		return nil, err
	}
	return byName[name], nil
}

// HasSchema returns true if this instance has a schema with the supplied name
// visible to the user, or false otherwise.
func (instance *Instance) HasSchema(name string) bool {
	s, _ := instance.Schema(name)
	return s != nil
}

// ShowCreateTable returns a string with a CREATE TABLE statement, representing
// how the instance views the specified table as having been created.
func (instance *Instance) ShowCreateTable(schema *Schema, table *Table) (string, error) {
	db, err := instance.Connect(schema.Name, "")
	if err != nil {
		return "", err
	}

	var createRows []struct {
		TableName       string `db:"Table"`
		CreateStatement string `db:"Create Table"`
	}
	query := fmt.Sprintf("SHOW CREATE TABLE %s", EscapeIdentifier(table.Name))
	if err := db.Select(&createRows, query); err != nil {
		return "", err
	}
	if len(createRows) != 1 {
		return "", sql.ErrNoRows
	}

	return createRows[0].CreateStatement, nil
}

// TableSize returns an estimate of the table's size on-disk, based on data in
// information_schema. If the table or schema does not exist on this instance,
// the error will be sql.ErrNoRows.
// Please note that use of innodb_stats_persistent may negatively impact the
// accuracy. For example, see https://bugs.mysql.com/bug.php?id=75428.
func (instance *Instance) TableSize(schema *Schema, table *Table) (int64, error) {
	var result int64
	db, err := instance.Connect("information_schema", "")
	if err != nil {
		return 0, err
	}
	err = db.Get(&result, `
		SELECT  data_length + index_length + data_free
		FROM    tables
		WHERE   table_schema = ? and table_name = ?`,
		schema.Name, table.Name)
	return result, err
}

// TableHasRows returns true if the table has at least one row. If an error
// occurs in querying, also returns true (along with the error) since a false
// positive is generally less dangerous in this case than a false negative.
func (instance *Instance) TableHasRows(schema *Schema, table *Table) (bool, error) {
	db, err := instance.Connect(schema.Name, "")
	if err != nil {
		return true, err
	}
	var result []int
	query := fmt.Sprintf("SELECT 1 FROM %s LIMIT 1", EscapeIdentifier(table.Name))
	if err := db.Select(&result, query); err != nil {
		return true, err
	}
	return len(result) != 0, nil
}

func (instance *Instance) purgeSchemaCache() {
	instance.Lock()
	instance.schemas = nil
	instance.Unlock()
}

// CreateSchema creates a new database schema with the supplied name.
func (instance *Instance) CreateSchema(name string) (*Schema, error) {
	db, err := instance.Connect("", "")
	if err != nil {
		return nil, err
	}
	// TODO: support DEFAULT CHARACTER SET and DEFAULT COLLATE
	schema := Schema{Name: name}
	_, err = db.Exec(schema.CreateStatement())
	if err != nil {
		return nil, err
	}

	// Purge schema cache; next call to Schema will repopulate
	instance.purgeSchemaCache()
	return instance.Schema(name)
}

// DropSchema first drops all tables in the schema, and then drops the database
// schema itself. If onlyIfEmpty==true, returns an error if any of the tables
// have any rows.
func (instance *Instance) DropSchema(schema *Schema, onlyIfEmpty bool) error {
	err := instance.DropTablesInSchema(schema, onlyIfEmpty)
	if err != nil {
		return err
	}

	db, err := instance.Connect(schema.Name, "")
	if err != nil {
		return err
	}
	_, err = db.Exec(schema.DropStatement())
	if err != nil {
		return err
	}

	prefix := fmt.Sprintf("%s?", schema.Name)
	instance.Lock()
	for key, connPool := range instance.connectionPool {
		if strings.HasPrefix(key, prefix) {
			connPool.Close()
			delete(instance.connectionPool, key)
		}
	}
	instance.Unlock()

	// Purge schema cache; next call to Schema will repopulate
	instance.purgeSchemaCache()
	return nil
}

// DropTablesInSchema drops all tables in a schema. If onlyIfEmpty==true,
// returns an error if any of the tables have any rows.
func (instance *Instance) DropTablesInSchema(schema *Schema, onlyIfEmpty bool) error {
	db, err := instance.Connect(schema.Name, "foreign_key_checks=0")
	if err != nil {
		return err
	}
	tables, err := schema.Tables()
	if err != nil {
		return err
	}

	if onlyIfEmpty {
		for _, t := range tables {
			hasRows, err := instance.TableHasRows(schema, t)
			if err != nil {
				return err
			}
			if hasRows {
				return fmt.Errorf("DropTablesInSchema: table %s.%s has at least one row", EscapeIdentifier(schema.Name), EscapeIdentifier(t.Name))
			}
		}
	}

	for _, t := range tables {
		_, err := db.Exec(t.DropStatement())
		if err != nil {
			return err
		}
	}

	schema.PurgeTableCache()
	return nil
}

// CloneSchema copies all tables (just definitions, not data) from src to dest.
// Ideally dest should be an empty schema, or at least be pre-verified for not
// having existing tables with conflicting names, but this is the caller's
// responsibility to confirm.
func (instance *Instance) CloneSchema(src, dest *Schema) error {
	db, err := instance.Connect(dest.Name, "foreign_key_checks=0")
	if err != nil {
		return err
	}
	tables, err := src.Tables()
	if err != nil {
		return err
	}
	for _, t := range tables {
		_, err := db.Exec(t.CreateStatement())
		if err != nil {
			return err
		}
	}
	dest.PurgeTableCache()
	return nil
}

// baseDSN returns a DSN with the database (schema) name and params stripped.
// Currently only supports MySQL, via go-sql-driver/mysql's DSN format.
func baseDSN(dsn string) string {
	tokens := strings.SplitAfter(dsn, "/")
	return strings.Join(tokens[0:len(tokens)-1], "")
}

// paramMap builds a map representing all params in the DSN.
// This does not rely on mysql.ParseDSN because that handles some vars
// separately; i.e. mysql.Config's params field does NOT include all
// params that are passed in!
func paramMap(dsn string) map[string]string {
	parts := strings.Split(dsn, "?")
	if len(parts) == 1 {
		return make(map[string]string)
	}
	params := parts[len(parts)-1]
	values, _ := url.ParseQuery(params)

	// Convert values, which is map[string][]string, to single-valued map[string]string
	// i.e. if a param is present multiple times, we only keep the first value
	result := make(map[string]string, len(values))
	for key := range values {
		result[key] = values.Get(key)
	}
	return result
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"text/template"
)

func main() {
	var application struct {
		ApplicationURIs []string `json:"application_uris"`
	}

	err := json.Unmarshal([]byte(os.Getenv("VCAP_APPLICATION")), &application)
	if err != nil {
		log.Fatalf("failed to parse VCAP_APPLICATION: %s", err)
	}

	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		var withoutAgentPath bool
		if strings.HasPrefix(req.URL.Path, "/without-agent-path") {
			req.URL.Path = strings.TrimPrefix(req.URL.Path, "/without-agent-path")
			withoutAgentPath = true
		}

		switch req.URL.Path {
		case "/v1/deployment/installer/agent/unix/paas-sh/latest":
			context := struct{ URI string }{URI: application.ApplicationURIs[0]}
			t := template.Must(template.New("install.sh").ParseFiles("install.sh"))
			err := t.Execute(w, context)
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				w.Write([]byte(err.Error()))
				return
			}

		case "/dynatrace-env.sh", "/liboneagentproc.so":
			contents, err := ioutil.ReadFile(strings.TrimPrefix(req.URL.Path, "/"))
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				w.Write([]byte(err.Error()))
				return
			}
			w.Write(contents)

		case "/manifest.json":
			payload := map[string]interface{}{}

			if !withoutAgentPath {
				file, err := os.Open("manifest.json")
				if err != nil {
					w.WriteHeader(http.StatusInternalServerError)
					w.Write([]byte(err.Error()))
					return
				}

				err = json.NewDecoder(file).Decode(&payload)
				if err != nil {
					w.WriteHeader(http.StatusInternalServerError)
					w.Write([]byte(err.Error()))
					return
				}
			}

			json.NewEncoder(w).Encode(payload)

		default:
			w.WriteHeader(http.StatusNotFound)
		}
	})

	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", os.Getenv("PORT")), nil))
}
<commit_msg>Fixes fake dynatrace to response with empty manifest.json<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"text/template"
)

func main() {
	var application struct {
		ApplicationURIs []string `json:"application_uris"`
	}

	err := json.Unmarshal([]byte(os.Getenv("VCAP_APPLICATION")), &application)
	if err != nil {
		log.Fatalf("failed to parse VCAP_APPLICATION: %s", err)
	}

	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		var withoutAgentPath bool
		path := req.URL.Path

		if strings.HasPrefix(path, "/without-agent-path") {
			path = strings.TrimPrefix(path, "/without-agent-path")
			withoutAgentPath = true
		}

		switch path {
		case "/v1/deployment/installer/agent/unix/paas-sh/latest":
			context := struct{ URI string }{URI: application.ApplicationURIs[0]}
			t := template.Must(template.New("install.sh").ParseFiles("install.sh"))
			err := t.Execute(w, context)
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				w.Write([]byte(err.Error()))
				return
			}

		case "/dynatrace-env.sh", "/liboneagentproc.so":
			contents, err := ioutil.ReadFile(strings.TrimPrefix(req.URL.Path, "/"))
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				w.Write([]byte(err.Error()))
				return
			}
			w.Write(contents)

		case "/manifest.json":
			payload := map[string]interface{}{}

			if !withoutAgentPath {
				file, err := os.Open("manifest.json")
				if err != nil {
					w.WriteHeader(http.StatusInternalServerError)
					w.Write([]byte(err.Error()))
					return
				}

				err = json.NewDecoder(file).Decode(&payload)
				if err != nil {
					w.WriteHeader(http.StatusInternalServerError)
					w.Write([]byte(err.Error()))
					return
				}
			}

			json.NewEncoder(w).Encode(payload)

		default:
			w.WriteHeader(http.StatusNotFound)
		}
	})

	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", os.Getenv("PORT")), nil))
}
<|endoftext|>


<commit_before>// This example demonstrates how to authenticate with Spotify.
// In order to run this example yourself, you'll need to:
//
//  1. Register an application at: https://developer.spotify.com/my-applications/
//       - Use "http://localhost:8080/callback" as the redirect URI
//  2. Set the SPOTIFY_ID environment variable to the client ID you got in step 1.
//  3. Set the SPOTIFY_SECRET environment variable to the client secret from step 1.
package main

import (
	"container/heap"
	"encoding/json"
	"fmt"
	"log"
	"net/http"

	"github.com/coolbry95/partydj/backend/pool"
	"github.com/pressly/chi"
	"github.com/zmb3/spotify"
)

// redirectURI is the OAuth redirect URI for the application.
// You must register an application at Spotify's developer portal
// and enter this value.
const redirectURI = "http://localhost:8080/callback"

var (
	auth = spotify.NewAuthenticator(redirectURI, spotify.ScopeUserLibraryModify, spotify.ScopePlaylistModifyPrivate,
		spotify.ScopePlaylistModifyPublic)
	ch    = make(chan spotify.Client)
	state = "stateless"
)

type DI struct {
	client spotify.Client
	pool   pool.Pool
}

func main() {

	var d *DI
	d = new(DI)

	r := chi.NewRouter()
	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		log.Println(r.URL.String())
	})
	r.Get("/callback", completeAuth)
	r.Get("/getpool", d.getPool)
	r.Post("/createpool", d.createPool)
	r.Post("/add_song/:poolID/:songID", d.addSong)
	r.Post("/upvote/:poolID/:songID", d.upVote)
	r.Post("/downvote/:poolID/:songID", d.downVote)

	url := auth.AuthURL(state)
	fmt.Println("Please log in to Spotify by visiting the following page in your browser:", url)

	go http.ListenAndServe(":8080", r)

	// wait for auth to complete
	d.client = <-ch

	d.pool.PlaylistID = spotify.ID("0nXlYUH7zBAzubO9Yub4rR")

	userID, err := d.client.CurrentUser()
	if err != nil {
		log.Println(err)
	}

	d.pool.UserID = userID.ID

	block := make(chan struct{})
	<-block

}

func (d *DI) addSong(w http.ResponseWriter, r *http.Request) {
	songID := chi.URLParam(r, "songID")
	//poolID := chi.URLParam(r, "poolID")

	s := &pool.Song{ID: spotify.ID(songID)}
	heap.Push(&d.pool, s)

}

func (d *DI) upVote(w http.ResponseWriter, r *http.Request) {
	// TODO check for user ID to see if they already voted
	songID := chi.URLParam(r, "songID")

	d.pool.UpVote(spotify.ID(songID))
	d.pool.UpdateSpotifyPlaylist(&d.client, d.pool.PlaylistID)
}

func (d *DI) downVote(w http.ResponseWriter, r *http.Request) {
	// TODO check for user ID to see if they already voted
	songID := chi.URLParam(r, "songID")
	//poolID := chi.URLParam(r, "poolID")

	d.pool.DownVote(spotify.ID(songID))
	d.pool.UpdateSpotifyPlaylist(&d.client, d.pool.PlaylistID)
}

func (d *DI) createPool(w http.ResponseWriter, r *http.Request) {
}

func (d *DI) getPool(w http.ResponseWriter, r *http.Request) {
	userid, err := d.client.CurrentUser()
	if err != nil {
		log.Println(err)
	}

	// TODO: instead of using existing playlist we require a new playlist to be created
	// playlist, err := d.client.CreatePlaylistForUser(userid.ID, playlistName, true)
	playlist, err := d.client.GetPlaylistTracks(userid.ID, "0nXlYUH7zBAzubO9Yub4rR")
	if err != nil {
		log.Println(err)
	}

	d.pool.SongHeap = make([]*pool.Song, 0, 10)
	for i, track := range playlist.Tracks {
		d.pool.SongHeap = append(d.pool.SongHeap, pool.TrackToSong(&track.Track.SimpleTrack, i))
	}

	tracks, err := d.client.GetTracks("7ccI9cStQbQdystvc6TvxD", "2U9v51tNOoLRhwrU6j83uU")
	if err != nil {
		fmt.Println(err)
	}

	for i, track := range tracks {
		s := pool.TrackToSong(&track.SimpleTrack, (i+1)*-100)
		//fmt.Println(d.pool)
		heap.Push(&d.pool, s)
		//fmt.Println(d.pool)
	}

	d.pool.AddNextSong(&d.client)

	json.NewEncoder(w).Encode(d.pool)
	return
}

func completeAuth(w http.ResponseWriter, r *http.Request) {
	tok, err := auth.Token(state, r)
	if err != nil {
		http.Error(w, "Couldn't get token", http.StatusForbidden)
		log.Fatal(err)
	}

	if st := r.FormValue("state"); st != state {
		http.NotFound(w, r)
		log.Fatalf("State mismatch: %s != %s\n", st, state)
	}

	// use the token to get an authenticated client
	client := auth.NewClient(tok)
	ch <- client
	http.Redirect(w, r, "/getpool", 301)
}
<commit_msg>only call AddNext when the current song finishes<commit_after>// This example demonstrates how to authenticate with Spotify.
// In order to run this example yourself, you'll need to:
//
//  1. Register an application at: https://developer.spotify.com/my-applications/
//       - Use "http://localhost:8080/callback" as the redirect URI
//  2. Set the SPOTIFY_ID environment variable to the client ID you got in step 1.
//  3. Set the SPOTIFY_SECRET environment variable to the client secret from step 1.
package main

import (
	"container/heap"
	"encoding/json"
	"fmt"
	"log"
	"net/http"

	"github.com/coolbry95/partydj/backend/pool"
	"github.com/pressly/chi"
	"github.com/zmb3/spotify"
)

// redirectURI is the OAuth redirect URI for the application.
// You must register an application at Spotify's developer portal
// and enter this value.
const redirectURI = "http://localhost:8080/callback"

var (
	auth = spotify.NewAuthenticator(redirectURI, spotify.ScopeUserLibraryModify, spotify.ScopePlaylistModifyPrivate,
		spotify.ScopePlaylistModifyPublic)
	ch    = make(chan spotify.Client)
	state = "stateless"
)

type DI struct {
	client spotify.Client
	pool   pool.Pool
}

func main() {

	var d *DI
	d = new(DI)

	r := chi.NewRouter()
	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		log.Println(r.URL.String())
	})
	r.Get("/callback", completeAuth)
	r.Get("/getpool", d.getPool)
	r.Post("/createpool", d.createPool)
	r.Post("/add_song/:poolID/:songID", d.addSong)
	r.Post("/upvote/:poolID/:songID", d.upVote)
	r.Post("/downvote/:poolID/:songID", d.downVote)

	url := auth.AuthURL(state)
	fmt.Println("Please log in to Spotify by visiting the following page in your browser:", url)

	go http.ListenAndServe(":8080", r)

	// wait for auth to complete
	d.client = <-ch

	d.pool.PlaylistID = spotify.ID("0nXlYUH7zBAzubO9Yub4rR")

	userID, err := d.client.CurrentUser()
	if err != nil {
		log.Println(err)
	}

	d.pool.UserID = userID.ID

	block := make(chan struct{})
	<-block

}

func (d *DI) addSong(w http.ResponseWriter, r *http.Request) {
	songID := chi.URLParam(r, "songID")
	//poolID := chi.URLParam(r, "poolID")

	s := &pool.Song{ID: spotify.ID(songID)}
	heap.Push(&d.pool, s)

}

func (d *DI) upVote(w http.ResponseWriter, r *http.Request) {
	// TODO check for user ID to see if they already voted
	songID := chi.URLParam(r, "songID")

	d.pool.UpVote(spotify.ID(songID))
	d.pool.UpdateSpotifyPlaylist(&d.client, d.pool.PlaylistID)
}

func (d *DI) downVote(w http.ResponseWriter, r *http.Request) {
	// TODO check for user ID to see if they already voted
	songID := chi.URLParam(r, "songID")
	//poolID := chi.URLParam(r, "poolID")

	d.pool.DownVote(spotify.ID(songID))
	d.pool.UpdateSpotifyPlaylist(&d.client, d.pool.PlaylistID)
}

func (d *DI) createPool(w http.ResponseWriter, r *http.Request) {
}

func (d *DI) getPool(w http.ResponseWriter, r *http.Request) {
	userid, err := d.client.CurrentUser()
	if err != nil {
		log.Println(err)
	}

	// TODO: instead of using existing playlist we require a new playlist to be created
	// playlist, err := d.client.CreatePlaylistForUser(userid.ID, playlistName, true)
	playlist, err := d.client.GetPlaylistTracks(userid.ID, "0nXlYUH7zBAzubO9Yub4rR")
	if err != nil {
		log.Println(err)
	}

	d.pool.SongHeap = make([]*pool.Song, 0, 10)
	for i, track := range playlist.Tracks {
		d.pool.SongHeap = append(d.pool.SongHeap, pool.TrackToSong(&track.Track.SimpleTrack, i))
	}

	tracks, err := d.client.GetTracks("7ccI9cStQbQdystvc6TvxD", "2U9v51tNOoLRhwrU6j83uU")
	if err != nil {
		fmt.Println(err)
	}

	for i, track := range tracks {
		s := pool.TrackToSong(&track.SimpleTrack, (i+1)*-100)
		//fmt.Println(d.pool)
		heap.Push(&d.pool, s)
		//fmt.Println(d.pool)
	}

	//TODO: only call this function only after the the current song finishes
	//d.pool.AddNextSong(&d.client)

	json.NewEncoder(w).Encode(d.pool)
	return
}

func completeAuth(w http.ResponseWriter, r *http.Request) {
	tok, err := auth.Token(state, r)
	if err != nil {
		http.Error(w, "Couldn't get token", http.StatusForbidden)
		log.Fatal(err)
	}

	if st := r.FormValue("state"); st != state {
		http.NotFound(w, r)
		log.Fatalf("State mismatch: %s != %s\n", st, state)
	}

	// use the token to get an authenticated client
	client := auth.NewClient(tok)
	ch <- client
	http.Redirect(w, r, "/getpool", 301)
}
<|endoftext|>


<commit_before>package http

import (
	"bytes"
	"fmt"
	"net"
	"net/http"
	"time"
)

type consumer struct {
	conn   net.Conn
	es     *eventSource
	in     chan []byte
	staled bool
}

func newConsumer(resp http.ResponseWriter, req *http.Request, es *eventSource) (*consumer, error) {
	conn, _, err := resp.(http.Hijacker).Hijack()
	if err != nil {
		return nil, err
	}

	consumer := &consumer{
		conn:   conn,
		es:     es,
		in:     make(chan []byte, 10),
		staled: false,
	}

	headers := [][]byte{
		[]byte("HTTP/1.1 200 OK"),
		[]byte("Content-Type: text/event-stream"),
	}

	if es.customHeadersFunc != nil {
		customHeaders := es.customHeadersFunc(req)
		headers = append(headers, customHeaders...)
		headers = append(headers, []byte(fmt.Sprintf("retry: %d\n", es.retry)))
	}

	headersData := append(bytes.Join(headers, []byte("\n")), []byte("\n\n")...)

	_, err = conn.Write(headersData)
	if err != nil {
		conn.Close()
		return nil, err
	}

	go func() {
		for {
			select {
			case message := <-consumer.in:
				conn.SetWriteDeadline(time.Now().Add(consumer.es.timeout))
				_, err := conn.Write(message)
				if err != nil {
					netErr, ok := err.(net.Error)
					if !ok || !netErr.Timeout() || consumer.es.closeOnTimeout {
						consumer.staled = true
						consumer.conn.Close()
						consumer.es.staled <- consumer
						return
					}
				}
			case <-time.After(time.Minute * time.Duration(es.idleTimeout)):
				consumer.conn.Close()
				consumer.es.staled <- consumer
				return
			}
		}
	}()

	return consumer, nil
}
<commit_msg>Actually break out of loop to end go routine.<commit_after>package http

import (
	"bytes"
	"fmt"
	"net"
	"net/http"
	"time"
)

type consumer struct {
	conn   net.Conn
	es     *eventSource
	in     chan []byte
	staled bool
}

func newConsumer(resp http.ResponseWriter, req *http.Request, es *eventSource) (*consumer, error) {
	conn, _, err := resp.(http.Hijacker).Hijack()
	if err != nil {
		return nil, err
	}

	consumer := &consumer{
		conn:   conn,
		es:     es,
		in:     make(chan []byte, 10),
		staled: false,
	}

	headers := [][]byte{
		[]byte("HTTP/1.1 200 OK"),
		[]byte("Content-Type: text/event-stream"),
	}

	if es.customHeadersFunc != nil {
		customHeaders := es.customHeadersFunc(req)
		headers = append(headers, customHeaders...)
		headers = append(headers, []byte(fmt.Sprintf("retry: %d\n", es.retry)))
	}

	headersData := append(bytes.Join(headers, []byte("\n")), []byte("\n\n")...)

	_, err = conn.Write(headersData)
	if err != nil {
		conn.Close()
		return nil, err
	}

	go func() {
		breakFromLoop := false
		for {
			select {
			case message := <-consumer.in:
				conn.SetWriteDeadline(time.Now().Add(consumer.es.timeout))
				_, err := conn.Write(message)
				if err != nil {
					netErr, ok := err.(net.Error)
					if !ok || !netErr.Timeout() || consumer.es.closeOnTimeout {
						consumer.staled = true
						consumer.conn.Close()
						consumer.es.staled <- consumer
						breakFromLoop = true
					}
				}
			case <-time.After(time.Minute * time.Duration(es.idleTimeout)):
				consumer.conn.Close()
				consumer.es.staled <- consumer
				breakFromLoop = true
			}
			if breakFromLoop {
				break
			}
		}
	}()

	return consumer, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strconv"
	"strings"

	mp "github.com/mackerelio/go-mackerel-plugin-helper"
)

var graphdef = map[string](mp.Graphs){
	"trafficserver.cache": mp.Graphs{
		Label: "Trafficserver Cache Hits/Misses",
		Unit:  "integer",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "cache_hits", Label: "Hits", Diff: true, Stacked: true, Type: "uint64"},
			mp.Metrics{Name: "cache_misses", Label: "Misses", Diff: true, Stacked: true, Type: "uint64"},
		},
	},
	"trafficserver.http_response_codes": mp.Graphs{
		Label: "Trafficserver HTTP Response Codes",
		Unit:  "integer",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "http_2xx", Label: "2xx", Diff: true, Stacked: true, Type: "uint64"},
			mp.Metrics{Name: "http_3xx", Label: "3xx", Diff: true, Stacked: true, Type: "uint64"},
			mp.Metrics{Name: "http_4xx", Label: "4xx", Diff: true, Stacked: true, Type: "uint64"},
			mp.Metrics{Name: "http_5xx", Label: "5xx", Diff: true, Stacked: true, Type: "uint64"},
		},
	},
	"trafficserver.connections": mp.Graphs{
		Label: "Trafficserver Current Connections",
		Unit:  "integer",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "conn_server", Label: "Server"},
			mp.Metrics{Name: "conn_client", Label: "Client"},
		},
	},
}

var metricVarDef = map[string]string{
	"cache_hits":   "proxy.node.cache_total_hits",
	"cache_misses": "proxy.node.cache_total_misses",
	"http_2xx":     "proxy.process.http.2xx_responses",
	"http_3xx":     "proxy.process.http.3xx_responses",
	"http_4xx":     "proxy.process.http.4xx_responses",
	"http_5xx":     "proxy.process.http.5xx_responses",
	"conn_server":  "proxy.node.current_server_connections",
	"conn_client":  "proxy.node.current_client_connections",
}

// TrafficserverPlugin mackerel plugin for apache trafficserver
type TrafficserverPlugin struct {
	Tempfile string
}

// FetchMetrics interface for mackerelplugin
func (m TrafficserverPlugin) FetchMetrics() (map[string]interface{}, error) {
	var err error
	strp, err := getDataWithCommand()
	if err != nil {
		return nil, err
	}

	stat := make(map[string]interface{})
	parseVars(strp, &stat)

	return stat, nil
}

func parseVars(text *string, statp *map[string]interface{}) error {
	stat := *statp

	varMetric := make(map[string]string)
	for metric, varkey := range metricVarDef {
		varMetric[varkey] = metric
	}

	lines := strings.Split(*text, "\n")
	for _, line := range lines {
		factors := strings.Split(line, " ")
		varkey := factors[0]

		if metric, present := varMetric[varkey]; present {
			stat[metric], _ = strconv.ParseUint(factors[1], 10, 64)
		}
	}

	return nil
}

func getDataWithCommand() (*string, error) {
	cmd := exec.Command("traffic_line", "-m", "^proxy")

	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		return nil, err
	}

	str := out.String()
	return &str, nil
}

// GraphDefinition interface for mackerelplugin
func (m TrafficserverPlugin) GraphDefinition() map[string](mp.Graphs) {
	return graphdef
}

var stderrLogger *log.Logger

func getStderrLogger() *log.Logger {
	if stderrLogger == nil {
		stderrLogger = log.New(os.Stderr, "", log.LstdFlags)
	}
	return stderrLogger
}

func main() {
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	var trafficserver TrafficserverPlugin

	helper := mp.NewMackerelPlugin(trafficserver)

	if *optTempfile != "" {
		helper.Tempfile = *optTempfile
	} else {
		helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-trafficserver")
	}

	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		helper.OutputDefinitions()
	} else {
		helper.OutputValues()
	}
}
<commit_msg>[trafficserver] don't set default tempfile name by plugin<commit_after>package main

import (
	"bytes"
	"flag"
	"log"
	"os"
	"os/exec"
	"strconv"
	"strings"

	mp "github.com/mackerelio/go-mackerel-plugin-helper"
)

var graphdef = map[string](mp.Graphs){
	"trafficserver.cache": mp.Graphs{
		Label: "Trafficserver Cache Hits/Misses",
		Unit:  "integer",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "cache_hits", Label: "Hits", Diff: true, Stacked: true, Type: "uint64"},
			mp.Metrics{Name: "cache_misses", Label: "Misses", Diff: true, Stacked: true, Type: "uint64"},
		},
	},
	"trafficserver.http_response_codes": mp.Graphs{
		Label: "Trafficserver HTTP Response Codes",
		Unit:  "integer",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "http_2xx", Label: "2xx", Diff: true, Stacked: true, Type: "uint64"},
			mp.Metrics{Name: "http_3xx", Label: "3xx", Diff: true, Stacked: true, Type: "uint64"},
			mp.Metrics{Name: "http_4xx", Label: "4xx", Diff: true, Stacked: true, Type: "uint64"},
			mp.Metrics{Name: "http_5xx", Label: "5xx", Diff: true, Stacked: true, Type: "uint64"},
		},
	},
	"trafficserver.connections": mp.Graphs{
		Label: "Trafficserver Current Connections",
		Unit:  "integer",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "conn_server", Label: "Server"},
			mp.Metrics{Name: "conn_client", Label: "Client"},
		},
	},
}

var metricVarDef = map[string]string{
	"cache_hits":   "proxy.node.cache_total_hits",
	"cache_misses": "proxy.node.cache_total_misses",
	"http_2xx":     "proxy.process.http.2xx_responses",
	"http_3xx":     "proxy.process.http.3xx_responses",
	"http_4xx":     "proxy.process.http.4xx_responses",
	"http_5xx":     "proxy.process.http.5xx_responses",
	"conn_server":  "proxy.node.current_server_connections",
	"conn_client":  "proxy.node.current_client_connections",
}

// TrafficserverPlugin mackerel plugin for apache trafficserver
type TrafficserverPlugin struct {
	Tempfile string
}

// FetchMetrics interface for mackerelplugin
func (m TrafficserverPlugin) FetchMetrics() (map[string]interface{}, error) {
	var err error
	strp, err := getDataWithCommand()
	if err != nil {
		return nil, err
	}

	stat := make(map[string]interface{})
	parseVars(strp, &stat)

	return stat, nil
}

func parseVars(text *string, statp *map[string]interface{}) error {
	stat := *statp

	varMetric := make(map[string]string)
	for metric, varkey := range metricVarDef {
		varMetric[varkey] = metric
	}

	lines := strings.Split(*text, "\n")
	for _, line := range lines {
		factors := strings.Split(line, " ")
		varkey := factors[0]

		if metric, present := varMetric[varkey]; present {
			stat[metric], _ = strconv.ParseUint(factors[1], 10, 64)
		}
	}

	return nil
}

func getDataWithCommand() (*string, error) {
	cmd := exec.Command("traffic_line", "-m", "^proxy")

	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		return nil, err
	}

	str := out.String()
	return &str, nil
}

// GraphDefinition interface for mackerelplugin
func (m TrafficserverPlugin) GraphDefinition() map[string](mp.Graphs) {
	return graphdef
}

var stderrLogger *log.Logger

func getStderrLogger() *log.Logger {
	if stderrLogger == nil {
		stderrLogger = log.New(os.Stderr, "", log.LstdFlags)
	}
	return stderrLogger
}

func main() {
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	var trafficserver TrafficserverPlugin

	helper := mp.NewMackerelPlugin(trafficserver)
	helper.Tempfile = *optTempfile

	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		helper.OutputDefinitions()
	} else {
		helper.OutputValues()
	}
}
<|endoftext|>


<commit_before>// +build !windows

package checkprocs

import (
	"errors"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"strings"
)

var threadsUnknown = runtime.GOOS == "darwin"

func getProcs() (proc []procState, err error) {
	var procs []procState
	psformat := "user,ppid,pid,vsz,rss,pcpu,nlwp,state,etime,time,command"
	if threadsUnknown {
		psformat = "user,ppid,pid,vsz,rss,pcpu,state,etime,time,command"
	}
	output, err := exec.Command("ps", "axwwo", psformat).Output()
	if err != nil {
		return procs, nil
	}
	for _, line := range strings.Split(string(output), "\n")[1:] {
		proc, err := parseProcState(line)
		if err != nil {
			continue
		}
		procs = append(procs, proc)
	}
	return procs, nil
}

func parseProcState(line string) (proc procState, err error) {
	fields := strings.Fields(line)
	fieldsMinLen := 11
	if threadsUnknown {
		fieldsMinLen = 10
	}
	if len(fields) < fieldsMinLen {
		return procState{}, errors.New("parseProcState: insufficient words")
	}
	vsz, _ := strconv.ParseInt(fields[3], 10, 64)
	rss, _ := strconv.ParseInt(fields[4], 10, 64)
	pcpu, _ := strconv.ParseFloat(fields[5], 64)
	if threadsUnknown {
		esec := timeStrToSeconds(fields[7])
		csec := timeStrToSeconds(fields[8])
		return procState{strings.Join(fields[9:], " "), fields[0], fields[1], fields[2], vsz, rss, pcpu, 1, fields[6], esec, csec}, nil
	}
	thcount, _ := strconv.ParseInt(fields[6], 10, 64)
	esec := timeStrToSeconds(fields[8])
	csec := timeStrToSeconds(fields[9])
	return procState{strings.Join(fields[10:], " "), fields[0], fields[1], fields[2], vsz, rss, pcpu, thcount, fields[7], esec, csec}, nil
}

var timeRegexp = regexp.MustCompile(`(?:(\d+)-)?(?:(\d+):)?(\d+)[:.](\d+)`)

func timeStrToSeconds(etime string) int64 {
	match := timeRegexp.FindStringSubmatch(etime)
	if match == nil || len(match) != 5 {
		return 0
	}
	days, _ := strconv.ParseInt(match[1], 10, 64)
	hours, _ := strconv.ParseInt(match[2], 10, 64)
	minutes, _ := strconv.ParseInt(match[3], 10, 64)
	seconds, _ := strconv.ParseInt(match[4], 10, 64)
	return (((days*24+hours)*60+minutes)*60 + seconds)
}
<commit_msg>[check-procs] fix `getProcs` error handling<commit_after>// +build !windows

package checkprocs

import (
	"errors"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"strings"
)

var threadsUnknown = runtime.GOOS == "darwin"

func getProcs() (proc []procState, err error) {
	var procs []procState
	psformat := "user,ppid,pid,vsz,rss,pcpu,nlwp,state,etime,time,command"
	if threadsUnknown {
		psformat = "user,ppid,pid,vsz,rss,pcpu,state,etime,time,command"
	}
	output, err := exec.Command("ps", "axwwo", psformat).Output()
	if err != nil {
		return nil, err
	}
	for _, line := range strings.Split(string(output), "\n")[1:] {
		proc, err := parseProcState(line)
		if err != nil {
			continue
		}
		procs = append(procs, proc)
	}
	return procs, nil
}

func parseProcState(line string) (proc procState, err error) {
	fields := strings.Fields(line)
	fieldsMinLen := 11
	if threadsUnknown {
		fieldsMinLen = 10
	}
	if len(fields) < fieldsMinLen {
		return procState{}, errors.New("parseProcState: insufficient words")
	}
	vsz, _ := strconv.ParseInt(fields[3], 10, 64)
	rss, _ := strconv.ParseInt(fields[4], 10, 64)
	pcpu, _ := strconv.ParseFloat(fields[5], 64)
	if threadsUnknown {
		esec := timeStrToSeconds(fields[7])
		csec := timeStrToSeconds(fields[8])
		return procState{strings.Join(fields[9:], " "), fields[0], fields[1], fields[2], vsz, rss, pcpu, 1, fields[6], esec, csec}, nil
	}
	thcount, _ := strconv.ParseInt(fields[6], 10, 64)
	esec := timeStrToSeconds(fields[8])
	csec := timeStrToSeconds(fields[9])
	return procState{strings.Join(fields[10:], " "), fields[0], fields[1], fields[2], vsz, rss, pcpu, thcount, fields[7], esec, csec}, nil
}

var timeRegexp = regexp.MustCompile(`(?:(\d+)-)?(?:(\d+):)?(\d+)[:.](\d+)`)

func timeStrToSeconds(etime string) int64 {
	match := timeRegexp.FindStringSubmatch(etime)
	if match == nil || len(match) != 5 {
		return 0
	}
	days, _ := strconv.ParseInt(match[1], 10, 64)
	hours, _ := strconv.ParseInt(match[2], 10, 64)
	minutes, _ := strconv.ParseInt(match[3], 10, 64)
	seconds, _ := strconv.ParseInt(match[4], 10, 64)
	return (((days*24+hours)*60+minutes)*60 + seconds)
}
<|endoftext|>


<commit_before>package main

import (
	"os"

	"github.com/mackerelio/mackerel-client-go"
	"github.com/mackerelio/mkr/format"
	"github.com/mackerelio/mkr/logger"
	"github.com/mackerelio/mkr/mackerelclient"
	"github.com/urfave/cli"
)

var commandAnnotations = cli.Command{
	Name:  "annotations",
	Usage: "Manipulate graph annotations",
	Description: `
    Manipulate graph annotations. Requests APIs under "/api/v0/graph-annotations".
    See https://mackerel.io/api-docs/entry/graph-annotations .
`,
	Subcommands: []cli.Command{
		{
			Name:      "create",
			Usage:     "create a graph annotation",
			ArgsUsage: "--title <title> [--description <descriptio>] --from <from> --to <to> --service -s <service> [--role -r <role>]",
			Description: `
    Creates a graph annotation.
`,
			Action: doAnnotationsCreate,
			Flags: []cli.Flag{
				cli.StringFlag{Name: "title", Usage: "Title for annotation"},
				cli.StringFlag{Name: "description", Usage: "Description for annotation"},
				cli.IntFlag{Name: "from", Usage: "Starting time (epoch seconds)"},
				cli.IntFlag{Name: "to", Usage: "Ending time (epoch seconds)"},
				cli.StringFlag{Name: "service, s", Usage: "Service name for annotation"},
				cli.StringSliceFlag{
					Name:  "role, r",
					Value: &cli.StringSlice{},
					Usage: "Roles for annotation. Multiple choices are allowed",
				},
			},
		},
		{
			Name:      "list",
			Usage:     "list annotations",
			ArgsUsage: "--from <from> --to <to> --service -s <service>",
			Description: `
    Shows annotations by service name and duration (from and to)
`,
			Action: doAnnotationsList,
			Flags: []cli.Flag{
				cli.StringFlag{Name: "service, s", Usage: "Service name for annotation"},
				cli.IntFlag{Name: "from", Usage: "Starting time (epoch seconds)"},
				cli.IntFlag{Name: "to", Usage: "Ending time (epoch seconds)"},
			},
		},
		{
			Name:      "update",
			Usage:     "update annotation",
			ArgsUsage: "--id <id> [--title <title>] [--description <descriptio>] --from <from> --to <to> --service -s <service> [--role -r <role>]",
			Description: `
    Updates an annotation
`,
			Action: doAnnotationsUpdate,
			Flags: []cli.Flag{
				cli.StringFlag{Name: "id", Usage: "Annotation ID."},
				cli.StringFlag{Name: "service, s", Usage: "Service name for annotation"},
				cli.StringFlag{Name: "title", Usage: "Title for annotation"},
				cli.StringFlag{Name: "description", Usage: "Description for annotation"},
				cli.IntFlag{Name: "from", Usage: "Starting time (epoch seconds)"},
				cli.IntFlag{Name: "to", Usage: "Ending time (epoch seconds)"},
				cli.StringSliceFlag{
					Name:  "role, r",
					Value: &cli.StringSlice{},
					Usage: "Roles for annotation. Multiple choices are allowed",
				},
			},
		},
		{
			Name:      "delete",
			Usage:     "delete annotation",
			ArgsUsage: "--id <id>",
			Description: `
    Delete graph annotation by annotation id
`,
			Action: doAnnotationsDelete,
			Flags: []cli.Flag{
				cli.StringFlag{Name: "id", Usage: "Graph annotation ID"},
			},
		},
	},
}

func doAnnotationsCreate(c *cli.Context) error {
	title := c.String("title")
	description := c.String("description")
	from := c.Int64("from")
	to := c.Int64("to")
	service := c.String("service")
	roles := c.StringSlice("role")

	if title == "" {
		_ = cli.ShowCommandHelp(c, "create")
		return cli.NewExitError("`title` is a required field to create a graph annotation.", 1)
	}

	if service == "" {
		_ = cli.ShowCommandHelp(c, "create")
		return cli.NewExitError("`service` is a required field to create a graph annotation.", 1)
	}

	if from == 0 {
		_ = cli.ShowCommandHelp(c, "create")
		return cli.NewExitError("`from` is a required field to create a graph annotation.", 1)
	}

	if to == 0 {
		_ = cli.ShowCommandHelp(c, "create")
		return cli.NewExitError("`to` is a required field to create a graph annotation.", 1)
	}

	client := mackerelclient.NewFromContext(c)
	annotation, err := client.CreateGraphAnnotation(&mackerel.GraphAnnotation{
		Title:       title,
		Description: description,
		From:        from,
		To:          to,
		Service:     service,
		Roles:       roles,
	})
	logger.DieIf(err)
	format.PrettyPrintJSON(os.Stdout, annotation)
	return nil
}

func doAnnotationsList(c *cli.Context) error {
	service := c.String("service")
	from := c.Int64("from")
	to := c.Int64("to")

	if service == "" {
		_ = cli.ShowCommandHelp(c, "list")
		return cli.NewExitError("`service` is a required field to list graph annotations.", 1)
	}

	if from == 0 {
		_ = cli.ShowCommandHelp(c, "list")
		return cli.NewExitError("`from` is a required field to list graph annotations.", 1)
	}

	if to == 0 {
		_ = cli.ShowCommandHelp(c, "list")
		return cli.NewExitError("`to` is a required field to list graph annotations.", 1)
	}

	client := mackerelclient.NewFromContext(c)
	annotations, err := client.FindGraphAnnotations(service, from, to)
	logger.DieIf(err)
	format.PrettyPrintJSON(os.Stdout, annotations)
	return nil
}

func doAnnotationsUpdate(c *cli.Context) error {
	annotationID := c.String("id")
	title := c.String("title")
	description := c.String("description")
	from := c.Int64("from")
	to := c.Int64("to")
	service := c.String("service")
	roles := c.StringSlice("role")

	if annotationID == "" {
		_ = cli.ShowCommandHelp(c, "update")
		return cli.NewExitError("`id` is a required field to delete a update annotation.", 1)
	}

	if service == "" {
		_ = cli.ShowCommandHelp(c, "update")
		return cli.NewExitError("`service` is a required field to update a graph annotation.", 1)
	}

	if from == 0 {
		_ = cli.ShowCommandHelp(c, "update")
		return cli.NewExitError("`from` is a required field to update a graph annotation.", 1)
	}

	if to == 0 {
		_ = cli.ShowCommandHelp(c, "update")
		return cli.NewExitError("`to` is a required field to update a graph annotation.", 1)
	}

	client := mackerelclient.NewFromContext(c)
	annotation, err := client.UpdateGraphAnnotation(annotationID, &mackerel.GraphAnnotation{
		Title:       title,
		Description: description,
		From:        from,
		To:          to,
		Service:     service,
		Roles:       roles,
	})
	logger.DieIf(err)
	format.PrettyPrintJSON(os.Stdout, annotation)
	return nil
}

func doAnnotationsDelete(c *cli.Context) error {
	annotationID := c.String("id")

	if annotationID == "" {
		_ = cli.ShowCommandHelp(c, "delete")
		return cli.NewExitError("`id` is a required field to delete a graph annotation.", 1)
	}

	client := mackerelclient.NewFromContext(c)
	annotation, err := client.DeleteGraphAnnotation(annotationID)
	logger.DieIf(err)
	format.PrettyPrintJSON(os.Stdout, annotation)
	return nil
}
<commit_msg>Use pipe to indicate short option is abbreviation of longer one<commit_after>package main

import (
	"os"

	"github.com/mackerelio/mackerel-client-go"
	"github.com/mackerelio/mkr/format"
	"github.com/mackerelio/mkr/logger"
	"github.com/mackerelio/mkr/mackerelclient"
	"github.com/urfave/cli"
)

var commandAnnotations = cli.Command{
	Name:  "annotations",
	Usage: "Manipulate graph annotations",
	Description: `
    Manipulate graph annotations. Requests APIs under "/api/v0/graph-annotations".
    See https://mackerel.io/api-docs/entry/graph-annotations .
`,
	Subcommands: []cli.Command{
		{
			Name:      "create",
			Usage:     "create a graph annotation",
			ArgsUsage: "--title <title> [--description <descriptio>] --from <from> --to <to> --service|-s <service> [--role|-r <role>]",
			Description: `
    Creates a graph annotation.
`,
			Action: doAnnotationsCreate,
			Flags: []cli.Flag{
				cli.StringFlag{Name: "title", Usage: "Title for annotation"},
				cli.StringFlag{Name: "description", Usage: "Description for annotation"},
				cli.IntFlag{Name: "from", Usage: "Starting time (epoch seconds)"},
				cli.IntFlag{Name: "to", Usage: "Ending time (epoch seconds)"},
				cli.StringFlag{Name: "service, s", Usage: "Service name for annotation"},
				cli.StringSliceFlag{
					Name:  "role, r",
					Value: &cli.StringSlice{},
					Usage: "Roles for annotation. Multiple choices are allowed",
				},
			},
		},
		{
			Name:      "list",
			Usage:     "list annotations",
			ArgsUsage: "--from <from> --to <to> --service|-s <service>",
			Description: `
    Shows annotations by service name and duration (from and to)
`,
			Action: doAnnotationsList,
			Flags: []cli.Flag{
				cli.StringFlag{Name: "service, s", Usage: "Service name for annotation"},
				cli.IntFlag{Name: "from", Usage: "Starting time (epoch seconds)"},
				cli.IntFlag{Name: "to", Usage: "Ending time (epoch seconds)"},
			},
		},
		{
			Name:      "update",
			Usage:     "update annotation",
			ArgsUsage: "--id <id> [--title <title>] [--description <descriptio>] --from <from> --to <to> --service|-s <service> [--role|-r <role>]",
			Description: `
    Updates an annotation
`,
			Action: doAnnotationsUpdate,
			Flags: []cli.Flag{
				cli.StringFlag{Name: "id", Usage: "Annotation ID."},
				cli.StringFlag{Name: "service, s", Usage: "Service name for annotation"},
				cli.StringFlag{Name: "title", Usage: "Title for annotation"},
				cli.StringFlag{Name: "description", Usage: "Description for annotation"},
				cli.IntFlag{Name: "from", Usage: "Starting time (epoch seconds)"},
				cli.IntFlag{Name: "to", Usage: "Ending time (epoch seconds)"},
				cli.StringSliceFlag{
					Name:  "role, r",
					Value: &cli.StringSlice{},
					Usage: "Roles for annotation. Multiple choices are allowed",
				},
			},
		},
		{
			Name:      "delete",
			Usage:     "delete annotation",
			ArgsUsage: "--id <id>",
			Description: `
    Delete graph annotation by annotation id
`,
			Action: doAnnotationsDelete,
			Flags: []cli.Flag{
				cli.StringFlag{Name: "id", Usage: "Graph annotation ID"},
			},
		},
	},
}

func doAnnotationsCreate(c *cli.Context) error {
	title := c.String("title")
	description := c.String("description")
	from := c.Int64("from")
	to := c.Int64("to")
	service := c.String("service")
	roles := c.StringSlice("role")

	if title == "" {
		_ = cli.ShowCommandHelp(c, "create")
		return cli.NewExitError("`title` is a required field to create a graph annotation.", 1)
	}

	if service == "" {
		_ = cli.ShowCommandHelp(c, "create")
		return cli.NewExitError("`service` is a required field to create a graph annotation.", 1)
	}

	if from == 0 {
		_ = cli.ShowCommandHelp(c, "create")
		return cli.NewExitError("`from` is a required field to create a graph annotation.", 1)
	}

	if to == 0 {
		_ = cli.ShowCommandHelp(c, "create")
		return cli.NewExitError("`to` is a required field to create a graph annotation.", 1)
	}

	client := mackerelclient.NewFromContext(c)
	annotation, err := client.CreateGraphAnnotation(&mackerel.GraphAnnotation{
		Title:       title,
		Description: description,
		From:        from,
		To:          to,
		Service:     service,
		Roles:       roles,
	})
	logger.DieIf(err)
	format.PrettyPrintJSON(os.Stdout, annotation)
	return nil
}

func doAnnotationsList(c *cli.Context) error {
	service := c.String("service")
	from := c.Int64("from")
	to := c.Int64("to")

	if service == "" {
		_ = cli.ShowCommandHelp(c, "list")
		return cli.NewExitError("`service` is a required field to list graph annotations.", 1)
	}

	if from == 0 {
		_ = cli.ShowCommandHelp(c, "list")
		return cli.NewExitError("`from` is a required field to list graph annotations.", 1)
	}

	if to == 0 {
		_ = cli.ShowCommandHelp(c, "list")
		return cli.NewExitError("`to` is a required field to list graph annotations.", 1)
	}

	client := mackerelclient.NewFromContext(c)
	annotations, err := client.FindGraphAnnotations(service, from, to)
	logger.DieIf(err)
	format.PrettyPrintJSON(os.Stdout, annotations)
	return nil
}

func doAnnotationsUpdate(c *cli.Context) error {
	annotationID := c.String("id")
	title := c.String("title")
	description := c.String("description")
	from := c.Int64("from")
	to := c.Int64("to")
	service := c.String("service")
	roles := c.StringSlice("role")

	if annotationID == "" {
		_ = cli.ShowCommandHelp(c, "update")
		return cli.NewExitError("`id` is a required field to delete a update annotation.", 1)
	}

	if service == "" {
		_ = cli.ShowCommandHelp(c, "update")
		return cli.NewExitError("`service` is a required field to update a graph annotation.", 1)
	}

	if from == 0 {
		_ = cli.ShowCommandHelp(c, "update")
		return cli.NewExitError("`from` is a required field to update a graph annotation.", 1)
	}

	if to == 0 {
		_ = cli.ShowCommandHelp(c, "update")
		return cli.NewExitError("`to` is a required field to update a graph annotation.", 1)
	}

	client := mackerelclient.NewFromContext(c)
	annotation, err := client.UpdateGraphAnnotation(annotationID, &mackerel.GraphAnnotation{
		Title:       title,
		Description: description,
		From:        from,
		To:          to,
		Service:     service,
		Roles:       roles,
	})
	logger.DieIf(err)
	format.PrettyPrintJSON(os.Stdout, annotation)
	return nil
}

func doAnnotationsDelete(c *cli.Context) error {
	annotationID := c.String("id")

	if annotationID == "" {
		_ = cli.ShowCommandHelp(c, "delete")
		return cli.NewExitError("`id` is a required field to delete a graph annotation.", 1)
	}

	client := mackerelclient.NewFromContext(c)
	annotation, err := client.DeleteGraphAnnotation(annotationID)
	logger.DieIf(err)
	format.PrettyPrintJSON(os.Stdout, annotation)
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 ogletest

import (
	"testing"
)

////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////

type fakeAnyOfMatcher struct {
	desc string
	res  MatchResult
	err  string
}

func (m *fakeAnyOfMatcher) Matches(c interface{}) (MatchResult, string) {
	return m.res, m.err
}

func (m *fakeAnyOfMatcher) Description() string {
	return m.desc
}

////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////

func TestAnyOfCallsWrapped(t *testing.T) {
	t.Fail()
}

func TestEmptySet(t *testing.T) {
	matcher := AnyOf()

	res, err := matcher.Matches(0)
	expectedRes := MATCH_FALSE
	expectedErr := ""

	if res != expectedRes {
		t.Errorf("Expected %v, got %v", expectedRes, res)
	}

	if err != expectedErr {
		t.Errorf("Expected %v, got %v", expectedErr, err)
	}
}

func TestOneTrue(t *testing.T) {
	matcher := AnyOf(
		&fakeAnyOfMatcher{"", MATCH_UNDEFINED, "foo"},
		17,
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
		&fakeAnyOfMatcher{"", MATCH_TRUE, ""},
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
	)

	res, err := matcher.Matches(0)
	expectedRes := MATCH_TRUE
	expectedErr := ""

	if res != expectedRes {
		t.Errorf("Expected %v, got %v", expectedRes, res)
	}

	if err != expectedErr {
		t.Errorf("Expected %v, got %v", expectedErr, err)
	}
}

func TestOneEqual(t *testing.T) {
	matcher := AnyOf(
		&fakeAnyOfMatcher{"", MATCH_UNDEFINED, "foo"},
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
		13,
		"taco",
		19,
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
	)

	res, err := matcher.Matches("taco")
	expectedRes := MATCH_TRUE
	expectedErr := ""

	if res != expectedRes {
		t.Errorf("Expected %v, got %v", expectedRes, res)
	}

	if err != expectedErr {
		t.Errorf("Expected %v, got %v", expectedErr, err)
	}
}

func TestOneUndefined(t *testing.T) {
	matcher := AnyOf(
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
		17,
		&fakeAnyOfMatcher{"", MATCH_UNDEFINED, "taco"},
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
	)

	res, err := matcher.Matches(0)
	expectedRes := MATCH_UNDEFINED
	expectedErr := "taco"

	if res != expectedRes {
		t.Errorf("Expected %v, got %v", expectedRes, res)
	}

	if err != expectedErr {
		t.Errorf("Expected %v, got %v", expectedErr, err)
	}
}

func TestAllFalse(t *testing.T) {
}

func TestDescriptionForEmptySet(t *testing.T) {
}

func TestDescriptionForNonEmptySet(t *testing.T) {
}
<commit_msg>TestAllFalseAndNotEqual<commit_after>// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 ogletest

import (
	"testing"
)

////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////

type fakeAnyOfMatcher struct {
	desc string
	res  MatchResult
	err  string
}

func (m *fakeAnyOfMatcher) Matches(c interface{}) (MatchResult, string) {
	return m.res, m.err
}

func (m *fakeAnyOfMatcher) Description() string {
	return m.desc
}

////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////

func TestAnyOfCallsWrapped(t *testing.T) {
	t.Fail()
}

func TestEmptySet(t *testing.T) {
	matcher := AnyOf()

	res, err := matcher.Matches(0)
	expectedRes := MATCH_FALSE
	expectedErr := ""

	if res != expectedRes {
		t.Errorf("Expected %v, got %v", expectedRes, res)
	}

	if err != expectedErr {
		t.Errorf("Expected %v, got %v", expectedErr, err)
	}
}

func TestOneTrue(t *testing.T) {
	matcher := AnyOf(
		&fakeAnyOfMatcher{"", MATCH_UNDEFINED, "foo"},
		17,
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
		&fakeAnyOfMatcher{"", MATCH_TRUE, ""},
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
	)

	res, err := matcher.Matches(0)
	expectedRes := MATCH_TRUE
	expectedErr := ""

	if res != expectedRes {
		t.Errorf("Expected %v, got %v", expectedRes, res)
	}

	if err != expectedErr {
		t.Errorf("Expected %v, got %v", expectedErr, err)
	}
}

func TestOneEqual(t *testing.T) {
	matcher := AnyOf(
		&fakeAnyOfMatcher{"", MATCH_UNDEFINED, "foo"},
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
		13,
		"taco",
		19,
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
	)

	res, err := matcher.Matches("taco")
	expectedRes := MATCH_TRUE
	expectedErr := ""

	if res != expectedRes {
		t.Errorf("Expected %v, got %v", expectedRes, res)
	}

	if err != expectedErr {
		t.Errorf("Expected %v, got %v", expectedErr, err)
	}
}

func TestOneUndefined(t *testing.T) {
	matcher := AnyOf(
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
		17,
		&fakeAnyOfMatcher{"", MATCH_UNDEFINED, "taco"},
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
	)

	res, err := matcher.Matches(0)
	expectedRes := MATCH_UNDEFINED
	expectedErr := "taco"

	if res != expectedRes {
		t.Errorf("Expected %v, got %v", expectedRes, res)
	}

	if err != expectedErr {
		t.Errorf("Expected %v, got %v", expectedErr, err)
	}
}

func TestAllFalseAndNotEqual(t *testing.T) {
	matcher := AnyOf(
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
		17,
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
		19,
	)

	res, err := matcher.Matches(0)
	expectedRes := MATCH_FALSE
	expectedErr := ""

	if res != expectedRes {
		t.Errorf("Expected %v, got %v", expectedRes, res)
	}

	if err != expectedErr {
		t.Errorf("Expected %v, got %v", expectedErr, err)
	}
}

func TestDescriptionForEmptySet(t *testing.T) {
}

func TestDescriptionForNonEmptySet(t *testing.T) {
}
<|endoftext|>