<commit_before>// Copyright 2014 ISRG.  All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package boulder

import (
	"errors"
	"github.com/streadway/amqp"
	"log"
	"time"
)

// TODO: AMQP-RPC messages should be wrapped in JWS.  To implement that,
// it will be necessary to make the following changes:
//
// * Constructors: Provision private key, acceptable public keys
// * After consume: Verify and discard JWS wrapper
// * Before publish: Add JWS wrapper

// General AMQP helpers

// XXX: I *think* these constants are appropriate.
// We will probably want to tweak these in the future.
const (
	AmqpExchange     = ""
	AmqpDurable      = false
	AmqpDeleteUnused = false
	AmqpExclusive    = false
	AmqpNoWait       = false
	AmqpNoLocal      = false
	AmqpAutoAck      = true
	AmqpMandatory    = false
	AmqpImmediate    = false
)

// A simplified way to get a channel for a given AMQP server
func amqpConnect(url string) (ch *amqp.Channel, err error) {
	conn, err := amqp.Dial(url)
	if err != nil {
		return
	}

	ch, err = conn.Channel()
	return
}

// A simplified way to declare and subscribe to an AMQP queue
func amqpSubscribe(ch *amqp.Channel, name string) (msgs <-chan amqp.Delivery, err error) {
	q, err := ch.QueueDeclare(
		name,
		AmqpDurable,
		AmqpDeleteUnused,
		AmqpExclusive,
		AmqpNoWait,
		nil)
	if err != nil {
		return
	}

	msgs, err = ch.Consume(
		q.Name,
		"",
		AmqpAutoAck,
		AmqpExclusive,
		AmqpNoLocal,
		AmqpNoWait,
		nil)
	return
}

// An AMQP-RPC Server listens on a specified queue within an AMQP channel.
// When messages arrive on that queue, it dispatches them based on type,
// and returns the response to the ReplyTo queue.
//
// To implement specific functionality, using code should use the Handle
// method to add specific actions.
type AmqpRpcServer struct {
	serverQueue   string
	channel       *amqp.Channel
	dispatchTable map[string]func([]byte) []byte
}

// Create a new AMQP-RPC server on the given queue and channel.
// Note that you must call Start() to actually start the server
// listening for requests.
func NewAmqpRpcServer(serverQueue string, channel *amqp.Channel) *AmqpRpcServer {
	return &AmqpRpcServer{
		serverQueue:   serverQueue,
		channel:       channel,
		dispatchTable: make(map[string]func([]byte) []byte),
	}
}

func (rpc *AmqpRpcServer) Handle(method string, handler func([]byte) []byte) {
	rpc.dispatchTable[method] = handler
}

// Starts the AMQP-RPC server running in a separate thread.
// There is currently no Stop() method.
func (rpc *AmqpRpcServer) Start() (err error) {
	msgs, err := amqpSubscribe(rpc.channel, rpc.serverQueue)
	if err != nil {
		return
	}

	go func() {
		for msg := range msgs {
			// XXX-JWS: jws.Verify(body)
			cb, present := rpc.dispatchTable[msg.Type]
			log.Printf(" [s<] received %s(%s) [%s]", msg.Type, b64enc(msg.Body), msg.CorrelationId)
			if present {
				response := cb(msg.Body)
				log.Printf(" [s>] sending %s(%s) [%s]", msg.Type, b64enc(response), msg.CorrelationId)
				rpc.channel.Publish(
					AmqpExchange,
					msg.ReplyTo,
					AmqpMandatory,
					AmqpImmediate,
					amqp.Publishing{
						CorrelationId: msg.CorrelationId,
						Type:          msg.Type,
						Body:          response, // XXX-JWS: jws.Sign(privKey, body)
					})
			}
		}
	}()
	return
}

// An AMQP-RPC client sends requests to a specific server queue,
// and uses a dedicated response queue for responses.
//
// To implement specific functionality, using code uses the Dispatch()
// method to send a method name and body, and get back a response. So
// you end up with wrapper methods of the form:
//
// ```
//   request = /* serialize request to []byte */
//   response = <-AmqpRpcClient.Dispatch(method, request)
//   return /* deserialized response */
// ```
//
// Callers that don't care about the response can just call Dispatch()
// and ignore the return value.
//
// DispatchSync will manage the channel for you, and also enforce a
// timeout on the transaction (default 60 seconds)
type AmqpRpcClient struct {
	serverQueue string
	clientQueue string
	channel     *amqp.Channel
	pending     map[string]chan []byte
	timeout     time.Duration
}

func NewAmqpRpcClient(clientQueue, serverQueue string, channel *amqp.Channel) (rpc *AmqpRpcClient, err error) {
	rpc = &AmqpRpcClient{
		serverQueue: serverQueue,
		clientQueue: clientQueue,
		channel:     channel,
		pending:     make(map[string]chan []byte),
		timeout:     10 * time.Second,
	}

	// Subscribe to the response queue and dispatch
	msgs, err := amqpSubscribe(rpc.channel, clientQueue)
	if err != nil {
		return
	}

	go func() {
		for msg := range msgs {
			// XXX-JWS: jws.Sign(privKey, body)
			corrID := msg.CorrelationId
			responseChan, present := rpc.pending[corrID]

			log.Printf(" [c<] received %s(%s) [%s]", msg.Type, b64enc(msg.Body), corrID)
			if present {
				responseChan <- msg.Body
				delete(rpc.pending, corrID)
			}
		}
	}()

	return
}

func (rpc *AmqpRpcClient) SetTimeout(ttl time.Duration) {
	rpc.timeout = ttl
}

func (rpc *AmqpRpcClient) Dispatch(method string, body []byte) chan []byte {
	// Create a channel on which to direct the response
	// At least in some cases, it's important that this channel
	// be buffered to avoid deadlock
	responseChan := make(chan []byte, 1)
	corrID := newToken()
	rpc.pending[corrID] = responseChan

	// Send the request
	log.Printf(" [c>] sending %s(%s) [%s]", method, b64enc(body), corrID)
	rpc.channel.Publish(
		AmqpExchange,
		rpc.serverQueue,
		AmqpMandatory,
		AmqpImmediate,
		amqp.Publishing{
			CorrelationId: corrID,
			ReplyTo:       rpc.clientQueue,
			Type:          method,
			Body:          body, // XXX-JWS: jws.Sign(privKey, body)
		})

	return responseChan
}

func (rpc *AmqpRpcClient) DispatchSync(method string, body []byte) (response []byte, err error) {
	select {
	case response = <-rpc.Dispatch(method, body):
		return
	case <-time.After(rpc.timeout):
		log.Printf(" [c!] AMQP-RPC timeout [%s]", method)
		err = errors.New("AMQP-RPC timeout")
		return
	}
	err = errors.New("Unknown error in SyncDispatch")
	return
}

func (rpc *AmqpRpcClient) SyncDispatchWithTimeout(method string, body []byte, ttl time.Duration) (response []byte, err error) {
	switch {

	}
	return
}
<commit_msg>Remove unreachable code in amqp-rpc.go.<commit_after>// Copyright 2014 ISRG.  All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package boulder

import (
	"errors"
	"github.com/streadway/amqp"
	"log"
	"time"
)

// TODO: AMQP-RPC messages should be wrapped in JWS.  To implement that,
// it will be necessary to make the following changes:
//
// * Constructors: Provision private key, acceptable public keys
// * After consume: Verify and discard JWS wrapper
// * Before publish: Add JWS wrapper

// General AMQP helpers

// XXX: I *think* these constants are appropriate.
// We will probably want to tweak these in the future.
const (
	AmqpExchange     = ""
	AmqpDurable      = false
	AmqpDeleteUnused = false
	AmqpExclusive    = false
	AmqpNoWait       = false
	AmqpNoLocal      = false
	AmqpAutoAck      = true
	AmqpMandatory    = false
	AmqpImmediate    = false
)

// A simplified way to get a channel for a given AMQP server
func amqpConnect(url string) (ch *amqp.Channel, err error) {
	conn, err := amqp.Dial(url)
	if err != nil {
		return
	}

	ch, err = conn.Channel()
	return
}

// A simplified way to declare and subscribe to an AMQP queue
func amqpSubscribe(ch *amqp.Channel, name string) (msgs <-chan amqp.Delivery, err error) {
	q, err := ch.QueueDeclare(
		name,
		AmqpDurable,
		AmqpDeleteUnused,
		AmqpExclusive,
		AmqpNoWait,
		nil)
	if err != nil {
		return
	}

	msgs, err = ch.Consume(
		q.Name,
		"",
		AmqpAutoAck,
		AmqpExclusive,
		AmqpNoLocal,
		AmqpNoWait,
		nil)
	return
}

// An AMQP-RPC Server listens on a specified queue within an AMQP channel.
// When messages arrive on that queue, it dispatches them based on type,
// and returns the response to the ReplyTo queue.
//
// To implement specific functionality, using code should use the Handle
// method to add specific actions.
type AmqpRpcServer struct {
	serverQueue   string
	channel       *amqp.Channel
	dispatchTable map[string]func([]byte) []byte
}

// Create a new AMQP-RPC server on the given queue and channel.
// Note that you must call Start() to actually start the server
// listening for requests.
func NewAmqpRpcServer(serverQueue string, channel *amqp.Channel) *AmqpRpcServer {
	return &AmqpRpcServer{
		serverQueue:   serverQueue,
		channel:       channel,
		dispatchTable: make(map[string]func([]byte) []byte),
	}
}

func (rpc *AmqpRpcServer) Handle(method string, handler func([]byte) []byte) {
	rpc.dispatchTable[method] = handler
}

// Starts the AMQP-RPC server running in a separate thread.
// There is currently no Stop() method.
func (rpc *AmqpRpcServer) Start() (err error) {
	msgs, err := amqpSubscribe(rpc.channel, rpc.serverQueue)
	if err != nil {
		return
	}

	go func() {
		for msg := range msgs {
			// XXX-JWS: jws.Verify(body)
			cb, present := rpc.dispatchTable[msg.Type]
			log.Printf(" [s<] received %s(%s) [%s]", msg.Type, b64enc(msg.Body), msg.CorrelationId)
			if present {
				response := cb(msg.Body)
				log.Printf(" [s>] sending %s(%s) [%s]", msg.Type, b64enc(response), msg.CorrelationId)
				rpc.channel.Publish(
					AmqpExchange,
					msg.ReplyTo,
					AmqpMandatory,
					AmqpImmediate,
					amqp.Publishing{
						CorrelationId: msg.CorrelationId,
						Type:          msg.Type,
						Body:          response, // XXX-JWS: jws.Sign(privKey, body)
					})
			}
		}
	}()
	return
}

// An AMQP-RPC client sends requests to a specific server queue,
// and uses a dedicated response queue for responses.
//
// To implement specific functionality, using code uses the Dispatch()
// method to send a method name and body, and get back a response. So
// you end up with wrapper methods of the form:
//
// ```
//   request = /* serialize request to []byte */
//   response = <-AmqpRpcClient.Dispatch(method, request)
//   return /* deserialized response */
// ```
//
// Callers that don't care about the response can just call Dispatch()
// and ignore the return value.
//
// DispatchSync will manage the channel for you, and also enforce a
// timeout on the transaction (default 60 seconds)
type AmqpRpcClient struct {
	serverQueue string
	clientQueue string
	channel     *amqp.Channel
	pending     map[string]chan []byte
	timeout     time.Duration
}

func NewAmqpRpcClient(clientQueue, serverQueue string, channel *amqp.Channel) (rpc *AmqpRpcClient, err error) {
	rpc = &AmqpRpcClient{
		serverQueue: serverQueue,
		clientQueue: clientQueue,
		channel:     channel,
		pending:     make(map[string]chan []byte),
		timeout:     10 * time.Second,
	}

	// Subscribe to the response queue and dispatch
	msgs, err := amqpSubscribe(rpc.channel, clientQueue)
	if err != nil {
		return
	}

	go func() {
		for msg := range msgs {
			// XXX-JWS: jws.Sign(privKey, body)
			corrID := msg.CorrelationId
			responseChan, present := rpc.pending[corrID]

			log.Printf(" [c<] received %s(%s) [%s]", msg.Type, b64enc(msg.Body), corrID)
			if present {
				responseChan <- msg.Body
				delete(rpc.pending, corrID)
			}
		}
	}()

	return
}

func (rpc *AmqpRpcClient) SetTimeout(ttl time.Duration) {
	rpc.timeout = ttl
}

