<commit_before>package mpawselasticache

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudwatch"
	mp "github.com/mackerelio/go-mackerel-plugin"
)

var metricsdefMemcached = []string{
	"CPUUtilization", "SwapUsage", "FreeableMemory", "NetworkBytesIn", "NetworkBytesOut",
	"BytesUsedForCacheItems", "BytesReadIntoMemcached", "BytesWrittenOutFromMemcached", "CasBadval", "CasHits",
	"CasMisses", "CmdFlush", "CmdGet", "CmdSet", "CurrConnections",
	"CurrItems", "DecrHits", "DecrMisses", "DeleteHits", "DeleteMisses",
	"Evictions", "GetHits", "GetMisses", "IncrHits", "IncrMisses",
	"Reclaimed", "BytesUsedForHash", "CmdConfigGet", "CmdConfigSet", "CmdTouch",
	"CurrConfig", "EvictedUnfetched", "ExpiredUnfetched", "SlabsMoved", "TouchHits",
	"TouchMisses", "NewConnections", "NewItems", "UnusedMemory",
}

var metricsdefRedis = []string{
	"CPUUtilization", "SwapUsage", "FreeableMemory", "NetworkBytesIn", "NetworkBytesOut",
	"CurrConnections", "Evictions", "Reclaimed", "NewConnections", "BytesUsedForCache",
	"CacheHits", "CacheMisses", "ReplicationLag", "GetTypeCmds", "SetTypeCmds",
	"KeyBasedCmds", "StringBasedCmds", "HashBasedCmds", "ListBasedCmds", "SetBasedCmds",
	"SortedSetBasedCmds", "CurrItems",
}

var graphdefMemcached = map[string]mp.Graphs{
	"ecache.CPUUtilization": {
		Label: "ECache CPU Utilization",
		Unit:  "percentage",
		Metrics: []mp.Metrics{
			{Name: "CPUUtilization", Label: "CPUUtilization"},
		},
	},
	"ecache.SwapUsage": {
		Label: "ECache Swap Usage",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "SwapUsage", Label: "SwapUsage"},
		},
	},
	"ecache.FreeableMemory": {
		Label: "ECache Freeable Memory",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "FreeableMemory", Label: "FreeableMemory"},
		},
	},
	"ecache.NetworkTraffic": {
		Label: "ECache Network Traffic",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "NetworkBytesIn", Label: "NetworkBytesIn"},
			{Name: "NetworkBytesOut", Label: "NetworkBytesOut"},
		},
	},
	"ecache.Command": {
		Label: "ECache Command",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "CmdGet", Label: "CmdGet"},
			{Name: "CmdSet", Label: "CmdSet"},
			{Name: "CmdFlush", Label: "CmdFlush"},
			{Name: "CmdTouch", Label: "CmdTouch"},
			{Name: "CmdConfigGet", Label: "CmdConfigGet"},
			{Name: "CmdConfigSet", Label: "CmdConfigSet"},
		},
	},
	"ecache.CacheHitAndMiss": {
		Label: "ECache Hits/Misses",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "GetHits", Label: "GetHits"},
			{Name: "GetMisses", Label: "GetMisses"},
			{Name: "DeleteHits", Label: "DeleteHits"},
			{Name: "DeleteMisses", Label: "DeleteMisses"},
			{Name: "IncrHits", Label: "IncrHits"},
			{Name: "IncrMisses", Label: "IncrMisses"},
			{Name: "DecrHits", Label: "DecrHits"},
			{Name: "DecrMisses", Label: "DecrMisses"},
			{Name: "CasBadval", Label: "CasBadval"},
			{Name: "CasHits", Label: "CasHits"},
			{Name: "CasMisses", Label: "CasMisses"},
			{Name: "TouchHits", Label: "TouchHits"},
			{Name: "TouchMisses", Label: "TouchMisses"},
		},
	},
	"ecache.Evictions": {
		Label: "ECache Evictions",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "Evictions", Label: "Evictions"},
		},
	},
	"ecache.Unfetched": {
		Label: "ECache Unfetched",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "EvictedUnfetched", Label: "EvictedUnfetched"},
			{Name: "ExpiredUnfetched", Label: "ExpiredUnfetched"},
		},
	},
	"ecache.Bytes": {
		Label: "ECache Traffics",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "BytesReadIntoMemcached", Label: "BytesReadIntoMemcached"},
			{Name: "BytesWrittenOutFromMemcached", Label: "BytesWrittenOutFromMemcached"},
		},
	},
	"ecache.Connections": {
		Label: "ECache Connections",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "CurrConnections", Label: "CurrConnections"},
			{Name: "NewConnections", Label: "NewConnections"},
		},
	},
	"ecache.Items": {
		Label: "ECache Items",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "CurrItems", Label: "CurrItems"},
			{Name: "NewItems", Label: "NewItems"},
			{Name: "Reclaimed", Label: "Reclaimed"},
			{Name: "CurrConfig", Label: "CurrConfig"},
			{Name: "SlabsMoved", Label: "SlabsMoved"},
		},
	},
	"ecache.MemoryUsage": {
		Label: "ECache Memory Usage",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "UnusedMemory", Label: "UnusedMemory"},
			{Name: "BytesUsedForHash", Label: "BytesUsedForHash"},
			{Name: "BytesUsedForCacheItems", Label: "BytesUsedForCacheItems"},
		},
	},
}

var graphdefRedis = map[string]mp.Graphs{
	"ecache.CPUUtilization": {
		Label: "ECache CPU Utilization",
		Unit:  "percentage",
		Metrics: []mp.Metrics{
			{Name: "CPUUtilization", Label: "CPUUtilization"},
		},
	},
	"ecache.SwapUsage": {
		Label: "ECache Swap Usage",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "SwapUsage", Label: "SwapUsage"},
		},
	},
	"ecache.FreeableMemory": {
		Label: "ECache Freeable Memory",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "FreeableMemory", Label: "FreeableMemory"},
		},
	},
	"ecache.NetworkTraffic": {
		Label: "ECache Network Traffic",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "NetworkBytesIn", Label: "NetworkBytesIn"},
			{Name: "NetworkBytesOut", Label: "NetworkBytesOut"},
		},
	},
	"ecache.Command": {
		Label: "ECache Command",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "GetTypeCmds", Label: "GetTypeCmds"},
			{Name: "SetTypeCmds", Label: "SetTypeCmds"},
			{Name: "KeyBasedCmds", Label: "KeyBasedCmds"},
			{Name: "StringBasedCmds", Label: "StringBasedCmds"},
			{Name: "HashBasedCmds", Label: "HashBasedCmds"},
			{Name: "ListBasedCmds", Label: "ListBasedCmds"},
			{Name: "SetBasedCmds", Label: "SetBasedCmds"},
			{Name: "SortedSetBasedCmds", Label: "SortedSetBasedCmds"},
		},
	},
	"ecache.CacheHitAndMiss": {
		Label: "ECache Hits/Misses",
		Unit:  "float",
		Metrics: []mp.Metrics{
			{Name: "CacheHits", Label: "CacheHits"},
			{Name: "CacheMisses", Label: "CacheMisses"},
		},
	},
	"ecache.Evictions": {
		Label: "ECache Evictions",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "Evictions", Label: "Evictions"},
		},
	},
	"ecache.Bytes": {
		Label: "ECache Traffics",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "BytesUsedForCache", Label: "BytesUsedForCache"},
		},
	},
	"ecache.Connections": {
		Label: "ECache Connections",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "CurrConnections", Label: "CurrConnections"},
			{Name: "NewConnections", Label: "NewConnections"},
		},
	},
	"ecache.Items": {
		Label: "ECache Items",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "CurrItems", Label: "CurrItems"},
			{Name: "Reclaimed", Label: "Reclaimed"},
		},
	},
}

// ECachePlugin mackerel plugin for elasticache
type ECachePlugin struct {
	Region          string
	AccessKeyID     string
	SecretAccessKey string
	CacheClusterID  string
	CacheNodeID     string
	ElastiCacheType string
	CacheMetrics    []string
}

func getLastPoint(cloudWatch *cloudwatch.CloudWatch, dimensions []*cloudwatch.Dimension, metricName string) (float64, error) {
	now := time.Now()

	response, err := cloudWatch.GetMetricStatistics(&cloudwatch.GetMetricStatisticsInput{
		Dimensions: dimensions,
		StartTime:  aws.Time(now.Add(time.Duration(180) * time.Second * -1)), // 3 mins (to fetch at least 1 data-point)
		EndTime:    aws.Time(now),
		MetricName: aws.String(metricName),
		Period:     aws.Int64(60),
		Statistics: []*string{aws.String("Average")},
		Namespace:  aws.String("AWS/ElastiCache"),
	})
	if err != nil {
		return 0, err
	}

	datapoints := response.Datapoints
	if len(datapoints) == 0 {
		return 0, errors.New("fetched no datapoints")
	}

	latest := new(time.Time)
	var latestVal float64
	for _, dp := range datapoints {
		if dp.Timestamp.Before(*latest) {
			continue
		}

		latest = dp.Timestamp
		latestVal = *dp.Average
	}

	return latestVal, nil
}

// FetchMetrics fetch elasticache values
func (p ECachePlugin) FetchMetrics() (map[string]float64, error) {
	sess, err := session.NewSession()
	if err != nil {
		return nil, err
	}

	config := aws.NewConfig()
	if p.AccessKeyID != "" && p.SecretAccessKey != "" {
		config = config.WithCredentials(credentials.NewStaticCredentials(p.AccessKeyID, p.SecretAccessKey, ""))
	}
	if p.Region != "" {
		config = config.WithRegion(p.Region)
	}

	cloudWatch := cloudwatch.New(sess, config)

	stat := make(map[string]float64)

	perInstances := []*cloudwatch.Dimension{
		{
			Name:  aws.String("CacheClusterId"),
			Value: aws.String(p.CacheClusterID),
		},
		{
			Name:  aws.String("CacheNodeId"),
			Value: aws.String(p.CacheNodeID),
		},
	}

	for _, met := range p.CacheMetrics {
		v, err := getLastPoint(cloudWatch, perInstances, met)
		if err == nil {
			stat[met] = v
		} else {
			log.Printf("%s: %s", met, err)
		}
	}

	return stat, nil
}

// GraphDefinition graph definition
func (p ECachePlugin) GraphDefinition() map[string]mp.Graphs {
	switch p.ElastiCacheType {
	case "memcached":
		return graphdefMemcached
	case "redis":
		return graphdefRedis
	default:
		log.Printf("elasticache-type is 'memcached' or 'redis'.")
		return nil
	}
}

// Do the plugin
func Do() {
	optRegion := flag.String("region", "", "AWS Region")
	optAccessKeyID := flag.String("access-key-id", "", "AWS Access Key ID")
	optSecretAccessKey := flag.String("secret-access-key", "", "AWS Secret Access Key")
	optCacheClusterID := flag.String("cache-cluster-id", "", "Cache Cluster Id")
	optCacheNodeID := flag.String("cache-node-id", "0001", "Cache Node Id")
	optElastiCacheType := flag.String("elasticache-type", "", "ElastiCache type")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	var ecache ECachePlugin

	ecache.Region = *optRegion
	ecache.AccessKeyID = *optAccessKeyID
	ecache.SecretAccessKey = *optSecretAccessKey
	ecache.CacheClusterID = *optCacheClusterID
	ecache.CacheNodeID = *optCacheNodeID
	ecache.ElastiCacheType = *optElastiCacheType
	switch ecache.ElastiCacheType {
	case "memcached":
		ecache.CacheMetrics = metricsdefMemcached
	case "redis":
		ecache.CacheMetrics = metricsdefRedis
	default:
		log.Printf("elasticache-type is 'memcached' or 'redis'.")
		os.Exit(1)
	}

	helper := mp.NewMackerelPlugin(ecache)
	if *optTempfile != "" {
		helper.Tempfile = *optTempfile
	} else {
		helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-aws-elasticache-%s-%s", *optCacheClusterID, *optCacheNodeID)
	}

	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		helper.OutputDefinitions()
	} else {
		helper.OutputValues()
	}
}
<commit_msg>[aws-elasticache] Rebirth region auto detection when running in ec2<commit_after>package mpawselasticache

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudwatch"
	mp "github.com/mackerelio/go-mackerel-plugin"
)

var metricsdefMemcached = []string{
	"CPUUtilization", "SwapUsage", "FreeableMemory", "NetworkBytesIn", "NetworkBytesOut",
	"BytesUsedForCacheItems", "BytesReadIntoMemcached", "BytesWrittenOutFromMemcached", "CasBadval", "CasHits",
	"CasMisses", "CmdFlush", "CmdGet", "CmdSet", "CurrConnections",
	"CurrItems", "DecrHits", "DecrMisses", "DeleteHits", "DeleteMisses",
	"Evictions", "GetHits", "GetMisses", "IncrHits", "IncrMisses",
	"Reclaimed", "BytesUsedForHash", "CmdConfigGet", "CmdConfigSet", "CmdTouch",
	"CurrConfig", "EvictedUnfetched", "ExpiredUnfetched", "SlabsMoved", "TouchHits",
	"TouchMisses", "NewConnections", "NewItems", "UnusedMemory",
}

