<commit_before>package mpfluentd

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

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

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

var prefix = "fluentd"

func metricName(names ...string) string {
	return prefix + "." + strings.Join(names, ".")
}

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

	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

	// extended metrics fluentd >= 1.6
	// https://www.fluentd.org/blog/fluentd-v1.6.0-has-been-released
	EmitRecords                      uint64  `json:"emit_records"`
	EmitCount                        uint64  `json:"emit_count"`
	WriteCount                       uint64  `json:"write_count"`
	RollbackCount                    uint64  `json:"rollback_count"`
	SlowFlushCount                   uint64  `json:"slow_flush_count"`
	FlushTimeCount                   uint64  `json:"flush_time_count"`
	BufferStageLength                uint64  `json:"buffer_stage_length"`
	BufferStageByteSize              uint64  `json:"buffer_stage_byte_size"`
	BufferQueueByteSize              uint64  `json:"buffer_queue_byte_size"`
	BufferAvailableBufferSpaceRatios float64 `json:"buffer_available_buffer_space_ratios"`
}

func (fpm FluentdPluginMetrics) getExtended(name string) float64 {
	switch name {
	case "emit_records":
		return float64(fpm.EmitRecords)
	case "emit_count":
		return float64(fpm.EmitCount)
	case "write_count":
		return float64(fpm.WriteCount)
	case "rollback_count":
		return float64(fpm.RollbackCount)
	case "slow_flush_count":
		return float64(fpm.SlowFlushCount)
	case "flush_time_count":
		return float64(fpm.FlushTimeCount)
	case "buffer_stage_length":
		return float64(fpm.BufferStageLength)
	case "buffer_stage_byte_size":
		return float64(fpm.BufferStageByteSize)
	case "buffer_queue_byte_size":
		return float64(fpm.BufferQueueByteSize)
	case "buffer_available_buffer_space_ratios":
		return fpm.BufferAvailableBufferSpaceRatios
	}
	return 0
}

// 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[metricName("retry_count", pid)] = float64(p.RetryCount)
		metrics[metricName("buffer_queue_length", pid)] = float64(p.BufferQueueLength)
		metrics[metricName("buffer_total_queued_size", pid)] = float64(p.BufferTotalQueuedSize)
		for _, name := range f.extendedMetrics {
			key := strings.Join([]string{"fluentd", name, pid}, ".")
			metrics[key] = p.getExtended(name)
		}
	}
	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) {
	req, err := http.NewRequest(http.MethodGet, f.Target, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("User-Agent", "mackerel-plugin-fluentd")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	return f.parseStats(body)
}

var defaultGraphs = map[string]mp.Graphs{
	"fluentd.retry_count": {
		Label: "Fluentd retry count",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: false},
		},
	},
	"fluentd.buffer_queue_length": {
		Label: "Fluentd queue length",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: false},
		},
	},
	"fluentd.buffer_total_queued_size": {
		Label: "Fluentd buffer total queued size",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: false},
		},
	},
}

var extendedGraphs = map[string]mp.Graphs{
	"fluentd.emit_records": {
		Label: "Fluentd emitted records",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: true},
		},
	},
	"fluentd.emit_count": {
		Label: "Fluentd emit calls",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: true},
		},
	},
	"fluentd.write_count": {
		Label: "Fluentd write/try_write calls",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: true},
		},
	},
	"fluentd.rollback_count": {
		Label: "Fluentd rollbacks",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: true},
		},
	},
	"fluentd.slow_flush_count": {
		Label: "Fluentd slow flushes",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: true},
		},
	},
	"fluentd.flush_time_count": {
		Label: "Fluentd buffer flush time in msec",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: true},
		},
	},
	"fluentd.buffer_stage_length": {
		Label: "Fluentd length of staged buffer chunks",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: false},
		},
	},
	"fluentd.buffer_stage_byte_size": {
		Label: "Fluentd bytesize of staged buffer chunks",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: false},
		},
	},
	"fluentd.buffer_queue_byte_size": {
		Label: "Fluentd bytesize of queued buffer chunks",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: false},
		},
	},
	"fluentd.buffer_available_buffer_space_ratios": {
		Label: "Fluentd available space for buffer",
		Unit:  "percentage",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: false},
		},
	},
}

// GraphDefinition interface for mackerelplugin
func (f FluentdMetrics) GraphDefinition() map[string]mp.Graphs {
	graphs := make(map[string]mp.Graphs, len(defaultGraphs))
	for key, g := range defaultGraphs {
		g := g
		graphs[key] = g
	}
	for _, name := range f.extendedMetrics {
		fullName := metricName(name)
		if g, ok := extendedGraphs[fullName]; ok {
			graphs[fullName] = g
		}
	}
	return graphs
}

// Do the plugin
func Do() {
	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")
	extendedMetricNames := flag.String("extended_metrics", "", "extended metric names joind with ',' or 'all' (fluentd >= v1.6.0)")
	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)
		}
	}

	var extendedMetrics []string
	switch *extendedMetricNames {
	case "all":
		for key := range extendedGraphs {
			extendedMetrics = append(extendedMetrics, strings.TrimPrefix(key, prefix+"."))
		}
	case "":
	default:
		for _, name := range strings.Split(*extendedMetricNames, ",") {
			fullName := metricName(name)
			if _, exists := extendedGraphs[fullName]; !exists {
				fmt.Fprintf(os.Stderr, "extended_metrics %s is not supported. See also https://www.fluentd.org/blog/fluentd-v1.6.0-has-been-released\n", name)
				os.Exit(1)
			}
			extendedMetrics = append(extendedMetrics, name)
		}
	}
	f := FluentdMetrics{
		Target:          fmt.Sprintf("http://%s:%s/api/plugins.json", *host, *port),
		Tempfile:        *tempFile,
		pluginType:      *pluginType,
		pluginIDPattern: pluginIDPattern,
		extendedMetrics: extendedMetrics,
	}

	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.SetTempfileByBasename(fmt.Sprintf("mackerel-plugin-fluentd-%s", strings.Join(tempFileSuffix, "-")))
	}

	helper.Run()
}
<commit_msg>[fluentd] add metric-key-prefix option.<commit_after>package mpfluentd

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

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

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

func metricName(names ...string) string {
	return strings.Join(names, ".")
}

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

	plugins []FluentdPluginMetrics
}

// MetricKeyPrefix interface for PluginWithPrefix
func (f FluentdMetrics) MetricKeyPrefix() string {
	if f.Prefix == "" {
		f.Prefix = "fluentd"
	}
	return f.Prefix
}

// 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

	// extended metrics fluentd >= 1.6
	// https://www.fluentd.org/blog/fluentd-v1.6.0-has-been-released
	EmitRecords                      uint64  `json:"emit_records"`
	EmitCount                        uint64  `json:"emit_count"`
	WriteCount                       uint64  `json:"write_count"`
	RollbackCount                    uint64  `json:"rollback_count"`
	SlowFlushCount                   uint64  `json:"slow_flush_count"`
	FlushTimeCount                   uint64  `json:"flush_time_count"`
	BufferStageLength                uint64  `json:"buffer_stage_length"`
	BufferStageByteSize              uint64  `json:"buffer_stage_byte_size"`
	BufferQueueByteSize              uint64  `json:"buffer_queue_byte_size"`
	BufferAvailableBufferSpaceRatios float64 `json:"buffer_available_buffer_space_ratios"`
}

func (fpm FluentdPluginMetrics) getExtended(name string) float64 {
	switch name {
	case "emit_records":
		return float64(fpm.EmitRecords)
	case "emit_count":
		return float64(fpm.EmitCount)
	case "write_count":
		return float64(fpm.WriteCount)
	case "rollback_count":
		return float64(fpm.RollbackCount)
	case "slow_flush_count":
		return float64(fpm.SlowFlushCount)
	case "flush_time_count":
		return float64(fpm.FlushTimeCount)
	case "buffer_stage_length":
		return float64(fpm.BufferStageLength)
	case "buffer_stage_byte_size":
		return float64(fpm.BufferStageByteSize)
	case "buffer_queue_byte_size":
		return float64(fpm.BufferQueueByteSize)
	case "buffer_available_buffer_space_ratios":
		return fpm.BufferAvailableBufferSpaceRatios
	}
	return 0
}

// 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[metricName("retry_count", pid)] = float64(p.RetryCount)
		metrics[metricName("buffer_queue_length", pid)] = float64(p.BufferQueueLength)
		metrics[metricName("buffer_total_queued_size", pid)] = float64(p.BufferTotalQueuedSize)
		for _, name := range f.extendedMetrics {
			metrics[metricName(name, pid)] = p.getExtended(name)
		}
	}
	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) {
	req, err := http.NewRequest(http.MethodGet, f.Target, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("User-Agent", "mackerel-plugin-fluentd")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	return f.parseStats(body)
}

var defaultGraphs = map[string]mp.Graphs{
	"retry_count": {
		Label: "retry count",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: false},
		},
	},
	"buffer_queue_length": {
		Label: "queue length",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: false},
		},
	},
	"buffer_total_queued_size": {
		Label: "buffer total queued size",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: false},
		},
	},
}

var extendedGraphs = map[string]mp.Graphs{
	"emit_records": {
		Label: "emitted records",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: true},
		},
	},
	"emit_count": {
		Label: "emit calls",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: true},
		},
	},
	"write_count": {
		Label: "write/try_write calls",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: true},
		},
	},
	"rollback_count": {
		Label: "rollbacks",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: true},
		},
	},
	"slow_flush_count": {
		Label: "slow flushes",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: true},
		},
	},
	"flush_time_count": {
		Label: "buffer flush time in msec",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: true},
		},
	},
	"buffer_stage_length": {
		Label: "length of staged buffer chunks",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: false},
		},
	},
	"buffer_stage_byte_size": {
		Label: "bytesize of staged buffer chunks",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: false},
		},
	},
	"buffer_queue_byte_size": {
		Label: "bytesize of queued buffer chunks",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: false},
		},
	},
	"buffer_available_buffer_space_ratios": {
		Label: "available space for buffer",
		Unit:  "percentage",
		Metrics: []mp.Metrics{
			{Name: "*", Label: "%1", Diff: false},
		},
	},
}

