<commit_before>package collector

import (
	//"fmt"
	"fullerite/config"
	"fullerite/metric"
	"reflect"
	"regexp"
	"strings"
	"sync"
	"time"

	l "github.com/Sirupsen/logrus"

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

const (
	endpoint = "unix:///var/run/docker.sock"
)

// DockerStats collector type.
// previousCPUValues contains the last cpu-usage values per container.
// dockerClient is the client for the Docker remote API.
type DockerStats struct {
	baseCollector
	previousCPUValues map[string]*CPUValues
	dockerClient      *docker.Client
	statsTimeout      int
	compiledRegex     map[string]*Regex
	skipRegex         *regexp.Regexp
	bufferRegex       *regexp.Regexp
	endpoint          string
	mu                *sync.Mutex
}

// CPUValues struct contains the last cpu-usage values in order to compute properly the current values.
// (see calculateCPUPercent() for more details)
type CPUValues struct {
	totCPU, systemCPU uint64
}

// Regex struct contains the info used to get the user specific dimensions from the docker env variables
// tag: is the environmental variable you want to get the value from
// regex: is the reg exp used to extract the value from the env var
type Regex struct {
	tag   string
	regex *regexp.Regexp
}

func init() {
	RegisterCollector("DockerStats", newDockerStats)
}

// newDockerStats creates a new DockerStats collector.
func newDockerStats(channel chan metric.Metric, initialInterval int, log *l.Entry) Collector {
	d := new(DockerStats)

	d.log = log
	d.channel = channel
	d.interval = initialInterval
	d.mu = new(sync.Mutex)

	d.name = "DockerStats"
	d.previousCPUValues = make(map[string]*CPUValues)
	d.compiledRegex = make(map[string]*Regex)
	return d
}

// GetEndpoint Returns endpoint of DockerStats instance
func (d *DockerStats) GetEndpoint() string {
	return d.endpoint
}

// Configure takes a dictionary of values with which the handler can configure itself.
func (d *DockerStats) Configure(configMap map[string]interface{}) {
	if timeout, exists := configMap["dockerStatsTimeout"]; exists {
		d.statsTimeout = min(config.GetAsInt(timeout, d.interval), d.interval)
	} else {
		d.statsTimeout = d.interval
	}
	if dockerEndpoint, exists := configMap["dockerEndPoint"]; exists {
		if str, ok := dockerEndpoint.(string); ok {
			d.endpoint = str
		} else {
			d.log.Warn("Failed to cast dokerEndPoint: ", reflect.TypeOf(dockerEndpoint))
		}
	} else {
		d.endpoint = endpoint
	}
	d.dockerClient, _ = docker.NewClient(d.endpoint)
	if generatedDimensions, exists := configMap["generatedDimensions"]; exists {
		for dimension, generator := range generatedDimensions.(map[string]interface{}) {
			for key, regx := range config.GetAsMap(generator) {
				re, err := regexp.Compile(regx)
				if err != nil {
					d.log.Warn("Failed to compile regex: ", regx, err)
				} else {
					d.compiledRegex[dimension] = &Regex{regex: re, tag: key}
				}
			}
		}
	}
	if skipRegex, skipExists := configMap["skipContainerRegex"]; skipExists {
		d.skipRegex = regexp.MustCompile(skipRegex.(string))
	}
	if bufferRegex, exists := configMap["bufferRegex"]; exists {
		d.bufferRegex = regexp.MustCompile(bufferRegex.(string))
	}
	d.configureCommonParams(configMap)
}

// Collect iterates on all the docker containers alive and, if possible, collects the correspondent
// memory and cpu statistics.
// For each container a gorutine is started to spin up the collection process.
func (d *DockerStats) Collect() {
	if d.dockerClient == nil {
		d.log.Error("Invalid endpoint: ", docker.ErrInvalidEndpoint)
		return
	}
	containers, err := d.dockerClient.ListContainers(docker.ListContainersOptions{All: false})
	if err != nil {
		d.log.Error("ListContainers() failed: ", err)
		return
	}
	for _, apiContainer := range containers {
		container, err := d.dockerClient.InspectContainer(apiContainer.ID)

		if err != nil {
			d.log.Error("InspectContainer() failed: ", err)
			continue
		}

		if d.skipRegex != nil && d.skipRegex.MatchString(container.Name) {
			d.log.Info("Skip container: ", container.Name)
			continue
		}
		if _, ok := d.previousCPUValues[container.ID]; !ok {
			d.previousCPUValues[container.ID] = new(CPUValues)
		}
		go d.getDockerContainerInfo(container)
	}
}

// getDockerContainerInfo gets container statistics for the given container.
// results is a channel to make possible the synchronization between the main process and the gorutines (wait-notify pattern).
func (d *DockerStats) getDockerContainerInfo(container *docker.Container) {
	errC := make(chan error, 1)
	statsC := make(chan *docker.Stats, 1)
	done := make(chan bool, 1)

	go func() {
		errC <- d.dockerClient.Stats(docker.StatsOptions{container.ID, statsC, false, done, time.Second * time.Duration(d.interval)})
	}()
	select {
	case stats, ok := <-statsC:
		if !ok {
			err := <-errC
			d.log.Error("Failed to collect docker container stats: ", err)
			break
		}
		done <- true

		metrics := d.extractMetrics(container, stats)
		d.sendMetrics(metrics)

		break
	case <-time.After(time.Duration(d.statsTimeout) * time.Second):
		d.log.Error("Timed out collecting stats for container ", container.ID)
		done <- true
		break
	}
}

func (d *DockerStats) extractMetrics(container *docker.Container, stats *docker.Stats) []metric.Metric {
	d.mu.Lock()
	defer d.mu.Unlock()
	metrics := d.buildMetrics(container, stats, calculateCPUPercent(d.previousCPUValues[container.ID].totCPU, d.previousCPUValues[container.ID].systemCPU, stats))

	d.previousCPUValues[container.ID].totCPU = stats.CPUStats.CPUUsage.TotalUsage
	d.previousCPUValues[container.ID].systemCPU = stats.CPUStats.SystemCPUUsage
	return metrics
}

// buildMetrics creates the actual metrics for the given container.
func (d DockerStats) buildMetrics(container *docker.Container, containerStats *docker.Stats, cpuPercentage float64) []metric.Metric {
	ret := []metric.Metric{}
	met := buildDockerMetric("DockerMemoryUsed", metric.Gauge, float64(containerStats.MemoryStats.Usage))
	if d.bufferRegex != nil && d.bufferRegex.MatchString(met.Name) {
		met.EnableBuffering()
	}
	ret = append(ret, met)
	met = buildDockerMetric("DockerMemoryLimit", metric.Gauge, float64(containerStats.MemoryStats.Limit))
	if d.bufferRegex != nil && d.bufferRegex.MatchString(met.Name) {
		met.EnableBuffering()
	}
	ret = append(ret, met)
	met = buildDockerMetric("DockerCpuPercentage", metric.Gauge, cpuPercentage)
	if d.bufferRegex != nil && d.bufferRegex.MatchString(met.Name) {
		met.EnableBuffering()
	}
	ret = append(ret, met)
	for netiface := range containerStats.Networks {
		// legacy format
		txb := buildDockerMetric("DockerTxBytes", metric.CumulativeCounter, float64(containerStats.Networks[netiface].TxBytes))
		txb.AddDimension("iface", netiface)
		ret = append(ret, txb)
		rxb := buildDockerMetric("DockerRxBytes", metric.CumulativeCounter, float64(containerStats.Networks[netiface].RxBytes))
		rxb.AddDimension("iface", netiface)
		ret = append(ret, rxb)
	}
	additionalDimensions := map[string]string{
		"container_id":   container.ID,
		"container_name": strings.TrimPrefix(container.Name, "/"),
	}
	metric.AddToAll(&ret, additionalDimensions)
	ret = append(ret, buildDockerMetric("DockerContainerCount", metric.Counter, 1))
	metric.AddToAll(&ret, d.extractDimensions(container))

	return ret
}

// sendMetrics writes all the metrics received to the collector channel.
func (d DockerStats) sendMetrics(metrics []metric.Metric) {
	for _, m := range metrics {
		d.Channel() <- m
	}
}

// Function that extracts additional dimensions from the docker environmental variables set up by the user
// in the configuration file.
func (d DockerStats) extractDimensions(container *docker.Container) map[string]string {
	envVars := container.Config.Env
	ret := map[string]string{}

	for dimension, r := range d.compiledRegex {
		for _, envVariable := range envVars {
			envArray := strings.Split(envVariable, "=")
			if r.tag == envArray[0] {
				subMatch := r.regex.FindStringSubmatch(envArray[1])
				if len(subMatch) > 0 {
					ret[dimension] = strings.Replace(subMatch[len(subMatch)-1], "--", "_", -1)
				}
			}
		}
	}
	d.log.Debug(ret)
	return ret
}

func buildDockerMetric(name string, metricType string, value float64) (m metric.Metric) {
	m = metric.New(name)
	m.MetricType = metricType
	m.Value = value
	m.MetricTime = time.Now()
	return m
}

// Function that compute the current cpu usage percentage combining current and last values.
func calculateCPUPercent(previousCPU, previousSystem uint64, stats *docker.Stats) float64 {
	var (
		cpuPercent = 0.0
		// calculate the change for the cpu usage of the container in between readings
		cpuDelta = float64(stats.CPUStats.CPUUsage.TotalUsage - previousCPU)
		// calculate the change for the entire system between readings
		systemDelta = float64(stats.CPUStats.SystemCPUUsage - previousSystem)
	)

	if systemDelta > 0.0 && cpuDelta > 0.0 {
		cpuPercent = (cpuDelta / systemDelta) * float64(len(stats.CPUStats.CPUUsage.PercpuUsage)) * 100.0
	}
	return cpuPercent
}

func min(x, y int) int {
	if x < y {
		return x
	}
	return y
}
<commit_msg>optimised EnableBuffering<commit_after>package collector

import (
	"fullerite/config"
	"fullerite/metric"
	"reflect"
	"regexp"
	"strings"
	"sync"
	"time"

	l "github.com/Sirupsen/logrus"

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

const (
	endpoint = "unix:///var/run/docker.sock"
)

// DockerStats collector type.
// previousCPUValues contains the last cpu-usage values per container.
// dockerClient is the client for the Docker remote API.
type DockerStats struct {
	baseCollector
	previousCPUValues map[string]*CPUValues
	dockerClient      *docker.Client
	statsTimeout      int
	compiledRegex     map[string]*Regex
	skipRegex         *regexp.Regexp
	bufferRegex       *regexp.Regexp
	endpoint          string
	mu                *sync.Mutex
}

// CPUValues struct contains the last cpu-usage values in order to compute properly the current values.
// (see calculateCPUPercent() for more details)
type CPUValues struct {
	totCPU, systemCPU uint64
}

// Regex struct contains the info used to get the user specific dimensions from the docker env variables
// tag: is the environmental variable you want to get the value from
// regex: is the reg exp used to extract the value from the env var
type Regex struct {
	tag   string
	regex *regexp.Regexp
}

func init() {
	RegisterCollector("DockerStats", newDockerStats)
}

// newDockerStats creates a new DockerStats collector.
func newDockerStats(channel chan metric.Metric, initialInterval int, log *l.Entry) Collector {
	d := new(DockerStats)

	d.log = log
	d.channel = channel
	d.interval = initialInterval
	d.mu = new(sync.Mutex)

	d.name = "DockerStats"
	d.previousCPUValues = make(map[string]*CPUValues)
	d.compiledRegex = make(map[string]*Regex)
	return d
}

// GetEndpoint Returns endpoint of DockerStats instance
func (d *DockerStats) GetEndpoint() string {
	return d.endpoint
}

// Configure takes a dictionary of values with which the handler can configure itself.
func (d *DockerStats) Configure(configMap map[string]interface{}) {
	if timeout, exists := configMap["dockerStatsTimeout"]; exists {
		d.statsTimeout = min(config.GetAsInt(timeout, d.interval), d.interval)
	} else {
		d.statsTimeout = d.interval
	}
	if dockerEndpoint, exists := configMap["dockerEndPoint"]; exists {
		if str, ok := dockerEndpoint.(string); ok {
			d.endpoint = str
		} else {
			d.log.Warn("Failed to cast dokerEndPoint: ", reflect.TypeOf(dockerEndpoint))
		}
	} else {
		d.endpoint = endpoint
	}
	d.dockerClient, _ = docker.NewClient(d.endpoint)
	if generatedDimensions, exists := configMap["generatedDimensions"]; exists {
		for dimension, generator := range generatedDimensions.(map[string]interface{}) {
			for key, regx := range config.GetAsMap(generator) {
				re, err := regexp.Compile(regx)
				if err != nil {
					d.log.Warn("Failed to compile regex: ", regx, err)
				} else {
					d.compiledRegex[dimension] = &Regex{regex: re, tag: key}
				}
			}
		}
	}
	if skipRegex, skipExists := configMap["skipContainerRegex"]; skipExists {
		d.skipRegex = regexp.MustCompile(skipRegex.(string))
	}
	if bufferRegex, exists := configMap["bufferRegex"]; exists {
		d.bufferRegex = regexp.MustCompile(bufferRegex.(string))
	}
	d.configureCommonParams(configMap)
}

// Collect iterates on all the docker containers alive and, if possible, collects the correspondent
// memory and cpu statistics.
// For each container a gorutine is started to spin up the collection process.
func (d *DockerStats) Collect() {
	if d.dockerClient == nil {
		d.log.Error("Invalid endpoint: ", docker.ErrInvalidEndpoint)
		return
	}
	containers, err := d.dockerClient.ListContainers(docker.ListContainersOptions{All: false})
	if err != nil {
		d.log.Error("ListContainers() failed: ", err)
		return
	}
	for _, apiContainer := range containers {
		container, err := d.dockerClient.InspectContainer(apiContainer.ID)

		if err != nil {
			d.log.Error("InspectContainer() failed: ", err)
			continue
		}

		if d.skipRegex != nil && d.skipRegex.MatchString(container.Name) {
			d.log.Info("Skip container: ", container.Name)
			continue
		}
		if _, ok := d.previousCPUValues[container.ID]; !ok {
			d.previousCPUValues[container.ID] = new(CPUValues)
		}
		go d.getDockerContainerInfo(container)
	}
}

// getDockerContainerInfo gets container statistics for the given container.
// results is a channel to make possible the synchronization between the main process and the gorutines (wait-notify pattern).
func (d *DockerStats) getDockerContainerInfo(container *docker.Container) {
	errC := make(chan error, 1)
	statsC := make(chan *docker.Stats, 1)
	done := make(chan bool, 1)

	go func() {
		errC <- d.dockerClient.Stats(docker.StatsOptions{container.ID, statsC, false, done, time.Second * time.Duration(d.interval)})
	}()
	select {
	case stats, ok := <-statsC:
		if !ok {
			err := <-errC
			d.log.Error("Failed to collect docker container stats: ", err)
			break
		}
		done <- true

		metrics := d.extractMetrics(container, stats)
		d.sendMetrics(metrics)

		break
	case <-time.After(time.Duration(d.statsTimeout) * time.Second):
		d.log.Error("Timed out collecting stats for container ", container.ID)
		done <- true
		break
	}
}

func (d *DockerStats) extractMetrics(container *docker.Container, stats *docker.Stats) []metric.Metric {
	d.mu.Lock()
	defer d.mu.Unlock()
	metrics := d.buildMetrics(container, stats, calculateCPUPercent(d.previousCPUValues[container.ID].totCPU, d.previousCPUValues[container.ID].systemCPU, stats))

	d.previousCPUValues[container.ID].totCPU = stats.CPUStats.CPUUsage.TotalUsage
	d.previousCPUValues[container.ID].systemCPU = stats.CPUStats.SystemCPUUsage
	return metrics
}

// buildMetrics creates the actual metrics for the given container.
func (d DockerStats) buildMetrics(container *docker.Container, containerStats *docker.Stats, cpuPercentage float64) []metric.Metric {
	ret := []metric.Metric{
		d.buildDockerMetric("DockerMemoryUsed", metric.Gauge, float64(containerStats.MemoryStats.Usage)),
		d.buildDockerMetric("DockerMemoryLimit", metric.Gauge, float64(containerStats.MemoryStats.Limit)),
		d.buildDockerMetric("DockerCpuPercentage", metric.Gauge, cpuPercentage),
	}
	for netiface := range containerStats.Networks {
		// legacy format
		txb := d.buildDockerMetric("DockerTxBytes", metric.CumulativeCounter, float64(containerStats.Networks[netiface].TxBytes))
		txb.AddDimension("iface", netiface)
		ret = append(ret, txb)
		rxb := d.buildDockerMetric("DockerRxBytes", metric.CumulativeCounter, float64(containerStats.Networks[netiface].RxBytes))
		rxb.AddDimension("iface", netiface)
		ret = append(ret, rxb)
	}
	additionalDimensions := map[string]string{
		"container_id":   container.ID,
		"container_name": strings.TrimPrefix(container.Name, "/"),
	}
	metric.AddToAll(&ret, additionalDimensions)
	ret = append(ret, d.buildDockerMetric("DockerContainerCount", metric.Counter, 1))
	metric.AddToAll(&ret, d.extractDimensions(container))

	return ret
}

// sendMetrics writes all the metrics received to the collector channel.
func (d DockerStats) sendMetrics(metrics []metric.Metric) {
	for _, m := range metrics {
		d.Channel() <- m
	}
}

// Function that extracts additional dimensions from the docker environmental variables set up by the user
// in the configuration file.
func (d DockerStats) extractDimensions(container *docker.Container) map[string]string {
	envVars := container.Config.Env
	ret := map[string]string{}

	for dimension, r := range d.compiledRegex {
		for _, envVariable := range envVars {
			envArray := strings.Split(envVariable, "=")
			if r.tag == envArray[0] {
				subMatch := r.regex.FindStringSubmatch(envArray[1])
				if len(subMatch) > 0 {
					ret[dimension] = strings.Replace(subMatch[len(subMatch)-1], "--", "_", -1)
				}
			}
		}
	}
	d.log.Debug(ret)
	return ret
}

func (d DockerStats) buildDockerMetric(name string, metricType string, value float64) (m metric.Metric) {
	m = metric.New(name)
	m.MetricType = metricType
	m.Value = value
	m.MetricTime = time.Now()
	if d.bufferRegex != nil && d.bufferRegex.MatchString(name) {
		m.Buffered = true
	}
	return m
}

// Function that compute the current cpu usage percentage combining current and last values.
func calculateCPUPercent(previousCPU, previousSystem uint64, stats *docker.Stats) float64 {
	var (
		cpuPercent = 0.0
		// calculate the change for the cpu usage of the container in between readings
		cpuDelta = float64(stats.CPUStats.CPUUsage.TotalUsage - previousCPU)
		// calculate the change for the entire system between readings
		systemDelta = float64(stats.CPUStats.SystemCPUUsage - previousSystem)
	)

	if systemDelta > 0.0 && cpuDelta > 0.0 {
		cpuPercent = (cpuDelta / systemDelta) * float64(len(stats.CPUStats.CPUUsage.PercpuUsage)) * 100.0
	}
	return cpuPercent
}

func min(x, y int) int {
	if x < y {
		return x
	}
	return y
}
<|endoftext|>


<commit_before>package collector

import (
	"fullerite/config"
	"fullerite/metric"
	"fmt"
	"regexp"
	"strings"
	"time"

	l "github.com/Sirupsen/logrus"

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

const (
	endpoint = "unix:///var/run/docker.sock"
)

// DockerStats collector type.
// previousCPUValues contains the last cpu-usage values per container.
// dockerClient is the client for the Docker remote API.
type DockerStats struct {
	baseCollector
	previousCPUValues map[string]*CPUValues
	dockerClient      *docker.Client
	statsTimeout      int
	compiledRegex     map[string]*Regex
	endpoint          string
}

// CPUValues struct contains the last cpu-usage values in order to compute properly the current values.
// (see calculateCPUPercent() for more details)
type CPUValues struct {
	totCPU, systemCPU uint64
}

// Regex struct contains the info used to get the user specific dimensions from the docker env variables
// tag: is the environmental variable you want to get the value from
// regex: is the reg exp used to extract the value from the env var
type Regex struct {
	tag   string
	regex *regexp.Regexp
}

func init() {
	RegisterCollector("DockerStats", newDockerStats)
}

// newDockerStats creates a new DockerStats collector.
func newDockerStats(channel chan metric.Metric, initialInterval int, log *l.Entry) Collector {
	d := new(DockerStats)

	d.log = log
	d.channel = channel
	d.interval = initialInterval

	d.name = "DockerStats"
	d.previousCPUValues = make(map[string]*CPUValues)
	d.compiledRegex = make(map[string]*Regex)

	return d
}

// Configure takes a dictionary of values with which the handler can configure itself.
func (d *DockerStats) Configure(configMap map[string]interface{}) {
	if timeout, exists := configMap["dockerStatsTimeout"]; exists {
		d.statsTimeout = min(config.GetAsInt(timeout, d.interval), d.interval)
	} else {
		d.statsTimeout = d.interval
	}
	if endpoint, exists := configMap["dockerEndPoint"]; exists {
		if str, ok := endpoint.(string); ok {
			d.endpoint = str
		} else {
    		etype := fmt.Sprintf("%T", endpoint)
			d.log.Warn("Failed to cast dokerEndPoint: ", etype)

		}
	} else {
		d.endpoint = "unix:///var/run/docker.sock"
	}
	d.dockerClient, _ = docker.NewClient(d.endpoint)
	if generatedDimensions, exists := configMap["generatedDimensions"]; exists {
		for dimension, generator := range generatedDimensions.(map[string]interface{}) {
			for key, regx := range config.GetAsMap(generator) {
				re, err := regexp.Compile(regx)
				if err != nil {
					d.log.Warn("Failed to compile regex: ", regx, err)
				} else {
					d.compiledRegex[dimension] = &Regex{regex: re, tag: key}
				}
			}
		}
	}
	d.configureCommonParams(configMap)
}

// Collect iterates on all the docker containers alive and, if possible, collects the correspondent
// memory and cpu statistics.
// For each container a gorutine is started to spin up the collection process.
func (d *DockerStats) Collect() {
	if d.dockerClient == nil {
		d.log.Error("Invalid endpoint: ", docker.ErrInvalidEndpoint)
		return
	}
	containers, err := d.dockerClient.ListContainers(docker.ListContainersOptions{All: false})
	if err != nil {
		d.log.Error("ListContainers() failed: ", err)
		return
	}
	for _, apiContainer := range containers {
		container, err := d.dockerClient.InspectContainer(apiContainer.ID)
		if err != nil {
			d.log.Error("InspectContainer() failed: ", err)
			continue
		}
		if _, ok := d.previousCPUValues[container.ID]; !ok {
			d.previousCPUValues[container.ID] = new(CPUValues)
		}
		go d.getDockerContainerInfo(container)
	}
}

// getDockerContainerInfo gets container statistics for the given container.
// results is a channel to make possible the synchronization between the main process and the gorutines (wait-notify pattern).
func (d DockerStats) getDockerContainerInfo(container *docker.Container) {
	errC := make(chan error, 1)
	statsC := make(chan *docker.Stats, 1)
	done := make(chan bool)

	go func() {
		errC <- d.dockerClient.Stats(docker.StatsOptions{container.ID, statsC, false, done, time.Second * time.Duration(d.interval)})
	}()
	select {
	case stats, ok := <-statsC:
		if !ok {
			err := <-errC
			d.log.Error("Failed to collect docker container stats: ", err)
			break
		}
		done <- true

		ret := d.buildMetrics(container, stats, calculateCPUPercent(d.previousCPUValues[container.ID].totCPU, d.previousCPUValues[container.ID].systemCPU, stats))

		d.sendMetrics(ret)

		d.previousCPUValues[container.ID].totCPU = stats.CPUStats.CPUUsage.TotalUsage
		d.previousCPUValues[container.ID].systemCPU = stats.CPUStats.SystemCPUUsage

		break
	case <-time.After(time.Duration(d.statsTimeout) * time.Second):
		d.log.Error("Timed out collecting stats for container ", container.ID)
		done <- true
		break
	}
}

// buildMetrics creates the actual metrics for the given container.
func (d DockerStats) buildMetrics(container *docker.Container, containerStats *docker.Stats, cpuPercentage float64) []metric.Metric {
	ret := []metric.Metric{
		buildDockerMetric("DockerRxBytes", metric.CumulativeCounter, float64(containerStats.Network.RxBytes)),
		buildDockerMetric("DockerTxBytes", metric.CumulativeCounter, float64(containerStats.Network.TxBytes)),
		buildDockerMetric("DockerMemoryUsed", metric.Gauge, float64(containerStats.MemoryStats.Usage)),
		buildDockerMetric("DockerMemoryLimit", metric.Gauge, float64(containerStats.MemoryStats.Limit)),
		buildDockerMetric("DockerCpuPercentage", metric.Gauge, cpuPercentage),
	}
	additionalDimensions := map[string]string{
		"container_id":   container.ID,
		"container_name": strings.TrimPrefix(container.Name, "/"),
	}
	metric.AddToAll(&ret, additionalDimensions)
	ret = append(ret, buildDockerMetric("DockerContainerCount", metric.Counter, 1))
	metric.AddToAll(&ret, d.extractDimensions(container))

	return ret
}

// sendMetrics writes all the metrics received to the collector channel.
func (d DockerStats) sendMetrics(metrics []metric.Metric) {
	for _, m := range metrics {
		d.Channel() <- m
	}
}

// Function that extracts additional dimensions from the docker environmental variables set up by the user
// in the configuration file.
func (d DockerStats) extractDimensions(container *docker.Container) map[string]string {
	envVars := container.Config.Env
	ret := map[string]string{}

	for dimension, r := range d.compiledRegex {
		for _, envVariable := range envVars {
			envArray := strings.Split(envVariable, "=")
			if r.tag == envArray[0] {
				subMatch := r.regex.FindStringSubmatch(envArray[1])
				if len(subMatch) > 0 {
					ret[dimension] = strings.Replace(subMatch[len(subMatch)-1], "--", "_", -1)
				}
			}
		}
	}
	d.log.Debug(ret)
	return ret
}

func buildDockerMetric(name string, metricType string, value float64) (m metric.Metric) {
	m = metric.New(name)
	m.MetricType = metricType
	m.Value = value
	return m
}

// Function that compute the current cpu usage percentage combining current and last values.
func calculateCPUPercent(previousCPU, previousSystem uint64, stats *docker.Stats) float64 {
	var (
		cpuPercent = 0.0
		// calculate the change for the cpu usage of the container in between readings
		cpuDelta = float64(stats.CPUStats.CPUUsage.TotalUsage - previousCPU)
		// calculate the change for the entire system between readings
		systemDelta = float64(stats.CPUStats.SystemCPUUsage - previousSystem)
	)

	if systemDelta > 0.0 && cpuDelta > 0.0 {
		cpuPercent = (cpuDelta / systemDelta) * float64(len(stats.CPUStats.CPUUsage.PercpuUsage)) * 100.0
	}
	return cpuPercent
}

func min(x, y int) int {
	if x < y {
		return x
	}
	return y
}
<commit_msg>rename endpoint variable<commit_after>package collector

import (
	"fullerite/config"
	"fullerite/metric"
	"fmt"
	"regexp"
	"strings"
	"time"

	l "github.com/Sirupsen/logrus"

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

const (
	endpoint = "unix:///var/run/docker.sock"
)

// DockerStats collector type.
// previousCPUValues contains the last cpu-usage values per container.
// dockerClient is the client for the Docker remote API.
type DockerStats struct {
	baseCollector
	previousCPUValues map[string]*CPUValues
	dockerClient      *docker.Client
	statsTimeout      int
	compiledRegex     map[string]*Regex
	endpoint          string
}

// CPUValues struct contains the last cpu-usage values in order to compute properly the current values.
// (see calculateCPUPercent() for more details)
type CPUValues struct {
	totCPU, systemCPU uint64
}

// Regex struct contains the info used to get the user specific dimensions from the docker env variables
// tag: is the environmental variable you want to get the value from
// regex: is the reg exp used to extract the value from the env var
type Regex struct {
	tag   string
	regex *regexp.Regexp
}

func init() {
	RegisterCollector("DockerStats", newDockerStats)
}

// newDockerStats creates a new DockerStats collector.
func newDockerStats(channel chan metric.Metric, initialInterval int, log *l.Entry) Collector {
	d := new(DockerStats)

	d.log = log
	d.channel = channel
	d.interval = initialInterval

	d.name = "DockerStats"
	d.previousCPUValues = make(map[string]*CPUValues)
	d.compiledRegex = make(map[string]*Regex)

	return d
}

// Configure takes a dictionary of values with which the handler can configure itself.
func (d *DockerStats) Configure(configMap map[string]interface{}) {
	if timeout, exists := configMap["dockerStatsTimeout"]; exists {
		d.statsTimeout = min(config.GetAsInt(timeout, d.interval), d.interval)
	} else {
		d.statsTimeout = d.interval
	}
	if dockerEndpoint, exists := configMap["dockerEndPoint"]; exists {
		if str, ok := dockerEndpoint.(string); ok {
			d.endpoint = str
		} else {
    		etype := fmt.Sprintf("%T", dockerEndpoint)
			d.log.Warn("Failed to cast dokerEndPoint: ", etype)

		}
	} else {
		d.endpoint = endpoint
	}
	d.dockerClient, _ = docker.NewClient(d.endpoint)
	if generatedDimensions, exists := configMap["generatedDimensions"]; exists {
		for dimension, generator := range generatedDimensions.(map[string]interface{}) {
			for key, regx := range config.GetAsMap(generator) {
				re, err := regexp.Compile(regx)
				if err != nil {
					d.log.Warn("Failed to compile regex: ", regx, err)
				} else {
					d.compiledRegex[dimension] = &Regex{regex: re, tag: key}
				}
			}
		}
	}
	d.configureCommonParams(configMap)
}

// Collect iterates on all the docker containers alive and, if possible, collects the correspondent
// memory and cpu statistics.
// For each container a gorutine is started to spin up the collection process.
func (d *DockerStats) Collect() {
	if d.dockerClient == nil {
		d.log.Error("Invalid endpoint: ", docker.ErrInvalidEndpoint)
		return
	}
	containers, err := d.dockerClient.ListContainers(docker.ListContainersOptions{All: false})
	if err != nil {
		d.log.Error("ListContainers() failed: ", err)
		return
	}
	for _, apiContainer := range containers {
		container, err := d.dockerClient.InspectContainer(apiContainer.ID)
		if err != nil {
			d.log.Error("InspectContainer() failed: ", err)
			continue
		}
		if _, ok := d.previousCPUValues[container.ID]; !ok {
			d.previousCPUValues[container.ID] = new(CPUValues)
		}
		go d.getDockerContainerInfo(container)
	}
}

// getDockerContainerInfo gets container statistics for the given container.
// results is a channel to make possible the synchronization between the main process and the gorutines (wait-notify pattern).
func (d DockerStats) getDockerContainerInfo(container *docker.Container) {
	errC := make(chan error, 1)
	statsC := make(chan *docker.Stats, 1)
	done := make(chan bool)

	go func() {
		errC <- d.dockerClient.Stats(docker.StatsOptions{container.ID, statsC, false, done, time.Second * time.Duration(d.interval)})
	}()
	select {
	case stats, ok := <-statsC:
		if !ok {
			err := <-errC
			d.log.Error("Failed to collect docker container stats: ", err)
			break
		}
		done <- true

		ret := d.buildMetrics(container, stats, calculateCPUPercent(d.previousCPUValues[container.ID].totCPU, d.previousCPUValues[container.ID].systemCPU, stats))

		d.sendMetrics(ret)

		d.previousCPUValues[container.ID].totCPU = stats.CPUStats.CPUUsage.TotalUsage
		d.previousCPUValues[container.ID].systemCPU = stats.CPUStats.SystemCPUUsage

		break
	case <-time.After(time.Duration(d.statsTimeout) * time.Second):
		d.log.Error("Timed out collecting stats for container ", container.ID)
		done <- true
		break
	}
}

// buildMetrics creates the actual metrics for the given container.
func (d DockerStats) buildMetrics(container *docker.Container, containerStats *docker.Stats, cpuPercentage float64) []metric.Metric {
	ret := []metric.Metric{
		buildDockerMetric("DockerRxBytes", metric.CumulativeCounter, float64(containerStats.Network.RxBytes)),
		buildDockerMetric("DockerTxBytes", metric.CumulativeCounter, float64(containerStats.Network.TxBytes)),
		buildDockerMetric("DockerMemoryUsed", metric.Gauge, float64(containerStats.MemoryStats.Usage)),
		buildDockerMetric("DockerMemoryLimit", metric.Gauge, float64(containerStats.MemoryStats.Limit)),
		buildDockerMetric("DockerCpuPercentage", metric.Gauge, cpuPercentage),
	}
	additionalDimensions := map[string]string{
		"container_id":   container.ID,
		"container_name": strings.TrimPrefix(container.Name, "/"),
	}
	metric.AddToAll(&ret, additionalDimensions)
	ret = append(ret, buildDockerMetric("DockerContainerCount", metric.Counter, 1))
	metric.AddToAll(&ret, d.extractDimensions(container))

	return ret
}

// sendMetrics writes all the metrics received to the collector channel.
func (d DockerStats) sendMetrics(metrics []metric.Metric) {
	for _, m := range metrics {
		d.Channel() <- m
	}
}

// Function that extracts additional dimensions from the docker environmental variables set up by the user
// in the configuration file.
func (d DockerStats) extractDimensions(container *docker.Container) map[string]string {
	envVars := container.Config.Env
	ret := map[string]string{}

	for dimension, r := range d.compiledRegex {
		for _, envVariable := range envVars {
			envArray := strings.Split(envVariable, "=")
			if r.tag == envArray[0] {
				subMatch := r.regex.FindStringSubmatch(envArray[1])
				if len(subMatch) > 0 {
					ret[dimension] = strings.Replace(subMatch[len(subMatch)-1], "--", "_", -1)
				}
			}
		}
	}
	d.log.Debug(ret)
	return ret
}

func buildDockerMetric(name string, metricType string, value float64) (m metric.Metric) {
	m = metric.New(name)
	m.MetricType = metricType
	m.Value = value
	return m
}

// Function that compute the current cpu usage percentage combining current and last values.
func calculateCPUPercent(previousCPU, previousSystem uint64, stats *docker.Stats) float64 {
	var (
		cpuPercent = 0.0
		// calculate the change for the cpu usage of the container in between readings
		cpuDelta = float64(stats.CPUStats.CPUUsage.TotalUsage - previousCPU)
		// calculate the change for the entire system between readings
		systemDelta = float64(stats.CPUStats.SystemCPUUsage - previousSystem)
	)

	if systemDelta > 0.0 && cpuDelta > 0.0 {
		cpuPercent = (cpuDelta / systemDelta) * float64(len(stats.CPUStats.CPUUsage.PercpuUsage)) * 100.0
	}
	return cpuPercent
}

func min(x, y int) int {
	if x < y {
		return x
	}
	return y
}
<|endoftext|>


<commit_before>package processors

import (
	"bytes"
	"fmt"
	"github.com/Pixboost/transformimgs/img"
	"os/exec"
	"strconv"
)

type ImageMagick struct {
	convertCmd  string
	identifyCmd string
	// AdditionalArgs are static arguments that will be passed to ImageMagick "convert" command for all operations.
	// Argument name and value should be in separate array elements.
	AdditionalArgs []string
	// GetAdditionalArgs could return additional argument to ImageMagick "convert" command.
	// "op" is the name of the operation: "optimise", "resize" or "fit".
	// Argument name and value should be in separate array elements.
	GetAdditionalArgs func(op string, image []byte, imageInfo *ImageInfo) []string
}

type ImageInfo struct {
	format  string
	quality int
	opaque  bool
	width   int
	height  int
}

var convertOpts = []string{
	"-unsharp", "0.25x0.08+8.3+0.045",
	"-dither", "None",
	"-posterize", "136",
	"-define", "jpeg:fancy-upsampling=off",
	"-define", "png:compression-filter=5",
	"-define", "png:compression-level=9",
	"-define", "png:compression-strategy=0",
	"-define", "webp:method=6",
	"-interlace", "None",
	"-colorspace", "sRGB",
	"-sampling-factor", "4:2:0",
	"+profile", "!icc,*",
}

var cutToFitOpts = []string{
	"-gravity", "center",
}

//If set then will print all commands to stdout.
var Debug bool = true

const (
	MaxWebpWidth  = 16383
	MaxWebpHeight = 16383
)

//Creates new imagemagick processor.
//im is a path to ImageMagick "convert" binary.
//idi is a path to ImageMagick "identify" command.
func NewImageMagick(im string, idi string) (*ImageMagick, error) {
	if len(im) == 0 {
		img.Log.Error("Path to \"convert\" command should be set by -imConvert flag")
		return nil, fmt.Errorf("path to imagemagick convert binary must be provided")
	}
	if len(idi) == 0 {
		img.Log.Error("Path to \"identify\" command should be set by -imIdentify flag")
		return nil, fmt.Errorf("path to imagemagick identify binary must be provided")
	}

	_, err := exec.LookPath(im)
	if err != nil {
		return nil, err
	}
	_, err = exec.LookPath(idi)
	if err != nil {
		return nil, err
	}

	return &ImageMagick{
		convertCmd:     im,
		identifyCmd:    idi,
		AdditionalArgs: []string{},
	}, nil
}

// Resize image to the given size preserving aspect ratio. No cropping applying.
func (p *ImageMagick) Resize(data []byte, size string, imgId string, supportedFormats []string) ([]byte, error) {
	imgInfo, err := p.loadImageInfo(bytes.NewReader(data), imgId)
	if err != nil {
		return nil, err
	}

	args := make([]string, 0)
	args = append(args, "-") //Input
	args = append(args, "-resize", size)
	if imgInfo.format == "JPEG" && imgInfo.quality < 82 {
		args = append(args, "-quality", "82")
	}
	args = append(args, p.AdditionalArgs...)
	if p.GetAdditionalArgs != nil {
		args = append(args, p.GetAdditionalArgs("resize", data, imgInfo)...)
	}
	args = append(args, convertOpts...)
	args = append(args, getConvertFormatOptions(imgInfo)...)
	args = append(args, getOutputFormat(imgInfo, supportedFormats)) //Output

	return p.execImagemagick(bytes.NewReader(data), args, imgId)
}

// Resize input image to exact size with cropping everything that out of the bounds.
// Size must specified in format WIDTHxHEIGHT. Both dimensions must be included.
func (p *ImageMagick) FitToSize(data []byte, size string, imgId string, supportedFormats []string) ([]byte, error) {
	imgInfo, err := p.loadImageInfo(bytes.NewReader(data), imgId)
	if err != nil {
		return nil, err
	}

	args := make([]string, 0)
	args = append(args, "-") //Input
	args = append(args, "-resize", size+"^")
	if imgInfo.format == "JPEG" && imgInfo.quality < 82 {
		args = append(args, "-quality", "82")
	}
	args = append(args, p.AdditionalArgs...)
	if p.GetAdditionalArgs != nil {
		args = append(args, p.GetAdditionalArgs("fit", data, imgInfo)...)
	}
	args = append(args, convertOpts...)
	args = append(args, cutToFitOpts...)
	args = append(args, "-extent", size)
	args = append(args, getConvertFormatOptions(imgInfo)...)
	args = append(args, getOutputFormat(imgInfo, supportedFormats)) //Output

	return p.execImagemagick(bytes.NewReader(data), args, imgId)
}

func (p *ImageMagick) Optimise(data []byte, imgId string, supportedFormats []string) ([]byte, error) {
	imgInfo, err := p.loadImageInfo(bytes.NewReader(data), imgId)
	if err != nil {
		return nil, err
	}
	quality := -1
	//Only changing quality if it wasn't set in original image
	if imgInfo.quality == 100 {
		quality = 82
	}

	args := make([]string, 0)
	args = append(args, "-") //Input
	if quality > 0 {
		args = append(args, "-quality", strconv.Itoa(quality))
	}
	args = append(args, p.AdditionalArgs...)
	if p.GetAdditionalArgs != nil {
		args = append(args, p.GetAdditionalArgs("optimise", data, imgInfo)...)
	}
	args = append(args, convertOpts...)
	args = append(args, getConvertFormatOptions(imgInfo)...)
	args = append(args, getOutputFormat(imgInfo, supportedFormats)) //Output

	result, err := p.execImagemagick(bytes.NewReader(data), args, imgId)
	if err != nil {
		return nil, err
	}

	if len(result) > len(data) {
		img.Log.Printf("[%s] WARNING: Optimised size [%d] is more than original [%d], fallback to original", imgId, len(result), len(data))
		result = data
	}

	return result, nil
}

func (p *ImageMagick) execImagemagick(in *bytes.Reader, args []string, imgId string) ([]byte, error) {
	var out, cmderr bytes.Buffer
	cmd := exec.Command(p.convertCmd)

	cmd.Args = append(cmd.Args, args...)

	cmd.Stdin = in
	cmd.Stdout = &out
	cmd.Stderr = &cmderr

	if Debug {
		img.Log.Printf("[%s] Running resize command, args '%v'\n", imgId, cmd.Args)
	}
	err := cmd.Run()
	if err != nil {
		img.Log.Printf("[%s] Error executing convert command: %s\n", imgId, err.Error())
		img.Log.Printf("[%s] ERROR: %s\n", imgId, cmderr.String())
		return nil, err
	}

	return out.Bytes(), nil
}

func (p *ImageMagick) loadImageInfo(in *bytes.Reader, imgId string) (*ImageInfo, error) {
	var out, cmderr bytes.Buffer
	cmd := exec.Command(p.identifyCmd)
	cmd.Args = append(cmd.Args, "-format", "%m %Q %[opaque] %w %h", "-")

	cmd.Stdin = in
	cmd.Stdout = &out
	cmd.Stderr = &cmderr

	if Debug {
		img.Log.Printf("[%s] Running identify command, args '%v'\n", imgId, cmd.Args)
	}
	err := cmd.Run()
	if err != nil {
		img.Log.Printf("[%s] Error executing identify command: %s\n", err.Error(), imgId)
		img.Log.Printf("[%s] ERROR: %s\n", cmderr.String(), imgId)
		return nil, err
	}

	imageInfo := &ImageInfo{}
	_, err = fmt.Sscanf(out.String(), "%s %d %t %d %d", &imageInfo.format, &imageInfo.quality, &imageInfo.opaque, &imageInfo.width, &imageInfo.height)
	if err != nil {
		return nil, err
	}

	return imageInfo, nil
}

func getOutputFormat(inf *ImageInfo, supportedFormats []string) string {
	webP := false
	for _, f := range supportedFormats {
		if f == "image/webp" && inf.height < MaxWebpHeight && inf.width < MaxWebpWidth {
			webP = true
		}
	}

	output := "-"
	if webP {
		output = "webp:-"
	}

	return output
}

func getConvertFormatOptions(inf *ImageInfo) []string {
	if inf.format == "PNG" {
		opts := []string{
			"-define", "webp:lossless=true",
		}
		if inf.opaque {
			opts = append(opts, "-colors", "256")
		}
		return opts
	}

	return []string{}
}
<commit_msg>* Removed PNG chunks that was removed by -strip option before.<commit_after>package processors

import (
	"bytes"
	"fmt"
	"github.com/Pixboost/transformimgs/img"
	"os/exec"
	"strconv"
)

type ImageMagick struct {
	convertCmd  string
	identifyCmd string
	// AdditionalArgs are static arguments that will be passed to ImageMagick "convert" command for all operations.
	// Argument name and value should be in separate array elements.
	AdditionalArgs []string
	// GetAdditionalArgs could return additional argument to ImageMagick "convert" command.
	// "op" is the name of the operation: "optimise", "resize" or "fit".
	// Argument name and value should be in separate array elements.
	GetAdditionalArgs func(op string, image []byte, imageInfo *ImageInfo) []string
}

type ImageInfo struct {
	format  string
	quality int
	opaque  bool
	width   int
	height  int
}

var convertOpts = []string{
	"-unsharp", "0.25x0.08+8.3+0.045",
	"-dither", "None",
	"-posterize", "136",
	"-define", "jpeg:fancy-upsampling=off",
	"-define", "png:compression-filter=5",
	"-define", "png:compression-level=9",
	"-define", "png:compression-strategy=0",
	"-define", "png:exclude-chunk=bKGD,cHRM,EXIF,gAMA,iCCP,iTXt,sRGB,tEXt,zCCP,zTXt,date",
	"-define", "webp:method=6",
	"-interlace", "None",
	"-colorspace", "sRGB",
	"-sampling-factor", "4:2:0",
	"+profile", "!icc,*",
}

var cutToFitOpts = []string{
	"-gravity", "center",
}

//If set then will print all commands to stdout.
var Debug bool = true

const (
	MaxWebpWidth  = 16383
	MaxWebpHeight = 16383
)

//Creates new imagemagick processor.
//im is a path to ImageMagick "convert" binary.
//idi is a path to ImageMagick "identify" command.
func NewImageMagick(im string, idi string) (*ImageMagick, error) {
	if len(im) == 0 {
		img.Log.Error("Path to \"convert\" command should be set by -imConvert flag")
		return nil, fmt.Errorf("path to imagemagick convert binary must be provided")
	}
	if len(idi) == 0 {
		img.Log.Error("Path to \"identify\" command should be set by -imIdentify flag")
		return nil, fmt.Errorf("path to imagemagick identify binary must be provided")
	}

	_, err := exec.LookPath(im)
	if err != nil {
		return nil, err
	}
	_, err = exec.LookPath(idi)
	if err != nil {
		return nil, err
	}

	return &ImageMagick{
		convertCmd:     im,
		identifyCmd:    idi,
		AdditionalArgs: []string{},
	}, nil
}

// Resize image to the given size preserving aspect ratio. No cropping applying.
func (p *ImageMagick) Resize(data []byte, size string, imgId string, supportedFormats []string) ([]byte, error) {
	imgInfo, err := p.loadImageInfo(bytes.NewReader(data), imgId)
	if err != nil {
		return nil, err
	}

	args := make([]string, 0)
	args = append(args, "-") //Input
	args = append(args, "-resize", size)
	if imgInfo.format == "JPEG" && imgInfo.quality < 82 {
		args = append(args, "-quality", "82")
	}
	args = append(args, p.AdditionalArgs...)
	if p.GetAdditionalArgs != nil {
		args = append(args, p.GetAdditionalArgs("resize", data, imgInfo)...)
	}
	args = append(args, convertOpts...)
	args = append(args, getConvertFormatOptions(imgInfo)...)
	args = append(args, getOutputFormat(imgInfo, supportedFormats)) //Output

	return p.execImagemagick(bytes.NewReader(data), args, imgId)
}

// Resize input image to exact size with cropping everything that out of the bounds.
// Size must specified in format WIDTHxHEIGHT. Both dimensions must be included.
func (p *ImageMagick) FitToSize(data []byte, size string, imgId string, supportedFormats []string) ([]byte, error) {
	imgInfo, err := p.loadImageInfo(bytes.NewReader(data), imgId)
	if err != nil {
		return nil, err
	}

	args := make([]string, 0)
	args = append(args, "-") //Input
	args = append(args, "-resize", size+"^")
	if imgInfo.format == "JPEG" && imgInfo.quality < 82 {
		args = append(args, "-quality", "82")
	}
	args = append(args, p.AdditionalArgs...)
	if p.GetAdditionalArgs != nil {
		args = append(args, p.GetAdditionalArgs("fit", data, imgInfo)...)
	}
	args = append(args, convertOpts...)
	args = append(args, cutToFitOpts...)
	args = append(args, "-extent", size)
	args = append(args, getConvertFormatOptions(imgInfo)...)
	args = append(args, getOutputFormat(imgInfo, supportedFormats)) //Output

	return p.execImagemagick(bytes.NewReader(data), args, imgId)
}

func (p *ImageMagick) Optimise(data []byte, imgId string, supportedFormats []string) ([]byte, error) {
	imgInfo, err := p.loadImageInfo(bytes.NewReader(data), imgId)
	if err != nil {
		return nil, err
	}
	quality := -1
	//Only changing quality if it wasn't set in original image
	if imgInfo.quality == 100 {
		quality = 82
	}

	args := make([]string, 0)
	args = append(args, "-") //Input
	if quality > 0 {
		args = append(args, "-quality", strconv.Itoa(quality))
	}
	args = append(args, p.AdditionalArgs...)
	if p.GetAdditionalArgs != nil {
		args = append(args, p.GetAdditionalArgs("optimise", data, imgInfo)...)
	}
	args = append(args, convertOpts...)
	args = append(args, getConvertFormatOptions(imgInfo)...)
	args = append(args, getOutputFormat(imgInfo, supportedFormats)) //Output

	result, err := p.execImagemagick(bytes.NewReader(data), args, imgId)
	if err != nil {
		return nil, err
	}

	if len(result) > len(data) {
		img.Log.Printf("[%s] WARNING: Optimised size [%d] is more than original [%d], fallback to original", imgId, len(result), len(data))
		result = data
	}

	return result, nil
}

func (p *ImageMagick) execImagemagick(in *bytes.Reader, args []string, imgId string) ([]byte, error) {
	var out, cmderr bytes.Buffer
	cmd := exec.Command(p.convertCmd)

	cmd.Args = append(cmd.Args, args...)

	cmd.Stdin = in
	cmd.Stdout = &out
	cmd.Stderr = &cmderr

	if Debug {
		img.Log.Printf("[%s] Running resize command, args '%v'\n", imgId, cmd.Args)
	}
	err := cmd.Run()
	if err != nil {
		img.Log.Printf("[%s] Error executing convert command: %s\n", imgId, err.Error())
		img.Log.Printf("[%s] ERROR: %s\n", imgId, cmderr.String())
		return nil, err
	}

	return out.Bytes(), nil
}

func (p *ImageMagick) loadImageInfo(in *bytes.Reader, imgId string) (*ImageInfo, error) {
	var out, cmderr bytes.Buffer
	cmd := exec.Command(p.identifyCmd)
	cmd.Args = append(cmd.Args, "-format", "%m %Q %[opaque] %w %h", "-")

	cmd.Stdin = in
	cmd.Stdout = &out
	cmd.Stderr = &cmderr

	if Debug {
		img.Log.Printf("[%s] Running identify command, args '%v'\n", imgId, cmd.Args)
	}
	err := cmd.Run()
	if err != nil {
		img.Log.Printf("[%s] Error executing identify command: %s\n", err.Error(), imgId)
		img.Log.Printf("[%s] ERROR: %s\n", cmderr.String(), imgId)
		return nil, err
	}

	imageInfo := &ImageInfo{}
	_, err = fmt.Sscanf(out.String(), "%s %d %t %d %d", &imageInfo.format, &imageInfo.quality, &imageInfo.opaque, &imageInfo.width, &imageInfo.height)
	if err != nil {
		return nil, err
	}

	return imageInfo, nil
}

func getOutputFormat(inf *ImageInfo, supportedFormats []string) string {
	webP := false
	for _, f := range supportedFormats {
		if f == "image/webp" && inf.height < MaxWebpHeight && inf.width < MaxWebpWidth {
			webP = true
		}
	}

	output := "-"
	if webP {
		output = "webp:-"
	}

	return output
}

func getConvertFormatOptions(inf *ImageInfo) []string {
	if inf.format == "PNG" {
		opts := []string{
			"-define", "webp:lossless=true",
		}
		if inf.opaque {
			opts = append(opts, "-colors", "256")
		}
		return opts
	}

	return []string{}
}
<|endoftext|>


<commit_before>// Package ca provides easy to use certificate authority related functions.
// This is a lightweight wrapper around "crypto/x509" package for
// creating CA certs, client certs, signing requests, and more.
//
// Any "cert, key []byte" type of function parameters and return types are
// always PEM encoded X.509 certificate and private key pairs.
// You can store the certificate/key pair with standard naming as
// "cert.pem" and "key.pem" in the file system.
//
// This package is mostly based on the example code provided at:
// http://golang.org/src/crypto/tls/generate_cert.go
package ca

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"math/big"
	"net"
	"strings"
	"time"
)

// CreateCACert creates a self-signed CA certificate.
// The created certificate can be used for signing other certificates and CRLs.
// The returned slices are the PEM encoded X.509 certificate and private key pair.
func CreateCACert(subject pkix.Name, validFor time.Duration, keyLength int) (cert, key []byte, err error) {
	c, p, err := createBaseCert(subject, validFor, keyLength)
	c.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageCRLSign
	c.BasicConstraintsValid = true
	c.IsCA = true

	cert, key, err = signAndEncodeCert(c, p, c, p)
	return
}

// CreateSigningCert creates an intermediate signing certificate for signing server or client certificates.
// The returned slices are the PEM encoded X.509 certificate and private key pair.
func CreateSigningCert(subject pkix.Name, validFor time.Duration, keyLength int, signingCert, signingKey []byte) (cert, key []byte, err error) {
	var (
		sc, c *x509.Certificate
		sk, k *rsa.PrivateKey
	)

	if sc, sk, err = parseCertAndKey(signingCert, signingKey); err != nil {
		return
	}

	if c, k, err = createBaseCert(subject, validFor, keyLength); err != nil {
		return
	}

	c.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageCRLSign

	cert, key, err = signAndEncodeCert(sc, sk, c, k)
	return
}

// CreateServerCert creates a hosting certificate for servers using TLS.
// The returned slices are the PEM encoded X.509 certificate and private key pair.
func CreateServerCert(subject pkix.Name, host string, validFor time.Duration, keyLength int, signingCert *x509.Certificate, signingKey *rsa.PrivateKey) (cert, key []byte, err error) {
	c, p, err := createBaseCert(subject, validFor, keyLength)
	c.KeyUsage = x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature
	c.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
	setHosts(host, c)

	cert, key, err = signAndEncodeCert(signingCert, signingKey, c, p)
	return
}

// CreateClientCert creates a client certificate signed by the provided signing certificate.
// Created certificate will have its extended key usage set to 'client authentication' and will be ready for use in TLS client authentication.
// The returned slices are the PEM encoded X.509 certificate and private key pair.
func CreateClientCert(subject pkix.Name, validFor time.Duration, keyLength int, signingCert *x509.Certificate, signingKey *rsa.PrivateKey) (cert, key []byte, err error) {
	c, p, err := createBaseCert(subject, validFor, keyLength)
	c.KeyUsage = x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature
	c.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}

	cert, key, err = signAndEncodeCert(signingCert, signingKey, c, p)
	return
}

// createBaseCert creates and returns x509.Certificate (unsigned) and rsa.PrivateKey objects with basic paramters set.
func createBaseCert(subject pkix.Name, validFor time.Duration, keyLength int) (*x509.Certificate, *rsa.PrivateKey, error) {
	privKey, err := rsa.GenerateKey(rand.Reader, keyLength)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to generate certificate private key using RSA: %v", err)
	}

	notBefore := time.Now()
	notAfter := notBefore.Add(validFor)
	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to generate the certificate serial number: %v", err)
	}

	cert := x509.Certificate{
		SerialNumber: serialNumber,
		Subject:      subject,
		NotBefore:    notBefore,
		NotAfter:     notAfter,
	}

	return &cert, privKey, nil
}