var metricsdefRedis = []string{
	"CPUUtilization", "SwapUsage", "FreeableMemory", "NetworkBytesIn", "NetworkBytesOut",
	"CurrConnections", "Evictions", "Reclaimed", "NewConnections", "BytesUsedForCache",
	"CacheHits", "CacheMisses", "ReplicationLag", "GetTypeCmds", "SetTypeCmds",
	"KeyBasedCmds", "StringBasedCmds", "HashBasedCmds", "ListBasedCmds", "SetBasedCmds",
	"SortedSetBasedCmds", "CurrItems",
}

var graphdefMemcached = map[string]mp.Graphs{
	"ecache.CPUUtilization": {
		Label: "ECache CPU Utilization",
		Unit:  "percentage",
		Metrics: []mp.Metrics{
			{Name: "CPUUtilization", Label: "CPUUtilization"},
		},
	},
	"ecache.SwapUsage": {
		Label: "ECache Swap Usage",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "SwapUsage", Label: "SwapUsage"},
		},
	},
	"ecache.FreeableMemory": {
		Label: "ECache Freeable Memory",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "FreeableMemory", Label: "FreeableMemory"},
		},
	},
	"ecache.NetworkTraffic": {
		Label: "ECache Network Traffic",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "NetworkBytesIn", Label: "NetworkBytesIn"},
			{Name: "NetworkBytesOut", Label: "NetworkBytesOut"},
		},
	},
	"ecache.Command": {
		Label: "ECache Command",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "CmdGet", Label: "CmdGet"},
			{Name: "CmdSet", Label: "CmdSet"},
			{Name: "CmdFlush", Label: "CmdFlush"},
			{Name: "CmdTouch", Label: "CmdTouch"},
			{Name: "CmdConfigGet", Label: "CmdConfigGet"},
			{Name: "CmdConfigSet", Label: "CmdConfigSet"},
		},
	},
	"ecache.CacheHitAndMiss": {
		Label: "ECache Hits/Misses",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "GetHits", Label: "GetHits"},
			{Name: "GetMisses", Label: "GetMisses"},
			{Name: "DeleteHits", Label: "DeleteHits"},
			{Name: "DeleteMisses", Label: "DeleteMisses"},
			{Name: "IncrHits", Label: "IncrHits"},
			{Name: "IncrMisses", Label: "IncrMisses"},
			{Name: "DecrHits", Label: "DecrHits"},
			{Name: "DecrMisses", Label: "DecrMisses"},
			{Name: "CasBadval", Label: "CasBadval"},
			{Name: "CasHits", Label: "CasHits"},
			{Name: "CasMisses", Label: "CasMisses"},
			{Name: "TouchHits", Label: "TouchHits"},
			{Name: "TouchMisses", Label: "TouchMisses"},
		},
	},
	"ecache.Evictions": {
		Label: "ECache Evictions",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "Evictions", Label: "Evictions"},
		},
	},
	"ecache.Unfetched": {
		Label: "ECache Unfetched",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "EvictedUnfetched", Label: "EvictedUnfetched"},
			{Name: "ExpiredUnfetched", Label: "ExpiredUnfetched"},
		},
	},
	"ecache.Bytes": {
		Label: "ECache Traffics",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "BytesReadIntoMemcached", Label: "BytesReadIntoMemcached"},
			{Name: "BytesWrittenOutFromMemcached", Label: "BytesWrittenOutFromMemcached"},
		},
	},
	"ecache.Connections": {
		Label: "ECache Connections",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "CurrConnections", Label: "CurrConnections"},
			{Name: "NewConnections", Label: "NewConnections"},
		},
	},
	"ecache.Items": {
		Label: "ECache Items",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "CurrItems", Label: "CurrItems"},
			{Name: "NewItems", Label: "NewItems"},
			{Name: "Reclaimed", Label: "Reclaimed"},
			{Name: "CurrConfig", Label: "CurrConfig"},
			{Name: "SlabsMoved", Label: "SlabsMoved"},
		},
	},
	"ecache.MemoryUsage": {
		Label: "ECache Memory Usage",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "UnusedMemory", Label: "UnusedMemory"},
			{Name: "BytesUsedForHash", Label: "BytesUsedForHash"},
			{Name: "BytesUsedForCacheItems", Label: "BytesUsedForCacheItems"},
		},
	},
}

var graphdefRedis = map[string]mp.Graphs{
	"ecache.CPUUtilization": {
		Label: "ECache CPU Utilization",
		Unit:  "percentage",
		Metrics: []mp.Metrics{
			{Name: "CPUUtilization", Label: "CPUUtilization"},
		},
	},
	"ecache.SwapUsage": {
		Label: "ECache Swap Usage",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "SwapUsage", Label: "SwapUsage"},
		},
	},
	"ecache.FreeableMemory": {
		Label: "ECache Freeable Memory",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "FreeableMemory", Label: "FreeableMemory"},
		},
	},
	"ecache.NetworkTraffic": {
		Label: "ECache Network Traffic",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "NetworkBytesIn", Label: "NetworkBytesIn"},
			{Name: "NetworkBytesOut", Label: "NetworkBytesOut"},
		},
	},
	"ecache.Command": {
		Label: "ECache Command",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "GetTypeCmds", Label: "GetTypeCmds"},
			{Name: "SetTypeCmds", Label: "SetTypeCmds"},
			{Name: "KeyBasedCmds", Label: "KeyBasedCmds"},
			{Name: "StringBasedCmds", Label: "StringBasedCmds"},
			{Name: "HashBasedCmds", Label: "HashBasedCmds"},
			{Name: "ListBasedCmds", Label: "ListBasedCmds"},
			{Name: "SetBasedCmds", Label: "SetBasedCmds"},
			{Name: "SortedSetBasedCmds", Label: "SortedSetBasedCmds"},
		},
	},
	"ecache.CacheHitAndMiss": {
		Label: "ECache Hits/Misses",
		Unit:  "float",
		Metrics: []mp.Metrics{
			{Name: "CacheHits", Label: "CacheHits"},
			{Name: "CacheMisses", Label: "CacheMisses"},
		},
	},
	"ecache.Evictions": {
		Label: "ECache Evictions",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "Evictions", Label: "Evictions"},
		},
	},
	"ecache.Bytes": {
		Label: "ECache Traffics",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "BytesUsedForCache", Label: "BytesUsedForCache"},
		},
	},
	"ecache.Connections": {
		Label: "ECache Connections",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "CurrConnections", Label: "CurrConnections"},
			{Name: "NewConnections", Label: "NewConnections"},
		},
	},
	"ecache.Items": {
		Label: "ECache Items",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "CurrItems", Label: "CurrItems"},
			{Name: "Reclaimed", Label: "Reclaimed"},
		},
	},
}

// ECachePlugin mackerel plugin for elasticache
type ECachePlugin struct {
	Region          string
	AccessKeyID     string
	SecretAccessKey string
	CacheClusterID  string
	CacheNodeID     string
	ElastiCacheType string
	CacheMetrics    []string
}

func getLastPoint(cloudWatch *cloudwatch.CloudWatch, dimensions []*cloudwatch.Dimension, metricName string) (float64, error) {
	now := time.Now()

	response, err := cloudWatch.GetMetricStatistics(&cloudwatch.GetMetricStatisticsInput{
		Dimensions: dimensions,
		StartTime:  aws.Time(now.Add(time.Duration(180) * time.Second * -1)), // 3 mins (to fetch at least 1 data-point)
		EndTime:    aws.Time(now),
		MetricName: aws.String(metricName),
		Period:     aws.Int64(60),
		Statistics: []*string{aws.String("Average")},
		Namespace:  aws.String("AWS/ElastiCache"),
	})
	if err != nil {
		return 0, err
	}

	datapoints := response.Datapoints
	if len(datapoints) == 0 {
		return 0, errors.New("fetched no datapoints")
	}

	latest := new(time.Time)
	var latestVal float64
	for _, dp := range datapoints {
		if dp.Timestamp.Before(*latest) {
			continue
		}

		latest = dp.Timestamp
		latestVal = *dp.Average
	}

	return latestVal, nil
}

// FetchMetrics fetch elasticache values
func (p ECachePlugin) FetchMetrics() (map[string]float64, error) {
	sess, err := session.NewSession()
	if err != nil {
		return nil, err
	}

	config := aws.NewConfig()
	if p.AccessKeyID != "" && p.SecretAccessKey != "" {
		config = config.WithCredentials(credentials.NewStaticCredentials(p.AccessKeyID, p.SecretAccessKey, ""))
	}
	if p.Region != "" {
		config = config.WithRegion(p.Region)
	}

	cloudWatch := cloudwatch.New(sess, config)

	stat := make(map[string]float64)

	perInstances := []*cloudwatch.Dimension{
		{
			Name:  aws.String("CacheClusterId"),
			Value: aws.String(p.CacheClusterID),
		},
		{
			Name:  aws.String("CacheNodeId"),
			Value: aws.String(p.CacheNodeID),
		},
	}

	for _, met := range p.CacheMetrics {
		v, err := getLastPoint(cloudWatch, perInstances, met)
		if err == nil {
			stat[met] = v
		} else {
			log.Printf("%s: %s", met, err)
		}
	}

	return stat, nil
}

// GraphDefinition graph definition
func (p ECachePlugin) GraphDefinition() map[string]mp.Graphs {
	switch p.ElastiCacheType {
	case "memcached":
		return graphdefMemcached
	case "redis":
		return graphdefRedis
	default:
		log.Printf("elasticache-type is 'memcached' or 'redis'.")
		return nil
	}
}

// Do the plugin
func Do() {
	optRegion := flag.String("region", "", "AWS Region")
	optAccessKeyID := flag.String("access-key-id", "", "AWS Access Key ID")
	optSecretAccessKey := flag.String("secret-access-key", "", "AWS Secret Access Key")
	optCacheClusterID := flag.String("cache-cluster-id", "", "Cache Cluster Id")
	optCacheNodeID := flag.String("cache-node-id", "0001", "Cache Node Id")
	optElastiCacheType := flag.String("elasticache-type", "", "ElastiCache type")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	var ecache ECachePlugin

	if *optRegion == "" {
		ec2metadata := ec2metadata.New(session.New())
		if ec2metadata.Available() {
			ecache.Region, _ = ec2metadata.Region()
		}
	} else {
		ecache.Region = *optRegion
	}

	ecache.Region = *optRegion
	ecache.AccessKeyID = *optAccessKeyID
	ecache.SecretAccessKey = *optSecretAccessKey
	ecache.CacheClusterID = *optCacheClusterID
	ecache.CacheNodeID = *optCacheNodeID
	ecache.ElastiCacheType = *optElastiCacheType
	switch ecache.ElastiCacheType {
	case "memcached":
		ecache.CacheMetrics = metricsdefMemcached
	case "redis":
		ecache.CacheMetrics = metricsdefRedis
	default:
		log.Printf("elasticache-type is 'memcached' or 'redis'.")
		os.Exit(1)
	}

	helper := mp.NewMackerelPlugin(ecache)
	if *optTempfile != "" {
		helper.Tempfile = *optTempfile
	} else {
		helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-aws-elasticache-%s-%s", *optCacheClusterID, *optCacheNodeID)
	}

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


<commit_before>//Command to test application without deploy:
//goapp serve app.yaml
//Command to deploy/update application:
//goapp deploy -application golangnode0 -version 0

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"strconv"
	"time"

	"appengine"
	"appengine/urlfetch"
)

//predefined parameters
var maxNodes int = 10
var isAliveCheckPeriod int = 500 //in millisecs

//changeable parameters
var statusContent string = "Default status"
var statusLog string = "Log: "

//nodesStates := make(map[int]map[string]string)
/*
example for this map
var nodesStates map[int]map[string]string{
	1: map[string]string{
		"alive":"1",
		"hasTask":"true",
		"taskStatus":"completed",
		"taskResult":"some_result_for_node"
	},
}
*/

type webPage struct {
	Title string
}

type nodeStats struct {
	NodeID           int    `json:"ID"`
	NodeCount        int    `json:"nodeCount"`
	HasTask          bool   `json:"hasTask"`
	TaskStatus       string `json:"taskStatus"` //running-copleted-loaded
	TaskResult       string `json:"taskResult"`
	TaskFragmentBody string `json:"taskFragmentBody"`
	TaskBody         string `json:"taskBody"`
}

type echoMessage struct {
	Title   string `json:"title"`
	Content string `json:"content"`
}

//types for periodical functions
type pFunc func()
type pFuncInt func(int, *http.Request)

//wrong func for Google App Engine deployment. Need to use appengine libs...=(
func echo() {

	url := "http://golangappnode1.appspot.com/status"

	var jsonStr = []byte(`{"msg":"Hello!"}`)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}

	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	statusContent = string(body)

}

func startPage(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		templatePage, _ := template.ParseFiles("start.html")
		templatePage.Execute(w, &webPage{"simplePage"})
	case "POST":
		r.ParseForm()
		//go echo()
		//fmt.Fprintf(w, "Successful read command/input from web-interface! Input contains - "+r.FormValue("nodeId")+" "+r.FormValue("echoContent"))
	}
}