func (rpc *AmqpRpcClient) Dispatch(method string, body []byte) chan []byte {
	// Create a channel on which to direct the response
	// At least in some cases, it's important that this channel
	// be buffered to avoid deadlock
	responseChan := make(chan []byte, 1)
	corrID := newToken()
	rpc.pending[corrID] = responseChan

	// Send the request
	log.Printf(" [c>] sending %s(%s) [%s]", method, b64enc(body), corrID)
	rpc.channel.Publish(
		AmqpExchange,
		rpc.serverQueue,
		AmqpMandatory,
		AmqpImmediate,
		amqp.Publishing{
			CorrelationId: corrID,
			ReplyTo:       rpc.clientQueue,
			Type:          method,
			Body:          body, // XXX-JWS: jws.Sign(privKey, body)
		})

	return responseChan
}

func (rpc *AmqpRpcClient) DispatchSync(method string, body []byte) (response []byte, err error) {
	select {
	case response = <-rpc.Dispatch(method, body):
		return
	case <-time.After(rpc.timeout):
		log.Printf(" [c!] AMQP-RPC timeout [%s]", method)
		err = errors.New("AMQP-RPC timeout")
		return
	}
}

func (rpc *AmqpRpcClient) SyncDispatchWithTimeout(method string, body []byte, ttl time.Duration) (response []byte, err error) {
	switch {

	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
	"strings"

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

// FluentdMetrics plugin for fluentd
type FluentdMetrics struct {
	Target          string
	Tempfile        string
	pluginType      string
	pluginIDPattern *regexp.Regexp

	plugins []FluentdPluginMetrics
}

// FluentdPluginMetrics metrics
type FluentdPluginMetrics struct {
	RetryCount            uint64 `json:"retry_count"`
	BufferQueueLength     uint64 `json:"buffer_queue_length"`
	BufferTotalQueuedSize uint64 `json:"buffer_total_queued_size"`
	OutputPlugin          bool   `json:"output_plugin"`
	Type                  string `json:"type"`
	PluginCategory        string `json:"plugin_category"`
	PluginID              string `json:"plugin_id"`
	normalizedPluginID    string
}

// FluentMonitorJSON monitor json
type FluentMonitorJSON struct {
	Plugins []FluentdPluginMetrics `json:"plugins"`
}

var normalizePluginIDRe = regexp.MustCompile(`[^-a-zA-Z0-9_]`)

func normalizePluginID(in string) string {
	return normalizePluginIDRe.ReplaceAllString(in, "_")
}

func (fpm FluentdPluginMetrics) getNormalizedPluginID() string {
	if fpm.normalizedPluginID == "" {
		fpm.normalizedPluginID = normalizePluginID(fpm.PluginID)
	}
	return fpm.normalizedPluginID
}

func (f *FluentdMetrics) parseStats(body []byte) (map[string]interface{}, error) {
	var j FluentMonitorJSON
	err := json.Unmarshal(body, &j)
	f.plugins = j.Plugins

	metrics := make(map[string]interface{})
	for _, p := range f.plugins {
		if f.nonTargetPlugin(p) {
			continue
		}
		pid := p.getNormalizedPluginID()
		metrics["fluentd.retry_count."+pid] = float64(p.RetryCount)
		metrics["fluentd.buffer_queue_length."+pid] = float64(p.BufferQueueLength)
		metrics["fluentd.buffer_total_queued_size."+pid] = float64(p.BufferTotalQueuedSize)
	}
	return metrics, err
}

func (f *FluentdMetrics) nonTargetPlugin(plugin FluentdPluginMetrics) bool {
	if plugin.PluginCategory != "output" {
		return true
	}
	if f.pluginType != "" && f.pluginType != plugin.Type {
		return true
	}
	if f.pluginIDPattern != nil && !f.pluginIDPattern.MatchString(plugin.PluginID) {
		return true
	}
	return false
}

// FetchMetrics interface for mackerelplugin
func (f FluentdMetrics) FetchMetrics() (map[string]interface{}, error) {
	resp, err := http.Get(f.Target)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	return f.parseStats(body)
}

// GraphDefinition interface for mackerelplugin
func (f FluentdMetrics) GraphDefinition() map[string](mp.Graphs) {
	return map[string](mp.Graphs){
		"fluentd.retry_count": mp.Graphs{
			Label: "Fluentd retry count",
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "*", Label: "%1", Diff: false},
			},
		},
		"fluentd.buffer_queue_length": mp.Graphs{
			Label: "Fluentd queue length",
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "*", Label: "%1", Diff: false},
			},
		},
		"fluentd.buffer_total_queued_size": mp.Graphs{
			Label: "Fluentd buffer total queued size",
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "*", Label: "%1", Diff: false},
			},
		},
	}
}

func main() {
	host := flag.String("host", "localhost", "fluentd monitor_agent port")
	port := flag.String("port", "24220", "fluentd monitor_agent port")
	pluginType := flag.String("plugin-type", "", "Gets the metric that matches this plugin type")
	pluginIDPatternString := flag.String("plugin-id-pattern", "", "Gets the metric that matches this plugin id pattern")
	tempFile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	var pluginIDPattern *regexp.Regexp
	var err error
	if *pluginIDPatternString != "" {
		pluginIDPattern, err = regexp.Compile(*pluginIDPatternString)
		if err != nil {
			fmt.Fprintf(os.Stderr, "failed to exec mackerel-plugin-fluentd: invalid plugin-id-pattern: %s\n", err)
			os.Exit(1)
		}
	}

	f := FluentdMetrics{
		Target:          fmt.Sprintf("http://%s:%s/api/plugins.json", *host, *port),
		Tempfile:        *tempFile,
		pluginType:      *pluginType,
		pluginIDPattern: pluginIDPattern,
	}

	helper := mp.NewMackerelPlugin(f)

	helper.Tempfile = *tempFile
	if *tempFile == "" {
		tempFileSuffix := []string{*host, *port}
		if *pluginType != "" {
			tempFileSuffix = append(tempFileSuffix, *pluginType)
		}
		if *pluginIDPatternString != "" {
			tempFileSuffix = append(tempFileSuffix, fmt.Sprintf("%x", md5.Sum([]byte(*pluginIDPatternString))))
		}
		helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-fluentd-%s", strings.Join(tempFileSuffix, "-"))
	}

	helper.Run()
}
<commit_msg>Fixed flag comment of mackerel-plugin-fluentd<commit_after>package main

import (
	"crypto/md5"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
	"strings"

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

// FluentdMetrics plugin for fluentd
type FluentdMetrics struct {
	Target          string
	Tempfile        string
	pluginType      string
	pluginIDPattern *regexp.Regexp

	plugins []FluentdPluginMetrics
}

// FluentdPluginMetrics metrics
type FluentdPluginMetrics struct {
	RetryCount            uint64 `json:"retry_count"`
	BufferQueueLength     uint64 `json:"buffer_queue_length"`
	BufferTotalQueuedSize uint64 `json:"buffer_total_queued_size"`
	OutputPlugin          bool   `json:"output_plugin"`
	Type                  string `json:"type"`
	PluginCategory        string `json:"plugin_category"`
	PluginID              string `json:"plugin_id"`
	normalizedPluginID    string
}

// FluentMonitorJSON monitor json
type FluentMonitorJSON struct {
	Plugins []FluentdPluginMetrics `json:"plugins"`
}

var normalizePluginIDRe = regexp.MustCompile(`[^-a-zA-Z0-9_]`)

func normalizePluginID(in string) string {
	return normalizePluginIDRe.ReplaceAllString(in, "_")
}

func (fpm FluentdPluginMetrics) getNormalizedPluginID() string {
	if fpm.normalizedPluginID == "" {
		fpm.normalizedPluginID = normalizePluginID(fpm.PluginID)
	}
	return fpm.normalizedPluginID
}

func (f *FluentdMetrics) parseStats(body []byte) (map[string]interface{}, error) {
	var j FluentMonitorJSON
	err := json.Unmarshal(body, &j)
	f.plugins = j.Plugins

	metrics := make(map[string]interface{})
	for _, p := range f.plugins {
		if f.nonTargetPlugin(p) {
			continue
		}
		pid := p.getNormalizedPluginID()
		metrics["fluentd.retry_count."+pid] = float64(p.RetryCount)
		metrics["fluentd.buffer_queue_length."+pid] = float64(p.BufferQueueLength)
		metrics["fluentd.buffer_total_queued_size."+pid] = float64(p.BufferTotalQueuedSize)
	}
	return metrics, err
}

func (f *FluentdMetrics) nonTargetPlugin(plugin FluentdPluginMetrics) bool {
	if plugin.PluginCategory != "output" {
		return true
	}
	if f.pluginType != "" && f.pluginType != plugin.Type {
		return true
	}
	if f.pluginIDPattern != nil && !f.pluginIDPattern.MatchString(plugin.PluginID) {
		return true
	}
	return false
}

// FetchMetrics interface for mackerelplugin
func (f FluentdMetrics) FetchMetrics() (map[string]interface{}, error) {
	resp, err := http.Get(f.Target)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	return f.parseStats(body)
}

// GraphDefinition interface for mackerelplugin
func (f FluentdMetrics) GraphDefinition() map[string](mp.Graphs) {
	return map[string](mp.Graphs){
		"fluentd.retry_count": mp.Graphs{
			Label: "Fluentd retry count",
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "*", Label: "%1", Diff: false},
			},
		},
		"fluentd.buffer_queue_length": mp.Graphs{
			Label: "Fluentd queue length",
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "*", Label: "%1", Diff: false},
			},
		},
		"fluentd.buffer_total_queued_size": mp.Graphs{
			Label: "Fluentd buffer total queued size",
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "*", Label: "%1", Diff: false},
			},
		},
	}
}

func main() {
	host := flag.String("host", "localhost", "fluentd monitor_agent host")
	port := flag.String("port", "24220", "fluentd monitor_agent port")
	pluginType := flag.String("plugin-type", "", "Gets the metric that matches this plugin type")
	pluginIDPatternString := flag.String("plugin-id-pattern", "", "Gets the metric that matches this plugin id pattern")
	tempFile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	var pluginIDPattern *regexp.Regexp
	var err error
	if *pluginIDPatternString != "" {
		pluginIDPattern, err = regexp.Compile(*pluginIDPatternString)
		if err != nil {
			fmt.Fprintf(os.Stderr, "failed to exec mackerel-plugin-fluentd: invalid plugin-id-pattern: %s\n", err)
			os.Exit(1)
		}
	}

	f := FluentdMetrics{
		Target:          fmt.Sprintf("http://%s:%s/api/plugins.json", *host, *port),
		Tempfile:        *tempFile,
		pluginType:      *pluginType,
		pluginIDPattern: pluginIDPattern,
	}

	helper := mp.NewMackerelPlugin(f)

	helper.Tempfile = *tempFile
	if *tempFile == "" {
		tempFileSuffix := []string{*host, *port}
		if *pluginType != "" {
			tempFileSuffix = append(tempFileSuffix, *pluginType)
		}
		if *pluginIDPatternString != "" {
			tempFileSuffix = append(tempFileSuffix, fmt.Sprintf("%x", md5.Sum([]byte(*pluginIDPatternString))))
		}
		helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-fluentd-%s", strings.Join(tempFileSuffix, "-"))
	}

	helper.Run()
}
<|endoftext|>


<commit_before>package mpredis

import (
	"flag"
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/fzzy/radix/redis"
	mp "github.com/mackerelio/go-mackerel-plugin-helper"
	"github.com/mackerelio/golib/logging"
)

var logger = logging.GetLogger("metrics.plugin.redis")

// RedisPlugin mackerel plugin for Redis
type RedisPlugin struct {
	Host     string
	Port     string
	Password string
	Socket   string
	Prefix   string
	Timeout  int
	Tempfile string
}

func authenticateByPassword(c *redis.Client, password string) error {
	if r := c.Cmd("AUTH", password); r.Err != nil {
		logger.Errorf("Failed to authenticate. %s", r.Err)
		return r.Err
	}
	return nil
}

func fetchPercentageOfMemory(c *redis.Client, stat map[string]interface{}) error {
	r := c.Cmd("CONFIG", "GET", "maxmemory")
	if r.Err != nil {
		logger.Errorf("Failed to run `CONFIG GET maxmemory` command. %s", r.Err)
		return r.Err
	}

	res, err := r.Hash()
	if err != nil {
		logger.Errorf("Failed to fetch maxmemory. %s", err)
		return err
	}

	maxsize, err := strconv.ParseFloat(res["maxmemory"], 64)
	if err != nil {
		logger.Errorf("Failed to parse maxmemory. %s", err)
		return err
	}

	if maxsize == 0.0 {
		stat["percentage_of_memory"] = 0.0
	} else {
		stat["percentage_of_memory"] = 100.0 * stat["used_memory"].(float64) / maxsize
	}

	return nil
}