// GraphDefinition interface for mackerelplugin
func (f FluentdMetrics) GraphDefinition() map[string]mp.Graphs {
	labelPrefix := strings.Title(f.Prefix)
	graphs := make(map[string]mp.Graphs, len(defaultGraphs))
	for key, g := range defaultGraphs {
		graphs[key] = mp.Graphs{
			Label:   (labelPrefix + " " + g.Label),
			Unit:    g.Unit,
			Metrics: g.Metrics,
		}
	}
	for _, name := range f.extendedMetrics {
		fullName := metricName(name)
		if g, ok := extendedGraphs[fullName]; ok {
			graphs[fullName] = mp.Graphs{
				Label:   (labelPrefix + " " + g.Label),
				Unit:    g.Unit,
				Metrics: g.Metrics,
			}
		}
	}
	return graphs
}

// Do the plugin
func Do() {
	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")
	prefix := flag.String("metric-key-prefix", "fluentd", "Metric key prefix")
	tempFile := flag.String("tempfile", "", "Temp file name")
	extendedMetricNames := flag.String("extended_metrics", "", "extended metric names joind with ',' or 'all' (fluentd >= v1.6.0)")
	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)
		}
	}

	var extendedMetrics []string
	switch *extendedMetricNames {
	case "all":
		for key := range extendedGraphs {
			extendedMetrics = append(extendedMetrics, key)
		}
	case "":
	default:
		for _, name := range strings.Split(*extendedMetricNames, ",") {
			fullName := metricName(name)
			if _, exists := extendedGraphs[fullName]; !exists {
				fmt.Fprintf(os.Stderr, "extended_metrics %s is not supported. See also https://www.fluentd.org/blog/fluentd-v1.6.0-has-been-released\n", name)
				os.Exit(1)
			}
			extendedMetrics = append(extendedMetrics, name)
		}
	}
	f := FluentdMetrics{
		Target:          fmt.Sprintf("http://%s:%s/api/plugins.json", *host, *port),
		Prefix:          *prefix,
		Tempfile:        *tempFile,
		pluginType:      *pluginType,
		pluginIDPattern: pluginIDPattern,
		extendedMetrics: extendedMetrics,
	}

	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.SetTempfileByBasename(fmt.Sprintf("mackerel-plugin-fluentd-%s", strings.Join(tempFileSuffix, "-")))
	}

	helper.Run()
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"sort"
	"time"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/service/elasticache"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsElasticacheCluster() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsElasticacheClusterCreate,
		Read:   resourceAwsElasticacheClusterRead,
		Delete: resourceAwsElasticacheClusterDelete,

		Schema: map[string]*schema.Schema{
			"cluster_id": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"engine": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"node_type": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"num_cache_nodes": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: true,
			},
			"parameter_group_name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"port": &schema.Schema{
				Type:     schema.TypeInt,
				Default:  11211,
				Optional: true,
				ForceNew: true,
			},
			"engine_version": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"subnet_group_name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"security_group_names": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},
			"security_group_ids": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},
			// Exported Attributes
			"cache_nodes": &schema.Schema{
				Type:     schema.TypeList,
				Computed: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"id": &schema.Schema{
							Type:     schema.TypeString,
							Computed: true,
						},
						"address": &schema.Schema{
							Type:     schema.TypeString,
							Computed: true,
						},
						"port": &schema.Schema{
							Type:     schema.TypeInt,
							Computed: true,
						},
					},
				},
			},
		},
	}
}

func resourceAwsElasticacheClusterCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).elasticacheconn

	clusterId := d.Get("cluster_id").(string)
	nodeType := d.Get("node_type").(string)           // e.g) cache.m1.small
	numNodes := int64(d.Get("num_cache_nodes").(int)) // 2
	engine := d.Get("engine").(string)                // memcached
	engineVersion := d.Get("engine_version").(string) // 1.4.14
	port := int64(d.Get("port").(int))                // 11211
	subnetGroupName := d.Get("subnet_group_name").(string)
	securityNameSet := d.Get("security_group_names").(*schema.Set)
	securityIdSet := d.Get("security_group_ids").(*schema.Set)

	securityNames := expandStringList(securityNameSet.List())
	securityIds := expandStringList(securityIdSet.List())

	req := &elasticache.CreateCacheClusterInput{
		CacheClusterID:          aws.String(clusterId),
		CacheNodeType:           aws.String(nodeType),
		NumCacheNodes:           aws.Long(numNodes),
		Engine:                  aws.String(engine),
		EngineVersion:           aws.String(engineVersion),
		Port:                    aws.Long(port),
		CacheSubnetGroupName:    aws.String(subnetGroupName),
		CacheSecurityGroupNames: securityNames,
		SecurityGroupIDs:        securityIds,
	}

	// parameter groups are optional and can be defaulted by AWS
	if v, ok := d.GetOk("parameter_group_name"); ok {
		req.CacheParameterGroupName = aws.String(v.(string))
	}

	_, err := conn.CreateCacheCluster(req)
	if err != nil {
		return fmt.Errorf("Error creating Elasticache: %s", err)
	}

	pending := []string{"creating"}
	stateConf := &resource.StateChangeConf{
		Pending:    pending,
		Target:     "available",
		Refresh:    CacheClusterStateRefreshFunc(conn, d.Id(), "available", pending),
		Timeout:    10 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	log.Printf("[DEBUG] Waiting for state to become available: %v", d.Id())
	_, sterr := stateConf.WaitForState()
	if sterr != nil {
		return fmt.Errorf("Error waiting for elasticache (%s) to be created: %s", d.Id(), sterr)
	}

	d.SetId(clusterId)

	return resourceAwsElasticacheClusterRead(d, meta)
}

func resourceAwsElasticacheClusterRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).elasticacheconn
	req := &elasticache.DescribeCacheClustersInput{
		CacheClusterID:    aws.String(d.Id()),
		ShowCacheNodeInfo: aws.Boolean(true),
	}

	res, err := conn.DescribeCacheClusters(req)
	if err != nil {
		return err
	}

	if len(res.CacheClusters) == 1 {
		c := res.CacheClusters[0]
		d.Set("cluster_id", c.CacheClusterID)
		d.Set("node_type", c.CacheNodeType)
		d.Set("num_cache_nodes", c.NumCacheNodes)
		d.Set("engine", c.Engine)
		d.Set("engine_version", c.EngineVersion)
		if c.ConfigurationEndpoint != nil {
			d.Set("port", c.ConfigurationEndpoint.Port)
		}
		d.Set("subnet_group_name", c.CacheSubnetGroupName)
		d.Set("security_group_names", c.CacheSecurityGroups)
		d.Set("security_group_ids", c.SecurityGroups)
		d.Set("parameter_group_name", c.CacheParameterGroup)

		if err := setCacheNodeData(d, c); err != nil {
			return err
		}
	}

	return nil
}

func setCacheNodeData(d *schema.ResourceData, c *elasticache.CacheCluster) error {
	sortedCacheNodes := make([]*elasticache.CacheNode, len(c.CacheNodes))
	copy(sortedCacheNodes, c.CacheNodes)
	sort.Sort(byCacheNodeId(sortedCacheNodes))

	cacheNodeData := make([]map[string]interface{}, 0, len(sortedCacheNodes))

	for _, node := range sortedCacheNodes {
		if node.CacheNodeID == nil || node.Endpoint == nil || node.Endpoint.Address == nil || node.Endpoint.Port == nil {
			return fmt.Errorf("Unexpected nil pointer in: %#v", node)
		}
		cacheNodeData = append(cacheNodeData, map[string]interface{}{
			"id":      *node.CacheNodeID,
			"address": *node.Endpoint.Address,
			"port":    int(*node.Endpoint.Port),
		})
	}

	return d.Set("cache_nodes", cacheNodeData)
}

type byCacheNodeId []*elasticache.CacheNode

func (b byCacheNodeId) Len() int      { return len(b) }
func (b byCacheNodeId) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func (b byCacheNodeId) Less(i, j int) bool {
	return b[i].CacheNodeID != nil && b[j].CacheNodeID != nil &&
		*b[i].CacheNodeID < *b[j].CacheNodeID
}

func resourceAwsElasticacheClusterDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).elasticacheconn

	req := &elasticache.DeleteCacheClusterInput{
		CacheClusterID: aws.String(d.Id()),
	}
	_, err := conn.DeleteCacheCluster(req)
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] Waiting for deletion: %v", d.Id())
	stateConf := &resource.StateChangeConf{
		Pending:    []string{"creating", "available", "deleting", "incompatible-parameters", "incompatible-network", "restore-failed"},
		Target:     "",
		Refresh:    CacheClusterStateRefreshFunc(conn, d.Id(), "", []string{}),
		Timeout:    10 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	_, sterr := stateConf.WaitForState()
	if sterr != nil {
		return fmt.Errorf("Error waiting for elasticache (%s) to delete: %s", d.Id(), sterr)
	}

	d.SetId("")

	return nil
}