func statusServer(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		fmt.Fprintf(w, "Get status - "+statusContent)
	case "POST":
		buf := new(bytes.Buffer)
		buf.ReadFrom(r.Body)
		newStr := buf.String()

		/*inputMsg := echoMessage{}
		err2 := json.NewDecoder(r.Body).Decode(&inputMsg)
		if err2 != nil {
			panic(err2)
		}*/

		thisNodeStats := nodeStats{
			1,
			2,
			false,
			"not running",
			"empty",
			"empty fragment",
			"empty",
		}

		jsonNodeStats, err1 := json.Marshal(thisNodeStats)
		if err1 != nil {
			panic(err1)
		}

		fmt.Fprintf(w, "Get data by params in POST - OK "+string(jsonNodeStats))
		//statusContent = "POST request handled, " + "Node id: " + string(nodeSends.id) + ", Echo content: " + nodeSends.content
		statusContent = "POST request handled, " + newStr //+ "Input message object content: " + inputMsg.Title + inputMsg.Content
	}
}

//Functions for isAlive checking realization
func checkIsAlive(nodeId int, req *http.Request) {
	ctx := appengine.NewContext(req)
	client := http.Client{Transport: &urlfetch.Transport{Context: ctx}}

	nodeUrl := "http://goappnode" + strconv.Itoa(nodeId) + ".appspot.com/"

	resp, err := client.Get(nodeUrl)
	if err != nil {
		panic(err)
	}

	if resp.StatusCode != 200 {
		statusLog += "Node #" + strconv.Itoa(nodeId) + " - offline"
	} else {

		statusLog += "Node #" + strconv.Itoa(nodeId) + " - online"
	}
	//statusLog = string(resp.StatusCode)
}

func alivePeriodicTest(r *http.Request) {
	for i := 0; i < 10; i++ {
		checkIsAlive(1, r)
	}
}

func periodicTask(period time.Duration, task pFuncInt, taskArg int, taskReq *http.Request) {
	for {
		task(taskArg, taskReq)
		time.Sleep(period * time.Millisecond)
	}
}

func isAliveServer(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, 1)
}

func logServer(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, statusLog)
}

func checkAliveStart(w http.ResponseWriter, r *http.Request) {
	//go periodicTask(200, checkIsAlive, 1, r)
	//checkIsAlive(1, r)
	alivePeriodicTest(r)
}

/*
func checkAliveStop(w http.ResponseWriter, r *http.Request) {

}
*/

func testEcho(w http.ResponseWriter, r *http.Request) {
	msg := echoMessage{
		"Message is",
		"",
	}

	r.ParseForm()
	c := appengine.NewContext(r)
	msg.Content = r.FormValue("echoContent")

	jsonMessage, err2 := json.Marshal(msg)
	if err2 != nil {
		panic(err2)
	}

	//jsonStr := []byte(`{"message":"` + r.FormValue("echoContent") + `"}`)
	jsonStr := []byte(jsonMessage)
	buf := bytes.NewBuffer(jsonStr)
	client := http.Client{Transport: &urlfetch.Transport{Context: c}}
	resp, err := client.Post("http://goappnode"+r.FormValue("nodeId")+".appspot.com/status", "application/octet-stream", buf)
	if err != nil {
		statusContent = err.Error()
		fmt.Println(err)
	}
	respBody, _ := ioutil.ReadAll(resp.Body)
	statusContent = "Response from node - " + string(respBody)
}

func init() {
	//view pages
	http.HandleFunc("/", startPage)
	//service pages
	http.HandleFunc("/echo", testEcho)
	http.HandleFunc("/status", statusServer)
	http.HandleFunc("/isalive", isAliveServer)
	http.HandleFunc("/startcheck", checkAliveStart)
	http.HandleFunc("/logs", logServer)

	//Wrong code for App Enine - server cant understand what it need to show
	//http.ListenAndServe(":80", nil)
}

//this func not needed for deploy on Google App Engine, init() func replace main()
/*
func main() {
	//fmt.Println("Hello, test server started on 8080 port.\n - /helloworld - show title page\n - /showinfo - show information about this thing")
	//http.ListenAndServe(":8080", nil)
	go sender()
}*/
<commit_msg>tst fix<commit_after>//Command to test application without deploy:
//goapp serve app.yaml
//Command to deploy/update application:
//goapp deploy -application golangnode0 -version 0

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"strconv"
	"time"

	"appengine"
	"appengine/urlfetch"
)

//predefined parameters
var maxNodes int = 10
var isAliveCheckPeriod int = 500 //in millisecs

//changeable parameters
var statusContent string = "Default status"
var statusLog string = "Log: "

//nodesStates := make(map[int]map[string]string)
/*
example for this map
var nodesStates map[int]map[string]string{
	1: map[string]string{
		"alive":"1",
		"hasTask":"true",
		"taskStatus":"completed",
		"taskResult":"some_result_for_node"
	},
}
*/

type webPage struct {
	Title string
}

type nodeStats struct {
	NodeID           int    `json:"ID"`
	NodeCount        int    `json:"nodeCount"`
	HasTask          bool   `json:"hasTask"`
	TaskStatus       string `json:"taskStatus"` //running-copleted-loaded
	TaskResult       string `json:"taskResult"`
	TaskFragmentBody string `json:"taskFragmentBody"`
	TaskBody         string `json:"taskBody"`
}

type echoMessage struct {
	Title   string `json:"title"`
	Content string `json:"content"`
}

//types for periodical functions
type pFunc func()
type pFuncInt func(int, *http.Request)

//wrong func for Google App Engine deployment. Need to use appengine libs...=(
func echo() {

	url := "http://golangappnode1.appspot.com/status"

	var jsonStr = []byte(`{"msg":"Hello!"}`)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}

	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	statusContent = string(body)

}

func startPage(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		templatePage, _ := template.ParseFiles("start.html")
		templatePage.Execute(w, &webPage{"simplePage"})
	case "POST":
		r.ParseForm()
		//go echo()
		//fmt.Fprintf(w, "Successful read command/input from web-interface! Input contains - "+r.FormValue("nodeId")+" "+r.FormValue("echoContent"))
	}
}

func statusServer(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		fmt.Fprintf(w, "Get status - "+statusContent)
	case "POST":
		buf := new(bytes.Buffer)
		buf.ReadFrom(r.Body)
		newStr := buf.String()

		/*inputMsg := echoMessage{}
		err2 := json.NewDecoder(r.Body).Decode(&inputMsg)
		if err2 != nil {
			panic(err2)
		}*/

		thisNodeStats := nodeStats{
			1,
			2,
			false,
			"not running",
			"empty",
			"empty fragment",
			"empty",
		}

		jsonNodeStats, err1 := json.Marshal(thisNodeStats)
		if err1 != nil {
			panic(err1)
		}

		fmt.Fprintf(w, "Get data by params in POST - OK "+string(jsonNodeStats))
		//statusContent = "POST request handled, " + "Node id: " + string(nodeSends.id) + ", Echo content: " + nodeSends.content
		statusContent = "POST request handled, " + newStr //+ "Input message object content: " + inputMsg.Title + inputMsg.Content
	}
}

//Functions for isAlive checking realization
func checkIsAlive(nodeId int, req *http.Request) {
	ctx := appengine.NewContext(req)
	client := http.Client{Transport: &urlfetch.Transport{Context: ctx}}

	nodeUrl := "http://goappnode" + strconv.Itoa(nodeId) + ".appspot.com/"

	resp, err := client.Get(nodeUrl)
	if err != nil {
		panic(err)
	}

	if resp.StatusCode != 200 {
		statusLog += "Node #" + strconv.Itoa(nodeId) + " - offline"
	} else {

		statusLog += "Node #" + strconv.Itoa(nodeId) + " - online"
	}
	//statusLog = string(resp.StatusCode)
}

func alivePeriodicTest(r *http.Request, done chan int) {
	for i := 0; i < 10; i++ {
		checkIsAlive(1, r)
	}
	done <- 0
}

func periodicTask(period time.Duration, task pFuncInt, taskArg int, taskReq *http.Request) {
	for {
		task(taskArg, taskReq)
		time.Sleep(period * time.Millisecond)
	}
}

func isAliveServer(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, 1)
}

func logServer(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, statusLog)
}

func checkAliveStart(w http.ResponseWriter, r *http.Request) {
	//go periodicTask(200, checkIsAlive, 1, r)
	//checkIsAlive(1, r)
	done := make(chan int)
	go alivePeriodicTest(r)
	<-done
}

/*
func checkAliveStop(w http.ResponseWriter, r *http.Request) {

}
*/

func testEcho(w http.ResponseWriter, r *http.Request) {
	msg := echoMessage{
		"Message is",
		"",
	}

	r.ParseForm()
	c := appengine.NewContext(r)
	msg.Content = r.FormValue("echoContent")

	jsonMessage, err2 := json.Marshal(msg)
	if err2 != nil {
		panic(err2)
	}

	//jsonStr := []byte(`{"message":"` + r.FormValue("echoContent") + `"}`)
	jsonStr := []byte(jsonMessage)
	buf := bytes.NewBuffer(jsonStr)
	client := http.Client{Transport: &urlfetch.Transport{Context: c}}
	resp, err := client.Post("http://goappnode"+r.FormValue("nodeId")+".appspot.com/status", "application/octet-stream", buf)
	if err != nil {
		statusContent = err.Error()
		fmt.Println(err)
	}
	respBody, _ := ioutil.ReadAll(resp.Body)
	statusContent = "Response from node - " + string(respBody)
}

func init() {
	//view pages
	http.HandleFunc("/", startPage)
	//service pages
	http.HandleFunc("/echo", testEcho)
	http.HandleFunc("/status", statusServer)
	http.HandleFunc("/isalive", isAliveServer)
	http.HandleFunc("/startcheck", checkAliveStart)
	http.HandleFunc("/logs", logServer)

	//Wrong code for App Enine - server cant understand what it need to show
	//http.ListenAndServe(":80", nil)
}

//this func not needed for deploy on Google App Engine, init() func replace main()
/*
func main() {
	//fmt.Println("Hello, test server started on 8080 port.\n - /helloworld - show title page\n - /showinfo - show information about this thing")
	//http.ListenAndServe(":8080", nil)
	go sender()
}*/
<|endoftext|>


<commit_before>//Command to test application without deploy:
//goapp serve app.yaml
//Command to deploy/update application:
//goapp deploy -application golangnode0 -version 0

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"net/smtp"
	"strings"

	"appengine"
	"appengine/urlfetch"
)

var statusContent string = "Default status"

type webPage struct {
	Title string
}

type node struct {
	id      int
	content string
}

type gmailUser struct {
	name string
	pswd string
}

func sendMail(msg string) {
	mailUser := gmailUser{
		"golangapplication@gmail.com",
		"",
	}
	auth := smtp.PlainAuth("",
		mailUser.name,
		mailUser.pswd,
		"smtp.gmail.com",
	)
	err := smtp.SendMail(
		"smtp.gmail.com:587",
		auth,
		mailUser.name,
		[]string{"rec@mail.com"},
		[]byte(msg),
	)
	if err != nil {
		log.Fatal(err)
	}
}

//wrong func for Google App Engine deployment. Need to use appengine libs...=(
func echo() {

	url := "http://golangappnode1.appspot.com/status"

	var jsonStr = []byte(`{"msg":"Hello!"}`)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}

	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	statusContent = string(body)

}

func helloWorld(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello World!")
}

func startPage(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		templatePage, _ := template.ParseFiles("start.html")
		templatePage.Execute(w, &webPage{"simplePage"})
	case "POST":
		r.ParseForm()
		//go echo()
		fmt.Fprintf(w, "Successful read command/input from web-interface! Input contains - "+r.FormValue("nodeId")+" "+r.FormValue("echoContent"))
	}
}

func statusServer(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		fmt.Fprintf(w, "Get status - "+statusContent)
	case "POST":
		r.ParseForm()
		fmt.Fprintf(w, "Get data by params in POST - OK")
		statusContent = "POST request handled, " + "Node id: " + strings.Join(r.Form["nodeId"], " ") + ", Echo content: " + strings.Join(r.Form["echoContent"], " ")
	}
}

func testEcho(w http.ResponseWriter, r *http.Request) {

	nodeOne := node{
		1,
		"Hello",
	}
	jsonNodeOne := json.Marshal(nodeOne)

	c := appengine.NewContext(r)
	var jsonStr = []byte(jsonNodeOne)
	//bs := []byte{1, 2, 3}
	buf := bytes.NewBuffer(jsonStr)
	client := http.Client{Transport: &urlfetch.Transport{Context: c}}
	resp, err := client.Post("http://goappnode1.appspot.com/status", "application/octet-stream", buf)
	if err != nil {
		statusContent = err.Error()
		fmt.Println(err)
	}
	respBody, _ := ioutil.ReadAll(resp.Body)
	statusContent = "Response from node - " + string(respBody)

}

func showInfo(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Information page for test project.")
	fmt.Fprintln(w, "Language - Go;")
	fmt.Fprintln(w, "Platform - Google Application Engine;")
}

func init() {
	http.HandleFunc("/", startPage)
	http.HandleFunc("/helloworld", helloWorld)
	http.HandleFunc("/showinfo", showInfo)
	http.HandleFunc("/status", statusServer)
	http.HandleFunc("/echo", testEcho)

	//Wrong code for App Enine - server cant understand what it need to show
	//http.ListenAndServe(":80", nil)
}