// setHosts parses the comma separated host name / IP list and adds them to Subject Alternate Name list of a server/hosting certificate.
func setHosts(host string, cert *x509.Certificate) {
	hosts := strings.Split(host, ",")
	for _, h := range hosts {
		if ip := net.ParseIP(h); ip != nil {
			cert.IPAddresses = append(cert.IPAddresses, ip)
		} else {
			cert.DNSNames = append(cert.DNSNames, h)
		}
	}
}

// Parses PEM encoded X.509 certificate and private key pair into x509.Certificate and rsa.PrivateKey objects.
func parseCertAndKey(cert, key []byte) (c *x509.Certificate, k *rsa.PrivateKey, err error) {
	pc, _ := pem.Decode(cert)
	if c, err = x509.ParseCertificate(pc.Bytes); err != nil {
		err = fmt.Errorf("Failed to parse private key with error: %v", err)
		return
	}

	pk, _ := pem.Decode(key)
	if k, err = x509.ParsePKCS1PrivateKey(pk.Bytes); err != nil {
		err = fmt.Errorf("Failed to parse certificate with error: %v", err)
		return
	}

	return
}

// signAndEncodeCert signs a given certificate with given signing cert/key pair and encodes resulting signed cert and private key in PEM format and returns.
func signAndEncodeCert(signingCert *x509.Certificate, signingKey *rsa.PrivateKey, c *x509.Certificate, k *rsa.PrivateKey) (cert, key []byte, err error) {
	certDerBytes, err := x509.CreateCertificate(rand.Reader, c, signingCert, &k.PublicKey, signingKey)
	if err != nil {
		return
	}

	cert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDerBytes})
	key = pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)})
	return
}
<commit_msg>always set basic constraint<commit_after>// Package ca provides easy to use certificate authority related functions.
// This is a lightweight wrapper around "crypto/x509" package for
// creating CA certs, client certs, signing requests, and more.
//
// Any "cert, key []byte" type of function parameters and return types are
// always PEM encoded X.509 certificate and private key pairs.
// You can store the certificate/key pair with standard naming as
// "cert.pem" and "key.pem" in the file system.
//
// This package is mostly based on the example code provided at:
// http://golang.org/src/crypto/tls/generate_cert.go
package ca

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"math/big"
	"net"
	"strings"
	"time"
)