func CacheClusterStateRefreshFunc(conn *elasticache.ElastiCache, clusterID, givenState string, pending []string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		resp, err := conn.DescribeCacheClusters(&elasticache.DescribeCacheClustersInput{
			CacheClusterID: aws.String(clusterID),
		})
		if err != nil {
			apierr := err.(aws.APIError)
			log.Printf("[DEBUG] message: %v, code: %v", apierr.Message, apierr.Code)
			if apierr.Message == fmt.Sprintf("CacheCluster not found: %v", clusterID) {
				log.Printf("[DEBUG] Detect deletion")
				return nil, "", nil
			}

			log.Printf("[ERROR] CacheClusterStateRefreshFunc: %s", err)
			return nil, "", err
		}

		c := resp.CacheClusters[0]
		log.Printf("[DEBUG] status: %v", *c.CacheClusterStatus)

		// return the current state if it's in the pending array
		for _, p := range pending {
			s := *c.CacheClusterStatus
			if p == s {
				log.Printf("[DEBUG] Return with status: %v", *c.CacheClusterStatus)
				return c, p, nil
			}
		}

		// return given state if it's not in pending
		if givenState != "" {
			return c, givenState, nil
		}
		log.Printf("[DEBUG] current status: %v", *c.CacheClusterStatus)
		return c, *c.CacheClusterStatus, nil
	}
}
<commit_msg>provider/aws: Add tag support to ElastiCache<commit_after>package aws

import (
	"fmt"
	"log"
	"sort"
	"strings"
	"time"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/service/elasticache"
	"github.com/awslabs/aws-sdk-go/service/iam"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsElasticacheCluster() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsElasticacheClusterCreate,
		Read:   resourceAwsElasticacheClusterRead,
		Update: resourceAwsElasticacheClusterUpdate,
		Delete: resourceAwsElasticacheClusterDelete,

		Schema: map[string]*schema.Schema{
			"cluster_id": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"engine": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"node_type": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"num_cache_nodes": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: true,
			},
			"parameter_group_name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"port": &schema.Schema{
				Type:     schema.TypeInt,
				Default:  11211,
				Optional: true,
				ForceNew: true,
			},
			"engine_version": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"subnet_group_name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"security_group_names": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},
			"security_group_ids": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},
			// Exported Attributes
			"cache_nodes": &schema.Schema{
				Type:     schema.TypeList,
				Computed: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"id": &schema.Schema{
							Type:     schema.TypeString,
							Computed: true,
						},
						"address": &schema.Schema{
							Type:     schema.TypeString,
							Computed: true,
						},
						"port": &schema.Schema{
							Type:     schema.TypeInt,
							Computed: true,
						},
					},
				},
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceAwsElasticacheClusterCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).elasticacheconn

	clusterId := d.Get("cluster_id").(string)
	nodeType := d.Get("node_type").(string)           // e.g) cache.m1.small
	numNodes := int64(d.Get("num_cache_nodes").(int)) // 2
	engine := d.Get("engine").(string)                // memcached
	engineVersion := d.Get("engine_version").(string) // 1.4.14
	port := int64(d.Get("port").(int))                // 11211
	subnetGroupName := d.Get("subnet_group_name").(string)
	securityNameSet := d.Get("security_group_names").(*schema.Set)
	securityIdSet := d.Get("security_group_ids").(*schema.Set)

	securityNames := expandStringList(securityNameSet.List())
	securityIds := expandStringList(securityIdSet.List())

	tags := tagsFromMapEC(d.Get("tags").(map[string]interface{}))
	req := &elasticache.CreateCacheClusterInput{
		CacheClusterID:          aws.String(clusterId),
		CacheNodeType:           aws.String(nodeType),
		NumCacheNodes:           aws.Long(numNodes),
		Engine:                  aws.String(engine),
		EngineVersion:           aws.String(engineVersion),
		Port:                    aws.Long(port),
		CacheSubnetGroupName:    aws.String(subnetGroupName),
		CacheSecurityGroupNames: securityNames,
		SecurityGroupIDs:        securityIds,
		Tags:                    tags,
	}

	// parameter groups are optional and can be defaulted by AWS
	if v, ok := d.GetOk("parameter_group_name"); ok {
		req.CacheParameterGroupName = aws.String(v.(string))
	}

	_, err := conn.CreateCacheCluster(req)
	if err != nil {
		return fmt.Errorf("Error creating Elasticache: %s", err)
	}

	pending := []string{"creating"}
	stateConf := &resource.StateChangeConf{
		Pending:    pending,
		Target:     "available",
		Refresh:    CacheClusterStateRefreshFunc(conn, d.Id(), "available", pending),
		Timeout:    10 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	log.Printf("[DEBUG] Waiting for state to become available: %v", d.Id())
	_, sterr := stateConf.WaitForState()
	if sterr != nil {
		return fmt.Errorf("Error waiting for elasticache (%s) to be created: %s", d.Id(), sterr)
	}

	d.SetId(clusterId)

	return resourceAwsElasticacheClusterRead(d, meta)
}

func resourceAwsElasticacheClusterRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).elasticacheconn
	req := &elasticache.DescribeCacheClustersInput{
		CacheClusterID:    aws.String(d.Id()),
		ShowCacheNodeInfo: aws.Boolean(true),
	}

	res, err := conn.DescribeCacheClusters(req)
	if err != nil {
		return err
	}

	if len(res.CacheClusters) == 1 {
		c := res.CacheClusters[0]
		d.Set("cluster_id", c.CacheClusterID)
		d.Set("node_type", c.CacheNodeType)
		d.Set("num_cache_nodes", c.NumCacheNodes)
		d.Set("engine", c.Engine)
		d.Set("engine_version", c.EngineVersion)
		if c.ConfigurationEndpoint != nil {
			d.Set("port", c.ConfigurationEndpoint.Port)
		}
		d.Set("subnet_group_name", c.CacheSubnetGroupName)
		d.Set("security_group_names", c.CacheSecurityGroups)
		d.Set("security_group_ids", c.SecurityGroups)
		d.Set("parameter_group_name", c.CacheParameterGroup)

		if err := setCacheNodeData(d, c); err != nil {
			return err
		}
		// list tags for resource
		// set tags
		arn, err := buildECARN(d, meta)
		if err != nil {
			log.Printf("[DEBUG] Error building ARN for ElastiCache Cluster, not setting Tags for cluster %s", *c.CacheClusterID)
		} else {
			resp, err := conn.ListTagsForResource(&elasticache.ListTagsForResourceInput{
				ResourceName: aws.String(arn),
			})

			if err != nil {
				log.Printf("[DEBUG] Error retreiving tags for ARN: %s", arn)
			}

			var et []*elasticache.Tag
			if len(resp.TagList) > 0 {
				et = resp.TagList
			}
			d.Set("tags", tagsToMapEC(et))
		}
	}

	return nil
}

func resourceAwsElasticacheClusterUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).elasticacheconn
	arn, err := buildECARN(d, meta)
	if err != nil {
		log.Printf("[DEBUG] Error building ARN for ElastiCache Cluster, not updating Tags for cluster %s", *c.CacheClusterID)
	} else {
		if err := setTagsEC(conn, d, arn); err != nil {
			return err
		}
	}
	return resourceAwsElasticacheClusterRead(d, meta)
}

func setCacheNodeData(d *schema.ResourceData, c *elasticache.CacheCluster) error {
	sortedCacheNodes := make([]*elasticache.CacheNode, len(c.CacheNodes))
	copy(sortedCacheNodes, c.CacheNodes)
	sort.Sort(byCacheNodeId(sortedCacheNodes))

	cacheNodeData := make([]map[string]interface{}, 0, len(sortedCacheNodes))

	for _, node := range sortedCacheNodes {
		if node.CacheNodeID == nil || node.Endpoint == nil || node.Endpoint.Address == nil || node.Endpoint.Port == nil {
			return fmt.Errorf("Unexpected nil pointer in: %#v", node)
		}
		cacheNodeData = append(cacheNodeData, map[string]interface{}{
			"id":      *node.CacheNodeID,
			"address": *node.Endpoint.Address,
			"port":    int(*node.Endpoint.Port),
		})
	}

	return d.Set("cache_nodes", cacheNodeData)
}

type byCacheNodeId []*elasticache.CacheNode

func (b byCacheNodeId) Len() int      { return len(b) }
func (b byCacheNodeId) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func (b byCacheNodeId) Less(i, j int) bool {
	return b[i].CacheNodeID != nil && b[j].CacheNodeID != nil &&
		*b[i].CacheNodeID < *b[j].CacheNodeID
}

func resourceAwsElasticacheClusterDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).elasticacheconn

	req := &elasticache.DeleteCacheClusterInput{
		CacheClusterID: aws.String(d.Id()),
	}
	_, err := conn.DeleteCacheCluster(req)
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] Waiting for deletion: %v", d.Id())
	stateConf := &resource.StateChangeConf{
		Pending:    []string{"creating", "available", "deleting", "incompatible-parameters", "incompatible-network", "restore-failed"},
		Target:     "",
		Refresh:    CacheClusterStateRefreshFunc(conn, d.Id(), "", []string{}),
		Timeout:    10 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	_, sterr := stateConf.WaitForState()
	if sterr != nil {
		return fmt.Errorf("Error waiting for elasticache (%s) to delete: %s", d.Id(), sterr)
	}

	d.SetId("")

	return nil
}

func CacheClusterStateRefreshFunc(conn *elasticache.ElastiCache, clusterID, givenState string, pending []string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		resp, err := conn.DescribeCacheClusters(&elasticache.DescribeCacheClustersInput{
			CacheClusterID: aws.String(clusterID),
		})
		if err != nil {
			apierr := err.(aws.APIError)
			log.Printf("[DEBUG] message: %v, code: %v", apierr.Message, apierr.Code)
			if apierr.Message == fmt.Sprintf("CacheCluster not found: %v", clusterID) {
				log.Printf("[DEBUG] Detect deletion")
				return nil, "", nil
			}

			log.Printf("[ERROR] CacheClusterStateRefreshFunc: %s", err)
			return nil, "", err
		}

		c := resp.CacheClusters[0]
		log.Printf("[DEBUG] status: %v", *c.CacheClusterStatus)

		// return the current state if it's in the pending array
		for _, p := range pending {
			s := *c.CacheClusterStatus
			if p == s {
				log.Printf("[DEBUG] Return with status: %v", *c.CacheClusterStatus)
				return c, p, nil
			}
		}

		// return given state if it's not in pending
		if givenState != "" {
			return c, givenState, nil
		}
		log.Printf("[DEBUG] current status: %v", *c.CacheClusterStatus)
		return c, *c.CacheClusterStatus, nil
	}
}