func fetchPercentageOfClients(c *redis.Client, stat map[string]interface{}) error {
	r := c.Cmd("CONFIG", "GET", "maxclients")
	if r.Err != nil {
		logger.Errorf("Failed to run `CONFIG GET maxclients` command. %s", r.Err)
		return r.Err
	}

	res, err := r.Hash()
	if err != nil {
		logger.Errorf("Failed to fetch maxclients. %s", err)
		return err
	}

	maxsize, err := strconv.ParseFloat(res["maxclients"], 64)
	if err != nil {
		logger.Errorf("Failed to parse maxclients. %s", err)
		return err
	}

	stat["percentage_of_clients"] = 100.0 * stat["connected_clients"].(float64) / maxsize

	return nil
}

func calculateCapacity(c *redis.Client, stat map[string]interface{}) error {
	if err := fetchPercentageOfMemory(c, stat); err != nil {
		return err
	}
	return fetchPercentageOfClients(c, stat)
}

// MetricKeyPrefix interface for PluginWithPrefix
func (m RedisPlugin) MetricKeyPrefix() string {
	if m.Prefix == "" {
		m.Prefix = "redis"
	}
	return m.Prefix
}

// FetchMetrics interface for mackerelplugin
func (m RedisPlugin) FetchMetrics() (map[string]interface{}, error) {
	network := "tcp"
	target := fmt.Sprintf("%s:%s", m.Host, m.Port)
	if m.Socket != "" {
		target = m.Socket
		network = "unix"
	}
	c, err := redis.DialTimeout(network, target, time.Duration(m.Timeout)*time.Second)
	if err != nil {
		logger.Errorf("Failed to connect redis. %s", err)
		return nil, err
	}
	defer c.Close()

	if m.Password != "" {
		if err = authenticateByPassword(c, m.Password); err != nil {
			return nil, err
		}
	}

	r := c.Cmd("info")
	if r.Err != nil {
		logger.Errorf("Failed to run info command. %s", r.Err)
		return nil, r.Err
	}
	str, err := r.Str()
	if err != nil {
		logger.Errorf("Failed to fetch information. %s", err)
		return nil, err
	}

	stat := make(map[string]interface{})

	keysStat := 0.0
	expiresStat := 0.0
	var slaves []string

	for _, line := range strings.Split(str, "\r\n") {
		if line == "" {
			continue
		}
		if re, _ := regexp.MatchString("^#", line); re {
			continue
		}

		record := strings.SplitN(line, ":", 2)
		if len(record) < 2 {
			continue
		}
		key, value := record[0], record[1]

		if re, _ := regexp.MatchString("^slave", key); re {
			slaves = append(slaves, key)
			kv := strings.SplitN(value, ",", 5)
			_, _, _, offset, lag := kv[0], kv[1], kv[2], kv[3], kv[4]
			offsetKv := strings.SplitN(offset, "=", 2)
			offsetFv, err := strconv.ParseFloat(offsetKv[1], 64)
			if err != nil {
				logger.Warningf("Failed to parse slaves. %s", err)
			}
			stat[fmt.Sprintf("%s_offset_delay", key)] = offsetFv
			lagKv := strings.SplitN(lag, "=", 2)
			lagFv, err := strconv.ParseFloat(lagKv[1], 64)
			if err != nil {
				logger.Warningf("Failed to parse slaves. %s", err)
			}
			stat[fmt.Sprintf("%s_lag", key)] = lagFv
			continue
		}

		if re, _ := regexp.MatchString("^db", key); re {
			kv := strings.SplitN(value, ",", 3)
			keys, expires := kv[0], kv[1]

			keysKv := strings.SplitN(keys, "=", 2)
			keysFv, err := strconv.ParseFloat(keysKv[1], 64)
			if err != nil {
				logger.Warningf("Failed to parse db keys. %s", err)
			}
			keysStat += keysFv

			expiresKv := strings.SplitN(expires, "=", 2)
			expiresFv, err := strconv.ParseFloat(expiresKv[1], 64)
			if err != nil {
				logger.Warningf("Failed to parse db expires. %s", err)
			}
			expiresStat += expiresFv

			continue
		}

		stat[key], err = strconv.ParseFloat(value, 64)
		if err != nil {
			continue
		}
	}

	stat["keys"] = keysStat
	stat["expires"] = expiresStat

	if _, ok := stat["keys"]; !ok {
		stat["keys"] = 0
	}
	if _, ok := stat["expires"]; !ok {
		stat["expires"] = 0
	}

	if _, ok := stat["expired_keys"]; ok {
		stat["expired"] = stat["expired_keys"]
	} else {
		stat["expired"] = 0.0
	}

	if err := calculateCapacity(c, stat); err != nil {
		logger.Infof("Failed to calculate capacity. (The cause may be that AWS Elasticache Redis has no `CONFIG` command.) Skip these metrics. %s", err)
	}

	for _, slave := range slaves {
		stat[fmt.Sprintf("%s_offset_delay", slave)] = stat["master_repl_offset"].(float64) - stat[fmt.Sprintf("%s_offset_delay", slave)].(float64)
	}

	return stat, nil
}

// GraphDefinition interface for mackerelplugin
func (m RedisPlugin) GraphDefinition() map[string]mp.Graphs {
	labelPrefix := strings.Title(m.Prefix)

	var graphdef = map[string]mp.Graphs{
		"queries": {
			Label: (labelPrefix + " Queries"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "total_commands_processed", Label: "Queries", Diff: true},
			},
		},
		"connections": {
			Label: (labelPrefix + " Connections"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "total_connections_received", Label: "Connections", Diff: true, Stacked: true},
				{Name: "rejected_connections", Label: "Rejected Connections", Diff: true, Stacked: true},
			},
		},
		"clients": {
			Label: (labelPrefix + " Clients"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "connected_clients", Label: "Connected Clients", Diff: false, Stacked: true},
				{Name: "blocked_clients", Label: "Blocked Clients", Diff: false, Stacked: true},
				{Name: "connected_slaves", Label: "Connected Slaves", Diff: false, Stacked: true},
			},
		},
		"keys": {
			Label: (labelPrefix + " Keys"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "keys", Label: "Keys", Diff: false},
				{Name: "expires", Label: "Keys with expiration", Diff: false},
				{Name: "expired", Label: "Expired Keys", Diff: false},
			},
		},
		"keyspace": {
			Label: (labelPrefix + " Keyspace"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "keyspace_hits", Label: "Keyspace Hits", Diff: true},
				{Name: "keyspace_misses", Label: "Keyspace Missed", Diff: true},
			},
		},
		"memory": {
			Label: (labelPrefix + " Memory"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "used_memory", Label: "Used Memory", Diff: false},
				{Name: "used_memory_rss", Label: "Used Memory RSS", Diff: false},
				{Name: "used_memory_peak", Label: "Used Memory Peak", Diff: false},
				{Name: "used_memory_lua", Label: "Used Memory Lua engine", Diff: false},
			},
		},
		"capacity": {
			Label: (labelPrefix + " Capacity"),
			Unit:  "percentage",
			Metrics: []mp.Metrics{
				{Name: "percentage_of_memory", Label: "Percentage of memory", Diff: false},
				{Name: "percentage_of_clients", Label: "Percentage of clients", Diff: false},
			},
		},
	}

	network := "tcp"
	target := fmt.Sprintf("%s:%s", m.Host, m.Port)
	if m.Socket != "" {
		target = m.Socket
		network = "unix"
	}

	c, err := redis.DialTimeout(network, target, time.Duration(m.Timeout)*time.Second)
	if err != nil {
		logger.Errorf("Failed to connect redis. %s", err)
		return nil
	}
	defer c.Close()

	if m.Password != "" {
		if err = authenticateByPassword(c, m.Password); err != nil {
			return nil
		}
	}

	r := c.Cmd("info")
	if r.Err != nil {
		logger.Errorf("Failed to run info command. %s", r.Err)
		return nil
	}
	str, err := r.Str()
	if err != nil {
		logger.Errorf("Failed to fetch information. %s", err)
		return nil
	}

	var metricsLag []mp.Metrics
	var metricsOffsetDelay []mp.Metrics
	for _, line := range strings.Split(str, "\r\n") {
		if line == "" {
			continue
		}

		record := strings.SplitN(line, ":", 2)
		if len(record) < 2 {
			continue
		}
		key, _ := record[0], record[1]

		if re, _ := regexp.MatchString("^slave\\d+", key); re {
			metricsLag = append(metricsLag, mp.Metrics{Name: fmt.Sprintf("%s_lag", key), Label: fmt.Sprintf("Replication lag to %s", key), Diff: false})
			metricsOffsetDelay = append(metricsOffsetDelay, mp.Metrics{Name: fmt.Sprintf("%s_offset_delay", key), Label: fmt.Sprintf("Offset delay to %s", key), Diff: false})
		}
	}

	graphdef["lag"] = mp.Graphs{
		Label:   (labelPrefix + " Slave Lag"),
		Unit:    "seconds",
		Metrics: metricsLag,
	}
	graphdef["offset_delay"] = mp.Graphs{
		Label:   (labelPrefix + " Slave Offset Delay"),
		Unit:    "count",
		Metrics: metricsOffsetDelay,
	}

	return graphdef
}

// Do the plugin
func Do() {
	optHost := flag.String("host", "localhost", "Hostname")
	optPort := flag.String("port", "6379", "Port")
	optPassowrd := flag.String("password", "", "Password")
	optSocket := flag.String("socket", "", "Server socket (overrides host and port)")
	optPrefix := flag.String("metric-key-prefix", "redis", "Metric key prefix")
	optTimeout := flag.Int("timeout", 5, "Timeout")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	redis := RedisPlugin{
		Timeout: *optTimeout,
		Prefix:  *optPrefix,
	}
	if *optSocket != "" {
		redis.Socket = *optSocket
	} else {
		redis.Host = *optHost
		redis.Port = *optPort
		redis.Password = *optPassowrd
	}
	helper := mp.NewMackerelPlugin(redis)
	helper.Tempfile = *optTempfile

	helper.Run()
}
<commit_msg>Fix test<commit_after>package mpredis

import (
	"flag"
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/fzzy/radix/redis"
	mp "github.com/mackerelio/go-mackerel-plugin-helper"
	"github.com/mackerelio/golib/logging"
)

var logger = logging.GetLogger("metrics.plugin.redis")

// RedisPlugin mackerel plugin for Redis
type RedisPlugin struct {
	Host     string
	Port     string
	Password string
	Socket   string
	Prefix   string
	Timeout  int
	Tempfile string
}

func authenticateByPassword(c *redis.Client, password string) error {
	if r := c.Cmd("AUTH", password); r.Err != nil {
		logger.Errorf("Failed to authenticate. %s", r.Err)
		return r.Err
	}
	return nil
}

func fetchPercentageOfMemory(c *redis.Client, stat map[string]interface{}) error {
	r := c.Cmd("CONFIG", "GET", "maxmemory")
	if r.Err != nil {
		logger.Errorf("Failed to run `CONFIG GET maxmemory` command. %s", r.Err)
		return r.Err
	}

	res, err := r.Hash()
	if err != nil {
		logger.Errorf("Failed to fetch maxmemory. %s", err)
		return err
	}

	maxsize, err := strconv.ParseFloat(res["maxmemory"], 64)
	if err != nil {
		logger.Errorf("Failed to parse maxmemory. %s", err)
		return err
	}

	if maxsize == 0.0 {
		stat["percentage_of_memory"] = 0.0
	} else {
		stat["percentage_of_memory"] = 100.0 * stat["used_memory"].(float64) / maxsize
	}

	return nil
}

func fetchPercentageOfClients(c *redis.Client, stat map[string]interface{}) error {
	r := c.Cmd("CONFIG", "GET", "maxclients")
	if r.Err != nil {
		logger.Errorf("Failed to run `CONFIG GET maxclients` command. %s", r.Err)
		return r.Err
	}

	res, err := r.Hash()
	if err != nil {
		logger.Errorf("Failed to fetch maxclients. %s", err)
		return err
	}

	maxsize, err := strconv.ParseFloat(res["maxclients"], 64)
	if err != nil {
		logger.Errorf("Failed to parse maxclients. %s", err)
		return err
	}

	stat["percentage_of_clients"] = 100.0 * stat["connected_clients"].(float64) / maxsize

	return nil
}