// CreateCACert creates a self-signed CA certificate.
// The created certificate can be used for signing other certificates and CRLs.
// The returned slices are the PEM encoded X.509 certificate and private key pair.
func CreateCACert(subject pkix.Name, validFor time.Duration, keyLength int) (cert, key []byte, err error) {
	c, p, err := createBaseCert(subject, validFor, keyLength)
	c.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageCRLSign
	c.IsCA = true

	cert, key, err = signAndEncodeCert(c, p, c, p)
	return
}

// CreateSigningCert creates an intermediate signing certificate for signing server or client certificates.
// The returned slices are the PEM encoded X.509 certificate and private key pair.
func CreateSigningCert(subject pkix.Name, validFor time.Duration, keyLength int, signingCert, signingKey []byte) (cert, key []byte, err error) {
	var (
		sc, c *x509.Certificate
		sk, k *rsa.PrivateKey
	)

	if sc, sk, err = parseCertAndKey(signingCert, signingKey); err != nil {
		return
	}

	if c, k, err = createBaseCert(subject, validFor, keyLength); err != nil {
		return
	}

	c.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageCRLSign
	c.IsCA = true

	cert, key, err = signAndEncodeCert(sc, sk, c, k)
	return
}

// CreateServerCert creates a hosting certificate for servers using TLS.
// The returned slices are the PEM encoded X.509 certificate and private key pair.
func CreateServerCert(subject pkix.Name, host string, validFor time.Duration, keyLength int, signingCert *x509.Certificate, signingKey *rsa.PrivateKey) (cert, key []byte, err error) {
	c, p, err := createBaseCert(subject, validFor, keyLength)
	c.KeyUsage = x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature
	c.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
	setHosts(host, c)

	cert, key, err = signAndEncodeCert(signingCert, signingKey, c, p)
	return
}