//this func not needed for deploy on Google App Engine, init() func replace main()
/*
func main() {
	//fmt.Println("Hello, test server started on 8080 port.\n - /helloworld - show title page\n - /showinfo - show information about this thing")
	//http.ListenAndServe(":8080", nil)
	go sender()
}*/
<commit_msg>fix err<commit_after>//Command to test application without deploy:
//goapp serve app.yaml
//Command to deploy/update application:
//goapp deploy -application golangnode0 -version 0

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"net/smtp"
	"strings"

	"appengine"
	"appengine/urlfetch"
)

var statusContent string = "Default status"

type webPage struct {
	Title string
}

type node struct {
	id      int
	content string
}

type gmailUser struct {
	name string
	pswd string
}

func sendMail(msg string) {
	mailUser := gmailUser{
		"golangapplication@gmail.com",
		"",
	}
	auth := smtp.PlainAuth("",
		mailUser.name,
		mailUser.pswd,
		"smtp.gmail.com",
	)
	err := smtp.SendMail(
		"smtp.gmail.com:587",
		auth,
		mailUser.name,
		[]string{"rec@mail.com"},
		[]byte(msg),
	)
	if err != nil {
		log.Fatal(err)
	}
}

//wrong func for Google App Engine deployment. Need to use appengine libs...=(
func echo() {

	url := "http://golangappnode1.appspot.com/status"

	var jsonStr = []byte(`{"msg":"Hello!"}`)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}

	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	statusContent = string(body)

}

func helloWorld(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello World!")
}

func startPage(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		templatePage, _ := template.ParseFiles("start.html")
		templatePage.Execute(w, &webPage{"simplePage"})
	case "POST":
		r.ParseForm()
		//go echo()
		fmt.Fprintf(w, "Successful read command/input from web-interface! Input contains - "+r.FormValue("nodeId")+" "+r.FormValue("echoContent"))
	}
}

func statusServer(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		fmt.Fprintf(w, "Get status - "+statusContent)
	case "POST":
		r.ParseForm()
		fmt.Fprintf(w, "Get data by params in POST - OK")
		statusContent = "POST request handled, " + "Node id: " + strings.Join(r.Form["nodeId"], " ") + ", Echo content: " + strings.Join(r.Form["echoContent"], " ")
	}
}

func testEcho(w http.ResponseWriter, r *http.Request) {

	nodeOne := node{
		1,
		"Hello",
	}

	jsonNodeOne, err1 := json.Marshal(nodeOne)
	if err1 != nil {
		panic(err1)
	}

	c := appengine.NewContext(r)
	var jsonStr = []byte(jsonNodeOne)
	//bs := []byte{1, 2, 3}
	buf := bytes.NewBuffer(jsonStr)
	client := http.Client{Transport: &urlfetch.Transport{Context: c}}
	resp, err := client.Post("http://goappnode1.appspot.com/status", "application/octet-stream", buf)
	if err != nil {
		statusContent = err.Error()
		fmt.Println(err)
	}
	respBody, _ := ioutil.ReadAll(resp.Body)
	statusContent = "Response from node - " + string(respBody)

}

func showInfo(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Information page for test project.")
	fmt.Fprintln(w, "Language - Go;")
	fmt.Fprintln(w, "Platform - Google Application Engine;")
}

func init() {
	http.HandleFunc("/", startPage)
	http.HandleFunc("/helloworld", helloWorld)
	http.HandleFunc("/showinfo", showInfo)
	http.HandleFunc("/status", statusServer)
	http.HandleFunc("/echo", testEcho)

	//Wrong code for App Enine - server cant understand what it need to show
	//http.ListenAndServe(":80", nil)
}

//this func not needed for deploy on Google App Engine, init() func replace main()
/*
func main() {
	//fmt.Println("Hello, test server started on 8080 port.\n - /helloworld - show title page\n - /showinfo - show information about this thing")
	//http.ListenAndServe(":8080", nil)
	go sender()
}*/
<|endoftext|>


<commit_before>package appgo

import (
	"bytes"
	"encoding/base64"
	"encoding/binary"
	"encoding/json"
	"math"
	"strconv"
)

var (
	base64Encoding = base64.RawURLEncoding
	binaryEndian   = binary.BigEndian
)

type Id int64

func (id Id) String() string {
	return strconv.FormatInt(int64(id), 10)
}

func (id Id) MarshalJSON() ([]byte, error) {
	var buffer bytes.Buffer
	buffer.WriteByte('"')
	buffer.WriteString(id.String())
	buffer.WriteByte('"')
	return buffer.Bytes(), nil
}

func (id *Id) UnmarshalJSON(b []byte) error {
	var s string
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	if val, err := strconv.ParseInt(s, 10, 64); err != nil {
		return err
	} else {
		*id = Id(val)
	}
	return nil
}

func IdFromStr(str string) Id {
	i, _ := strconv.ParseInt(str, 10, 64)
	return Id(i)
}

func IdFromBase64(str string) Id {
	if len(str) == 0 {
		return 0
	} else if data, err := base64Encoding.DecodeString(str); err != nil {
		return 0
	} else {
		val := binaryEndian.Uint64(data)
		return Id(int64(val))
	}
}

func IdMax() Id {
	return Id(math.MaxInt64)
}

func (id Id) Base64() string {
	buf := make([]byte, 8)
	binaryEndian.PutUint64(buf, uint64(int64(id)))
	return base64Encoding.EncodeToString(buf)
}
<commit_msg>deal with appgo.Id = ""<commit_after>package appgo

import (
	"bytes"
	"encoding/base64"
	"encoding/binary"
	"encoding/json"
	"math"
	"strconv"
)

var (
	base64Encoding = base64.RawURLEncoding
	binaryEndian   = binary.BigEndian
)

type Id int64

func (id Id) String() string {
	return strconv.FormatInt(int64(id), 10)
}

func (id Id) MarshalJSON() ([]byte, error) {
	var buffer bytes.Buffer
	buffer.WriteByte('"')
	buffer.WriteString(id.String())
	buffer.WriteByte('"')
	return buffer.Bytes(), nil
}

func (id *Id) UnmarshalJSON(b []byte) error {
	var s string
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	if s == "" {
		*id = 0
		return nil
	}
	if val, err := strconv.ParseInt(s, 10, 64); err != nil {
		return err
	} else {
		*id = Id(val)
	}
	return nil
}

func IdFromStr(str string) Id {
	i, _ := strconv.ParseInt(str, 10, 64)
	return Id(i)
}

func IdFromBase64(str string) Id {
	if len(str) == 0 {
		return 0
	} else if data, err := base64Encoding.DecodeString(str); err != nil {
		return 0
	} else {
		val := binaryEndian.Uint64(data)
		return Id(int64(val))
	}
}

func IdMax() Id {
	return Id(math.MaxInt64)
}

func (id Id) Base64() string {
	buf := make([]byte, 8)
	binaryEndian.PutUint64(buf, uint64(int64(id)))
	return base64Encoding.EncodeToString(buf)
}
<|endoftext|>


<commit_before>package is

import "regexp"

const (
	EmailPattern = `^[a-zA-Z0-9!#$%&\'*+\\/=?^_{|}~-]+(?:\.[a-zA-Z0-9!#$%&\'*+\\/=?^_{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$`
	UrlPattern   = `((http|https|ftp){1}\:\/\/)?([a-zA-Z0-9]+[a-zA-Z0-9\-\.]+[a-zA-Z0-9]+\.(net|cn|co|hk|tw|com|edu|gov|us|int|mil|org|int|mil|vg|uk|idv|tk|se|nz|nu|nl|ms|jp|jobs|it|ind|gen|firm|in|gs|fr|fm|eu|es|de|bz|be|at|am|ag|mx|asia|ws|xxx|tv|cc|ca|mobi|me|biz|arpa|info|name|pro|aero|coop|museum|ly|eg|mk)|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})){1}(:[a-zA-Z0-9]*)?\/?([a-zA-Z0-9\-\._\?\'\/\\\+&%\$#\=~])*`
)

func Email(v string) bool {
	ok, err := regexp.MatchString(EmailPattern, v)
	return ok && err == nil
}

func Url(v string) bool {
	ok, err := regexp.MatchString(UrlPattern, v)
	return ok && err == nil
}
<commit_msg>add Zero function<commit_after>package is

import (
	"reflect"
	"regexp"
	"time"
)

var typeTime = reflect.TypeOf(time.Time{})

const (
	EmailPattern = `^[a-zA-Z0-9!#$%&\'*+\\/=?^_{|}~-]+(?:\.[a-zA-Z0-9!#$%&\'*+\\/=?^_{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$`
	UrlPattern   = `((http|https|ftp){1}\:\/\/)?([a-zA-Z0-9]+[a-zA-Z0-9\-\.]+[a-zA-Z0-9]+\.(net|cn|co|hk|tw|com|edu|gov|us|int|mil|org|int|mil|vg|uk|idv|tk|se|nz|nu|nl|ms|jp|jobs|it|ind|gen|firm|in|gs|fr|fm|eu|es|de|bz|be|at|am|ag|mx|asia|ws|xxx|tv|cc|ca|mobi|me|biz|arpa|info|name|pro|aero|coop|museum|ly|eg|mk)|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})){1}(:[a-zA-Z0-9]*)?\/?([a-zA-Z0-9\-\._\?\'\/\\\+&%\$#\=~])*`
)

func Email(v string) bool {
	ok, err := regexp.MatchString(EmailPattern, v)
	return ok && err == nil
}

func Url(v string) bool {
	ok, err := regexp.MatchString(UrlPattern, v)
	return ok && err == nil
}

// borrow from "labix.org/v2/mgo/bson"
func Zero(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.String:
		return len(v.String()) == 0
	case reflect.Ptr, reflect.Interface:
		return v.IsNil()
	case reflect.Slice:
		return v.Len() == 0
	case reflect.Map:
		return v.Len() == 0
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Struct:
		if v.Type() == typeTime {
			return v.Interface().(time.Time).IsZero()
		}
		for i := v.NumField() - 1; i >= 0; i-- {
			if !Zero(v.Field(i)) {
				return false
			}
		}
		return true
	}
	return false
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 AKUALAB INC., All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package ju provides utilities for manipulating json objects.
package ju