func buildECARN(d *schema.ResourceData, meta interface{}) (string, error) {
	iamconn := meta.(*AWSClient).iamconn
	region := meta.(*AWSClient).region
	// An zero value GetUserInput{} defers to the currently logged in user
	resp, err := iamconn.GetUser(&iam.GetUserInput{})
	if err != nil {
		return "", err
	}
	userARN := *resp.User.ARN
	accountID := strings.Split(userARN, ":")[4]
	arn := fmt.Sprintf("arn:aws:elasticache:%s:%s:cluster:%s", region, accountID, d.Id())
	return arn, nil
}
<|endoftext|>


<commit_before>package models

import (
	"os"
	"strconv"
	"syscall"

	client "github.com/coreos/etcd/clientv3"
	mgo "gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"

	"sunteng/commons/log"
	"sunteng/cronsun/conf"
)

const (
	Coll_Node = "node"
)

// 执行 cron cmd 的进程
// 注册到 /cronsun/proc/<id>
type Node struct {
	ID  string `bson:"_id" json:"id"`  // ip
	PID string `bson:"pid" json:"pid"` // 进程 pid

	Alived    bool `bson:"alived" json:"alived"` // 是否可用
	Connected bool `bson:"-" json:"connected"`   // 当 Alived 为 true 时有效，表示心跳是否正常
}

func (n *Node) String() string {
	return "node[" + n.ID + "] pid[" + n.PID + "]"
}

func (n *Node) Put(opts ...client.OpOption) (*client.PutResponse, error) {
	return DefalutClient.Put(conf.Config.Proc+n.ID, n.PID, opts...)
}

func (n *Node) Del() (*client.DeleteResponse, error) {
	return DefalutClient.Delete(conf.Config.Proc+n.ID, client.WithFromKey())
}

// 判断 node 是否已注册到 etcd
// 存在则返回进行 pid，不存在返回 -1
func (n *Node) Exist() (pid int, err error) {
	resp, err := DefalutClient.Get(conf.Config.Proc+n.ID, client.WithFromKey())
	if err != nil {
		return
	}

	if len(resp.Kvs) == 0 {
		return -1, nil
	}

	if pid, err = strconv.Atoi(string(resp.Kvs[0].Value)); err != nil {
		if _, err = DefalutClient.Delete(conf.Config.Proc+n.ID, client.WithFromKey()); err != nil {
			return
		}
		return -1, nil
	}

	p, err := os.FindProcess(pid)
	if err != nil {
		return -1, nil
	}

	// TODO: 暂时不考虑 linux/unix 以外的系统
	if p != nil && p.Signal(syscall.Signal(0)) == nil {
		return
	}

	return -1, nil
}

func GetNodes() (nodes []*Node, err error) {
	err = mgoDB.WithC(Coll_Node, func(c *mgo.Collection) error {
		return c.Find(nil).All(&nodes)
	})

	return
}

// On 结点实例启动后，在 mongoDB 中记录存活信息
func (n *Node) On() {
	n.Alived = true
	if err := mgoDB.Upsert(Coll_Node, bson.M{"_id": n.ID}, n); err != nil {
		log.Error(err.Error())
	}
}

// On 结点实例停用后，在 mongoDB 中去掉存活信息
func (n *Node) Down() {
	n.Alived = false
	if err := mgoDB.Upsert(Coll_Node, bson.M{"_id": n.ID}, n); err != nil {
		log.Error(err.Error())
	}
}
<commit_msg>node: 修复删除 key 参数错误导致删错 key 的问题<commit_after>package models

import (
	"os"
	"strconv"
	"syscall"

	client "github.com/coreos/etcd/clientv3"
	mgo "gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"

	"sunteng/commons/log"
	"sunteng/cronsun/conf"
)

const (
	Coll_Node = "node"
)

// 执行 cron cmd 的进程
// 注册到 /cronsun/proc/<id>
type Node struct {
	ID  string `bson:"_id" json:"id"`  // ip
	PID string `bson:"pid" json:"pid"` // 进程 pid

	Alived    bool `bson:"alived" json:"alived"` // 是否可用
	Connected bool `bson:"-" json:"connected"`   // 当 Alived 为 true 时有效，表示心跳是否正常
}

func (n *Node) String() string {
	return "node[" + n.ID + "] pid[" + n.PID + "]"
}

func (n *Node) Put(opts ...client.OpOption) (*client.PutResponse, error) {
	return DefalutClient.Put(conf.Config.Proc+n.ID, n.PID, opts...)
}

func (n *Node) Del() (*client.DeleteResponse, error) {
	return DefalutClient.Delete(conf.Config.Proc + n.ID)
}

// 判断 node 是否已注册到 etcd
// 存在则返回进行 pid，不存在返回 -1
func (n *Node) Exist() (pid int, err error) {
	resp, err := DefalutClient.Get(conf.Config.Proc + n.ID)
	if err != nil {
		return
	}

	if len(resp.Kvs) == 0 {
		return -1, nil
	}

	if pid, err = strconv.Atoi(string(resp.Kvs[0].Value)); err != nil {
		if _, err = DefalutClient.Delete(conf.Config.Proc + n.ID); err != nil {
			return
		}
		return -1, nil
	}

	p, err := os.FindProcess(pid)
	if err != nil {
		return -1, nil
	}

	// TODO: 暂时不考虑 linux/unix 以外的系统
	if p != nil && p.Signal(syscall.Signal(0)) == nil {
		return
	}

	return -1, nil
}

func GetNodes() (nodes []*Node, err error) {
	err = mgoDB.WithC(Coll_Node, func(c *mgo.Collection) error {
		return c.Find(nil).All(&nodes)
	})

	return
}

// On 结点实例启动后，在 mongoDB 中记录存活信息
func (n *Node) On() {
	n.Alived = true
	if err := mgoDB.Upsert(Coll_Node, bson.M{"_id": n.ID}, n); err != nil {
		log.Error(err.Error())
	}
}

// On 结点实例停用后，在 mongoDB 中去掉存活信息
func (n *Node) Down() {
	n.Alived = false
	if err := mgoDB.Upsert(Coll_Node, bson.M{"_id": n.ID}, n); err != nil {
		log.Error(err.Error())
	}
}
<|endoftext|>


<commit_before>package version

/* CHECKLIST
 * [x] Uses interfaces as appropriate
 * [x] Private package variables use underscore prefix
 * [x] All parameters validated
 * [x] All errors handled
 * [x] Reviewed for concurrency safety
 * [x] Code complete
 * [x] Full test coverage
 */

var (
	Base        string
	ShortCommit string
	FullCommit  string
)

func NewDefaultReporter() (Reporter, error) {
	return NewReporter(Base, ShortCommit, FullCommit)
}
<commit_msg>Add concurrency warning to default version<commit_after>package version

/* CHECKLIST
 * [x] Uses interfaces as appropriate
 * [x] Private package variables use underscore prefix
 * [x] All parameters validated
 * [x] All errors handled
 * [x] Reviewed for concurrency safety
 * [x] Code complete
 * [x] Full test coverage
 */

// WARNING: Concurrent modification of these global variables is unsupported

var (
	Base        string
	ShortCommit string
	FullCommit  string
)

func NewDefaultReporter() (Reporter, error) {
	return NewReporter(Base, ShortCommit, FullCommit)
}
<|endoftext|>


<commit_before>package component

import (
	"fmt"
	"net"

	"github.com/jcelliott/lumber"
	"github.com/nanobox-io/golang-docker-client"

	"github.com/nanobox-io/nanobox/models"
	"github.com/nanobox-io/nanobox/util/dhcp"
	"github.com/nanobox-io/nanobox/util/display"
	"github.com/nanobox-io/nanobox/util/provider"
)

// Destroy destroys a component from the provider and database
func Destroy(appModel *models.App, componentModel *models.Component) error {
	display.OpenContext(componentModel.Label)
	defer display.CloseContext()

	// remove the docker container
	if err := destroyContainer(componentModel.ID); err != nil {
		return err
	}

	// detach from the host network
	if err := detachNetwork(appModel, componentModel); err != nil {
		return fmt.Errorf("failed to detach container from the host network: %s", err.Error())
	}

	// purge evars
	if err := componentModel.PurgeEvars(appModel); err != nil {
		lumber.Error("component:Destroy:models.Component.PurgeEvars(%+v): %s", appModel, err.Error())
		return fmt.Errorf("failed to purge component evars from app: %s", err.Error())
	}

	// destroy the data model
	if err := componentModel.Delete(); err != nil {
		lumber.Error("component:Destroy:models.Component.Delete(): %s", err.Error())
		return fmt.Errorf("failed to destroy component model: %s", err.Error())
	}

	return nil
}

// destroyContainer destroys a docker container associated with this component
func destroyContainer(id string) error {
	display.StartTask("Destroying docker container")
	defer display.StopTask()

	// if i dont know the id then i cant remove it
	if id == "" {
		return nil
	}

	if err := docker.ContainerRemove(id); err != nil {
		lumber.Error("component:Destroy:docker.ContainerRemove(%s): %s", id, err.Error())
		display.ErrorTask()
		return fmt.Errorf("failed to remove docker container: %s", err.Error())
	}

	return nil
}