// CreateClientCert creates a client certificate signed by the provided signing certificate.
// Created certificate will have its extended key usage set to 'client authentication' and will be ready for use in TLS client authentication.
// The returned slices are the PEM encoded X.509 certificate and private key pair.
func CreateClientCert(subject pkix.Name, validFor time.Duration, keyLength int, signingCert *x509.Certificate, signingKey *rsa.PrivateKey) (cert, key []byte, err error) {
	c, p, err := createBaseCert(subject, validFor, keyLength)
	c.KeyUsage = x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature
	c.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}

	cert, key, err = signAndEncodeCert(signingCert, signingKey, c, p)
	return
}

// createBaseCert creates and returns x509.Certificate (unsigned) and rsa.PrivateKey objects with basic paramters set.
func createBaseCert(subject pkix.Name, validFor time.Duration, keyLength int) (*x509.Certificate, *rsa.PrivateKey, error) {
	privKey, err := rsa.GenerateKey(rand.Reader, keyLength)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to generate certificate private key using RSA: %v", err)
	}

	notBefore := time.Now()
	notAfter := notBefore.Add(validFor)
	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to generate the certificate serial number: %v", err)
	}

	cert := x509.Certificate{
		SerialNumber:          serialNumber,
		Subject:               subject,
		NotBefore:             notBefore,
		NotAfter:              notAfter,
		BasicConstraintsValid: true,
	}

	return &cert, privKey, nil
}