func calculateCapacity(c *redis.Client, stat map[string]interface{}) error {
	if err := fetchPercentageOfMemory(c, stat); err != nil {
		return err
	}
	return fetchPercentageOfClients(c, stat)
}

// MetricKeyPrefix interface for PluginWithPrefix
func (m RedisPlugin) MetricKeyPrefix() string {
	if m.Prefix == "" {
		m.Prefix = "redis"
	}
	return m.Prefix
}

// FetchMetrics interface for mackerelplugin
func (m RedisPlugin) FetchMetrics() (map[string]interface{}, error) {
	network := "tcp"
	target := fmt.Sprintf("%s:%s", m.Host, m.Port)
	if m.Socket != "" {
		target = m.Socket
		network = "unix"
	}
	c, err := redis.DialTimeout(network, target, time.Duration(m.Timeout)*time.Second)
	if err != nil {
		logger.Errorf("Failed to connect redis. %s", err)
		return nil, err
	}
	defer c.Close()

	if m.Password != "" {
		if err = authenticateByPassword(c, m.Password); err != nil {
			return nil, err
		}
	}

	r := c.Cmd("info")
	if r.Err != nil {
		logger.Errorf("Failed to run info command. %s", r.Err)
		return nil, r.Err
	}
	str, err := r.Str()
	if err != nil {
		logger.Errorf("Failed to fetch information. %s", err)
		return nil, err
	}

	stat := make(map[string]interface{})

	keysStat := 0.0
	expiresStat := 0.0
	var slaves []string

	for _, line := range strings.Split(str, "\r\n") {
		if line == "" {
			continue
		}
		if re, _ := regexp.MatchString("^#", line); re {
			continue
		}

		record := strings.SplitN(line, ":", 2)
		if len(record) < 2 {
			continue
		}
		key, value := record[0], record[1]

		if re, _ := regexp.MatchString("^slave\\d+", key); re {
			slaves = append(slaves, key)
			kv := strings.Split(value, ",")
			var offset, lag string
			if len(kv) == 5 {
				_, _, _, offset, lag = kv[0], kv[1], kv[2], kv[3], kv[4]
				lagKv := strings.SplitN(lag, "=", 2)
				lagFv, err := strconv.ParseFloat(lagKv[1], 64)
				if err != nil {
					logger.Warningf("Failed to parse slaves. %s", err)
				}
				stat[fmt.Sprintf("%s_lag", key)] = lagFv
			} else {
				_, _, _, offset = kv[0], kv[1], kv[2], kv[3]
			}
			offsetKv := strings.SplitN(offset, "=", 2)
			offsetFv, err := strconv.ParseFloat(offsetKv[1], 64)
			if err != nil {
				logger.Warningf("Failed to parse slaves. %s", err)
			}
			stat[fmt.Sprintf("%s_offset_delay", key)] = offsetFv
			continue
		}

		if re, _ := regexp.MatchString("^db", key); re {
			kv := strings.SplitN(value, ",", 3)
			keys, expires := kv[0], kv[1]

			keysKv := strings.SplitN(keys, "=", 2)
			keysFv, err := strconv.ParseFloat(keysKv[1], 64)
			if err != nil {
				logger.Warningf("Failed to parse db keys. %s", err)
			}
			keysStat += keysFv

			expiresKv := strings.SplitN(expires, "=", 2)
			expiresFv, err := strconv.ParseFloat(expiresKv[1], 64)
			if err != nil {
				logger.Warningf("Failed to parse db expires. %s", err)
			}
			expiresStat += expiresFv

			continue
		}

		stat[key], err = strconv.ParseFloat(value, 64)
		if err != nil {
			continue
		}
	}

	stat["keys"] = keysStat
	stat["expires"] = expiresStat

	if _, ok := stat["keys"]; !ok {
		stat["keys"] = 0
	}
	if _, ok := stat["expires"]; !ok {
		stat["expires"] = 0
	}

	if _, ok := stat["expired_keys"]; ok {
		stat["expired"] = stat["expired_keys"]
	} else {
		stat["expired"] = 0.0
	}

	if err := calculateCapacity(c, stat); err != nil {
		logger.Infof("Failed to calculate capacity. (The cause may be that AWS Elasticache Redis has no `CONFIG` command.) Skip these metrics. %s", err)
	}

	for _, slave := range slaves {
		stat[fmt.Sprintf("%s_offset_delay", slave)] = stat["master_repl_offset"].(float64) - stat[fmt.Sprintf("%s_offset_delay", slave)].(float64)
	}

	return stat, nil
}

// GraphDefinition interface for mackerelplugin
func (m RedisPlugin) GraphDefinition() map[string]mp.Graphs {
	labelPrefix := strings.Title(m.Prefix)

	var graphdef = map[string]mp.Graphs{
		"queries": {
			Label: (labelPrefix + " Queries"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "total_commands_processed", Label: "Queries", Diff: true},
			},
		},
		"connections": {
			Label: (labelPrefix + " Connections"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "total_connections_received", Label: "Connections", Diff: true, Stacked: true},
				{Name: "rejected_connections", Label: "Rejected Connections", Diff: true, Stacked: true},
			},
		},
		"clients": {
			Label: (labelPrefix + " Clients"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "connected_clients", Label: "Connected Clients", Diff: false, Stacked: true},
				{Name: "blocked_clients", Label: "Blocked Clients", Diff: false, Stacked: true},
				{Name: "connected_slaves", Label: "Connected Slaves", Diff: false, Stacked: true},
			},
		},
		"keys": {
			Label: (labelPrefix + " Keys"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "keys", Label: "Keys", Diff: false},
				{Name: "expires", Label: "Keys with expiration", Diff: false},
				{Name: "expired", Label: "Expired Keys", Diff: false},
			},
		},
		"keyspace": {
			Label: (labelPrefix + " Keyspace"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "keyspace_hits", Label: "Keyspace Hits", Diff: true},
				{Name: "keyspace_misses", Label: "Keyspace Missed", Diff: true},
			},
		},
		"memory": {
			Label: (labelPrefix + " Memory"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "used_memory", Label: "Used Memory", Diff: false},
				{Name: "used_memory_rss", Label: "Used Memory RSS", Diff: false},
				{Name: "used_memory_peak", Label: "Used Memory Peak", Diff: false},
				{Name: "used_memory_lua", Label: "Used Memory Lua engine", Diff: false},
			},
		},
		"capacity": {
			Label: (labelPrefix + " Capacity"),
			Unit:  "percentage",
			Metrics: []mp.Metrics{
				{Name: "percentage_of_memory", Label: "Percentage of memory", Diff: false},
				{Name: "percentage_of_clients", Label: "Percentage of clients", Diff: false},
			},
		},
	}

	network := "tcp"
	target := fmt.Sprintf("%s:%s", m.Host, m.Port)
	if m.Socket != "" {
		target = m.Socket
		network = "unix"
	}

	c, err := redis.DialTimeout(network, target, time.Duration(m.Timeout)*time.Second)
	if err != nil {
		logger.Errorf("Failed to connect redis. %s", err)
		return nil
	}
	defer c.Close()

	if m.Password != "" {
		if err = authenticateByPassword(c, m.Password); err != nil {
			return nil
		}
	}

	r := c.Cmd("info")
	if r.Err != nil {
		logger.Errorf("Failed to run info command. %s", r.Err)
		return nil
	}
	str, err := r.Str()
	if err != nil {
		logger.Errorf("Failed to fetch information. %s", err)
		return nil
	}

	var metricsLag []mp.Metrics
	var metricsOffsetDelay []mp.Metrics
	for _, line := range strings.Split(str, "\r\n") {
		if line == "" {
			continue
		}

		record := strings.SplitN(line, ":", 2)
		if len(record) < 2 {
			continue
		}
		key, _ := record[0], record[1]

		if re, _ := regexp.MatchString("^slave\\d+", key); re {
			metricsLag = append(metricsLag, mp.Metrics{Name: fmt.Sprintf("%s_lag", key), Label: fmt.Sprintf("Replication lag to %s", key), Diff: false})
			metricsOffsetDelay = append(metricsOffsetDelay, mp.Metrics{Name: fmt.Sprintf("%s_offset_delay", key), Label: fmt.Sprintf("Offset delay to %s", key), Diff: false})
		}
	}

	if len(metricsLag) > 0 {
		graphdef["lag"] = mp.Graphs{
			Label:   (labelPrefix + " Slave Lag"),
			Unit:    "seconds",
			Metrics: metricsLag,
		}
	}
	if len(metricsOffsetDelay) > 0 {
		graphdef["offset_delay"] = mp.Graphs{
			Label:   (labelPrefix + " Slave Offset Delay"),
			Unit:    "count",
			Metrics: metricsOffsetDelay,
		}
	}

	return graphdef
}

// Do the plugin
func Do() {
	optHost := flag.String("host", "localhost", "Hostname")
	optPort := flag.String("port", "6379", "Port")
	optPassowrd := flag.String("password", "", "Password")
	optSocket := flag.String("socket", "", "Server socket (overrides host and port)")
	optPrefix := flag.String("metric-key-prefix", "redis", "Metric key prefix")
	optTimeout := flag.Int("timeout", 5, "Timeout")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	redis := RedisPlugin{
		Timeout: *optTimeout,
		Prefix:  *optPrefix,
	}
	if *optSocket != "" {
		redis.Socket = *optSocket
	} else {
		redis.Host = *optHost
		redis.Port = *optPort
		redis.Password = *optPassowrd
	}
	helper := mp.NewMackerelPlugin(redis)
	helper.Tempfile = *optTempfile

	helper.Run()
}
<|endoftext|>


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

package options

import (
	"flag"

	"k8s.io/api/core/v1"
)

// ServerOption is the main context object for the controller manager.
type ServerOption struct {
	Kubeconfig           string
	MasterURL            string
	Threadiness          int
	PrintVersion         bool
	JSONLogFormat        bool
	EnableGangScheduling bool
	Namespace            string
}

// NewServerOption creates a new CMServer with a default config.
func NewServerOption() *ServerOption {
	s := ServerOption{}
	return &s
}

// AddFlags adds flags for a specific CMServer to the specified FlagSet.
func (s *ServerOption) AddFlags(fs *flag.FlagSet) {
	fs.StringVar(&s.MasterURL, "master", "",
		`The url of the Kubernetes API server,
		 will overrides any value in kubeconfig, only required if out-of-cluster.`)

	fs.StringVar(&s.Namespace, "namespace", v1.NamespaceAll,
		`The namespace to monitor tfjobs. If unset, it monitors all namespaces cluster-wide.
                If set, it only monitors tfjobs in the given namespace.`)

	fs.IntVar(&s.Threadiness, "threadiness", 1,
		`How many threads to process the main logic`)

	fs.BoolVar(&s.PrintVersion, "version", false, "Show version and quit")

	fs.BoolVar(&s.JSONLogFormat, "json-log-format", true,
		"Set true to use json style log format. Set false to use plaintext style log format")
	fs.BoolVar(&s.EnableGangScheduling, "enable-gang-scheduling", false, "Set true to enable gang scheduling by kube-arbitrator.")
}
<commit_msg>Replace kube-arbitrator with kube-batch<commit_after>// Copyright 2018 The Kubeflow Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package options

import (
	"flag"

	"k8s.io/api/core/v1"
)

// ServerOption is the main context object for the controller manager.
type ServerOption struct {
	Kubeconfig           string
	MasterURL            string
	Threadiness          int
	PrintVersion         bool
	JSONLogFormat        bool
	EnableGangScheduling bool
	Namespace            string
}

// NewServerOption creates a new CMServer with a default config.
func NewServerOption() *ServerOption {
	s := ServerOption{}
	return &s
}