// detachNetwork detaches the network from the host
func detachNetwork(appModel *models.App, componentModel *models.Component) error {
	display.StartTask("Releasing IPs")
	defer display.StopTask()

	if componentModel.ExternalIP == "" {
		return nil
	}

	// remove NAT
	if err := provider.RemoveNat(componentModel.ExternalIP, componentModel.InternalIP); err != nil {
		lumber.Error("component:detachNetwork:provider.RemoveNat(%s, %s): %s", componentModel.ExternalIP, componentModel.InternalIP, err.Error())
		display.ErrorTask()
		return fmt.Errorf("failed to remove NAT from provider: %s", err.Error())
	}

	// remove IP
	if err := provider.RemoveIP(componentModel.ExternalIP); err != nil {
		lumber.Error("component:detachNetwork:provider.RemoveIP(%s): %s", componentModel.ExternalIP, err.Error())
		display.ErrorTask()
		return fmt.Errorf("failed to remove IP from provider: %s", err.Error())
	}

	// return the external IP
	// don't return the external IP if this is portal
	if componentModel.Name != "portal" && appModel.GlobalIPs[componentModel.Name] == "" {
		ip := net.ParseIP(componentModel.ExternalIP)
		if err := dhcp.ReturnIP(ip); err != nil {
			lumber.Error("component:detachNetwork:dhcp.ReturnIP(%s): %s", ip, err.Error())
			display.ErrorTask()
			return fmt.Errorf("failed to release IP back to pool: %s", err.Error())
		}
	}

	// return the internal IP
	// don't return the internal IP if it's an app-level cache
	if appModel.LocalIPs[componentModel.Name] == "" {
		ip := net.ParseIP(componentModel.InternalIP)
		if err := dhcp.ReturnIP(ip); err != nil {
			lumber.Error("component:detachNetwork:dhcp.ReturnIP(%s): %s", ip, err.Error())
			display.ErrorTask()
			return fmt.Errorf("failed to release IP back to pool: %s", err.Error())
		}
	}

	return nil
}
<commit_msg>remove failures if the container didnt exist fixes #250<commit_after>package component

import (
	"fmt"
	"net"

	"github.com/jcelliott/lumber"
	"github.com/nanobox-io/golang-docker-client"

	"github.com/nanobox-io/nanobox/models"
	"github.com/nanobox-io/nanobox/util/dhcp"
	"github.com/nanobox-io/nanobox/util/display"
	"github.com/nanobox-io/nanobox/util/provider"
)

// Destroy destroys a component from the provider and database
func Destroy(appModel *models.App, componentModel *models.Component) error {
	display.OpenContext(componentModel.Label)
	defer display.CloseContext()

	// remove the docker container
	if err := destroyContainer(componentModel.ID); err != nil {
		// report the error but continue on
		lumber.Error("component:Destroy:destroyContainer(%s): %s",componentModel.ID, err)
		// return err
	}

	// detach from the host network
	if err := detachNetwork(appModel, componentModel); err != nil {
		return fmt.Errorf("failed to detach container from the host network: %s", err.Error())
	}

	// purge evars
	if err := componentModel.PurgeEvars(appModel); err != nil {
		lumber.Error("component:Destroy:models.Component.PurgeEvars(%+v): %s", appModel, err.Error())
		return fmt.Errorf("failed to purge component evars from app: %s", err.Error())
	}

	// destroy the data model
	if err := componentModel.Delete(); err != nil {
		lumber.Error("component:Destroy:models.Component.Delete(): %s", err.Error())
		return fmt.Errorf("failed to destroy component model: %s", err.Error())
	}

	return nil
}

// destroyContainer destroys a docker container associated with this component
func destroyContainer(id string) error {
	display.StartTask("Destroying docker container")
	defer display.StopTask()

	// if i dont know the id then i cant remove it
	if id == "" {
		return nil
	}

	if err := docker.ContainerRemove(id); err != nil {
		lumber.Error("component:Destroy:docker.ContainerRemove(%s): %s", id, err.Error())
		display.ErrorTask()
		return fmt.Errorf("failed to remove docker container: %s", err.Error())
	}

	return nil
}