// setHosts parses the comma separated host name / IP list and adds them to Subject Alternate Name list of a server/hosting certificate.
func setHosts(host string, cert *x509.Certificate) {
	hosts := strings.Split(host, ",")
	for _, h := range hosts {
		if ip := net.ParseIP(h); ip != nil {
			cert.IPAddresses = append(cert.IPAddresses, ip)
		} else {
			cert.DNSNames = append(cert.DNSNames, h)
		}
	}
}

// Parses PEM encoded X.509 certificate and private key pair into x509.Certificate and rsa.PrivateKey objects.
func parseCertAndKey(cert, key []byte) (c *x509.Certificate, k *rsa.PrivateKey, err error) {
	pc, _ := pem.Decode(cert)
	if c, err = x509.ParseCertificate(pc.Bytes); err != nil {
		err = fmt.Errorf("Failed to parse private key with error: %v", err)
		return
	}

	pk, _ := pem.Decode(key)
	if k, err = x509.ParsePKCS1PrivateKey(pk.Bytes); err != nil {
		err = fmt.Errorf("Failed to parse certificate with error: %v", err)
		return
	}

	return
}

// signAndEncodeCert signs a given certificate with given signing cert/key pair and encodes resulting signed cert and private key in PEM format and returns.
func signAndEncodeCert(signingCert *x509.Certificate, signingKey *rsa.PrivateKey, c *x509.Certificate, k *rsa.PrivateKey) (cert, key []byte, err error) {
	certDerBytes, err := x509.CreateCertificate(rand.Reader, c, signingCert, &k.PublicKey, signingKey)
	if err != nil {
		return
	}

	cert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDerBytes})
	key = pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)})
	return
}
<|endoftext|>