import (
	"bufio"
	"compress/gzip"
	"encoding/json"
	"errors"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

// Done is returned as the error value when there are no more objects to process.
var Done = errors.New("no more json objects")

// ReadJSON unmarshals json data from an io.Reader.
// The param "o" must be a pointer to an object.
func ReadJSON(r io.Reader, o interface{}) error {
	dec := json.NewDecoder(r)
	err := dec.Decode(o)
	if err != nil && err != io.EOF {
		return err
	}
	return nil
}

// ReadJSONFile unmarshals json data from a file.
func ReadJSONFile(fn string, o interface{}) error {

	f, err := os.Open(fn)
	if err != nil {
		return err
	}
	e := ReadJSON(f, o)
	if e != nil {
		return e
	}
	e = f.Close()
	return e
}

// WriteJSON writes an object to an io.Writer.
func WriteJSON(w io.Writer, o interface{}) error {

	enc := json.NewEncoder(w)
	err := enc.Encode(o)
	if err != nil {
		return err
	}
	return nil
}

// WriteJSONFile writes to a file.
func WriteJSONFile(fn string, o interface{}) error {

	e := os.MkdirAll(filepath.Dir(fn), 0755)
	if e != nil {
		return e
	}
	f, err := os.Create(fn)
	if err != nil {
		return err
	}
	ee := WriteJSON(f, o)
	if ee != nil {
		return ee
	}
	e = f.Close()
	return e
}

// JSONStreamer will unmarshal a stream of JSON objects.
type JSONStreamer struct {
	fs  io.ReadCloser
	dec *json.Decoder
}

// NewJSONStreamer creates a new streamer to read json objects.
// See FileStreamer to specify the path.
func NewJSONStreamer(path string) (*JSONStreamer, error) {
	fs, err := FileStreamer(path, ".json")
	if err != nil {
		return nil, err
	}
	js := &JSONStreamer{
		fs:  fs,
		dec: json.NewDecoder(fs),
	}
	return js, nil
}

// Next returns the next JSON object.
// When there are no more results, Done is returned as the error.
func (js *JSONStreamer) Next(dst interface{}) error {
	e := js.dec.Decode(dst)
	if e == io.EOF {
		return Done
	}
	return e
}

// Close the JSON streamer. Will close the underlyign readers.
func (js *JSONStreamer) Close() error {
	return js.fs.Close()
}

// FileStreamer returns a reader that streams data from multiple files. The list of files can be specified in multiple ways:
// (1) path is a single file. The file may be gzipped in which case the name extension must be ".gz".
// (2) path is a directory. Reads from all the files in that directory such that (a) the filename must not start with a period,
// (b) the filename has extension ".gz", (c) the "ext" parameter is empty or the allowed extensions are listed, (d) path is not a symboic link.
// (3) path is a file with extension ".list" that contains a list of paths to files. Read from all the files in the list.
//
// The return value is of type io.ReadCloser. It is the caller's responsibility to call Close on the ReadCloser when done.
func FileStreamer(path string, ext ...string) (io.ReadCloser, error) {
	r, e := regexp.Compile("^[^.].*[.][[:alnum:]]+")
	if e != nil {
		return nil, e
	}
	allowed := map[string]bool{".gz": true}
	for _, v := range ext {
		if !strings.HasPrefix(v, ".") {
			v = "." + v
		}
		allowed[v] = true
	}
	fi, err := os.Stat(path)
	if err != nil {
		return nil, err
	}
	fext := filepath.Ext(path)
	switch {
	case fi.IsDir():
		return streamDir(path, allowed, r)
	case fext == ".list":
		return streamList(path)
	default:
		return streamFile(path)
	}
}

func matchExt(ext string, allowed map[string]bool) bool {
	if len(allowed) == 1 {
		return true
	}
	_, ok := allowed[ext]
	if ok {
		return true
	}
	return false
}

func streamDir(path string, allowed map[string]bool, r *regexp.Regexp) (io.ReadCloser, error) {
	files := []string{}
	filepath.Walk(path, func(fn string, info os.FileInfo, err error) error {
		if !r.MatchString(filepath.Base(fn)) {
			return nil
		}
		ext := filepath.Ext(fn)
		if !matchExt(ext, allowed) {
			return nil
		}
		files = append(files, fn)
		return nil
	})
	return &multi{files: files}, nil
}

type multi struct {
	files  []string
	idx    int
	reader io.ReadCloser
}

func (m *multi) Read(p []byte) (int, error) {
	if len(m.files) == 0 {
		return 0, io.EOF
	}
	if m.reader == nil {
		// Edge case, calling Read after last reader is closed.
		if m.idx >= len(m.files) {
			return 0, io.EOF
		}
		f, err := os.Open(m.files[m.idx])
		if err != nil {
			return 0, err
		}
		if filepath.Ext(m.files[m.idx]) == ".gz" {
			m.reader, err = NewGZIPReader(f)
			if err != nil {
				return 0, err
			}
		} else {
			m.reader = f
		}
		m.idx++
	}
	n, e := m.reader.Read(p)
	switch {

	case e == nil:
		// We are good.
		return n, nil

	case e == io.EOF && m.idx < len(m.files):
		// End of reader but we have more files.
		err := m.reader.Close()
		if err != nil {
			return n, err
		}
		m.reader = nil
		return n, nil // we are not done yet!

	case e == io.EOF:
		// End of last reader.
		err := m.reader.Close()
		if err != nil {
			return 0, err
		}
		m.reader = nil
		return n, io.EOF // we are done!

	default:
		// Some unknown error.
		err := m.reader.Close()
		if err != nil {
			return 0, err
		}
		return n, e
	}
}

// Close closes the underlying resources.
func (m *multi) Close() error {
	m.idx = 0
	m.files = nil
	if m.reader != nil {
		err := m.reader.Close()
		if err != nil {
			return err
		}
	}
	return nil
}

func streamFile(path string) (io.ReadCloser, error) {
	f, e := os.Open(path)
	if e != nil {
		return nil, e
	}
	if filepath.Ext(path) == ".gz" {
		r, err := NewGZIPReader(f)
		if err != nil {
			return nil, err
		}
		return r, nil
	}
	return f, nil
}

func streamList(path string) (io.ReadCloser, error) {

	f, e := os.Open(path)
	if e != nil {
		return nil, nil
	}
	defer f.Close()
	scanner := bufio.NewScanner(f)
	files := []string{}
	for scanner.Scan() {
		line := scanner.Text()
		files = append(files, line)
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}
	return &multi{files: files}, nil
}

// GZIPReader is a wrapper to read compressed gzip files.
type GZIPReader struct {
	inReader   io.ReadCloser
	gzipReader *gzip.Reader
}

// NewGZIPReader creates a new GZIPReader that reads from r.
// The return value implements io.ReadCloser. It is the caller's responsibility to call Close when done.
func NewGZIPReader(r io.ReadCloser) (*GZIPReader, error) {
	gr := &GZIPReader{inReader: r}
	var err error
	gr.gzipReader, err = gzip.NewReader(gr.inReader)
	if err != nil {
		return nil, err
	}
	return gr, nil
}

// Read implements the io.Read interface.
func (g *GZIPReader) Read(p []byte) (int, error) {
	return g.gzipReader.Read(p)
}

// Close closes the gzip reader and the wrapped reader.
func (g *GZIPReader) Close() error {

	if g.inReader != nil {
		err := g.inReader.Close()
		if err != nil {
			return err
		}
	}
	err := g.gzipReader.Close()
	return err
}
<commit_msg>Add buffer to parallel json reader.<commit_after>// Copyright (c) 2015 AKUALAB INC., All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package ju provides utilities for manipulating json objects.
package ju

import (
	"bufio"
	"compress/gzip"
	"encoding/json"
	"errors"
	"io"
	"log"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"strings"
	"sync"
)

// Done is returned as the error value when there are no more objects to process.
var Done = errors.New("no more json objects")

// ReadJSON unmarshals json data from an io.Reader.
// The param "o" must be a pointer to an object.
func ReadJSON(r io.Reader, o interface{}) error {
	dec := json.NewDecoder(r)
	err := dec.Decode(o)
	if err != nil && err != io.EOF {
		return err
	}
	return nil
}

// ReadJSONFile unmarshals json data from a file.
func ReadJSONFile(fn string, o interface{}) error {

	f, err := os.Open(fn)
	if err != nil {
		return err
	}
	e := ReadJSON(f, o)
	if e != nil {
		return e
	}
	e = f.Close()
	return e
}

// WriteJSON writes an object to an io.Writer.
func WriteJSON(w io.Writer, o interface{}) error {

	enc := json.NewEncoder(w)
	err := enc.Encode(o)
	if err != nil {
		return err
	}
	return nil
}

// WriteJSONFile writes to a file.
func WriteJSONFile(fn string, o interface{}) error {

	e := os.MkdirAll(filepath.Dir(fn), 0755)
	if e != nil {
		return e
	}
	f, err := os.Create(fn)
	if err != nil {
		return err
	}
	ee := WriteJSON(f, o)
	if ee != nil {
		return ee
	}
	e = f.Close()
	return e
}

// JSONStreamer will unmarshal a stream of JSON objects.
type JSONStreamer struct {
	fs  io.ReadCloser
	dec *json.Decoder
}

// NewJSONStreamer creates a new streamer to read json objects.
// See FileStreamer to specify the path.
func NewJSONStreamer(path string) (*JSONStreamer, error) {
	fs, err := FileStreamer(path, ".json")
	if err != nil {
		return nil, err
	}
	js := &JSONStreamer{
		fs:  fs,
		dec: json.NewDecoder(fs),
	}
	return js, nil
}

// Next returns the next JSON object.
// When there are no more results, Done is returned as the error.
func (js *JSONStreamer) Next(dst interface{}) error {
	e := js.dec.Decode(dst)
	if e == io.EOF {
		return Done
	}
	return e
}

// Close the JSON streamer. Will close the underlyign readers.
func (js *JSONStreamer) Close() error {
	return js.fs.Close()
}

// We can pass a list of files in various ways. See FileStreamer documentation.
// This function returns a slice of file paths.
func extractPaths(path string, ext ...string) ([]string, error) {
	files := []string{}
	r, e := regexp.Compile("^[^.].*[.][[:alnum:]]+")
	if e != nil {
		return nil, e
	}
	allowed := map[string]bool{".gz": true}
	for _, v := range ext {
		if !strings.HasPrefix(v, ".") {
			v = "." + v
		}
		allowed[v] = true
	}
	fi, err := os.Stat(path)
	if err != nil {
		return nil, err
	}
	fext := filepath.Ext(path)
	switch {
	case fi.IsDir():
		filepath.Walk(path, func(fn string, info os.FileInfo, err error) error {
			if !r.MatchString(filepath.Base(fn)) {
				return nil
			}
			ext := filepath.Ext(fn)
			if !matchExt(ext, allowed) {
				return nil
			}
			files = append(files, fn)
			return nil
		})

	case fext == ".list":
		f, e := os.Open(path)
		if e != nil {
			return nil, e
		}
		defer f.Close()
		scanner := bufio.NewScanner(f)
		files := []string{}
		for scanner.Scan() {
			line := scanner.Text()
			files = append(files, line)
		}
		if err := scanner.Err(); err != nil {
			return nil, err
		}
	default:
		files = append(files, path)
	}
	return files, nil
}

// FileStreamer returns a reader that streams data from multiple files. The list of files can be specified in multiple ways:
// (1) path is a single file. The file may be gzipped in which case the name extension must be ".gz".
// (2) path is a directory. Reads from all the files in that directory such that (a) the filename must not start with a period,
// (b) the filename has extension ".gz", (c) the "ext" parameter is empty or the allowed extensions are listed, (d) path is not a symboic link.
// (3) path is a file with extension ".list" that contains a list of paths to files. Read from all the files in the list.
//
// The return value is of type io.ReadCloser. It is the caller's responsibility to call Close on the ReadCloser when done.
func FileStreamer(path string, ext ...string) (io.ReadCloser, error) {
	paths, err := extractPaths(path, ext...)
	if err != nil {
		return nil, err
	}
	return &multi{files: paths}, nil
}

func matchExt(ext string, allowed map[string]bool) bool {
	if len(allowed) == 1 {
		return true
	}
	_, ok := allowed[ext]
	if ok {
		return true
	}
	return false
}

type multi struct {
	files  []string
	idx    int
	reader io.ReadCloser
}

func (m *multi) Read(p []byte) (int, error) {
	if len(m.files) == 0 {
		return 0, io.EOF
	}
	if m.reader == nil {
		// Edge case, calling Read after last reader is closed.
		if m.idx >= len(m.files) {
			return 0, io.EOF
		}
		f, err := os.Open(m.files[m.idx])
		if err != nil {
			return 0, err
		}
		if filepath.Ext(m.files[m.idx]) == ".gz" {
			m.reader, err = NewGZIPReader(f)
			if err != nil {
				return 0, err
			}
		} else {
			m.reader = f
		}
		m.idx++
	}
	n, e := m.reader.Read(p)
	switch {

	case e == nil:
		// We are good.
		return n, nil

	case e == io.EOF && m.idx < len(m.files):
		// End of reader but we have more files.
		err := m.reader.Close()
		if err != nil {
			return n, err
		}
		m.reader = nil
		return n, nil // we are not done yet!

	case e == io.EOF:
		// End of last reader.
		err := m.reader.Close()
		if err != nil {
			return 0, err
		}
		m.reader = nil
		return n, io.EOF // we are done!

	default:
		// Some unknown error.
		err := m.reader.Close()
		if err != nil {
			return 0, err
		}
		return n, e
	}
}

// Close closes the underlying resources.
func (m *multi) Close() error {
	m.idx = 0
	m.files = nil
	if m.reader != nil {
		err := m.reader.Close()
		if err != nil {
			return err
		}
	}
	return nil
}

func streamFile(path string) (io.ReadCloser, error) {
	f, e := os.Open(path)
	if e != nil {
		return nil, e
	}
	if filepath.Ext(path) == ".gz" {
		r, err := NewGZIPReader(f)
		if err != nil {
			return nil, err
		}
		return r, nil
	}
	return f, nil
}

// GZIPReader is a wrapper to read compressed gzip files.
type GZIPReader struct {
	inReader   io.ReadCloser
	gzipReader *gzip.Reader
}

// NewGZIPReader creates a new GZIPReader that reads from r.
// The return value implements io.ReadCloser. It is the caller's responsibility to call Close when done.
func NewGZIPReader(r io.ReadCloser) (*GZIPReader, error) {
	gr := &GZIPReader{inReader: r}
	var err error
	gr.gzipReader, err = gzip.NewReader(gr.inReader)
	if err != nil {
		return nil, err
	}
	return gr, nil
}

// Read implements the io.Read interface.
func (g *GZIPReader) Read(p []byte) (int, error) {
	return g.gzipReader.Read(p)
}

// Close closes the gzip reader and the wrapped reader.
func (g *GZIPReader) Close() error {

	if g.inReader != nil {
		err := g.inReader.Close()
		if err != nil {
			return err
		}
	}
	err := g.gzipReader.Close()
	return err
}

// ReadJSONParallel creates a new streamer to read json objects.
// See FileStreamer to specify the path.
// Run it on a seprate goroutine.
func ReadJSONParallel(path string, obj interface{}, objCh chan interface{}, numWorkers int) {

	// List of filel paths.
	paths, err := extractPaths(path, ".json")
	if err != nil {
		log.Fatal(err)
	}

	// We need to know when all workers finish doing the work.
	var wg sync.WaitGroup
	wg.Add(numWorkers)
	log.Printf("starting %d workers", numWorkers)
	pathCh := make(chan string, 10)

	// Do the work concurrently in the background.
	for w := 0; w < numWorkers; w++ {
		go func() {
			worker(obj, pathCh, objCh)
			wg.Done()
		}()
	}

	// Push paths into channel so workers can do their job concurrently.
	for _, v := range paths {
		pathCh <- v
	}
	// Signal that all work is in the channel.
	close(pathCh)

	// Wait for all workers to finish.
	wg.Wait()
	close(objCh)
}

func worker(obj interface{}, pathCh chan string, objCh chan interface{}) {

	for {
		path, more := <-pathCh
		if !more {
			return
		}
		reader, err := streamFile(path)
		if err != nil {
			log.Fatalln("worker error when processing file ", path)
		}
		dec := json.NewDecoder(reader)
		n := 0
		for {
			val := reflect.ValueOf(obj)
			val = reflect.Indirect(val)
			x := reflect.New(val.Type()).Interface()
			e := dec.Decode(x)
			if e == io.EOF {
				// log.Printf("read %8d records from file %s", n, path)
				break
			}
			objCh <- x
			n++
		}
	}
}

// Writer writes json objects.
type Writer struct {
	writer io.WriteCloser
	path   string
	enc    *json.Encoder
}

// NewWriter writes graphs to files.
// path is the filename, if the ext is "gz", the data is gzipped.
func NewWriter(path string) (*Writer, error) {

	writer := &Writer{
		path: path,
	}
	e := os.MkdirAll(filepath.Dir(path), 0755)
	if e != nil {
		return nil, e
	}
	w, e := os.Create(path)
	if e != nil {
		return nil, e
	}

	writer.enc = json.NewEncoder(w)
	writer.writer = w
	if filepath.Ext(path) == ".gz" {
		gz := gzip.NewWriter(w)
		writer.enc = json.NewEncoder(gz)
		writer.writer = gz
	}

	return writer, nil
}

// WriteJSON writes a json object.
func (w *Writer) Write(o interface{}) error {

	err := w.enc.Encode(o)
	if err != nil {
		return err
	}
	return nil
}

// Close closes the writer.
func (w *Writer) Close() error {
	if w.writer != nil {
		return w.writer.Close()
	}
	return nil
}
<|endoftext|>


<commit_before>package browse

import (
	//"bytes"
	"encoding/json"
	"github.com/mholt/caddy/middleware"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"sort"
	"testing"
	"text/template"
	"time"
)

// "sort" package has "IsSorted" function, but no "IsReversed";
func isReversed(data sort.Interface) bool {
	n := data.Len()
	for i := n - 1; i > 0; i-- {
		if !data.Less(i, i-1) {
			return false
		}
	}
	return true
}

func TestSort(t *testing.T) {
	// making up []fileInfo with bogus values;
	// to be used to make up our "listing"
	fileInfos := []FileInfo{
		{
			Name:    "fizz",
			Size:    4,
			ModTime: time.Now().AddDate(-1, 1, 0),
		},
		{
			Name:    "buzz",
			Size:    2,
			ModTime: time.Now().AddDate(0, -3, 3),
		},
		{
			Name:    "bazz",
			Size:    1,
			ModTime: time.Now().AddDate(0, -2, -23),
		},
		{
			Name:    "jazz",
			Size:    3,
			ModTime: time.Now(),
		},
	}
	listing := Listing{
		Name:    "foobar",
		Path:    "/fizz/buzz",
		CanGoUp: false,
		Items:   fileInfos,
	}

	// sort by name
	listing.Sort = "name"
	listing.applySort()
	if !sort.IsSorted(byName(listing)) {
		t.Errorf("The listing isn't name sorted: %v", listing.Items)
	}

	// sort by size
	listing.Sort = "size"
	listing.applySort()
	if !sort.IsSorted(bySize(listing)) {
		t.Errorf("The listing isn't size sorted: %v", listing.Items)
	}

	// sort by Time
	listing.Sort = "time"
	listing.applySort()
	if !sort.IsSorted(byTime(listing)) {
		t.Errorf("The listing isn't time sorted: %v", listing.Items)
	}

	// reverse by name
	listing.Sort = "name"
	listing.Order = "desc"
	listing.applySort()
	if !isReversed(byName(listing)) {
		t.Errorf("The listing isn't reversed by name: %v", listing.Items)
	}

	// reverse by size
	listing.Sort = "size"
	listing.Order = "desc"
	listing.applySort()
	if !isReversed(bySize(listing)) {
		t.Errorf("The listing isn't reversed by size: %v", listing.Items)
	}

	// reverse by time
	listing.Sort = "time"
	listing.Order = "desc"
	listing.applySort()
	if !isReversed(byTime(listing)) {
		t.Errorf("The listing isn't reversed by time: %v", listing.Items)
	}
}

func TestBrowseTemplate(t *testing.T) {
	tmpl, err := template.ParseFiles("testdata/photos.tpl")
	if err != nil {
		t.Fatalf("An error occured while parsing the template: %v", err)
	}

	b := Browse{
		Next: middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
			t.Fatalf("Next shouldn't be called")
			return 0, nil
		}),
		Root: "./testdata",
		Configs: []Config{
			Config{
				PathScope: "/photos",
				Template:  tmpl,
			},
		},
	}

	req, err := http.NewRequest("GET", "/photos/", nil)
	if err != nil {
		t.Fatalf("Test: Could not create HTTP request: %v", err)
	}

	rec := httptest.NewRecorder()

	b.ServeHTTP(rec, req)
	if rec.Code != http.StatusOK {
		t.Fatalf("Wrong status, expected %d, got %d", http.StatusOK, rec.Code)
	}

	respBody := rec.Body.String()
	expectedBody := `<!DOCTYPE html>

<title>Template</title>

Header

/photos/

test.html

test2.html

`

	if respBody != expectedBody {
		t.Fatalf("Expected body: %v got: %v", expectedBody, respBody)
	}

}