// AddFlags adds flags for a specific CMServer to the specified FlagSet.
func (s *ServerOption) AddFlags(fs *flag.FlagSet) {
	fs.StringVar(&s.MasterURL, "master", "",
		`The url of the Kubernetes API server,
		 will overrides any value in kubeconfig, only required if out-of-cluster.`)

	fs.StringVar(&s.Namespace, "namespace", v1.NamespaceAll,
		`The namespace to monitor tfjobs. If unset, it monitors all namespaces cluster-wide.
                If set, it only monitors tfjobs in the given namespace.`)

	fs.IntVar(&s.Threadiness, "threadiness", 1,
		`How many threads to process the main logic`)

	fs.BoolVar(&s.PrintVersion, "version", false, "Show version and quit")

	fs.BoolVar(&s.JSONLogFormat, "json-log-format", true,
		"Set true to use json style log format. Set false to use plaintext style log format")
	fs.BoolVar(&s.EnableGangScheduling, "enable-gang-scheduling", false, "Set true to enable gang scheduling by kube-batch.")
}
<|endoftext|>


<commit_before>package check_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"time"

	"terraform-resource/check"
	"terraform-resource/models"
	"terraform-resource/storage"
	"terraform-resource/test/helpers"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Check with Migrated From Storage", func() {

	var (
		checkInput             models.InRequest
		bucket                 string
		backendEnvName         string
		storageEnvName         string
		pathToBackendStatefile string
		pathToStorageStatefile string
		awsVerifier            *helpers.AWSVerifier
		workingDir             string
	)

	BeforeEach(func() {
		accessKey := os.Getenv("AWS_ACCESS_KEY")
		Expect(accessKey).ToNot(BeEmpty(), "AWS_ACCESS_KEY must be set")

		secretKey := os.Getenv("AWS_SECRET_KEY")
		Expect(secretKey).ToNot(BeEmpty(), "AWS_SECRET_KEY must be set")

		bucket = os.Getenv("AWS_BUCKET")
		Expect(bucket).ToNot(BeEmpty(), "AWS_BUCKET must be set")

		bucketPath := os.Getenv("AWS_BUCKET_SUBFOLDER")
		Expect(bucketPath).ToNot(BeEmpty(), "AWS_BUCKET_SUBFOLDER must be set")
		bucketPath = path.Join(bucketPath, helpers.RandomString("check-storage-test"))

		region := os.Getenv("AWS_REGION") // optional
		if region == "" {
			region = "us-east-1"
		}

		awsVerifier = helpers.NewAWSVerifier(
			accessKey,
			secretKey,
			region,
			"",
		)

		var err error
		workingDir, err = ioutil.TempDir(os.TempDir(), "terraform-resource-check-backend-test")
		Expect(err).ToNot(HaveOccurred())

		// ensure relative paths resolve correctly
		err = os.Chdir(workingDir)
		Expect(err).ToNot(HaveOccurred())

		fixturesDir := path.Join(helpers.ProjectRoot(), "fixtures")
		err = exec.Command("cp", "-r", fixturesDir, workingDir).Run()
		Expect(err).ToNot(HaveOccurred())

		workspacePath := helpers.RandomString("check-backend-test")

		backendEnvName = "s3-test-fixture-backend"
		pathToBackendStatefile = path.Join(workspacePath, backendEnvName, "terraform.tfstate")
		storageEnvName = "s3-test-fixture-storage"
		pathToStorageStatefile = path.Join(bucketPath, fmt.Sprintf("%s.tfstate", storageEnvName))

		checkInput = models.InRequest{
			Source: models.Source{
				Terraform: models.Terraform{
					BackendType: "s3",
					BackendConfig: map[string]interface{}{
						"bucket":               bucket,
						"key":                  "terraform.tfstate",
						"access_key":           accessKey,
						"secret_key":           secretKey,
						"region":               region,
						"workspace_key_prefix": workspacePath,
					},
				},
				MigratedFromStorage: storage.Model{
					Bucket:          bucket,
					BucketPath:      bucketPath,
					AccessKeyID:     accessKey,
					SecretAccessKey: secretKey,
					RegionName:      region,
				},
			},
		}
	})

	AfterEach(func() {
		_ = os.RemoveAll(workingDir)
		awsVerifier.DeleteObjectFromS3(bucket, pathToBackendStatefile)
		awsVerifier.DeleteObjectFromS3(bucket, pathToStorageStatefile)
	})

	Context("when both backend and legacy storage are empty", func() {
		It("returns an empty version list", func() {
			runner := check.Runner{}
			resp, err := runner.Run(checkInput)
			Expect(err).ToNot(HaveOccurred())

			expectedOutput := []models.Version{}
			Expect(resp).To(Equal(expectedOutput))
		})
	})

	Context("when both Backend and Legacy Storage contains state files", func() {
		BeforeEach(func() {
			// TODO: can we need current and previous fixtures?
			backendFixture, err := os.Open(helpers.FileLocation("fixtures/s3-backend/terraform-current.tfstate"))
			Expect(err).ToNot(HaveOccurred())
			defer backendFixture.Close()
			awsVerifier.UploadObjectToS3(bucket, pathToBackendStatefile, backendFixture)

			storageFixture, err := os.Open(helpers.FileLocation("fixtures/s3-storage/terraform-current.tfstate"))
			Expect(err).ToNot(HaveOccurred())
			defer storageFixture.Close()
			awsVerifier.UploadObjectToS3(bucket, pathToStorageStatefile, storageFixture)
			time.Sleep(1 * time.Second)
		})

		Context("when watching a single env with `source.env_name`", func() {
			BeforeEach(func() {
				checkInput.Source.EnvName = backendEnvName
			})

			It("returns the latest version from the backend when no version is given", func() {
				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{
					models.Version{
						Serial:  "1",
						EnvName: backendEnvName,
					},
				}
				Expect(resp).To(Equal(expectOutput))
			})

			It("returns the latest version when the given version matches latest version", func() {
				checkInput.Version = models.Version{
					Serial:  "1",
					EnvName: backendEnvName,
				}

				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{
					models.Version{
						Serial:  "1",
						EnvName: backendEnvName,
					},
				}
				Expect(resp).To(Equal(expectOutput))
			})
		})

		Context("when watching a multiple envs with `source.env_name` unset", func() {
			BeforeEach(func() {
				checkInput.Source.EnvName = ""
			})

			It("returns an empty version list when no version is given", func() {
				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{}
				Expect(resp).To(Equal(expectOutput))
			})

			It("returns the latest version when the given backend version matches latest version", func() {
				checkInput.Version = models.Version{
					Serial:  "1",
					EnvName: backendEnvName,
				}

				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{
					models.Version{
						Serial:  "1",
						EnvName: backendEnvName,
					},
				}
				Expect(resp).To(Equal(expectOutput))
			})

			It("returns the latest version when the given storage version matches latest version", func() {
				lastModified := awsVerifier.GetLastModifiedFromS3(bucket, pathToStorageStatefile)

				checkInput.Version = models.Version{
					LastModified: lastModified,
					EnvName:      storageEnvName,
				}

				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{
					models.Version{
						LastModified: lastModified,
						EnvName:      storageEnvName,
					},
				}
				Expect(resp).To(Equal(expectOutput))
			})
		})
	})

	Context("when only Backend contains state files", func() {
		BeforeEach(func() {
			// TODO: can we need current and previous fixtures?
			currFixture, err := os.Open(helpers.FileLocation("fixtures/s3-backend/terraform-current.tfstate"))
			Expect(err).ToNot(HaveOccurred())
			defer currFixture.Close()
			awsVerifier.UploadObjectToS3(bucket, pathToBackendStatefile, currFixture)
			time.Sleep(1 * time.Second)
		})

		Context("when watching a single env with `source.env_name`", func() {
			BeforeEach(func() {
				checkInput.Source.EnvName = backendEnvName
			})

			It("returns the latest version from the backend when no version is given", func() {
				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{
					models.Version{
						Serial:  "1",
						EnvName: backendEnvName,
					},
				}
				Expect(resp).To(Equal(expectOutput))
			})

			It("returns the latest version when the given version matches latest version", func() {
				checkInput.Version = models.Version{
					Serial:  "1",
					EnvName: backendEnvName,
				}

				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{
					models.Version{
						Serial:  "1",
						EnvName: backendEnvName,
					},
				}
				Expect(resp).To(Equal(expectOutput))
			})
		})

		Context("when watching a multiple envs with `source.env_name` unset", func() {
			BeforeEach(func() {
				checkInput.Source.EnvName = ""
			})

			It("returns an empty version list when no version is given", func() {
				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{}
				Expect(resp).To(Equal(expectOutput))
			})

			It("returns the latest version when the given version matches latest version", func() {
				checkInput.Version = models.Version{
					Serial:  "1",
					EnvName: backendEnvName,
				}

				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{
					models.Version{
						Serial:  "1",
						EnvName: backendEnvName,
					},
				}
				Expect(resp).To(Equal(expectOutput))
			})
		})
	})

	Context("when only Legacy Storage contains state files", func() {
		BeforeEach(func() {
			// TODO: can we need current and previous fixtures?
			currFixture, err := os.Open(helpers.FileLocation("fixtures/s3-storage/terraform-current.tfstate"))
			Expect(err).ToNot(HaveOccurred())
			defer currFixture.Close()
			awsVerifier.UploadObjectToS3(bucket, pathToStorageStatefile, currFixture)
			time.Sleep(1 * time.Second)
		})

		Context("when watching a single env with `source.env_name`", func() {
			BeforeEach(func() {
				checkInput.Source.EnvName = storageEnvName
			})

			It("returns the latest version from the backend when no version is given", func() {
				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				lastModified := awsVerifier.GetLastModifiedFromS3(bucket, pathToStorageStatefile)

				expectOutput := []models.Version{
					models.Version{
						LastModified: lastModified,
						EnvName:      storageEnvName,
					},
				}
				Expect(resp).To(Equal(expectOutput))
			})

			It("returns the latest version when the given version matches latest version", func() {
				lastModified := awsVerifier.GetLastModifiedFromS3(bucket, pathToStorageStatefile)

				checkInput.Version = models.Version{
					LastModified: lastModified,
					EnvName:      storageEnvName,
				}

				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{
					models.Version{
						LastModified: lastModified,
						EnvName:      storageEnvName,
					},
				}
				Expect(resp).To(Equal(expectOutput))
			})
		})

		Context("when watching a multiple envs with `source.env_name` unset", func() {
			BeforeEach(func() {
				checkInput.Source.EnvName = ""
			})

			It("returns an empty version list when no version is given", func() {
				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{}
				Expect(resp).To(Equal(expectOutput))
			})

			It("returns the latest version when the given version matches latest version", func() {
				lastModified := awsVerifier.GetLastModifiedFromS3(bucket, pathToStorageStatefile)

				checkInput.Version = models.Version{
					LastModified: lastModified,
					EnvName:      storageEnvName,
				}

				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{
					models.Version{
						LastModified: lastModified,
						EnvName:      storageEnvName,
					},
				}
				Expect(resp).To(Equal(expectOutput))
			})
		})
	})
})
<commit_msg>Remove TODOs for fixtures for now<commit_after>package check_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"time"

	"terraform-resource/check"
	"terraform-resource/models"
	"terraform-resource/storage"
	"terraform-resource/test/helpers"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Check with Migrated From Storage", func() {

	var (
		checkInput             models.InRequest
		bucket                 string
		backendEnvName         string
		storageEnvName         string
		pathToBackendStatefile string
		pathToStorageStatefile string
		awsVerifier            *helpers.AWSVerifier
		workingDir             string
	)

	BeforeEach(func() {
		accessKey := os.Getenv("AWS_ACCESS_KEY")
		Expect(accessKey).ToNot(BeEmpty(), "AWS_ACCESS_KEY must be set")

		secretKey := os.Getenv("AWS_SECRET_KEY")
		Expect(secretKey).ToNot(BeEmpty(), "AWS_SECRET_KEY must be set")

		bucket = os.Getenv("AWS_BUCKET")
		Expect(bucket).ToNot(BeEmpty(), "AWS_BUCKET must be set")

		bucketPath := os.Getenv("AWS_BUCKET_SUBFOLDER")
		Expect(bucketPath).ToNot(BeEmpty(), "AWS_BUCKET_SUBFOLDER must be set")
		bucketPath = path.Join(bucketPath, helpers.RandomString("check-storage-test"))

		region := os.Getenv("AWS_REGION") // optional
		if region == "" {
			region = "us-east-1"
		}

		awsVerifier = helpers.NewAWSVerifier(
			accessKey,
			secretKey,
			region,
			"",
		)

		var err error
		workingDir, err = ioutil.TempDir(os.TempDir(), "terraform-resource-check-backend-test")
		Expect(err).ToNot(HaveOccurred())

		// ensure relative paths resolve correctly
		err = os.Chdir(workingDir)
		Expect(err).ToNot(HaveOccurred())

		fixturesDir := path.Join(helpers.ProjectRoot(), "fixtures")
		err = exec.Command("cp", "-r", fixturesDir, workingDir).Run()
		Expect(err).ToNot(HaveOccurred())

		workspacePath := helpers.RandomString("check-backend-test")

		backendEnvName = "s3-test-fixture-backend"
		pathToBackendStatefile = path.Join(workspacePath, backendEnvName, "terraform.tfstate")
		storageEnvName = "s3-test-fixture-storage"
		pathToStorageStatefile = path.Join(bucketPath, fmt.Sprintf("%s.tfstate", storageEnvName))

		checkInput = models.InRequest{
			Source: models.Source{
				Terraform: models.Terraform{
					BackendType: "s3",
					BackendConfig: map[string]interface{}{
						"bucket":               bucket,
						"key":                  "terraform.tfstate",
						"access_key":           accessKey,
						"secret_key":           secretKey,
						"region":               region,
						"workspace_key_prefix": workspacePath,
					},
				},
				MigratedFromStorage: storage.Model{
					Bucket:          bucket,
					BucketPath:      bucketPath,
					AccessKeyID:     accessKey,
					SecretAccessKey: secretKey,
					RegionName:      region,
				},
			},
		}
	})

	AfterEach(func() {
		_ = os.RemoveAll(workingDir)
		awsVerifier.DeleteObjectFromS3(bucket, pathToBackendStatefile)
		awsVerifier.DeleteObjectFromS3(bucket, pathToStorageStatefile)
	})

	Context("when both backend and legacy storage are empty", func() {
		It("returns an empty version list", func() {
			runner := check.Runner{}
			resp, err := runner.Run(checkInput)
			Expect(err).ToNot(HaveOccurred())

			expectedOutput := []models.Version{}
			Expect(resp).To(Equal(expectedOutput))
		})
	})

	Context("when both Backend and Legacy Storage contains state files", func() {
		BeforeEach(func() {
			backendFixture, err := os.Open(helpers.FileLocation("fixtures/s3-backend/terraform-current.tfstate"))
			Expect(err).ToNot(HaveOccurred())
			defer backendFixture.Close()
			awsVerifier.UploadObjectToS3(bucket, pathToBackendStatefile, backendFixture)

			storageFixture, err := os.Open(helpers.FileLocation("fixtures/s3-storage/terraform-current.tfstate"))
			Expect(err).ToNot(HaveOccurred())
			defer storageFixture.Close()
			awsVerifier.UploadObjectToS3(bucket, pathToStorageStatefile, storageFixture)
			time.Sleep(1 * time.Second)
		})

		Context("when watching a single env with `source.env_name`", func() {
			BeforeEach(func() {
				checkInput.Source.EnvName = backendEnvName
			})

			It("returns the latest version from the backend when no version is given", func() {
				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{
					models.Version{
						Serial:  "1",
						EnvName: backendEnvName,
					},
				}
				Expect(resp).To(Equal(expectOutput))
			})

			It("returns the latest version when the given version matches latest version", func() {
				checkInput.Version = models.Version{
					Serial:  "1",
					EnvName: backendEnvName,
				}

				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{
					models.Version{
						Serial:  "1",
						EnvName: backendEnvName,
					},
				}
				Expect(resp).To(Equal(expectOutput))
			})
		})

		Context("when watching a multiple envs with `source.env_name` unset", func() {
			BeforeEach(func() {
				checkInput.Source.EnvName = ""
			})

			It("returns an empty version list when no version is given", func() {
				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{}
				Expect(resp).To(Equal(expectOutput))
			})

			It("returns the latest version when the given backend version matches latest version", func() {
				checkInput.Version = models.Version{
					Serial:  "1",
					EnvName: backendEnvName,
				}

				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{
					models.Version{
						Serial:  "1",
						EnvName: backendEnvName,
					},
				}
				Expect(resp).To(Equal(expectOutput))
			})

			It("returns the latest version when the given storage version matches latest version", func() {
				lastModified := awsVerifier.GetLastModifiedFromS3(bucket, pathToStorageStatefile)

				checkInput.Version = models.Version{
					LastModified: lastModified,
					EnvName:      storageEnvName,
				}

				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{
					models.Version{
						LastModified: lastModified,
						EnvName:      storageEnvName,
					},
				}
				Expect(resp).To(Equal(expectOutput))
			})
		})
	})

	Context("when only Backend contains state files", func() {
		BeforeEach(func() {
			currFixture, err := os.Open(helpers.FileLocation("fixtures/s3-backend/terraform-current.tfstate"))
			Expect(err).ToNot(HaveOccurred())
			defer currFixture.Close()
			awsVerifier.UploadObjectToS3(bucket, pathToBackendStatefile, currFixture)
			time.Sleep(1 * time.Second)
		})

		Context("when watching a single env with `source.env_name`", func() {
			BeforeEach(func() {
				checkInput.Source.EnvName = backendEnvName
			})

			It("returns the latest version from the backend when no version is given", func() {
				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{
					models.Version{
						Serial:  "1",
						EnvName: backendEnvName,
					},
				}
				Expect(resp).To(Equal(expectOutput))
			})

			It("returns the latest version when the given version matches latest version", func() {
				checkInput.Version = models.Version{
					Serial:  "1",
					EnvName: backendEnvName,
				}

				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{
					models.Version{
						Serial:  "1",
						EnvName: backendEnvName,
					},
				}
				Expect(resp).To(Equal(expectOutput))
			})
		})

		Context("when watching a multiple envs with `source.env_name` unset", func() {
			BeforeEach(func() {
				checkInput.Source.EnvName = ""
			})

			It("returns an empty version list when no version is given", func() {
				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{}
				Expect(resp).To(Equal(expectOutput))
			})

			It("returns the latest version when the given version matches latest version", func() {
				checkInput.Version = models.Version{
					Serial:  "1",
					EnvName: backendEnvName,
				}

				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{
					models.Version{
						Serial:  "1",
						EnvName: backendEnvName,
					},
				}
				Expect(resp).To(Equal(expectOutput))
			})
		})
	})

	Context("when only Legacy Storage contains state files", func() {
		BeforeEach(func() {
			currFixture, err := os.Open(helpers.FileLocation("fixtures/s3-storage/terraform-current.tfstate"))
			Expect(err).ToNot(HaveOccurred())
			defer currFixture.Close()
			awsVerifier.UploadObjectToS3(bucket, pathToStorageStatefile, currFixture)
			time.Sleep(1 * time.Second)
		})

		Context("when watching a single env with `source.env_name`", func() {
			BeforeEach(func() {
				checkInput.Source.EnvName = storageEnvName
			})

			It("returns the latest version from the backend when no version is given", func() {
				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				lastModified := awsVerifier.GetLastModifiedFromS3(bucket, pathToStorageStatefile)

				expectOutput := []models.Version{
					models.Version{
						LastModified: lastModified,
						EnvName:      storageEnvName,
					},
				}
				Expect(resp).To(Equal(expectOutput))
			})

			It("returns the latest version when the given version matches latest version", func() {
				lastModified := awsVerifier.GetLastModifiedFromS3(bucket, pathToStorageStatefile)

				checkInput.Version = models.Version{
					LastModified: lastModified,
					EnvName:      storageEnvName,
				}

				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{
					models.Version{
						LastModified: lastModified,
						EnvName:      storageEnvName,
					},
				}
				Expect(resp).To(Equal(expectOutput))
			})
		})

		Context("when watching a multiple envs with `source.env_name` unset", func() {
			BeforeEach(func() {
				checkInput.Source.EnvName = ""
			})

			It("returns an empty version list when no version is given", func() {
				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{}
				Expect(resp).To(Equal(expectOutput))
			})

			It("returns the latest version when the given version matches latest version", func() {
				lastModified := awsVerifier.GetLastModifiedFromS3(bucket, pathToStorageStatefile)

				checkInput.Version = models.Version{
					LastModified: lastModified,
					EnvName:      storageEnvName,
				}

				runner := check.Runner{}
				resp, err := runner.Run(checkInput)
				Expect(err).ToNot(HaveOccurred())

				expectOutput := []models.Version{
					models.Version{
						LastModified: lastModified,
						EnvName:      storageEnvName,
					},
				}
				Expect(resp).To(Equal(expectOutput))
			})
		})
	})
})
<|endoftext|>