// detachNetwork detaches the network from the host
func detachNetwork(appModel *models.App, componentModel *models.Component) error {
	display.StartTask("Releasing IPs")
	defer display.StopTask()

	if componentModel.ExternalIP == "" || componentModel.InternalIP == "" {
		return nil
	}

	// remove NAT
	if err := provider.RemoveNat(componentModel.ExternalIP, componentModel.InternalIP); err != nil {
		lumber.Error("component:detachNetwork:provider.RemoveNat(%s, %s): %s", componentModel.ExternalIP, componentModel.InternalIP, err.Error())
		display.ErrorTask()
		return fmt.Errorf("failed to remove NAT from provider: %s", err.Error())
	}

	// remove IP
	if err := provider.RemoveIP(componentModel.ExternalIP); err != nil {
		lumber.Error("component:detachNetwork:provider.RemoveIP(%s): %s", componentModel.ExternalIP, err.Error())
		display.ErrorTask()
		return fmt.Errorf("failed to remove IP from provider: %s", err.Error())
	}

	// return the external IP
	// don't return the external IP if this is portal
	if componentModel.Name != "portal" && appModel.GlobalIPs[componentModel.Name] == "" {
		ip := net.ParseIP(componentModel.ExternalIP)
		if err := dhcp.ReturnIP(ip); err != nil {
			lumber.Error("component:detachNetwork:dhcp.ReturnIP(%s): %s", ip, err.Error())
			display.ErrorTask()
			return fmt.Errorf("failed to release IP back to pool: %s", err.Error())
		}
	}

	// return the internal IP
	// don't return the internal IP if it's an app-level cache
	if appModel.LocalIPs[componentModel.Name] == "" {
		ip := net.ParseIP(componentModel.InternalIP)
		if err := dhcp.ReturnIP(ip); err != nil {
			lumber.Error("component:detachNetwork:dhcp.ReturnIP(%s): %s", ip, err.Error())
			display.ErrorTask()
			return fmt.Errorf("failed to release IP back to pool: %s", err.Error())
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package version

// NOTE: we should probably be versioning the ABCI and the abci-cli separately

const Maj = "0"
const Min = "10"
const Fix = "3"

const Version = "0.10.3"
<commit_msg>version bump<commit_after>package version

// NOTE: we should probably be versioning the ABCI and the abci-cli separately

const Maj = "0"
const Min = "10"
const Fix = "4"

const Version = "0.10.4-dev"
<|endoftext|>


<commit_before>// The version package provides a location to set the release versions for all
// packages to consume, without creating import cycles.
//
// This pckage should not import any other terraform packages.
package version

import (
	"fmt"

	version "github.com/hashicorp/go-version"
)

// The main version number that is being run at the moment.
const Version = "0.11.0"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
var Prerelease = "dev"

// SemVer is an instance of version.Version. This has the secondary
// benefit of verifying during tests and init time that our version is a
// proper semantic version, which should always be the case.
var SemVer = version.Must(version.NewVersion(Version))

// Header is the header name used to send the current terraform version
// in http requests.
const Header = "Terraform-Version"

// String returns the complete version string, including prerelease
func String() string {
	if Prerelease != "" {
		return fmt.Sprintf("%s-%s", Version, Prerelease)
	}
	return Version
}
<commit_msg>v0.11.0-rc1<commit_after>// The version package provides a location to set the release versions for all
// packages to consume, without creating import cycles.
//
// This pckage should not import any other terraform packages.
package version

import (
	"fmt"

	version "github.com/hashicorp/go-version"
)

// The main version number that is being run at the moment.
const Version = "0.11.0"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
var Prerelease = "rc1"

// SemVer is an instance of version.Version. This has the secondary
// benefit of verifying during tests and init time that our version is a
// proper semantic version, which should always be the case.
var SemVer = version.Must(version.NewVersion(Version))

// Header is the header name used to send the current terraform version
// in http requests.
const Header = "Terraform-Version"

// String returns the complete version string, including prerelease
func String() string {
	if Prerelease != "" {
		return fmt.Sprintf("%s-%s", Version, Prerelease)
	}
	return Version
}
<|endoftext|>


<commit_before>package version

import (
	"fmt"

	"github.com/spf13/cobra"

	"stash.ovh.net/sailabove/sailgo/internal"
)

var Cmd = &cobra.Command{
	Use:     "version",
	Short:   "Display Version of sailgo : sailgo version",
	Long:    `sailgo version`,
	Aliases: []string{"v"},
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Printf("Version sailgo : %s\n", internal.VERSION)
		internal.ReadConfig()
	},
}
<commit_msg>chore : version for IC<commit_after>package version

import (
	"fmt"

	"github.com/spf13/cobra"

	"stash.ovh.net/sailabove/sailgo/internal"
)

var versionNewLine bool

func init() {
	Cmd.Flags().BoolVarP(&versionNewLine, "versionNewLine", "", true, "New line after version number.")
}

var Cmd = &cobra.Command{
	Use:     "version",
	Short:   "Display Version of sailgo : sailgo version",
	Long:    `sailgo version`,
	Aliases: []string{"v"},
	Run: func(cmd *cobra.Command, args []string) {
		if versionNewLine {
			fmt.Printf("Version sailgo : %s\n", internal.VERSION)
			internal.ReadConfig()
		} else {
			fmt.Printf(internal.VERSION)
		}
	},
}
<|endoftext|>


<commit_before>// Package version returns the version of the application
package version

// Version returns the current application version
const Version = "1.1.2"
<commit_msg>Bump version to 1.1.3<commit_after>// Package version returns the version of the application
package version

// Version returns the current application version
const Version = "1.1.3"
<|endoftext|>


<commit_before>package version

const (
	Version = "0.1.7+git"
)
<commit_msg>version: 0.1.8 bump<commit_after>package version

const (
	Version = "0.1.8"
)
<|endoftext|>


<commit_before>package version

// VERSION ...
const VERSION = "1.1.44"
<commit_msg>v1.1.45<commit_after>package version

// VERSION ...
const VERSION = "1.1.45"
<|endoftext|>


<commit_before>// Copyright 2016 The etcd-operator 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 version

var (
	Version = "0.3.3"
	GitSHA  = "Not provided (use ./build instead of go build)"
)
<commit_msg>version: bump 0.3.3+git<commit_after>// Copyright 2016 The etcd-operator 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 version

var (
	Version = "0.3.3+git"
	GitSHA  = "Not provided (use ./build instead of go build)"
)
<|endoftext|>


<commit_before>package version

// Version is the version of the build.
const Version = "1.8.1-dev"
<commit_msg>Release v1.9.0<commit_after>package version

// Version is the version of the build.
const Version = "1.9.0"
<|endoftext|>


<commit_before>package version

var (
	VERSION = "0.2.1"

	// GITCOMMIT will be overwritten automatically by the build system
	GITCOMMIT = "HEAD"

	FULL_VERSION = VERSION + " (" + GITCOMMIT + ")"
)
<commit_msg>bump version<commit_after>package version

var (
	VERSION = "0.2.2"

	// GITCOMMIT will be overwritten automatically by the build system
	GITCOMMIT = "HEAD"

	FULL_VERSION = VERSION + " (" + GITCOMMIT + ")"
)
<|endoftext|>


<commit_before>package version

var (
	// Version should be updated by hand at each release
	Version = "0.5.0-rc2"

	// GitCommit will be overwritten automatically by the build system
	GitCommit = "HEAD"
)
<commit_msg>Bump version to rc3<commit_after>package version

var (
	// Version should be updated by hand at each release
	Version = "0.5.0-rc3"

	// GitCommit will be overwritten automatically by the build system
	GitCommit = "HEAD"
)
<|endoftext|>


<commit_before>package version

// Version is the version of the build.
const Version = "1.10.1-dev"
<commit_msg>bump to v1.11.0-dev<commit_after>package version

// Version is the version of the build.
const Version = "1.11.0-dev"
<|endoftext|>


<commit_before>package agent

import (
	"github.com/hashicorp/serf/testutil"
	"io"
	"net"
	"os"
	"testing"
)

func testRPCClient(t *testing.T) (*RPCClient, *Agent, *AgentIPC) {
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	lw := NewLogWriter(512)
	mult := io.MultiWriter(os.Stderr, lw)

	agent := testAgent(mult)
	ipc := NewAgentIPC(agent, l, mult, lw)

	client, err := NewRPCClient(l.Addr().String())
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	return client, agent, ipc
}

func TestClientRegister(t *testing.T) {
	client, agent, ipc := testRPCClient(t)
	defer ipc.Shutdown()
	defer client.Close()
	defer agent.Shutdown()

	if err := agent.Start(); err != nil {
		t.Fatalf("err: %s", err)
	}

	testutil.Yield()

	resp, err := client.Register([]string{"/etc/watchdog.json"}, true, true)
	if err != nil {
		t.Fatal(err.Error())
	}

	if resp[0] != "process_1" {
		t.Errorf("Expected process name to be %s", "process_1")
	}
}
<commit_msg>Fixed register test<commit_after>package agent

import (
	"github.com/hashicorp/serf/testutil"
	"io"
	"io/ioutil"
	"net"
	"os"
	"testing"
)

func testRPCClient(t *testing.T) (*RPCClient, *Agent, *AgentIPC) {
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	lw := NewLogWriter(512)
	mult := io.MultiWriter(os.Stderr, lw)

	agent := testAgent(mult)
	ipc := NewAgentIPC(agent, l, mult, lw)

	client, err := NewRPCClient(l.Addr().String())
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	return client, agent, ipc
}

var basicConfig string = `{
  "name": "my_app",
  "disabled": false,
  "program": "/usr/local/bin/node",
  "program_arguments": [],
  "keep_alive": false,
  "run_at_load": true
}`

func TestClientRegister(t *testing.T) {
	tf, err := ioutil.TempFile("", "my_app.json")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	tf.Write([]byte(basicConfig))
	tf.Close()

	client, agent, ipc := testRPCClient(t)
	defer ipc.Shutdown()
	defer client.Close()
	defer agent.Shutdown()

	if err := agent.Start(); err != nil {
		t.Fatalf("err: %s", err)
	}

	testutil.Yield()

	resp, err := client.Register([]string{tf.Name()}, true, true)
	if err != nil {
		t.Fatal(err.Error())
	}

	if len(resp) == 0 {
		t.Fatal("No processes were registered")
	}

	if resp[0] != "my_app" {
		t.Errorf("Expected process name to be %s, got %v", "my_app", resp[0])
	}
}
<|endoftext|>


<commit_before>package mux

import (
	"fmt"
	"gondola/cache"
	"gondola/cookies"
	"gondola/errors"
	"gondola/serialize"
	"math"
	"net/http"
	"reflect"
	"runtime"
	"strconv"
	"strings"
	"time"
)

type ContextFinalizer func(*Context)

type Context struct {
	http.ResponseWriter
	R             *http.Request
	submatches    []string
	params        map[string]string
	c             *cache.Cache
	cached        bool
	fromCache     bool
	handlerName   string
	mux           *Mux
	statusCode    int
	customContext interface{}
	started       time.Time
	cookies       *cookies.Cookies
	Data          interface{} /* Left to the user */
}

// Count returns the number of elements captured
// by the pattern which matched the handler.
func (c *Context) Count() int {
	return len(c.submatches) - 1
}

// IndexValue returns the captured parameter
// at the given index or an empty string if no
// such parameter exists. Pass -1 to obtain
// the whole match.
func (c *Context) IndexValue(idx int) string {
	if idx >= -1 && idx < len(c.submatches)-1 {
		return c.submatches[idx+1]
	}
	return ""
}

// ParseIndexValue uses the captured parameter
// at the given index and tries to parse it
// into the given argument. See ParseFormValue
// for examples as well as the supported types.
func (c *Context) ParseIndexValue(idx int, arg interface{}) bool {
	val := c.IndexValue(idx)
	return c.parseTypedValue(val, arg)
}

// ParamValue returns the named captured parameter
// with the given name or an empty string if it
// does not exist.
func (c *Context) ParamValue(name string) string {
	return c.params[name]
}

// ParseParamValue uses the named captured parameter
// with the given name and tries to parse it into
// the given argument. See ParseFormValue
// for examples as well as the supported types.
func (c *Context) ParseParamValue(name string, arg interface{}) bool {
	val := c.ParamValue(name)
	return c.parseTypedValue(val, arg)
}

// FormValue returns the result of performing
// FormValue on the incoming request and trims
// any whitespaces on both sides. See the
// documentation for net/http for more details.
func (c *Context) FormValue(name string) string {
	return strings.TrimSpace(c.R.FormValue(name))
}

// RequireFormValue works like FormValue, but raises
// a MissingParameter error if the value is not present
// or empty.
func (c *Context) RequireFormValue(name string) string {
	val := c.FormValue(name)
	if val == "" {
		errors.MissingParameter(name)
	}
	return val
}

// ParseFormValue tries to parse the named form value into the given
// arg e.g.
// var f float32
// ctx.ParseFormValue("quality", &f)
// var width uint
// ctx.ParseFormValue("width", &width)
// Supported types are: bool, u?int(8|16|32|64)? and float(32|64)
func (c *Context) ParseFormValue(name string, arg interface{}) bool {
	val := c.FormValue(name)
	return c.parseTypedValue(val, arg)
}

// RequireParseFormValue works like ParseFormValue but raises a
// MissingParameterError if the parameter is missing or an
// InvalidParameterTypeError if the parameter does not have the
// required type
func (c *Context) RequireParseFormValue(name string, arg interface{}) {
	val := c.RequireFormValue(name)
	if !c.parseTypedValue(val, arg) {
		t := reflect.TypeOf(arg)
		for t.Kind() == reflect.Ptr {
			t = t.Elem()
		}
		errors.InvalidParameterType(name, t.String())
	}
}

// StatusCode returns the response status code. If the headers
// haven't been written yet, it returns 0
func (c *Context) StatusCode() int {
	return c.statusCode
}

func (c *Context) funcName(depth int) string {
	funcName := "???"
	caller, _, _, ok := runtime.Caller(depth + 1)
	if ok {
		f := runtime.FuncForPC(caller)
		if f != nil {
			fullName := strings.Trim(f.Name(), ".")
			parts := strings.Split(fullName, ".")
			simpleName := parts[len(parts)-1]
			funcName = fmt.Sprintf("%s()", simpleName)
		}
	}
	return funcName
}

func (c *Context) parseTypedValue(val string, arg interface{}) bool {
	v := reflect.ValueOf(arg)
	for v.Type().Kind() == reflect.Ptr {
		v = v.Elem()
	}
	if !v.CanSet() {
		panic(fmt.Errorf("Invalid argument type passed to %s. Please pass %s instead of %s.",
			c.funcName(1), reflect.PtrTo(v.Type()), v.Type()))
	}
	switch v.Type().Kind() {
	case reflect.Bool:
		res := false
		if val != "" && val != "0" && strings.ToLower(val) != "false" {
			res = true
		}
		v.SetBool(res)
		return true
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		res, err := strconv.ParseInt(val, 0, 64)
		if err == nil {
			if v.OverflowInt(res) {
				if res > 0 {
					res = int64(math.Pow(2, float64(8*v.Type().Size()-1)) - 1)
				} else {
					res = -int64(math.Pow(2, float64(8*v.Type().Size()-1)))
				}
			}
			v.SetInt(res)
			return true
		}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		res, err := strconv.ParseUint(val, 0, 64)
		if err == nil {
			if v.OverflowUint(res) {
				res = uint64(math.Pow(2, float64(8*v.Type().Size())) - 1)
			}
			v.SetUint(res)
			return true
		}
	case reflect.Float32, reflect.Float64:
		res, err := strconv.ParseFloat(val, 64)
		if err == nil {
			v.SetFloat(res)
			return true
		}
	case reflect.String:
		v.SetString(val)
		return true
	default:
		panic(fmt.Errorf("Invalid arguent type passed to %s: %s. Please, see ParseFormValue() docs for a list of the supported types.",
			c.funcName(1), v.Type()))
	}
	return false
}

// Cache returns the default cache
// See gondola/cache for a further
// explanation
func (c *Context) Cache() *cache.Cache {
	if c.c == nil {
		c.c = cache.NewDefault()
	}
	return c.c
}

// Redirect sends an HTTP redirect to the client,
// using the provided redirect, which may be either
// absolute or relative. The permanent argument
// indicates if the redirect should be sent as a
// permanent or a temporary one.
func (c *Context) Redirect(redir string, permanent bool) {
	code := http.StatusFound
	if permanent {
		code = http.StatusMovedPermanently
	}
	http.Redirect(c, c.R, redir, code)
}

// Error replies to the request with the specified
// message and HTTP code. If an error handler
// has been defined for the mux, it will be
// given the opportunity to intercept the
// error and provide its own response.
func (c *Context) Error(error string, code int) {
	c.mux.handleHTTPError(c, error, code)
}

// NotFound is equivalent to calling Error()
// with http.StatusNotFound.
func (c *Context) NotFound(error string) {
	c.Error(error, http.StatusNotFound)
}

// Forbidden is equivalent to calling Error()
// with http.StatusForbidden.
func (c *Context) Forbidden(error string) {
	c.Error(error, http.StatusForbidden)
}

// BadRequest is equivalent to calling Error()
// with http.StatusBadRequest.
func (c *Context) BadRequest(error string) {
	c.Error(error, http.StatusBadRequest)
}

// SetCached is used internaly by cache layers.
// Don't call this method
func (c *Context) SetCached(b bool) {
	c.cached = b
}

// SetServedFromCache is used internally by cache layers.
// Don't call this method
func (c *Context) SetServedFromCache(b bool) {
	c.fromCache = b
}

// Cached() returns true if the request was
// cached by a cache layer
// (see gondola/cache/layer)
func (c *Context) Cached() bool {
	return c.cached
}

// ServedFromCache returns true if the request
// was served by a cache layer
// (see gondola/cache/layer)
func (c *Context) ServedFromCache() bool {
	return c.fromCache
}

// HandlerName returns the name of the handler which
// handled this context
func (c *Context) HandlerName() string {
	return c.handlerName
}

// Mux returns the Mux this Context originated from
func (c *Context) Mux() *Mux {
	return c.mux
}

// MustReverse calls MustReverse on the mux this context originated
// from. See the documentation on Mux for details.
func (c *Context) MustReverse(name string, args ...interface{}) string {
	return c.mux.MustReverse(name, args...)
}

// Reverse calls Reverse on the mux this context originated
// from. See the documentation on Mux for details.
func (c *Context) Reverse(name string, args ...interface{}) (string, error) {
	return c.mux.Reverse(name, args...)
}

// RedirectReverse calls Reverse to find the URL and then sends
// the redirect to the client. See the documentation on Mux.Reverse
// for further details.
func (c *Context) RedirectReverse(permanent bool, name string, args ...interface{}) error {
	rev, err := c.Reverse(name, args...)
	if err != nil {
		return err
	}
	c.Redirect(rev, permanent)
	return nil
}

// Cookies returns a coookies.Cookies object which
// can be used to set and delete cookies. See the documentation
// on gondola/cookies for more information.
func (c *Context) Cookies() *cookies.Cookies {
	if c.cookies == nil {
		mux := c.Mux()
		c.cookies = cookies.New(c.R, c, mux.Secret(),
			mux.EncryptionKey(), mux.DefaultCookieOptions())
	}
	return c.cookies
}

// Execute loads the template with the given name using the
// mux template loader and executes it with the data argument.
func (c *Context) Execute(name string, data interface{}) error {
	tmpl, err := c.mux.LoadTemplate(name)
	if err != nil {
		return err
	}
	return tmpl.Execute(c, data)
}

// MustExecute works like Execute, but panics if there's an error
func (c *Context) MustExecute(name string, data interface{}) {
	err := c.Execute(name, data)
	if err != nil {
		panic(err)
	}
}

// WriteJson is equivalent to serialize.WriteJson(ctx, data)
func (c *Context) WriteJson(data interface{}) (int, error) {
	return serialize.WriteJson(c, data)
}

// WriteXml is equivalent to serialize.WriteXml(ctx, data)
func (c *Context) WriteXml(data interface{}) (int, error) {
	return serialize.WriteXml(c, data)
}

// Custom returns the custom type context wrapped in
// an interface{}. Intended for use in templates
// e.g. {{ context.Custom.MyCustomMethod }}
//
// For use in code it's better to use the function
// you previously defined as the context transformation for
// the mux e.g.
// function in your own code to avoid type assertions
// type mycontext mux.Context
// func Context(ctx *mux.Context) *mycontext {
//	return (*mycontext)(ctx)
// }
// ...
// mymux.SetContextTransform(Context)
func (c *Context) Custom() interface{} {
	if c.customContext == nil {
		if c.mux.contextTransform != nil {
			result := c.mux.contextTransform.Call([]reflect.Value{reflect.ValueOf(c)})
			c.customContext = result[0].Interface()
		} else {
			c.customContext = c
		}
	}
	return c.customContext
}

// Close closes any resources opened by the context
// (for now, the cache connection). It's automatically
// called by the mux, so you don't need to call it
// manually
func (c *Context) Close() {
	if c.c != nil {
		c.c.Close()
		c.c = nil
	}
}

// Intercept http.ResponseWriter calls to find response
// status code

func (c *Context) WriteHeader(code int) {
	c.statusCode = code
	c.ResponseWriter.WriteHeader(code)
}

func (c *Context) Write(data []byte) (int, error) {
	n, err := c.ResponseWriter.Write(data)
	if c.statusCode == 0 {
		c.statusCode = http.StatusOK
	}
	return n, err
}
<commit_msg>Add MustRedirectReverse()<commit_after>package mux

import (
	"fmt"
	"gondola/cache"
	"gondola/cookies"
	"gondola/errors"
	"gondola/serialize"
	"math"
	"net/http"
	"reflect"
	"runtime"
	"strconv"
	"strings"
	"time"
)

type ContextFinalizer func(*Context)

type Context struct {
	http.ResponseWriter
	R             *http.Request
	submatches    []string
	params        map[string]string
	c             *cache.Cache
	cached        bool
	fromCache     bool
	handlerName   string
	mux           *Mux
	statusCode    int
	customContext interface{}
	started       time.Time
	cookies       *cookies.Cookies
	Data          interface{} /* Left to the user */
}

// Count returns the number of elements captured
// by the pattern which matched the handler.
func (c *Context) Count() int {
	return len(c.submatches) - 1
}

// IndexValue returns the captured parameter
// at the given index or an empty string if no
// such parameter exists. Pass -1 to obtain
// the whole match.
func (c *Context) IndexValue(idx int) string {
	if idx >= -1 && idx < len(c.submatches)-1 {
		return c.submatches[idx+1]
	}
	return ""
}

// ParseIndexValue uses the captured parameter
// at the given index and tries to parse it
// into the given argument. See ParseFormValue
// for examples as well as the supported types.
func (c *Context) ParseIndexValue(idx int, arg interface{}) bool {
	val := c.IndexValue(idx)
	return c.parseTypedValue(val, arg)
}

// ParamValue returns the named captured parameter
// with the given name or an empty string if it
// does not exist.
func (c *Context) ParamValue(name string) string {
	return c.params[name]
}

// ParseParamValue uses the named captured parameter
// with the given name and tries to parse it into
// the given argument. See ParseFormValue
// for examples as well as the supported types.
func (c *Context) ParseParamValue(name string, arg interface{}) bool {
	val := c.ParamValue(name)
	return c.parseTypedValue(val, arg)
}

// FormValue returns the result of performing
// FormValue on the incoming request and trims
// any whitespaces on both sides. See the
// documentation for net/http for more details.
func (c *Context) FormValue(name string) string {
	return strings.TrimSpace(c.R.FormValue(name))
}

// RequireFormValue works like FormValue, but raises
// a MissingParameter error if the value is not present
// or empty.
func (c *Context) RequireFormValue(name string) string {
	val := c.FormValue(name)
	if val == "" {
		errors.MissingParameter(name)
	}
	return val
}

// ParseFormValue tries to parse the named form value into the given
// arg e.g.
// var f float32
// ctx.ParseFormValue("quality", &f)
// var width uint
// ctx.ParseFormValue("width", &width)
// Supported types are: bool, u?int(8|16|32|64)? and float(32|64)
func (c *Context) ParseFormValue(name string, arg interface{}) bool {
	val := c.FormValue(name)
	return c.parseTypedValue(val, arg)
}

// RequireParseFormValue works like ParseFormValue but raises a
// MissingParameterError if the parameter is missing or an
// InvalidParameterTypeError if the parameter does not have the
// required type
func (c *Context) RequireParseFormValue(name string, arg interface{}) {
	val := c.RequireFormValue(name)
	if !c.parseTypedValue(val, arg) {
		t := reflect.TypeOf(arg)
		for t.Kind() == reflect.Ptr {
			t = t.Elem()
		}
		errors.InvalidParameterType(name, t.String())
	}
}

// StatusCode returns the response status code. If the headers
// haven't been written yet, it returns 0
func (c *Context) StatusCode() int {
	return c.statusCode
}

func (c *Context) funcName(depth int) string {
	funcName := "???"
	caller, _, _, ok := runtime.Caller(depth + 1)
	if ok {
		f := runtime.FuncForPC(caller)
		if f != nil {
			fullName := strings.Trim(f.Name(), ".")
			parts := strings.Split(fullName, ".")
			simpleName := parts[len(parts)-1]
			funcName = fmt.Sprintf("%s()", simpleName)
		}
	}
	return funcName
}

func (c *Context) parseTypedValue(val string, arg interface{}) bool {
	v := reflect.ValueOf(arg)
	for v.Type().Kind() == reflect.Ptr {
		v = v.Elem()
	}
	if !v.CanSet() {
		panic(fmt.Errorf("Invalid argument type passed to %s. Please pass %s instead of %s.",
			c.funcName(1), reflect.PtrTo(v.Type()), v.Type()))
	}
	switch v.Type().Kind() {
	case reflect.Bool:
		res := false
		if val != "" && val != "0" && strings.ToLower(val) != "false" {
			res = true
		}
		v.SetBool(res)
		return true
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		res, err := strconv.ParseInt(val, 0, 64)
		if err == nil {
			if v.OverflowInt(res) {
				if res > 0 {
					res = int64(math.Pow(2, float64(8*v.Type().Size()-1)) - 1)
				} else {
					res = -int64(math.Pow(2, float64(8*v.Type().Size()-1)))
				}
			}
			v.SetInt(res)
			return true
		}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		res, err := strconv.ParseUint(val, 0, 64)
		if err == nil {
			if v.OverflowUint(res) {
				res = uint64(math.Pow(2, float64(8*v.Type().Size())) - 1)
			}
			v.SetUint(res)
			return true
		}
	case reflect.Float32, reflect.Float64:
		res, err := strconv.ParseFloat(val, 64)
		if err == nil {
			v.SetFloat(res)
			return true
		}
	case reflect.String:
		v.SetString(val)
		return true
	default:
		panic(fmt.Errorf("Invalid arguent type passed to %s: %s. Please, see ParseFormValue() docs for a list of the supported types.",
			c.funcName(1), v.Type()))
	}
	return false
}

// Cache returns the default cache
// See gondola/cache for a further
// explanation
func (c *Context) Cache() *cache.Cache {
	if c.c == nil {
		c.c = cache.NewDefault()
	}
	return c.c
}

// Redirect sends an HTTP redirect to the client,
// using the provided redirect, which may be either
// absolute or relative. The permanent argument
// indicates if the redirect should be sent as a
// permanent or a temporary one.
func (c *Context) Redirect(redir string, permanent bool) {
	code := http.StatusFound
	if permanent {
		code = http.StatusMovedPermanently
	}
	http.Redirect(c, c.R, redir, code)
}

// Error replies to the request with the specified
// message and HTTP code. If an error handler
// has been defined for the mux, it will be
// given the opportunity to intercept the
// error and provide its own response.
func (c *Context) Error(error string, code int) {
	c.mux.handleHTTPError(c, error, code)
}

// NotFound is equivalent to calling Error()
// with http.StatusNotFound.
func (c *Context) NotFound(error string) {
	c.Error(error, http.StatusNotFound)
}

// Forbidden is equivalent to calling Error()
// with http.StatusForbidden.
func (c *Context) Forbidden(error string) {
	c.Error(error, http.StatusForbidden)
}

// BadRequest is equivalent to calling Error()
// with http.StatusBadRequest.
func (c *Context) BadRequest(error string) {
	c.Error(error, http.StatusBadRequest)
}

// SetCached is used internaly by cache layers.
// Don't call this method
func (c *Context) SetCached(b bool) {
	c.cached = b
}

// SetServedFromCache is used internally by cache layers.
// Don't call this method
func (c *Context) SetServedFromCache(b bool) {
	c.fromCache = b
}

// Cached() returns true if the request was
// cached by a cache layer
// (see gondola/cache/layer)
func (c *Context) Cached() bool {
	return c.cached
}

// ServedFromCache returns true if the request
// was served by a cache layer
// (see gondola/cache/layer)
func (c *Context) ServedFromCache() bool {
	return c.fromCache
}

// HandlerName returns the name of the handler which
// handled this context
func (c *Context) HandlerName() string {
	return c.handlerName
}

// Mux returns the Mux this Context originated from
func (c *Context) Mux() *Mux {
	return c.mux
}

// MustReverse calls MustReverse on the mux this context originated
// from. See the documentation on Mux for details.
func (c *Context) MustReverse(name string, args ...interface{}) string {
	return c.mux.MustReverse(name, args...)
}

// Reverse calls Reverse on the mux this context originated
// from. See the documentation on Mux for details.
func (c *Context) Reverse(name string, args ...interface{}) (string, error) {
	return c.mux.Reverse(name, args...)
}

// RedirectReverse calls Reverse to find the URL and then sends
// the redirect to the client. See the documentation on Mux.Reverse
// for further details.
func (c *Context) RedirectReverse(permanent bool, name string, args ...interface{}) error {
	rev, err := c.Reverse(name, args...)
	if err != nil {
		return err
	}
	c.Redirect(rev, permanent)
	return nil
}

// MustRedirectReverse works like RedirectReverse, but panics if
// there's an error.
func (c *Context) MustRedirectReverse(permanent bool, name string, args ...interface{}) {
	err := c.RedirectReverse(permanent, name, args...)
	if err != nil {
		panic(err)
	}
}

// Cookies returns a coookies.Cookies object which
// can be used to set and delete cookies. See the documentation
// on gondola/cookies for more information.
func (c *Context) Cookies() *cookies.Cookies {
	if c.cookies == nil {
		mux := c.Mux()
		c.cookies = cookies.New(c.R, c, mux.Secret(),
			mux.EncryptionKey(), mux.DefaultCookieOptions())
	}
	return c.cookies
}

// Execute loads the template with the given name using the
// mux template loader and executes it with the data argument.
func (c *Context) Execute(name string, data interface{}) error {
	tmpl, err := c.mux.LoadTemplate(name)
	if err != nil {
		return err
	}
	return tmpl.Execute(c, data)
}

// MustExecute works like Execute, but panics if there's an error
func (c *Context) MustExecute(name string, data interface{}) {
	err := c.Execute(name, data)
	if err != nil {
		panic(err)
	}
}

// WriteJson is equivalent to serialize.WriteJson(ctx, data)
func (c *Context) WriteJson(data interface{}) (int, error) {
	return serialize.WriteJson(c, data)
}

// WriteXml is equivalent to serialize.WriteXml(ctx, data)
func (c *Context) WriteXml(data interface{}) (int, error) {
	return serialize.WriteXml(c, data)
}

// Custom returns the custom type context wrapped in
// an interface{}. Intended for use in templates
// e.g. {{ context.Custom.MyCustomMethod }}
//
// For use in code it's better to use the function
// you previously defined as the context transformation for
// the mux e.g.
// function in your own code to avoid type assertions
// type mycontext mux.Context
// func Context(ctx *mux.Context) *mycontext {
//	return (*mycontext)(ctx)
// }
// ...
// mymux.SetContextTransform(Context)
func (c *Context) Custom() interface{} {
	if c.customContext == nil {
		if c.mux.contextTransform != nil {
			result := c.mux.contextTransform.Call([]reflect.Value{reflect.ValueOf(c)})
			c.customContext = result[0].Interface()
		} else {
			c.customContext = c
		}
	}
	return c.customContext
}

// Close closes any resources opened by the context
// (for now, the cache connection). It's automatically
// called by the mux, so you don't need to call it
// manually
func (c *Context) Close() {
	if c.c != nil {
		c.c.Close()
		c.c = nil
	}
}

// Intercept http.ResponseWriter calls to find response
// status code

func (c *Context) WriteHeader(code int) {
	c.statusCode = code
	c.ResponseWriter.WriteHeader(code)
}

func (c *Context) Write(data []byte) (int, error) {
	n, err := c.ResponseWriter.Write(data)
	if c.statusCode == 0 {
		c.statusCode = http.StatusOK
	}
	return n, err
}
<|endoftext|>


<commit_before>package eventbus

type subSettings struct {
	buffer int
}

var subSettingsDefault = subSettings{
	buffer: 16,
}

func BufSize(n int) func(interface{}) error {
	return func(s interface{}) error {
		s.(*subSettings).buffer = n
		return nil
	}
}

type emitterSettings struct {
	makeStateful bool
}

// Stateful is an Emitter option which makes makes the eventbus channel
// 'remember' last event sent, and when a new subscriber joins the
// bus, the remembered event is immediately sent to the subscription
// channel.
//
// This allows to provide state tracking for dynamic systems, and/or
// allows new subscribers to verify that there are Emitters on the channel
func Stateful(s interface{}) error {
	s.(*emitterSettings).makeStateful = true
	return nil
}
<commit_msg>Correct a typo<commit_after>package eventbus

type subSettings struct {
	buffer int
}

var subSettingsDefault = subSettings{
	buffer: 16,
}

func BufSize(n int) func(interface{}) error {
	return func(s interface{}) error {
		s.(*subSettings).buffer = n
		return nil
	}
}

type emitterSettings struct {
	makeStateful bool
}

// Stateful is an Emitter option which makes  the eventbus channel
// 'remember' last event sent, and when a new subscriber joins the
// bus, the remembered event is immediately sent to the subscription
// channel.
//
// This allows to provide state tracking for dynamic systems, and/or
// allows new subscribers to verify that there are Emitters on the channel
func Stateful(s interface{}) error {
	s.(*emitterSettings).makeStateful = true
	return nil
}
<|endoftext|>