func TestBrowseJson(t *testing.T) {

	b := Browse{
		Next: middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
			t.Fatalf("Next shouldn't be called")
			return 0, nil
		}),
		Root: "./testdata",
		Configs: []Config{
			Config{
				PathScope: "/photos",
			},
		},
	}

	req, err := http.NewRequest("GET", "/photos/", nil)
	if err != nil {
		t.Fatalf("Test: Could not create HTTP request: %v", err)
	}
	req.Header.Set("Accept", "application/json")
	rec := httptest.NewRecorder()

	b.ServeHTTP(rec, req)
	if rec.Code != http.StatusOK {
		t.Fatalf("Wrong status, expected %d, got %d", http.StatusOK, rec.Code)
	}
	if rec.HeaderMap.Get("Content-Type") != "application/json; charset=utf-8" {
		t.Fatalf("Expected Content type to be application/json; charset=utf-8, but got %s ", rec.HeaderMap.Get("Content-Type"))
	}

	actualJsonResponseString := rec.Body.String()
	//t.Logf("json response body: %s\n", respBody)
	//generating the listing to compare it with the response body
	file, err := os.Open(b.Root + req.URL.Path)
	if err != nil {
		if os.IsPermission(err) {
			t.Fatalf("Os Permission Error")

		}

	}
	defer file.Close()

	files, err := file.Readdir(-1)
	if err != nil {
		t.Fatalf("Unable to Read Contents of the directory")
	}
	var fileinfos []FileInfo
	for _, f := range files {
		name := f.Name()

		if f.IsDir() {
			name += "/"
		}

		url := url.URL{Path: name}

		fileinfos = append(fileinfos, FileInfo{
			IsDir:   f.IsDir(),
			Name:    f.Name(),
			Size:    f.Size(),
			URL:     url.String(),
			ModTime: f.ModTime(),
			Mode:    f.Mode(),
		})
	}
	listing := Listing{
		Items: fileinfos,
	}
	listing.Sort = "name"
	listing.Order = "asc"
	listing.applySort()

	//var buf bytes.Buffer
	marsh, err := json.Marshal(listing.Items)
	if err != nil {
		t.Fatalf("Unable to Marshal the listing ")
	}
	//t.Logf("json value: %s\n", string(marsh))
	expectedJsonString := string(marsh)
	if actualJsonResponseString != expectedJsonString {
		t.Errorf("Json response string doesnt match the expected Json response ")
	}
}
<commit_msg>Left over comments removed <commit_after>package browse

import (
        "encoding/json"
	"github.com/mholt/caddy/middleware"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"sort"
	"testing"
	"text/template"
	"time"
)

// "sort" package has "IsSorted" function, but no "IsReversed";
func isReversed(data sort.Interface) bool {
	n := data.Len()
	for i := n - 1; i > 0; i-- {
		if !data.Less(i, i-1) {
			return false
		}
	}
	return true
}

func TestSort(t *testing.T) {
	// making up []fileInfo with bogus values;
	// to be used to make up our "listing"
	fileInfos := []FileInfo{
		{
			Name:    "fizz",
			Size:    4,
			ModTime: time.Now().AddDate(-1, 1, 0),
		},
		{
			Name:    "buzz",
			Size:    2,
			ModTime: time.Now().AddDate(0, -3, 3),
		},
		{
			Name:    "bazz",
			Size:    1,
			ModTime: time.Now().AddDate(0, -2, -23),
		},
		{
			Name:    "jazz",
			Size:    3,
			ModTime: time.Now(),
		},
	}
	listing := Listing{
		Name:    "foobar",
		Path:    "/fizz/buzz",
		CanGoUp: false,
		Items:   fileInfos,
	}

	// sort by name
	listing.Sort = "name"
	listing.applySort()
	if !sort.IsSorted(byName(listing)) {
		t.Errorf("The listing isn't name sorted: %v", listing.Items)
	}

	// sort by size
	listing.Sort = "size"
	listing.applySort()
	if !sort.IsSorted(bySize(listing)) {
		t.Errorf("The listing isn't size sorted: %v", listing.Items)
	}

	// sort by Time
	listing.Sort = "time"
	listing.applySort()
	if !sort.IsSorted(byTime(listing)) {
		t.Errorf("The listing isn't time sorted: %v", listing.Items)
	}

	// reverse by name
	listing.Sort = "name"
	listing.Order = "desc"
	listing.applySort()
	if !isReversed(byName(listing)) {
		t.Errorf("The listing isn't reversed by name: %v", listing.Items)
	}

	// reverse by size
	listing.Sort = "size"
	listing.Order = "desc"
	listing.applySort()
	if !isReversed(bySize(listing)) {
		t.Errorf("The listing isn't reversed by size: %v", listing.Items)
	}

	// reverse by time
	listing.Sort = "time"
	listing.Order = "desc"
	listing.applySort()
	if !isReversed(byTime(listing)) {
		t.Errorf("The listing isn't reversed by time: %v", listing.Items)
	}
}

func TestBrowseTemplate(t *testing.T) {
	tmpl, err := template.ParseFiles("testdata/photos.tpl")
	if err != nil {
		t.Fatalf("An error occured while parsing the template: %v", err)
	}

	b := Browse{
		Next: middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
			t.Fatalf("Next shouldn't be called")
			return 0, nil
		}),
		Root: "./testdata",
		Configs: []Config{
			Config{
				PathScope: "/photos",
				Template:  tmpl,
			},
		},
	}

	req, err := http.NewRequest("GET", "/photos/", nil)
	if err != nil {
		t.Fatalf("Test: Could not create HTTP request: %v", err)
	}

	rec := httptest.NewRecorder()

	b.ServeHTTP(rec, req)
	if rec.Code != http.StatusOK {
		t.Fatalf("Wrong status, expected %d, got %d", http.StatusOK, rec.Code)
	}

	respBody := rec.Body.String()
	expectedBody := `<!DOCTYPE html>

<title>Template</title>

Header

/photos/

test.html

test2.html

`

	if respBody != expectedBody {
		t.Fatalf("Expected body: %v got: %v", expectedBody, respBody)
	}

}

func TestBrowseJson(t *testing.T) {

	b := Browse{
		Next: middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
			t.Fatalf("Next shouldn't be called")
			return 0, nil
		}),
		Root: "./testdata",
		Configs: []Config{
			Config{
				PathScope: "/photos",
			},
		},
	}

	req, err := http.NewRequest("GET", "/photos/", nil)
	if err != nil {
		t.Fatalf("Test: Could not create HTTP request: %v", err)
	}
	req.Header.Set("Accept", "application/json")
	rec := httptest.NewRecorder()

	b.ServeHTTP(rec, req)
	if rec.Code != http.StatusOK {
		t.Fatalf("Wrong status, expected %d, got %d", http.StatusOK, rec.Code)
	}
	if rec.HeaderMap.Get("Content-Type") != "application/json; charset=utf-8" {
		t.Fatalf("Expected Content type to be application/json; charset=utf-8, but got %s ", rec.HeaderMap.Get("Content-Type"))
	}

	actualJsonResponseString := rec.Body.String()

	//generating the listing to compare it with the response body
	file, err := os.Open(b.Root + req.URL.Path)
	if err != nil {
		if os.IsPermission(err) {
			t.Fatalf("Os Permission Error")

		}

	}
	defer file.Close()

	files, err := file.Readdir(-1)
	if err != nil {
		t.Fatalf("Unable to Read Contents of the directory")
	}
	var fileinfos []FileInfo
	for _, f := range files {
		name := f.Name()

		if f.IsDir() {
			name += "/"
		}

		url := url.URL{Path: name}

		fileinfos = append(fileinfos, FileInfo{
			IsDir:   f.IsDir(),
			Name:    f.Name(),
			Size:    f.Size(),
			URL:     url.String(),
			ModTime: f.ModTime(),
			Mode:    f.Mode(),
		})
	}
	listing := Listing{
		Items: fileinfos,
	}
	listing.Sort = "name"
	listing.Order = "asc"
	listing.applySort()

	marsh, err := json.Marshal(listing.Items)
	if err != nil {
		t.Fatalf("Unable to Marshal the listing ")
	}
	expectedJsonString := string(marsh)
	if actualJsonResponseString != expectedJsonString {
		t.Errorf("Json response string doesnt match the expected Json response ")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"math"
	"os"
	"sort"
	"time"

	"github.com/octo/icestat/bahn"
)