<commit_before>package api

import (
	"context"
	"crypto/sha256"
	"database/sql"
	"encoding/hex"
	"gonder/models"
	"log"
	"net/http"
	"net/url"
	"time"
)

type Auth struct {
	name   string
	userID int64
	unitID int64
}

func CheckAuth(fn http.HandlerFunc) http.HandlerFunc {
	auth := new(Auth)
	return func(w http.ResponseWriter, r *http.Request) {
		var authorize bool
		user, password, _ := r.BasicAuth()
		auth.userID, auth.unitID, authorize = check(user, password)
		// ToDo rate limit bad auth
		if !authorize {
			//if user != "" {
			//	ip := models.GetIP(r)
			//	apiLog.Printf("%s bad user login '%s'", ip, user)
			//	if models.Config.GonderEmail != "" && models.Config.AdminEmail != "" {
			//		go func() {
			//			bldr := &smtpSender.Builder{
			//				From: models.Config.GonderEmail,
			//				To: models.Config.AdminEmail,
			//				Subject: "Bad login to Gonder",
			//			}
			//			bldr.AddTextPlain([]byte(ip + " bad user login '" + user + "'"))
			//
			//			email := bldr.Email("", func(result smtpSender.Result){
			//				apiLog.Print("Error send mail:", result.Err)
			//			})
			//			email.Send(&smtpSender.Connect{}, nil)
			//		}()
			//	}
			//}
			time.Sleep(time.Second * 3)
			w.Header().Set("WWW-Authenticate", `Basic realm="Gonder"`)
			w.WriteHeader(401)
			return
		}
		auth.name = user

		uri, err := url.QueryUnescape(r.RequestURI)
		if err != nil {
			log.Println(err)
		}
		apiLog.Printf("host: %s user: '%s' %s %s", models.GetIP(r), auth.name, r.Method, uri)

		ctx := context.WithValue(r.Context(), "Auth", auth)
		fn(w, r.WithContext(ctx))
	}
}

func (a *Auth) GroupRight(group interface{}) bool {
	if a.IsAdmin() {
		return true
	}

	var r = true
	var c int
	err := models.Db.QueryRow("SELECT COUNT(*) FROM `auth_user_group` WHERE `auth_user_id`=? AND `group_id`=?", a.userID, group).Scan(&c)
	if err != nil {
		log.Println(err)
		r = false
	}
	if c == 0 {
		r = false
	}
	return r
}

func (a *Auth) CampaignRight(campaign interface{}) bool {
	if a.IsAdmin() {
		return true
	}

	var r = true
	var c int
	err := models.Db.QueryRow("SELECT COUNT(*) FROM `auth_user_group` WHERE `auth_user_id`=? AND `group_id`=(SELECT `group_id` FROM `campaign` WHERE id=?)", a.userID, campaign).Scan(&c)
	if err != nil {
		log.Println(err)
		r = false
	}
	if c == 0 {
		r = false
	}
	return r
}

func (a *Auth) Right(right string) bool {
	var r bool

	if a.IsAdmin() {
		return true
	}

	err := models.Db.QueryRow("SELECT COUNT(auth_right.id) user_right FROM `auth_user` JOIN `auth_unit_right` ON auth_user.auth_unit_id = auth_unit_right.auth_unit_id JOIN `auth_right` ON auth_unit_right.auth_right_id = auth_right.id WHERE auth_user.id = ? AND auth_right.name = ?", a.userID, right).Scan(&r)
	if err != nil {
		log.Println(err)
		return false
	}

	return r
}

func (a *Auth) IsAdmin() bool {
	// admins has group 0
	return a.unitID == 0
}

func check(user, password string) (int64, int64, bool) {
	l := false
	var passwordHash string
	var userID, unitID int64

	hash := sha256.New()
	if _, err := hash.Write([]byte(password)); err != nil {
		apiLog.Print(err)
	}
	md := hash.Sum(nil)
	shaPassword := hex.EncodeToString(md)

	err := models.Db.QueryRow("SELECT `id`, `auth_unit_id`, `password` FROM `auth_user` WHERE `name`=?", user).Scan(&userID, &unitID, &passwordHash)
	if err != nil && err != sql.ErrNoRows {
		log.Println(err)
	}

	if shaPassword == passwordHash {
		l = true
	}

	return userID, unitID, l
}