<commit_before>package errutil

import (
	"errors"
	"testing"
)

func stringerr() error {
	return Wrap("error burnt out from here")
}

func stringerr2() error {
	return Wrap(stringerr())
}

func stringerr3() error {
	return Wrap(stringerr2())
}

func standarderr() error {
	return Wrap(errors.New("error burnt out from here"))
}

func standarderr2() error {
	return Wrap(standarderr())
}

func standarderr3() error {
	return Wrap(standarderr2())
}

type fixture struct {
	name   string
	expect string
	err    func() error
}

var fixtures = []fixture{
	{
		name: "string error",
		expect: `[Error Tracks 2006/01/02 15:04:05] error burnt out from here
/Users/bom_d_van/Code/go/workspace/src/github.com/bom-d-van/goutil/errutil/wrap_test.go:9
/Users/bom_d_van/Code/go/workspace/src/github.com/bom-d-van/goutil/errutil/wrap_test.go:13
/Users/bom_d_van/Code/go/workspace/src/github.com/bom-d-van/goutil/errutil/wrap_test.go:17`,
		err: stringerr3,
	},
	{
		name: "standard error",
		expect: `[Error Tracks 2006/01/02 15:04:05] error burnt out from here
/Users/bom_d_van/Code/go/workspace/src/github.com/bom-d-van/goutil/errutil/wrap_test.go:21
/Users/bom_d_van/Code/go/workspace/src/github.com/bom-d-van/goutil/errutil/wrap_test.go:25
/Users/bom_d_van/Code/go/workspace/src/github.com/bom-d-van/goutil/errutil/wrap_test.go:29`,
		err: standarderr3,
	},
}

func TestWrap(t *testing.T) {
	getNow = func() string { return "2006/01/02 15:04:05" }
	var erri interface{}
	for _, tf := range fixtures {
		erri = tf.err()
		err := erri.(Err)
		if err.Details() != tf.expect {
			t.Errorf("Test %s:\nExpect:\n%s\nGot:\n%s", tf.name, tf.expect, err.Details())
		}
	}
}
<commit_msg>fix tests<commit_after>package errutil

import (
	"errors"
	"testing"
)

func stringerr() error {
	return Wrap("error burnt out from here")
}

func stringerr2() error {
	return Wrap(stringerr())
}

func stringerr3() error {
	return Wrap(stringerr2())
}

func standarderr() error {
	return Wrap(errors.New("error burnt out from here"))
}

func standarderr2() error {
	return Wrap(standarderr())
}

func standarderr3() error {
	return Wrap(standarderr2())
}

type fixture struct {
	name   string
	expect string
	err    func() error
}

var fixtures = []fixture{
	{
		name: "string error",
		expect: `[Error Tracks 2006/01/02 15:04:05] error burnt out from here
/Users/bom_d_van/Code/go/workspace/src/github.com/bom-d-van/goutils/errutils/wrap_test.go:9
/Users/bom_d_van/Code/go/workspace/src/github.com/bom-d-van/goutils/errutils/wrap_test.go:13
/Users/bom_d_van/Code/go/workspace/src/github.com/bom-d-van/goutils/errutils/wrap_test.go:17`,
		err: stringerr3,
	},
	{
		name: "standard error",
		expect: `[Error Tracks 2006/01/02 15:04:05] error burnt out from here
/Users/bom_d_van/Code/go/workspace/src/github.com/bom-d-van/goutils/errutils/wrap_test.go:21
/Users/bom_d_van/Code/go/workspace/src/github.com/bom-d-van/goutils/errutils/wrap_test.go:25
/Users/bom_d_van/Code/go/workspace/src/github.com/bom-d-van/goutils/errutils/wrap_test.go:29`,
		err: standarderr3,
	},
}

func TestWrap(t *testing.T) {
	getNow = func() string { return "2006/01/02 15:04:05" }
	var erri interface{}
	for _, tf := range fixtures {
		erri = tf.err()
		err := erri.(Err)
		if err.Details() != tf.expect {
			t.Errorf("Test %s:\nExpect:\n%s\nGot:\n%s", tf.name, tf.expect, err.Details())
		}
	}
}
<|endoftext|>


<commit_before>package dev

import (
	"bytes"
	"testing"
)

type controlLogger struct {
	*testing.T
}

func (t *controlLogger) Printf(format string, v ...interface{}) {
	t.Logf("controlLogger: "+format, v...)
}

func TestControl1(t *testing.T) {
	logger := &controlLogger{t}
	debug := false

	empty := []byte{}
	crlf := []byte{CR, LF}
	four := []byte("1234")
	five := []byte("12345")
	oneBS := []byte{BS}
	oneCR := []byte{CR}
	fiveBS := append([]byte("12345"), BS)
	fiveCR := append([]byte("12345"), CR)
	middleBS := []byte{'1', '2', '3', BS, '4', '5'}
	middleCR := []byte{'1', '2', '3', CR, '4', '5'}

	control(t, debug, logger, "empty", empty, empty, empty, empty)
	control(t, debug, logger, "bufCRLF", crlf, empty, crlf, empty)
	control(t, debug, logger, "suffixCRLF", empty, crlf, empty, crlf)
	control(t, debug, logger, "bothCRLF", crlf, crlf, crlf, crlf)
	control(t, debug, logger, "no-control", five, five, five, five)

	control(t, debug, logger, "suffix-BS1", empty, oneBS, empty, empty)
	control(t, debug, logger, "suffix-BS2", five, oneBS, four, empty)
	control(t, debug, logger, "suffix-fiveBS1", empty, fiveBS, empty, four)
	control(t, debug, logger, "suffix-fiveBS2", five, fiveBS, five, four)
	control(t, debug, logger, "suffix-middleBS1", empty, middleBS, empty, []byte("1245"))
	control(t, debug, logger, "suffix-middleBS2", five, middleBS, five, []byte("1245"))

	control(t, debug, logger, "suffix-CR1", empty, oneCR, empty, empty)
	control(t, debug, logger, "suffix-CR2", five, oneCR, empty, empty)
	control(t, debug, logger, "suffix-fiveCR1", empty, fiveCR, empty, empty)
	control(t, debug, logger, "suffix-fiveCR2", five, fiveCR, empty, empty)
	control(t, debug, logger, "suffix-middleCR1", empty, middleCR, empty, []byte("45"))
	control(t, debug, logger, "suffix-middleCR2", five, middleCR, empty, []byte("45"))
}

func control(t *testing.T, debug bool, logger hasPrintf, label string, inputBuf, inputSuffix, expectedBuf, expectedSuffix []byte) {
	buf := clone(inputBuf)
	suffix := clone(inputSuffix)

	gotBuf, gotSuffix := removeControlChars(logger, debug, buf, suffix)

	if !bytes.Equal(gotBuf, expectedBuf) {
		t.Errorf("%s: buf mismatch: got=%q wanted=%q", label, gotBuf, expectedBuf)
	}

	if !bytes.Equal(gotSuffix, expectedSuffix) {
		t.Errorf("%s: suffix mismatch: got=%q wanted=%q", label, gotSuffix, expectedSuffix)
	}
}

func clone(a []byte) []byte {
	b := make([]byte, len(a))
	copy(b, a)
	return b
}
<commit_msg>More tests.<commit_after>package dev

import (
	"bytes"
	"testing"
)

type controlLogger struct {
	*testing.T
}

func (t *controlLogger) Printf(format string, v ...interface{}) {
	t.Logf("controlLogger: "+format, v...)
}

func TestControl1(t *testing.T) {
	logger := &controlLogger{t}
	debug := false

	empty := []byte{}
	crlf := []byte{CR, LF}
	four := []byte("1234")
	five := []byte("12345")
	oneBS := []byte{BS}
	oneCR := []byte{CR}
	fiveBS := append([]byte("12345"), BS)
	fiveCR := append([]byte("12345"), CR)
	BSfive := append([]byte{BS}, []byte("12345")...)
	CRfive := append([]byte{CR}, []byte("12345")...)
	middleBS := []byte{'1', '2', '3', BS, '4', '5'}
	middleCR := []byte{'1', '2', '3', CR, '4', '5'}

	control(t, debug, logger, "empty", empty, empty, empty, empty)
	control(t, debug, logger, "bufCRLF", crlf, empty, crlf, empty)
	control(t, debug, logger, "suffixCRLF", empty, crlf, empty, crlf)
	control(t, debug, logger, "bothCRLF", crlf, crlf, crlf, crlf)
	control(t, debug, logger, "no-control", five, five, five, five)

	control(t, debug, logger, "suffix-BS1", empty, oneBS, empty, empty)
	control(t, debug, logger, "suffix-BS2", five, oneBS, four, empty)
	control(t, debug, logger, "suffix-BSfive1", empty, BSfive, empty, five)
	control(t, debug, logger, "suffix-BSfive2", five, BSfive, four, five)
	control(t, debug, logger, "suffix-fiveBS1", empty, fiveBS, empty, four)
	control(t, debug, logger, "suffix-fiveBS2", five, fiveBS, five, four)
	control(t, debug, logger, "suffix-middleBS1", empty, middleBS, empty, []byte("1245"))
	control(t, debug, logger, "suffix-middleBS2", five, middleBS, five, []byte("1245"))

	control(t, debug, logger, "suffix-CR1", empty, oneCR, empty, empty)
	control(t, debug, logger, "suffix-CR2", five, oneCR, empty, empty)
	control(t, debug, logger, "suffix-CRfive1", empty, CRfive, empty, five)
	control(t, debug, logger, "suffix-CRfive2", five, CRfive, empty, five)
	control(t, debug, logger, "suffix-fiveCR1", empty, fiveCR, empty, empty)
	control(t, debug, logger, "suffix-fiveCR2", five, fiveCR, empty, empty)
	control(t, debug, logger, "suffix-middleCR1", empty, middleCR, empty, []byte("45"))
	control(t, debug, logger, "suffix-middleCR2", five, middleCR, empty, []byte("45"))
}