var (
	interval    = flag.Duration("interval", 10*time.Second, "Interval in which to report statistics.")
	count       = flag.Int("count", -1, "Number of iterations.")
	destination = flag.String("destination", "", "Optional destination to anticipate.")
)

type speedDistribution struct {
	data []float64
}

func (s *speedDistribution) add(kmh float64) {
	s.data = append(s.data, kmh)
	sort.Float64s(s.data)
}

func (s speedDistribution) max() float64 {
	if len(s.data) == 0 {
		return math.NaN()
	}

	return s.data[len(s.data)-1]
}

func (s speedDistribution) percentile(p float64) float64 {
	if len(s.data) == 0 {
		return math.NaN()
	}

	idx := float64(len(s.data)) * p / 100.0
	return s.data[int(idx-0.5)]
}

func (s speedDistribution) median() float64 {
	return s.percentile(50.0)
}

func (s speedDistribution) average() float64 {
	var sum float64
	var num int

	for _, speed := range s.data {
		if !math.IsNaN(speed) {
			sum += speed
			num++
		}
	}

	if num == 0 {
		return math.NaN()
	}

	return sum / float64(num)
}

func formatDuration(d time.Duration) string {
	h, m := math.Modf(d.Hours())
	m *= 60.0

	return fmt.Sprintf("%.0f:%02.0f", h, m)
}

func main() {
	var sd speedDistribution

	flag.Parse()

	for *count != 0 {
		if *count > 0 {
			*count -= 1
		}

		trip, err := bahn.TripInfo()
		if err != nil {
			log.Print("TripInfo: ", err)
			time.Sleep(*interval)
			continue
		}

		s, err := bahn.StatusInfo()
		if err != nil {
			log.Print("StatusInfo: ", err)
			time.Sleep(*interval)
			continue
		}

		sd.add(s.Speed)

		nextStop := trip.NextStop
		finalStop := trip.Stops[len(trip.Stops)-1]
		if *destination != "" {
			var ok bool
			finalStop, ok = trip.FindStop(*destination)

			if !ok {
				log.Printf("stop %q not found. Valid stops are:", *destination)
				for _, stop := range trip.Stops {
					log.Printf("  * %q", stop.Station)
				}
				os.Exit(1)
			}
		}

		if finalStop.Passed {
			fmt.Printf("Train has passed %v\n", finalStop)
			break
		}

		if nextStop == nil {
			fmt.Printf("Train arrived in %v\n", trip.Stops[len(trip.Stops)-1])
			break
		}

		if nextStop == nil || finalStop == nil {
			time.Sleep(*interval)
			continue
		}

		fmt.Printf("%s%s to %q (via %q): "+
			"distance=%.0f(%.0f) km, "+
			"eta=%s(%s), "+
			"delay=%s(%s), "+
			"speed=%.0f/%.0f/%.0f km/h (cur/avg/max)\n",
			trip.TrainType, trip.TrainID, finalStop.Station, nextStop.Station,
			trip.DistanceTo(finalStop), trip.DistanceTo(nextStop),
			formatDuration(finalStop.ETA()), formatDuration(nextStop.ETA()),
			formatDuration(finalStop.Delay()), formatDuration(nextStop.Delay()),
			s.Speed, sd.average(), sd.max())

		if *count != 0 {
			time.Sleep(*interval)
		}
	}
}
<commit_msg>icestat: Use the new "Position" API.<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"math"
	"os"
	"sort"
	"time"

	"github.com/octo/icestat/bahn"
)

var (
	interval    = flag.Duration("interval", 10*time.Second, "Interval in which to report statistics.")
	count       = flag.Int("count", -1, "Number of iterations.")
	destination = flag.String("destination", "", "Optional destination to anticipate.")
)

type speedDistribution struct {
	data []float64
}

func (s *speedDistribution) add(kmh float64) {
	s.data = append(s.data, kmh)
	sort.Float64s(s.data)
}

func (s speedDistribution) max() float64 {
	if len(s.data) == 0 {
		return math.NaN()
	}

	return s.data[len(s.data)-1]
}

func (s speedDistribution) percentile(p float64) float64 {
	if len(s.data) == 0 {
		return math.NaN()
	}

	idx := float64(len(s.data)) * p / 100.0
	return s.data[int(idx-0.5)]
}

func (s speedDistribution) median() float64 {
	return s.percentile(50.0)
}

func (s speedDistribution) average() float64 {
	var sum float64
	var num int

	for _, speed := range s.data {
		if !math.IsNaN(speed) {
			sum += speed
			num++
		}
	}

	if num == 0 {
		return math.NaN()
	}

	return sum / float64(num)
}

func formatDuration(d time.Duration) string {
	h, m := math.Modf(d.Hours())
	m *= 60.0

	return fmt.Sprintf("%.0f:%02.0f", h, m)
}

func main() {
	var sd speedDistribution

	flag.Parse()

	for *count != 0 {
		if *count > 0 {
			*count -= 1
		}

		trip, err := bahn.TripInfo()
		if err != nil {
			log.Print("TripInfo: ", err)
			time.Sleep(*interval)
			continue
		}

		pos, err := bahn.PositionInfo()
		if err != nil {
			log.Print("PositionInfo: ", err)
			time.Sleep(*interval)
			continue
		}

		sd.add(pos.Speed)

		nextStop := trip.NextStop
		finalStop := trip.Stops[len(trip.Stops)-1]
		if *destination != "" {
			var ok bool
			finalStop, ok = trip.FindStop(*destination)

			if !ok {
				log.Printf("stop %q not found. Valid stops are:", *destination)
				for _, stop := range trip.Stops {
					log.Printf("  * %q", stop.Station)
				}
				os.Exit(1)
			}
		}

		if finalStop.Passed {
			fmt.Printf("Train has passed %v\n", finalStop)
			break
		}

		if nextStop == nil {
			fmt.Printf("Train arrived in %v\n", trip.Stops[len(trip.Stops)-1])
			break
		}

		if nextStop == nil || finalStop == nil {
			time.Sleep(*interval)
			continue
		}

		fmt.Printf("%s%s to %q (via %q): "+
			"distance=%.0f(%.0f) km, "+
			"eta=%s(%s), "+
			"delay=%s(%s), "+
			"speed=%.0f/%.0f/%.0f km/h (cur/avg/max)\n",
			trip.TrainType, trip.TrainID, finalStop.Station, nextStop.Station,
			trip.DistanceTo(finalStop), trip.DistanceTo(nextStop),
			formatDuration(finalStop.ETA()), formatDuration(nextStop.ETA()),
			formatDuration(finalStop.Delay()), formatDuration(nextStop.Delay()),
			pos.Speed, sd.average(), sd.max())

		if *count != 0 {
			time.Sleep(*interval)
		}
	}
}
<|endoftext|>


<commit_before>package ige

import "crypto/cipher"

type ige struct {
	b              cipher.Block
	blockSize      int
	lastplaintext  []byte
	lastciphertext []byte
}

//test vector @ https://web.archive.org/web/20120418022623/http://www.links.org/files/openssl-ige.pdf

func newIGE(b cipher.Block, iv []byte) (ret *ige) {
	//in IGE mode len(iv) is 2 * block size
	blockSize := b.BlockSize()
	internal := make([]byte, len(iv))
	copy(internal, iv)
	ret = &ige{
		b:              b,
		blockSize:      blockSize,
		lastciphertext: internal[:blockSize],
		lastplaintext:  internal[blockSize:],
	}
	return
}

type igeEncrypter ige

//cryptBlocks was written in Encrypter perspective
//since IGE mode use nearly identical algorithm
//we can use it as Decrypter too
func cryptBlocks(dst, src []byte, x *igeEncrypter, cryptFunc func([]byte, []byte)) {
	if len(src)%x.blockSize != 0 {
		panic("crypto/cipher: input not full blocks")
	}
	if len(dst) < len(src) {
		panic("crypto/cipher: output smaller than input")
	}

	for len(src) > 0 {
		//use lastciphertext as scratch memory
		for i := 0; i < x.blockSize; i++ {
			x.lastciphertext[i] ^= src[i]
		}
		cryptFunc(x.lastciphertext, x.lastciphertext) //x.b.Encrypt
		for i := 0; i < x.blockSize; i++ {
			x.lastciphertext[i] ^= x.lastplaintext[i]
		}

		//update internal state
		copy(x.lastplaintext, src)
		//copy to destination
		copy(dst, x.lastciphertext)

		src = src[x.blockSize:]
		dst = dst[x.blockSize:]
	}
}

// NewIGEEncrypter returns a BlockMode which encrypts in
// infinite garble extension mode, using the given Block.
// The length of iv must be 2 times of Block's block size.
func NewIGEEncrypter(b cipher.Block, iv []byte) cipher.BlockMode {
	if len(iv) != b.BlockSize()*2 {
		panic("ige.NewIGEEncrypter: IV length must equal 2 * block size")
	}
	return (*igeEncrypter)(newIGE(b, iv))
}

func (x *igeEncrypter) BlockSize() int { return x.blockSize }

func (x *igeEncrypter) CryptBlocks(dst, src []byte) {
	//just alias
	cryptBlocks(dst, src, x, x.b.Encrypt)
}

func (x *igeEncrypter) SetIV(iv []byte) {
	if len(iv) != x.blockSize*2 {
		panic("cipher: incorrect length IV")
	}
	copy(x.lastplaintext, iv[:x.blockSize])
	copy(x.lastciphertext, iv[x.blockSize:])
}

type igeDecrypter ige

// NewIGEDecrypter returns a BlockMode which decrypts in
// infinite garble extension mode, using the given Block.
// The length of iv must be 2 times of Block's block size.
func NewIGEDecrypter(b cipher.Block, iv []byte) cipher.BlockMode {
	if len(iv) != b.BlockSize()*2 {
		panic("cipher.NewIGEDecrypter: IV length must equal 2 * block size")
	}
	return (*igeDecrypter)(newIGE(b, iv))
}

func (x *igeDecrypter) BlockSize() int { return x.blockSize }

func (x *igeDecrypter) CryptBlocks(dst, src []byte) {
	//swap block to make final x.lastciphertext, x.lastplaintext make sense
	var tmp []byte
	tmp = x.lastciphertext
	x.lastciphertext = x.lastplaintext
	x.lastplaintext = tmp
	cryptBlocks(dst, src, (*igeEncrypter)(x), x.b.Decrypt)
	x.lastplaintext = x.lastciphertext
	x.lastciphertext = tmp
	tmp = nil
}

func (x *igeDecrypter) SetIV(iv []byte) {
	((*igeEncrypter)(x)).SetIV(iv)
}
<commit_msg>Embed license into source file.<commit_after>/*The MIT-Zero License

Copyright (c) 2014 Sarun Rattanasiri

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.*/

package ige

import "crypto/cipher"

type ige struct {
	b              cipher.Block
	blockSize      int
	lastplaintext  []byte
	lastciphertext []byte
}

//test vector @ https://web.archive.org/web/20120418022623/http://www.links.org/files/openssl-ige.pdf

func newIGE(b cipher.Block, iv []byte) (ret *ige) {
	//in IGE mode len(iv) is 2 * block size
	blockSize := b.BlockSize()
	internal := make([]byte, len(iv))
	copy(internal, iv)
	ret = &ige{
		b:              b,
		blockSize:      blockSize,
		lastciphertext: internal[:blockSize],
		lastplaintext:  internal[blockSize:],
	}
	return
}

type igeEncrypter ige

//cryptBlocks was written in Encrypter perspective
//since IGE mode use nearly identical algorithm
//we can use it as Decrypter too
func cryptBlocks(dst, src []byte, x *igeEncrypter, cryptFunc func([]byte, []byte)) {
	if len(src)%x.blockSize != 0 {
		panic("crypto/cipher: input not full blocks")
	}
	if len(dst) < len(src) {
		panic("crypto/cipher: output smaller than input")
	}

	for len(src) > 0 {
		//use lastciphertext as scratch memory
		for i := 0; i < x.blockSize; i++ {
			x.lastciphertext[i] ^= src[i]
		}
		cryptFunc(x.lastciphertext, x.lastciphertext) //x.b.Encrypt
		for i := 0; i < x.blockSize; i++ {
			x.lastciphertext[i] ^= x.lastplaintext[i]
		}

		//update internal state
		copy(x.lastplaintext, src)
		//copy to destination
		copy(dst, x.lastciphertext)

		src = src[x.blockSize:]
		dst = dst[x.blockSize:]
	}
}

// NewIGEEncrypter returns a BlockMode which encrypts in
// infinite garble extension mode, using the given Block.
// The length of iv must be 2 times of Block's block size.
func NewIGEEncrypter(b cipher.Block, iv []byte) cipher.BlockMode {
	if len(iv) != b.BlockSize()*2 {
		panic("ige.NewIGEEncrypter: IV length must equal 2 * block size")
	}
	return (*igeEncrypter)(newIGE(b, iv))
}