// ToDo
func Logout(w http.ResponseWriter, r *http.Request) {
	r.Header.Set("Authorization", "Basic")
	http.Error(w, "Logout. Bye!", http.StatusUnauthorized)
}
<commit_msg>pause before login<commit_after>package api

import (
	"context"
	"crypto/sha256"
	"database/sql"
	"encoding/hex"
	"gonder/models"
	"log"
	"net/http"
	"net/url"
	"time"
)

type Auth struct {
	name   string
	userID int64
	unitID int64
}

func CheckAuth(fn http.HandlerFunc) http.HandlerFunc {
	auth := new(Auth)
	return func(w http.ResponseWriter, r *http.Request) {
		var authorize bool
		user, password, _ := r.BasicAuth()
		auth.userID, auth.unitID, authorize = check(user, password)
		// ToDo rate limit bad auth or/and user requests
		if !authorize {
			//if user != "" {
			//	ip := models.GetIP(r)
			//	apiLog.Printf("%s bad user login '%s'", ip, user)
			//	if models.Config.GonderEmail != "" && models.Config.AdminEmail != "" {
			//		go func() {
			//			bldr := &smtpSender.Builder{
			//				From: models.Config.GonderEmail,
			//				To: models.Config.AdminEmail,
			//				Subject: "Bad login to Gonder",
			//			}
			//			bldr.AddTextPlain([]byte(ip + " bad user login '" + user + "'"))
			//
			//			email := bldr.Email("", func(result smtpSender.Result){
			//				apiLog.Print("Error send mail:", result.Err)
			//			})
			//			email.Send(&smtpSender.Connect{}, nil)
			//		}()
			//	}
			//}
			time.Sleep(time.Second * 3)
			w.Header().Set("WWW-Authenticate", `Basic realm="Gonder"`)
			w.WriteHeader(401)
			return
		}
		auth.name = user

		uri, err := url.QueryUnescape(r.RequestURI)
		if err != nil {
			log.Println(err)
		}
		apiLog.Printf("host: %s user: '%s' %s %s", models.GetIP(r), auth.name, r.Method, uri)

		ctx := context.WithValue(r.Context(), "Auth", auth)
		fn(w, r.WithContext(ctx))
	}
}

func (a *Auth) GroupRight(group interface{}) bool {
	if a.IsAdmin() {
		return true
	}

	var r = true
	var c int
	err := models.Db.QueryRow("SELECT COUNT(*) FROM `auth_user_group` WHERE `auth_user_id`=? AND `group_id`=?", a.userID, group).Scan(&c)
	if err != nil {
		log.Println(err)
		r = false
	}
	if c == 0 {
		r = false
	}
	return r
}

func (a *Auth) CampaignRight(campaign interface{}) bool {
	if a.IsAdmin() {
		return true
	}

	var r = true
	var c int
	err := models.Db.QueryRow("SELECT COUNT(*) FROM `auth_user_group` WHERE `auth_user_id`=? AND `group_id`=(SELECT `group_id` FROM `campaign` WHERE id=?)", a.userID, campaign).Scan(&c)
	if err != nil {
		log.Println(err)
		r = false
	}
	if c == 0 {
		r = false
	}
	return r
}

func (a *Auth) Right(right string) bool {
	var r bool

	if a.IsAdmin() {
		return true
	}

	err := models.Db.QueryRow("SELECT COUNT(auth_right.id) user_right FROM `auth_user` JOIN `auth_unit_right` ON auth_user.auth_unit_id = auth_unit_right.auth_unit_id JOIN `auth_right` ON auth_unit_right.auth_right_id = auth_right.id WHERE auth_user.id = ? AND auth_right.name = ?", a.userID, right).Scan(&r)
	if err != nil {
		log.Println(err)
		return false
	}

	return r
}

func (a *Auth) IsAdmin() bool {
	// admins has group 0
	return a.unitID == 0
}

func check(user, password string) (int64, int64, bool) {
	l := false
	var passwordHash string
	var userID, unitID int64

	hash := sha256.New()
	if _, err := hash.Write([]byte(password)); err != nil {
		apiLog.Print(err)
	}
	md := hash.Sum(nil)
	shaPassword := hex.EncodeToString(md)

	err := models.Db.QueryRow("SELECT `id`, `auth_unit_id`, `password` FROM `auth_user` WHERE `name`=?", user).Scan(&userID, &unitID, &passwordHash)
	if err != nil && err != sql.ErrNoRows {
		log.Println(err)
	}

	if shaPassword == passwordHash {
		l = true
	}

	return userID, unitID, l
}

// ToDo
func Logout(w http.ResponseWriter, r *http.Request) {
	r.Header.Set("Authorization", "Basic")
	http.Error(w, "Logout. Bye!", http.StatusUnauthorized)
}
<|endoftext|>


<commit_before>// Defines different types of keys, like negative-infinity, positive-infinity,
// integer, string, bytes, json etc ...
//
// All the key types must implement `Key` interface.

package api

import (
    "bytes"
    "encoding/binary"
)

type Ord int8

const (
    LT    Ord = -1
    EQUAL Ord = 0
    GT    Ord = 1
)

var (
    NegInf = NInf{}
    PosInf = PInf{}
    infs = map[int]Key{1: PosInf, -1: NegInf}
)

// Inf returns a Key that is "bigger than" any other item, if sign is positive.
// Otherwise  it returns a Key that is "smaller than" any other item.
func Inf(sign int) Key {
    return infs[sign]
}

// Negative infinity
type NInf struct{}

func (NInf) Less(Key) bool {
    return true
}

func (NInf) Compare(Key) Ord {
    return EQUAL
}

func (NInf) Bytes() []byte {
    return []byte{}
}

// Positive inifinity
type PInf struct{}

func (PInf) Less(Key) bool {
    return false
}

func (PInf) Compare(Key) Ord {
    return EQUAL
}

func (PInf) Bytes() []byte {
    return []byte{}
}

// []byte
type Bytes []byte

func (this Bytes) Compare(than Key) (result Ord) {
    switch than.(type) {
    case NInf:
        result = GT
    case Bytes:
        switch bytes.Compare(this, than.(Bytes)) {
        case -1:
            result = LT
        case 0:
            result = EQUAL
        case 1:
            result = GT
        }
    case PInf:
        result = LT
    }
    return
}

func (this Bytes) Less(than Key) (rc bool) {
    return this.Compare(than) == LT
}

func (this Bytes) Bytes() []byte {
    return []byte(this)
}

// JSON
type JSON []byte

func (this JSON) Less(than Key) (rc bool) { // TODO: Incompleted
    return this.Compare(than) == LT
}

func (this JSON) Compare(than Key) Ord { // TODO: Incomplete
    return LT
}

func (this JSON) Bytes() []byte {
    return []byte(this)
}

// Int is UInt64
type Int64 int64

func (x Int64) Compare(than Key) (result Ord) {
    switch than.(type) {
    case NInf:
        result = GT
    case Int64:
        switch {
        case x < than.(Int64):
            result = LT
        case x == than.(Int64):
            result = EQUAL
        case x > than.(Int64):
            result = GT
        }
    case PInf:
        result = LT
    }
    return
}

func (x Int64) Less(than Key) bool {
    return x.Compare(than) == LT
}

func (x Int64) Bytes() []byte {
    data := make([]byte, 8)
    binary.PutVarint(data, int64(x))
    return data
}

// String
type String string

func (x String) Compare(than Key) (result Ord) {
    switch than.(type) {
    case NInf:
        result = GT
    case String:
        switch {
        case x < than.(String):
            result = LT
        case x == than.(String):
            result = EQUAL
        case x > than.(String):
            result = GT
        }
    case PInf:
        result = LT
    }
    return
}

func (x String) Less(than Key) bool {
    return x.Compare(than) == LT
}

func (x String) Bytes() []byte {
    return []byte(x)
}
<commit_msg>Implement different types of keys.<commit_after>// Defines different types of keys, like negative-infinity, positive-infinity,
// integer, string, bytes, json etc ...
//
// All the key types must implement `Key` interface.

package api

import (
    "bytes"
    "encoding/binary"
)

var (
    NInf = nInf{}
    PInf = pInf{}
    infs = map[int]Key{1: PInf, -1: NInf}
)

// Inf returns a Key that is "bigger than" any other item, if sign is positive.
// Otherwise  it returns a Key that is "smaller than" any other item.
func Inf(sign int) Key {
    return infs[sign]
}

// Negative infinity
type nInf struct{}

func (nInf) Less(Key) bool {
    return true
}

func (nInf) Bytes() []byte {
    return []byte{}
}

// Positive inifinity
type pInf struct{}

func (pInf) Less(Key) bool {
    return false
}

func (pInf) Bytes() []byte {
    return []byte{}
}

// []byte
type Bytes []byte

func (this Bytes) Less(than Key) (rc bool) {
    switch than.(type) {
    case nInf:
        rc = false
    case Bytes:
        rc = bytes.Compare(this, than.(Bytes)) < 0
    case pInf:
        rc = true
    }
    return
}

func (this Bytes) Bytes() []byte {
    return []byte(this)
}

// JSON
type JSON []byte

func (this JSON) Less(than Key) (rc bool) { // TODO: To be completed
    switch than.(type) {
    case nInf:
        rc = false
    case JSON:
        rc = false
    case pInf:
        rc = true
    }
    return
}

func (this JSON) Bytes() []byte {
    return []byte(this)
}

// Int is UInt64
type Int int64

func (x Int) Less(than Key) (rc bool) {
    switch than.(type) {
    case nInf:
        rc = false
    case Int:
        rc = x < than.(Int)
    case pInf:
        rc = true
    }
    return
}

func (x Int) Bytes() []byte {
    data := make([]byte, 8)
    binary.PutVarint(data, int64(x))
    return data
}

// String
type String string

func (x String) Less(than Key) (rc bool) {
    switch than.(type) {
    case nInf:
        rc = false
    case Int:
        rc = x < than.(String)
    case pInf:
        rc = true
    }
    return
}

func (x String) Bytes() []byte {
    return []byte(x)
}
<|endoftext|>


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

package main

import (
	"flag"
	"fmt"
	"github.com/bmizerany/pat"
	"github.com/globocom/config"
	"github.com/globocom/tsuru/app"
	"github.com/globocom/tsuru/log"
	"github.com/globocom/tsuru/provision"
	_ "github.com/globocom/tsuru/provision/juju"
	_ "github.com/globocom/tsuru/provision/local"
	stdlog "log"
	"log/syslog"
	"net/http"
	"os"
)

func fatal(err error) {
	fmt.Fprintln(os.Stderr, err)
	log.Fatal(err)
}