func control(t *testing.T, debug bool, logger hasPrintf, label string, inputBuf, inputSuffix, expectedBuf, expectedSuffix []byte) {
	buf := clone(inputBuf)
	suffix := clone(inputSuffix)

	gotBuf, gotSuffix := removeControlChars(logger, debug, buf, suffix)

	if !bytes.Equal(gotBuf, expectedBuf) {
		t.Errorf("%s: buf mismatch: got=%q wanted=%q", label, gotBuf, expectedBuf)
	}

	if !bytes.Equal(gotSuffix, expectedSuffix) {
		t.Errorf("%s: suffix mismatch: got=%q wanted=%q", label, gotSuffix, expectedSuffix)
	}
}

func clone(a []byte) []byte {
	b := make([]byte, len(a))
	copy(b, a)
	return b
}
<|endoftext|>


<commit_before>package sinmetalcraft

import (
	"encoding/json"
	"fmt"
	"net/http"

	"google.golang.org/appengine"
	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/log"
	"google.golang.org/appengine/urlfetch"

	"google.golang.org/api/compute/v1"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

func init() {
	api := ServerApi{}

	http.HandleFunc("/api/1/server", api.Handler)
}

type ServerApi struct{}

type ServerApiPutParam struct {
	KeyStr    string `json:"key"`
	Operation string `json:"operation"`
}

func (a *ServerApi) Handler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		a.Post(w, r)
	} else if r.Method == "PUT" {
		a.Put(w, r)
	} else if r.Method == "GET" {
		a.List(w, r)
	} else if r.Method == "DELETE" {
		a.Delete(w, r)
	} else {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}
}