func (x *igeEncrypter) BlockSize() int { return x.blockSize }

func (x *igeEncrypter) CryptBlocks(dst, src []byte) {
	//just alias
	cryptBlocks(dst, src, x, x.b.Encrypt)
}

func (x *igeEncrypter) SetIV(iv []byte) {
	if len(iv) != x.blockSize*2 {
		panic("cipher: incorrect length IV")
	}
	copy(x.lastplaintext, iv[:x.blockSize])
	copy(x.lastciphertext, iv[x.blockSize:])
}

type igeDecrypter ige

// NewIGEDecrypter returns a BlockMode which decrypts in
// infinite garble extension mode, using the given Block.
// The length of iv must be 2 times of Block's block size.
func NewIGEDecrypter(b cipher.Block, iv []byte) cipher.BlockMode {
	if len(iv) != b.BlockSize()*2 {
		panic("cipher.NewIGEDecrypter: IV length must equal 2 * block size")
	}
	return (*igeDecrypter)(newIGE(b, iv))
}

func (x *igeDecrypter) BlockSize() int { return x.blockSize }

func (x *igeDecrypter) CryptBlocks(dst, src []byte) {
	//swap block to make final x.lastciphertext, x.lastplaintext make sense
	var tmp []byte
	tmp = x.lastciphertext
	x.lastciphertext = x.lastplaintext
	x.lastplaintext = tmp
	cryptBlocks(dst, src, (*igeEncrypter)(x), x.b.Decrypt)
	x.lastplaintext = x.lastciphertext
	x.lastciphertext = tmp
	tmp = nil
}

func (x *igeDecrypter) SetIV(iv []byte) {
	((*igeEncrypter)(x)).SetIV(iv)
}
<|endoftext|>


<commit_before>package logic

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"
	"sync"

	"code.google.com/p/go-uuid/uuid"

	log "github.com/cihub/seelog"
	serverprotocol "github.com/stampzilla/stampzilla-go/nodes/stampzilla-server/protocol"
)

type Logic struct {
	states map[string]string
	Rules_ []Rule
	re     *regexp.Regexp
	sync.RWMutex
	Nodes *serverprotocol.Nodes `inject:""`
}

func NewLogic() *Logic {
	l := &Logic{states: make(map[string]string)}
	l.re = regexp.MustCompile(`^([^\s\[][^\s\[]*)?(\[.*?([0-9]).*?\])?$`)
	return l
}

func (l *Logic) States() map[string]string {
	l.RLock()
	defer l.RUnlock()
	return l.states
}
func (l *Logic) GetStateByUuid(uuid string) string {
	l.RLock()
	defer l.RUnlock()
	if state, ok := l.states[uuid]; ok {
		return state
	}
	return ""
}
func (l *Logic) SetState(uuid, jsonData string) {
	l.Lock()
	l.states[uuid] = jsonData
	l.Unlock()
}
func (l *Logic) Rules() []Rule {
	l.RLock()
	defer l.RUnlock()
	return l.Rules_
}
func (l *Logic) AddRule(name string) Rule {
	r := &rule{Name: name, Uuid_: uuid.New(), nodes: l.Nodes}
	l.Lock()
	defer l.Unlock()
	l.Rules_ = append(l.Rules_, r)
	return r
}
func (self *Logic) EmptyRules() {
	self.Lock()
	defer self.Unlock()
	self.Rules_ = make([]Rule, 0)
}

func (l *Logic) EvaluateRules() {
	for _, rule := range l.Rules() {
		evaluation := l.evaluateRule(rule)
		fmt.Println("ruleEvaluationResult:", evaluation)
		if evaluation != rule.CondState() {
			rule.SetCondState(evaluation)
			if evaluation {
				rule.RunEnter()
				continue
			}
			rule.RunExit()
		}
	}
}
func (l *Logic) evaluateRule(r Rule) bool {
	var state string
	for _, cond := range r.Conditions() {
		fmt.Println(cond.StatePath())
		//for _, state := range l.States() {
		//var value string
		if state = l.GetStateByUuid(cond.Uuid()); state == "" {
			return false
		}

		value, err := l.path(state, cond.StatePath())
		if err != nil {
			log.Error(err)
		}

		fmt.Println("path output:", value)
		// All conditions must evaluate to true
		if !cond.Check(value) {
			return false
		}
	}
	return true
}

func (l *Logic) ListenForChanges(uuid string) chan string {
	c := make(chan string)
	go l.listen(uuid, c)
	return c
}

func (l *Logic) StopListen(uuid string) {

}

// listen will run in a own goroutine and listen to incoming state changes and Parse them
func (l *Logic) listen(uuid string, c chan string) {
	for {
		select {
		case state, open := <-c:
			if !open {
				return
			}
			l.SetState(uuid, state)
			l.EvaluateRules()
		}
	}
}

func (l *Logic) path(state string, jp string) (interface{}, error) {
	var v interface{}
	err := json.Unmarshal([]byte(state), &v)
	if err != nil {
		return nil, err
	}
	if jp == "" {
		return nil, errors.New("invalid path")
	}
	for _, token := range strings.Split(jp, ".") {
		sl := l.re.FindAllStringSubmatch(token, -1)
		//fmt.Println("REGEXPtoken: ", token)
		//fmt.Println("REGEXP: ", sl)
		if len(sl) == 0 {
			return nil, errors.New("invalid path1")
		}
		ss := sl[0]
		if ss[1] != "" {
			switch v1 := v.(type) {
			case map[string]interface{}:
				v = v1[ss[1]]
			}
		}
		if ss[3] != "" {
			ii, err := strconv.Atoi(ss[3])
			is := ss[3]
			if err != nil {
				return nil, errors.New("invalid path2")
			}
			switch v2 := v.(type) {
			case []interface{}:
				v = v2[ii]
			case map[string]interface{}:
				v = v2[is]
			}
		}
	}
	return v, nil
}

func (l *Logic) SaveRulesToFile(path string) {
	configFile, err := os.Create(path)
	if err != nil {
		log.Error("creating config file", err.Error())
		return
	}
	var out bytes.Buffer
	b, err := json.Marshal(l.Rules)
	if err != nil {
		log.Error("error marshal json", err)
	}
	json.Indent(&out, b, "", "\t")
	out.WriteTo(configFile)
}

func (l *Logic) RestoreRulesFromFile(path string) {
	configFile, err := os.Open(path)
	if err != nil {
		log.Error("opening config file", err.Error())
		return
	}

	type local_rule struct {
		Name         string           `json:"name"`
		Uuid         string           `json:"uuid"`
		Conditions_  []*ruleCondition `json:"conditions"`
		EnterActions []*ruleAction    `json:"enterActions"`
		ExitActions  []*ruleAction    `json:"exitActions"`
	}

	var rules []*local_rule
	jsonParser := json.NewDecoder(configFile)
	if err = jsonParser.Decode(&rules); err != nil {
		log.Error(err)
	}

	l.EmptyRules()

	for _, rule := range rules {
		r := l.AddRule(rule.Name)

		//Set the uuid from json if it exists. Otherwise use the generated one
		if rule.Uuid != "" {
			r.SetUuid(rule.Uuid)
		}
		for _, cond := range rule.Conditions_ {
			r.AddCondition(cond)
		}
		for _, a := range rule.EnterActions {
			r.AddEnterAction(a)
		}
		for _, a := range rule.ExitActions {
			r.AddExitAction(a)
		}
	}
}
<commit_msg>Restored again<commit_after>package logic

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"
	"sync"

	"code.google.com/p/go-uuid/uuid"

	log "github.com/cihub/seelog"
	serverprotocol "github.com/stampzilla/stampzilla-go/nodes/stampzilla-server/protocol"
)

type Logic struct {
	states map[string]string
	Rules_ []Rule
	re     *regexp.Regexp
	sync.RWMutex
	Nodes *serverprotocol.Nodes `inject:""`
}

func NewLogic() *Logic {
	l := &Logic{states: make(map[string]string)}
	l.re = regexp.MustCompile(`^([^\s\[][^\s\[]*)?(\[.*?([0-9]).*?\])?$`)
	return l
}

func (l *Logic) States() map[string]string {
	l.RLock()
	defer l.RUnlock()
	return l.states
}
func (l *Logic) GetStateByUuid(uuid string) string {
	l.RLock()
	defer l.RUnlock()
	if state, ok := l.states[uuid]; ok {
		return state
	}
	return ""
}
func (l *Logic) SetState(uuid, jsonData string) {
	l.Lock()
	l.states[uuid] = jsonData
	l.Unlock()
}
func (l *Logic) Rules() []Rule {
	l.RLock()
	defer l.RUnlock()
	return l.Rules_
}
func (l *Logic) AddRule(name string) Rule {
	r := &rule{Name: name, Uuid_: uuid.New(), nodes: l.Nodes}
	l.Lock()
	defer l.Unlock()
	l.Rules_ = append(l.Rules_, r)
	return r
}
func (self *Logic) EmptyRules() {
	self.Lock()
	defer self.Unlock()
	self.Rules_ = make([]Rule, 0)
}

func (l *Logic) EvaluateRules() {
	for _, rule := range l.Rules() {
		evaluation := l.evaluateRule(rule)
		fmt.Println("ruleEvaluationResult:", evaluation)
		if evaluation != rule.CondState() {
			rule.SetCondState(evaluation)
			if evaluation {
				rule.RunEnter()
				continue
			}
			rule.RunExit()
		}
	}
}
func (l *Logic) evaluateRule(r Rule) bool {
	var state string
	for _, cond := range r.Conditions() {
		fmt.Println(cond.StatePath())
		//for _, state := range l.States() {
		//var value string
		if state = l.GetStateByUuid(cond.Uuid()); state == "" {
			return false
		}

		value, err := l.path(state, cond.StatePath())
		if err != nil {
			log.Error(err)
		}

		fmt.Println("path output:", value)
		// All conditions must evaluate to true
		if !cond.Check(value) {
			return false
		}
	}
	return true
}

func (l *Logic) ListenForChanges(uuid string) *chan string {
	c := make(chan string)
	go l.listen(uuid, c)
	return &c
}

func (l *Logic) StopListen(uuid string) {

}

// listen will run in a own goroutine and listen to incoming state changes and Parse them
func (l *Logic) listen(uuid string, c chan string) {
	for {
		select {
		case state, open := <-c:
			if !open {
				return
			}
			l.SetState(uuid, state)
			l.EvaluateRules()
		}
	}
}

func (l *Logic) path(state string, jp string) (interface{}, error) {
	var v interface{}
	err := json.Unmarshal([]byte(state), &v)
	if err != nil {
		return nil, err
	}
	if jp == "" {
		return nil, errors.New("invalid path")
	}
	for _, token := range strings.Split(jp, ".") {
		sl := l.re.FindAllStringSubmatch(token, -1)
		//fmt.Println("REGEXPtoken: ", token)
		//fmt.Println("REGEXP: ", sl)
		if len(sl) == 0 {
			return nil, errors.New("invalid path1")
		}
		ss := sl[0]
		if ss[1] != "" {
			switch v1 := v.(type) {
			case map[string]interface{}:
				v = v1[ss[1]]
			}
		}
		if ss[3] != "" {
			ii, err := strconv.Atoi(ss[3])
			is := ss[3]
			if err != nil {
				return nil, errors.New("invalid path2")
			}
			switch v2 := v.(type) {
			case []interface{}:
				v = v2[ii]
			case map[string]interface{}:
				v = v2[is]
			}
		}
	}
	return v, nil
}

func (l *Logic) SaveRulesToFile(path string) {
	configFile, err := os.Create(path)
	if err != nil {
		log.Error("creating config file", err.Error())
		return
	}
	var out bytes.Buffer
	b, err := json.Marshal(l.Rules)
	if err != nil {
		log.Error("error marshal json", err)
	}
	json.Indent(&out, b, "", "\t")
	out.WriteTo(configFile)
}

func (l *Logic) RestoreRulesFromFile(path string) {
	configFile, err := os.Open(path)
	if err != nil {
		log.Error("opening config file", err.Error())
		return
	}

	type local_rule struct {
		Name         string           `json:"name"`
		Uuid         string           `json:"uuid"`
		Conditions_  []*ruleCondition `json:"conditions"`
		EnterActions []*ruleAction    `json:"enterActions"`
		ExitActions  []*ruleAction    `json:"exitActions"`
	}

	var rules []*local_rule
	jsonParser := json.NewDecoder(configFile)
	if err = jsonParser.Decode(&rules); err != nil {
		log.Error(err)
	}

	l.EmptyRules()

	for _, rule := range rules {
		r := l.AddRule(rule.Name)

		//Set the uuid from json if it exists. Otherwise use the generated one
		if rule.Uuid != "" {
			r.SetUuid(rule.Uuid)
		}
		for _, cond := range rule.Conditions_ {
			r.AddCondition(cond)
		}
		for _, a := range rule.EnterActions {
			r.AddEnterAction(a)
		}
		for _, a := range rule.ExitActions {
			r.AddExitAction(a)
		}
	}
}
<|endoftext|>