func main() {
	logger, err := syslog.NewLogger(syslog.LOG_INFO, stdlog.LstdFlags)
	if err != nil {
		stdlog.Fatal(err)
	}
	log.SetLogger(logger)
	configFile := flag.String("config", "/etc/tsuru/tsuru.conf", "tsuru config file")
	dry := flag.Bool("dry", false, "dry-run: does not start the server (for testing purpose)")
	flag.Parse()
	err = config.ReadAndWatchConfigFile(*configFile)
	if err != nil {
		fatal(err)
	}
	connString, err := config.GetString("database:url")
	if err != nil {
		fatal(err)
	}
	dbName, err := config.GetString("database:name")
	if err != nil {
		fatal(err)
	}
	fmt.Printf("Using the database %q from the server %q.\n\n", dbName, connString)

	m := pat.New()

	m.Get("/services/instances", AuthorizationRequiredHandler(ServicesInstancesHandler))
	m.Post("/services/instances", AuthorizationRequiredHandler(CreateInstanceHandler))
	m.Put("/services/instances/:instance/:app", AuthorizationRequiredHandler(BindHandler))
	m.Del("/services/instances/:instance/:app", AuthorizationRequiredHandler(UnbindHandler))
	m.Del("/services/c/instances/:name", AuthorizationRequiredHandler(RemoveServiceInstanceHandler))
	m.Get("/services/instances/:instance/status", AuthorizationRequiredHandler(ServiceInstanceStatusHandler))

	m.Get("/services", AuthorizationRequiredHandler(ServicesHandler))
	m.Post("/services", AuthorizationRequiredHandler(CreateHandler))
	m.Put("/services", AuthorizationRequiredHandler(UpdateHandler))
	m.Del("/services/:name", AuthorizationRequiredHandler(DeleteHandler))
	m.Get("/services/:name", AuthorizationRequiredHandler(ServiceInfoHandler))
	m.Get("/services/c/:name/doc", AuthorizationRequiredHandler(Doc))
	m.Get("/services/:name/doc", AuthorizationRequiredHandler(GetDocHandler))
	m.Put("/services/:name/doc", AuthorizationRequiredHandler(AddDocHandler))
	m.Put("/services/:service/:team", AuthorizationRequiredHandler(GrantServiceAccessToTeamHandler))
	m.Del("/services/:service/:team", AuthorizationRequiredHandler(RevokeServiceAccessFromTeamHandler))

	m.Del("/apps/:name", AuthorizationRequiredHandler(appDelete))
	m.Get("/apps/:name/repository/clone", Handler(cloneRepository))
	m.Get("/apps/:name/avaliable", Handler(appIsAvailable))
	m.Get("/apps/:name", AuthorizationRequiredHandler(appInfo))
	m.Post("/apps/:name", AuthorizationRequiredHandler(setCName))
	m.Post("/apps/:name/run", AuthorizationRequiredHandler(RunCommand))
	m.Get("/apps/:name/restart", AuthorizationRequiredHandler(RestartHandler))
	m.Get("/apps/:name/env", AuthorizationRequiredHandler(getEnv))
	m.Post("/apps/:name/env", AuthorizationRequiredHandler(setEnv))
	m.Del("/apps/:name/env", AuthorizationRequiredHandler(unsetEnv))
	m.Get("/apps", AuthorizationRequiredHandler(appList))
	m.Post("/apps", AuthorizationRequiredHandler(createApp))
	m.Put("/apps/:name/units", AuthorizationRequiredHandler(addUnits))
	m.Del("/apps/:name/units", AuthorizationRequiredHandler(removeUnits))
	m.Put("/apps/:app/:team", AuthorizationRequiredHandler(GrantAccessToTeamHandler))
	m.Del("/apps/:app/:team", AuthorizationRequiredHandler(RevokeAccessFromTeamHandler))
	m.Get("/apps/:name/log", AuthorizationRequiredHandler(appLog))
	m.Post("/apps/:name/log", Handler(AddLogHandler))

	m.Post("/users", Handler(CreateUser))
	m.Post("/users/:email/tokens", Handler(login))
	m.Put("/users/password", AuthorizationRequiredHandler(ChangePassword))
	m.Del("/users", AuthorizationRequiredHandler(RemoveUser))
	m.Post("/users/keys", AuthorizationRequiredHandler(AddKeyToUser))
	m.Del("/users/keys", AuthorizationRequiredHandler(RemoveKeyFromUser))

	m.Get("/teams", AuthorizationRequiredHandler(ListTeams))
	m.Post("/teams", AuthorizationRequiredHandler(CreateTeam))
	m.Del("/teams/:name", AuthorizationRequiredHandler(RemoveTeam))
	m.Put("/teams/:team/:user", AuthorizationRequiredHandler(AddUserToTeam))
	m.Del("/teams/:team/:user", AuthorizationRequiredHandler(RemoveUserFromTeam))

	m.Get("/healers", Handler(healers))
	m.Get("/healers/:healer", Handler(healer))

	if !*dry {
		provisioner, err := config.GetString("provisioner")
		if err != nil {
			fmt.Printf("Warning: %q didn't declare a provisioner, using default provisioner.\n", *configFile)
			provisioner = "juju"
		}
		app.Provisioner, err = provision.Get(provisioner)
		if err != nil {
			fatal(err)
		}
		fmt.Printf("Using %q provisioner.\n\n", provisioner)

		listen, err := config.GetString("listen")
		if err != nil {
			fatal(err)
		}
		tls, _ := config.GetBool("use-tls")
		if tls {
			certFile, err := config.GetString("tls-cert-file")
			if err != nil {
				fatal(err)
			}
			keyFile, err := config.GetString("tls-key-file")
			if err != nil {
				fatal(err)
			}
			fmt.Printf("tsuru HTTP/TLS server listening at %s...\n", listen)
			fatal(http.ListenAndServeTLS(listen, certFile, keyFile, m))
		} else {
			fmt.Printf("tsuru HTTP server listening at %s...\n", listen)
			fatal(http.ListenAndServe(listen, m))
		}
	}
}
<commit_msg>api: split listener and server creation<commit_after>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"github.com/bmizerany/pat"
	"github.com/globocom/config"
	"github.com/globocom/tsuru/app"
	"github.com/globocom/tsuru/log"
	"github.com/globocom/tsuru/provision"
	_ "github.com/globocom/tsuru/provision/juju"
	_ "github.com/globocom/tsuru/provision/local"
	stdlog "log"
	"log/syslog"
	"net"
	"net/http"
	"os"
)

func fatal(err error) {
	fmt.Fprintln(os.Stderr, err)
	log.Fatal(err)
}

func main() {
	logger, err := syslog.NewLogger(syslog.LOG_INFO, stdlog.LstdFlags)
	if err != nil {
		stdlog.Fatal(err)
	}
	log.SetLogger(logger)
	configFile := flag.String("config", "/etc/tsuru/tsuru.conf", "tsuru config file")
	dry := flag.Bool("dry", false, "dry-run: does not start the server (for testing purpose)")
	flag.Parse()
	err = config.ReadAndWatchConfigFile(*configFile)
	if err != nil {
		fatal(err)
	}
	connString, err := config.GetString("database:url")
	if err != nil {
		fatal(err)
	}
	dbName, err := config.GetString("database:name")
	if err != nil {
		fatal(err)
	}
	fmt.Printf("Using the database %q from the server %q.\n\n", dbName, connString)

	m := pat.New()

	m.Get("/services/instances", AuthorizationRequiredHandler(ServicesInstancesHandler))
	m.Post("/services/instances", AuthorizationRequiredHandler(CreateInstanceHandler))
	m.Put("/services/instances/:instance/:app", AuthorizationRequiredHandler(BindHandler))
	m.Del("/services/instances/:instance/:app", AuthorizationRequiredHandler(UnbindHandler))
	m.Del("/services/c/instances/:name", AuthorizationRequiredHandler(RemoveServiceInstanceHandler))
	m.Get("/services/instances/:instance/status", AuthorizationRequiredHandler(ServiceInstanceStatusHandler))

	m.Get("/services", AuthorizationRequiredHandler(ServicesHandler))
	m.Post("/services", AuthorizationRequiredHandler(CreateHandler))
	m.Put("/services", AuthorizationRequiredHandler(UpdateHandler))
	m.Del("/services/:name", AuthorizationRequiredHandler(DeleteHandler))
	m.Get("/services/:name", AuthorizationRequiredHandler(ServiceInfoHandler))
	m.Get("/services/c/:name/doc", AuthorizationRequiredHandler(Doc))
	m.Get("/services/:name/doc", AuthorizationRequiredHandler(GetDocHandler))
	m.Put("/services/:name/doc", AuthorizationRequiredHandler(AddDocHandler))
	m.Put("/services/:service/:team", AuthorizationRequiredHandler(GrantServiceAccessToTeamHandler))
	m.Del("/services/:service/:team", AuthorizationRequiredHandler(RevokeServiceAccessFromTeamHandler))

	m.Del("/apps/:name", AuthorizationRequiredHandler(appDelete))
	m.Get("/apps/:name/repository/clone", Handler(cloneRepository))
	m.Get("/apps/:name/avaliable", Handler(appIsAvailable))
	m.Get("/apps/:name", AuthorizationRequiredHandler(appInfo))
	m.Post("/apps/:name", AuthorizationRequiredHandler(setCName))
	m.Post("/apps/:name/run", AuthorizationRequiredHandler(RunCommand))
	m.Get("/apps/:name/restart", AuthorizationRequiredHandler(RestartHandler))
	m.Get("/apps/:name/env", AuthorizationRequiredHandler(getEnv))
	m.Post("/apps/:name/env", AuthorizationRequiredHandler(setEnv))
	m.Del("/apps/:name/env", AuthorizationRequiredHandler(unsetEnv))
	m.Get("/apps", AuthorizationRequiredHandler(appList))
	m.Post("/apps", AuthorizationRequiredHandler(createApp))
	m.Put("/apps/:name/units", AuthorizationRequiredHandler(addUnits))
	m.Del("/apps/:name/units", AuthorizationRequiredHandler(removeUnits))
	m.Put("/apps/:app/:team", AuthorizationRequiredHandler(GrantAccessToTeamHandler))
	m.Del("/apps/:app/:team", AuthorizationRequiredHandler(RevokeAccessFromTeamHandler))
	m.Get("/apps/:name/log", AuthorizationRequiredHandler(appLog))
	m.Post("/apps/:name/log", Handler(AddLogHandler))

	m.Post("/users", Handler(CreateUser))
	m.Post("/users/:email/tokens", Handler(login))
	m.Put("/users/password", AuthorizationRequiredHandler(ChangePassword))
	m.Del("/users", AuthorizationRequiredHandler(RemoveUser))
	m.Post("/users/keys", AuthorizationRequiredHandler(AddKeyToUser))
	m.Del("/users/keys", AuthorizationRequiredHandler(RemoveKeyFromUser))

	m.Get("/teams", AuthorizationRequiredHandler(ListTeams))
	m.Post("/teams", AuthorizationRequiredHandler(CreateTeam))
	m.Del("/teams/:name", AuthorizationRequiredHandler(RemoveTeam))
	m.Put("/teams/:team/:user", AuthorizationRequiredHandler(AddUserToTeam))
	m.Del("/teams/:team/:user", AuthorizationRequiredHandler(RemoveUserFromTeam))

	m.Get("/healers", Handler(healers))
	m.Get("/healers/:healer", Handler(healer))

	if !*dry {
		provisioner, err := config.GetString("provisioner")
		if err != nil {
			fmt.Printf("Warning: %q didn't declare a provisioner, using default provisioner.\n", *configFile)
			provisioner = "juju"
		}
		app.Provisioner, err = provision.Get(provisioner)
		if err != nil {
			fatal(err)
		}
		fmt.Printf("Using %q provisioner.\n\n", provisioner)

		listen, err := config.GetString("listen")
		if err != nil {
			fatal(err)
		}
		tls, _ := config.GetBool("use-tls")
		if tls {
			certFile, err := config.GetString("tls-cert-file")
			if err != nil {
				fatal(err)
			}
			keyFile, err := config.GetString("tls-key-file")
			if err != nil {
				fatal(err)
			}
			fmt.Printf("tsuru HTTP/TLS server listening at %s...\n", listen)
			fatal(http.ListenAndServeTLS(listen, certFile, keyFile, m))
		} else {
			listener, err := net.Listen("tcp", listen)
			if err != nil {
				fatal(err)
			}
			fmt.Printf("tsuru HTTP server listening at %s...\n", listen)
			fatal(http.Serve(listener, m))
		}
	}
}
<|endoftext|>


<commit_before>package api

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

	"github.com/FederationOfFathers/dashboard/bridge"
)

func init() {
	Router.Path("/api/v0/ping").Methods("GET").Handler(jwtHandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			id := getSlackUserID(r)
			user, _ := bridge.Data.Slack.User(id)
			admin, _ := bridge.Data.Slack.IsUserIDAdmin(id)
			enc := json.NewEncoder(w)
			enc.Encode(map[string]interface{}{
				"user":     user,
				"admin":    admin,
				"groups":   bridge.Data.Slack.UserGroups(id),
				"channels": bridge.Data.Slack.UserChannels(id),
			})
		},
	))
}
<commit_msg>include a member group visibility map to speed up the initial page load (remove an api call for just viewing the users groups)<commit_after>package api

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

	"github.com/FederationOfFathers/dashboard/bridge"
)

func init() {
	Router.Path("/api/v0/ping").Methods("GET").Handler(jwtHandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			id := getSlackUserID(r)
			user, _ := bridge.Data.Slack.User(id)
			admin, _ := bridge.Data.Slack.IsUserIDAdmin(id)
			userGroups := bridge.Data.Slack.UserGroups(id)
			userGroupsVisible := map[string]string{}
			for _, group := range userGroups {
				var visible string
				visDB().Get(group.ID, &visible)
				if visible != "true" {
					visible = "false"
				}
				userGroupsVisible[group.ID] = visible
			}
			var rval = map[string]interface{}{
				"user":          user,
				"admin":         admin,
				"groups":        userGroups,
				"group_visible": userGroupsVisible,
				"channels":      bridge.Data.Slack.UserChannels(id),
			}
			json.NewEncoder(w).Encode(rval)
		},
	))
}
<|endoftext|>