// create new instance
func (a *ServerApi) Post(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)

	var form map[string]string
	err := json.NewDecoder(r.Body).Decode(&form)
	if err != nil {
		log.Infof(ctx, "rquest body, %v", r.Body)
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"message": "invalid request."}`))
		return
	}
	defer r.Body.Close()

	if len(form["key"]) < 1 {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"message": "key is required."}`))
		return
	}
	key, err := datastore.DecodeKey(form["key"])
	if err != nil {
		log.Infof(ctx, "decode key error. param = %s, err = %s", form["key"], err.Error())
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"message": "invalid key."}`))
		return
	}

	var minecraft Minecraft
	err = datastore.Get(ctx, key, &minecraft)
	if err == datastore.ErrNoSuchEntity {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte(fmt.Sprintf(`{"message": "%s is not found."}`, form["key"])))
		return
	}
	if err != nil {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(fmt.Sprintf(`{"message": "%s"}`, err.Error())))
		return
	}
	minecraft.Key = key

	client := &http.Client{
		Transport: &oauth2.Transport{
			Source: google.AppEngineTokenSource(ctx, compute.ComputeScope),
			Base:   &urlfetch.Transport{Context: ctx},
		},
	}
	s, err := compute.New(client)
	if err != nil {
		log.Errorf(ctx, "ERROR compute.New: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	is := compute.NewInstancesService(s)
	name, err := createInstance(ctx, is, minecraft)
	if err != nil {
		log.Errorf(ctx, "ERROR compute instances create: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(http.StatusCreated)
	w.Write([]byte(fmt.Sprintf(`{"message" : "%s create done!"}`, name)))
}

// reset or start instance
func (a *ServerApi) Put(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)

	var param ServerApiPutParam
	err := json.NewDecoder(r.Body).Decode(&param)
	if err != nil {
		log.Infof(ctx, "rquest body, %v", r.Body)
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"message": "invalid request."}`))
		return
	}
	defer r.Body.Close()

	key, err := datastore.DecodeKey(param.KeyStr)
	if err != nil {
		log.Infof(ctx, "invalid key, %v", r.Body)
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"message": "invalid key."}`))
		return
	}

	if param.Operation != "start" && param.Operation != "reset" {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"invalid request" : "operation param is start or reset"}`))
		return
	}

	var minecraft Minecraft
	err = datastore.Get(ctx, key, &minecraft)
	if err == datastore.ErrNoSuchEntity {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte(fmt.Sprintf(`{"message": "%s is not found."}`, param.KeyStr)))
		return
	}
	if err != nil {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(fmt.Sprintf(`{"message": "%s"}`, err.Error())))
		return
	}
	minecraft.Key = key

	client := &http.Client{
		Transport: &oauth2.Transport{
			Source: google.AppEngineTokenSource(ctx, compute.ComputeScope),
			Base:   &urlfetch.Transport{Context: ctx},
		},
	}
	s, err := compute.New(client)
	if err != nil {
		log.Errorf(ctx, "ERROR compute.New: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	is := compute.NewInstancesService(s)

	var name string
	if param.Operation == "start" {
		name, err = startInstance(ctx, is, minecraft)
		if err != nil {
			log.Errorf(ctx, "ERROR compute Instances Start: %s", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
	} else if param.Operation == "reset" {
		name, err = resetInstance(ctx, is, minecraft)
		if err != nil {
			log.Errorf(ctx, "ERROR compute Instances Reset: %s", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
	} else {
		log.Errorf(ctx, `{"invalid request" : "operation param is start or reset"}`)
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"invalid request" : "operation param is start or reset"}`))
		return
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(fmt.Sprintf(`"{"message": "%s %s done!"}`, name, param.Operation)))
}

// delete instance
func (a *ServerApi) Delete(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)

	keyStr := r.FormValue("key")

	key, err := datastore.DecodeKey(keyStr)
	if err != nil {
		log.Infof(ctx, "invalid key, %v", r.Body)
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"message": "invalid key."}`))
		return
	}

	var minecraft Minecraft
	err = datastore.Get(ctx, key, &minecraft)
	if err == datastore.ErrNoSuchEntity {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte(fmt.Sprintf(`{"message": "%s is not found."}`, keyStr)))
		return
	}
	if err != nil {
		log.Errorf(ctx, "ERROR, Get Minecraft error = %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	client := &http.Client{
		Transport: &oauth2.Transport{
			Source: google.AppEngineTokenSource(ctx, compute.ComputeScope),
			Base:   &urlfetch.Transport{Context: ctx},
		},
	}
	s, err := compute.New(client)
	if err != nil {
		log.Errorf(ctx, "ERROR compute.New: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	is := compute.NewInstancesService(s)

	name, err := deleteInstance(ctx, is, minecraft)
	if err != nil {
		log.Errorf(ctx, "ERROR compute Instances Delete: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(fmt.Sprintf(`"{"message": "%s delete done!"}`, name)))
}

// list instance
func (a *ServerApi) List(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)

	client := &http.Client{
		Transport: &oauth2.Transport{
			Source: google.AppEngineTokenSource(ctx, compute.ComputeScope),
			Base:   &urlfetch.Transport{Context: ctx},
		},
	}
	s, err := compute.New(client)
	if err != nil {
		log.Errorf(ctx, "ERROR compute.New: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	is := compute.NewInstancesService(s)
	instances, cursor, err := listInstance(ctx, is, "asia-east1-b")
	if err != nil {
		log.Errorf(ctx, "ERROR compute.Instance List: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	var res []MinecraftApiResponse
	for _, item := range instances {
		res = append(res, MinecraftApiResponse{
			InstanceName:      item.Name,
			Zone:              item.Zone,
			IPAddr:            item.NetworkInterfaces[0].AccessConfigs[0].NatIP,
			Status:            item.Status,
			CreationTimestamp: item.CreationTimestamp,
		})
	}

	apiRes := MinecraftApiListResponse{
		Items:  res,
		Cursor: cursor,
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(apiRes)
}
<commit_msg>インスタンス操作APIに認証処理を追加した<commit_after>package sinmetalcraft

import (
	"encoding/json"
	"fmt"
	"net/http"

	"google.golang.org/appengine"
	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/log"
	"google.golang.org/appengine/urlfetch"
	"google.golang.org/appengine/user"

	"google.golang.org/api/compute/v1"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

func init() {
	api := ServerApi{}

	http.HandleFunc("/api/1/server", api.Handler)
}

type ServerApi struct{}

type ServerApiPutParam struct {
	KeyStr    string `json:"key"`
	Operation string `json:"operation"`
}

func (a *ServerApi) Handler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		a.Post(w, r)
	} else if r.Method == "PUT" {
		a.Put(w, r)
	} else if r.Method == "GET" {
		a.List(w, r)
	} else if r.Method == "DELETE" {
		a.Delete(w, r)
	} else {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}
}

// create new instance
func (a *ServerApi) Post(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)

	u := user.Current(ctx)
	if u == nil {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusUnauthorized)
		loginURL, err := user.LoginURL(ctx, "")
		if err != nil {
			log.Errorf(ctx, "get user login URL error, %s", err.Error())
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		w.Write([]byte(fmt.Sprintf(`{"loginURL":"%s"}`, loginURL)))
		return
	}

	var form map[string]string
	err := json.NewDecoder(r.Body).Decode(&form)
	if err != nil {
		log.Infof(ctx, "rquest body, %v", r.Body)
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"message": "invalid request."}`))
		return
	}
	defer r.Body.Close()

	if len(form["key"]) < 1 {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"message": "key is required."}`))
		return
	}
	key, err := datastore.DecodeKey(form["key"])
	if err != nil {
		log.Infof(ctx, "decode key error. param = %s, err = %s", form["key"], err.Error())
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"message": "invalid key."}`))
		return
	}

	var minecraft Minecraft
	err = datastore.Get(ctx, key, &minecraft)
	if err == datastore.ErrNoSuchEntity {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte(fmt.Sprintf(`{"message": "%s is not found."}`, form["key"])))
		return
	}
	if err != nil {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(fmt.Sprintf(`{"message": "%s"}`, err.Error())))
		return
	}
	minecraft.Key = key

	client := &http.Client{
		Transport: &oauth2.Transport{
			Source: google.AppEngineTokenSource(ctx, compute.ComputeScope),
			Base:   &urlfetch.Transport{Context: ctx},
		},
	}
	s, err := compute.New(client)
	if err != nil {
		log.Errorf(ctx, "ERROR compute.New: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	is := compute.NewInstancesService(s)
	name, err := createInstance(ctx, is, minecraft)
	if err != nil {
		log.Errorf(ctx, "ERROR compute instances create: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(http.StatusCreated)
	w.Write([]byte(fmt.Sprintf(`{"message" : "%s create done!"}`, name)))
}

// reset or start instance
func (a *ServerApi) Put(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)

	u := user.Current(ctx)
	if u == nil {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusUnauthorized)
		loginURL, err := user.LoginURL(ctx, "")
		if err != nil {
			log.Errorf(ctx, "get user login URL error, %s", err.Error())
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		w.Write([]byte(fmt.Sprintf(`{"loginURL":"%s"}`, loginURL)))
		return
	}

	var param ServerApiPutParam
	err := json.NewDecoder(r.Body).Decode(&param)
	if err != nil {
		log.Infof(ctx, "rquest body, %v", r.Body)
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"message": "invalid request."}`))
		return
	}
	defer r.Body.Close()

	key, err := datastore.DecodeKey(param.KeyStr)
	if err != nil {
		log.Infof(ctx, "invalid key, %v", r.Body)
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"message": "invalid key."}`))
		return
	}

	if param.Operation != "start" && param.Operation != "reset" {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"invalid request" : "operation param is start or reset"}`))
		return
	}

	var minecraft Minecraft
	err = datastore.Get(ctx, key, &minecraft)
	if err == datastore.ErrNoSuchEntity {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte(fmt.Sprintf(`{"message": "%s is not found."}`, param.KeyStr)))
		return
	}
	if err != nil {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(fmt.Sprintf(`{"message": "%s"}`, err.Error())))
		return
	}
	minecraft.Key = key

	client := &http.Client{
		Transport: &oauth2.Transport{
			Source: google.AppEngineTokenSource(ctx, compute.ComputeScope),
			Base:   &urlfetch.Transport{Context: ctx},
		},
	}
	s, err := compute.New(client)
	if err != nil {
		log.Errorf(ctx, "ERROR compute.New: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	is := compute.NewInstancesService(s)

	var name string
	if param.Operation == "start" {
		name, err = startInstance(ctx, is, minecraft)
		if err != nil {
			log.Errorf(ctx, "ERROR compute Instances Start: %s", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
	} else if param.Operation == "reset" {
		name, err = resetInstance(ctx, is, minecraft)
		if err != nil {
			log.Errorf(ctx, "ERROR compute Instances Reset: %s", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
	} else {
		log.Errorf(ctx, `{"invalid request" : "operation param is start or reset"}`)
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"invalid request" : "operation param is start or reset"}`))
		return
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(fmt.Sprintf(`"{"message": "%s %s done!"}`, name, param.Operation)))
}

// delete instance
func (a *ServerApi) Delete(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)

	u := user.Current(ctx)
	if u == nil {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusUnauthorized)
		loginURL, err := user.LoginURL(ctx, "")
		if err != nil {
			log.Errorf(ctx, "get user login URL error, %s", err.Error())
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		w.Write([]byte(fmt.Sprintf(`{"loginURL":"%s"}`, loginURL)))
		return
	}

	keyStr := r.FormValue("key")

	key, err := datastore.DecodeKey(keyStr)
	if err != nil {
		log.Infof(ctx, "invalid key, %v", r.Body)
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"message": "invalid key."}`))
		return
	}

	var minecraft Minecraft
	err = datastore.Get(ctx, key, &minecraft)
	if err == datastore.ErrNoSuchEntity {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte(fmt.Sprintf(`{"message": "%s is not found."}`, keyStr)))
		return
	}
	if err != nil {
		log.Errorf(ctx, "ERROR, Get Minecraft error = %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	minecraft.Key = key

	client := &http.Client{
		Transport: &oauth2.Transport{
			Source: google.AppEngineTokenSource(ctx, compute.ComputeScope),
			Base:   &urlfetch.Transport{Context: ctx},
		},
	}
	s, err := compute.New(client)
	if err != nil {
		log.Errorf(ctx, "ERROR compute.New: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	is := compute.NewInstancesService(s)

	name, err := deleteInstance(ctx, is, minecraft)
	if err != nil {
		log.Errorf(ctx, "ERROR compute Instances Delete: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(fmt.Sprintf(`{"message": "%s delete done!"}`, name)))
}

// list instance
func (a *ServerApi) List(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)

	u := user.Current(ctx)
	if u == nil {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusUnauthorized)
		loginURL, err := user.LoginURL(ctx, "")
		if err != nil {
			log.Errorf(ctx, "get user login URL error, %s", err.Error())
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		w.Write([]byte(fmt.Sprintf(`{"loginURL":"%s"}`, loginURL)))
		return
	}
	if user.IsAdmin(ctx) == false {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusForbidden)
		return
	}

	client := &http.Client{
		Transport: &oauth2.Transport{
			Source: google.AppEngineTokenSource(ctx, compute.ComputeScope),
			Base:   &urlfetch.Transport{Context: ctx},
		},
	}
	s, err := compute.New(client)
	if err != nil {
		log.Errorf(ctx, "ERROR compute.New: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	is := compute.NewInstancesService(s)
	instances, cursor, err := listInstance(ctx, is, "asia-east1-b")
	if err != nil {
		log.Errorf(ctx, "ERROR compute.Instance List: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	var res []MinecraftApiResponse
	for _, item := range instances {
		res = append(res, MinecraftApiResponse{
			InstanceName:      item.Name,
			Zone:              item.Zone,
			IPAddr:            item.NetworkInterfaces[0].AccessConfigs[0].NatIP,
			Status:            item.Status,
			CreationTimestamp: item.CreationTimestamp,
		})
	}

	apiRes := MinecraftApiListResponse{
		Items:  res,
		Cursor: cursor,
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(apiRes)
}
<|endoftext|>


<commit_before>package auth

import (
	"database/sql"
	"errors"
	"net/http"

	"github.com/mdlayher/wavepipe/data"
)

// SimpleAuth represents the simple authentication method, which is used when the environment
// variable WAVEPIPE_DEBUG is set to "1". It is meant for development ONLY.
type SimpleAuth struct{}

// Authenticate uses the simple authentication method to log in to the API, returning
// a session user and a pair of client/server errors
func (a SimpleAuth) Authenticate(req *http.Request) (*data.User, *data.Session, error, error) {
	// Username for authentication
	var username string

	// Check for empty authorization header
	if req.Header.Get("Authorization") == "" {
		// If no header, check for credentials via querystring parameters
		query := req.URL.Query()
		username = query.Get("u")
	} else {
		// Fetch credentials from HTTP Basic auth
		tempUsername, _, err := basicCredentials(req.Header.Get("Authorization"))
		if err != nil {
			return nil, nil, err, nil
		}

		// Copy credentials
		username = tempUsername
	}

	// Check if username is blank
	if username == "" {
		return nil, nil, ErrNoUsername, nil
	}

	// Attempt to load user by username
	user := new(data.User)
	user.Username = username
	if err := user.Load(); err != nil {
		// Check for invalid user
		if err == sql.ErrNoRows {
			return nil, nil, errors.New("invalid username"), nil
		}

		// Server error
		return nil, nil, nil, err
	}

	// No errors, return session user, but no session because one does not exist yet
	return user, nil, nil, nil
}
<commit_msg>api/auth/simple: verify that SimpleAuth is only invoked in debug mode<commit_after>package auth

import (
	"database/sql"
	"errors"
	"net/http"
	"os"

	"github.com/mdlayher/wavepipe/data"
)

// SimpleAuth represents the simple authentication method, which is used when the environment
// variable WAVEPIPE_DEBUG is set to "1". It is meant for development ONLY.
type SimpleAuth struct{}

// Authenticate uses the simple authentication method to log in to the API, returning
// a session user and a pair of client/server errors
func (a SimpleAuth) Authenticate(req *http.Request) (*data.User, *data.Session, error, error) {
	// Verify that SimpleAuth was triggered in debug mode
	if os.Getenv("WAVEPIPE_DEBUG") != "1" {
		return nil, nil, nil, errors.New("not in debug mode")
	}

	// Username for authentication
	var username string

	// Check for empty authorization header
	if req.Header.Get("Authorization") == "" {
		// If no header, check for credentials via querystring parameters
		query := req.URL.Query()
		username = query.Get("u")
	} else {
		// Fetch credentials from HTTP Basic auth
		tempUsername, _, err := basicCredentials(req.Header.Get("Authorization"))
		if err != nil {
			return nil, nil, err, nil
		}

		// Copy credentials
		username = tempUsername
	}

	// Check if username is blank
	if username == "" {
		return nil, nil, ErrNoUsername, nil
	}

	// Attempt to load user by username
	user := new(data.User)
	user.Username = username
	if err := user.Load(); err != nil {
		// Check for invalid user
		if err == sql.ErrNoRows {
			return nil, nil, errors.New("invalid username"), nil
		}

		// Server error
		return nil, nil, nil, err
	}

	// No errors, return session user, but no session because one does not exist yet
	return user, nil, nil, nil
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"net/http"
	"strings"
)

const (
	before = `<!DOCTYPE html>
<!-- If you are reading this, there is a good chance you would prefer sending an
"Accept: application/json" header and receiving actual JSON responses. -->
<link rel="stylesheet" type="text/css" href="%CSS%" />
<script src="%JS%"></script>
<script>
var curlUser='${RANCHER_ACCESS_KEY}:${RANCHER_SECRET_KEY}';
var schemas="%SCHEMAS%";
//var docs = "http://url-to-your-docs/site";
//BEFORE DATA
var data =
`
	defaultCssUrl = "https://releases.rancher.com/api-ui/1.0.4/ui.css"
	defaultJsUrl  = "https://releases.rancher.com/api-ui/1.0.4/ui.js"
)

var (
	after = []byte(`;
</script>`)
)

type HtmlWriter struct {
	CssUrl, JsUrl string
	r             *http.Request
}

func (j *HtmlWriter) Write(obj interface{}, rw http.ResponseWriter) error {
	apiContext := GetApiContext(j.r)
	rw.Header().Set("X-API-Schemas", apiContext.UrlBuilder.Collection("schema"))

	if rw.Header().Get("Content-Type") == "" {
		rw.Header().Set("Content-Type", "text/html; charset=utf-8")
	}

	if _, err := rw.Write([]byte(j.before())); err != nil {
		return err
	}

	enc := json.NewEncoder(rw)
	if err := enc.Encode(obj); err != nil {
		return err
	}

	if _, err := rw.Write([]byte(after)); err != nil {
		return err
	}

	return nil
}

func (j *HtmlWriter) before() string {
	css := j.CssUrl
	if css == "" {
		css = defaultCssUrl
	}

	js := j.JsUrl
	if js == "" {
		js = defaultJsUrl
	}

	apiContext := GetApiContext(j.r)

	content := strings.Replace(before, "%CSS%", css, -1)
	content = strings.Replace(content, "%JS%", js, -1)
	content = strings.Replace(content, "%SCHEMAS%", apiContext.UrlBuilder.Collection("schema"), -1)

	return content
}
<commit_msg>Switch to min version UI<commit_after>package api

import (
	"encoding/json"
	"net/http"
	"strings"
)

const (
	before = `<!DOCTYPE html>
<!-- If you are reading this, there is a good chance you would prefer sending an
"Accept: application/json" header and receiving actual JSON responses. -->
<link rel="stylesheet" type="text/css" href="%CSS%" />
<script src="%JS%"></script>
<script>
var curlUser='${RANCHER_ACCESS_KEY}:${RANCHER_SECRET_KEY}';
var schemas="%SCHEMAS%";
//var docs = "http://url-to-your-docs/site";
//BEFORE DATA
var data =
`
	defaultCssUrl = "https://releases.rancher.com/api-ui/1.0.4/ui.min.css"
	defaultJsUrl  = "https://releases.rancher.com/api-ui/1.0.4/ui.min.js"
)

var (
	after = []byte(`;
</script>`)
)

type HtmlWriter struct {
	CssUrl, JsUrl string
	r             *http.Request
}

func (j *HtmlWriter) Write(obj interface{}, rw http.ResponseWriter) error {
	apiContext := GetApiContext(j.r)
	rw.Header().Set("X-API-Schemas", apiContext.UrlBuilder.Collection("schema"))

	if rw.Header().Get("Content-Type") == "" {
		rw.Header().Set("Content-Type", "text/html; charset=utf-8")
	}

	if _, err := rw.Write([]byte(j.before())); err != nil {
		return err
	}

	enc := json.NewEncoder(rw)
	if err := enc.Encode(obj); err != nil {
		return err
	}

	if _, err := rw.Write([]byte(after)); err != nil {
		return err
	}

	return nil
}

func (j *HtmlWriter) before() string {
	css := j.CssUrl
	if css == "" {
		css = defaultCssUrl
	}

	js := j.JsUrl
	if js == "" {
		js = defaultJsUrl
	}

	apiContext := GetApiContext(j.r)

	content := strings.Replace(before, "%CSS%", css, -1)
	content = strings.Replace(content, "%JS%", js, -1)
	content = strings.Replace(content, "%SCHEMAS%", apiContext.UrlBuilder.Collection("schema"), -1)

	return content
}
<|endoftext|>