<commit_before>package hystrix

import (
	"sync"
	"time"

	"github.com/afex/hystrix-go/hystrix/metric_collector"
	"github.com/afex/hystrix-go/hystrix/rolling"
)

type commandExecution struct {
	Type        string        `json:"type"`
	Start       time.Time     `json:"start_time"`
	RunDuration time.Duration `json:"run_duration"`
}

type metricExchange struct {
	Name    string
	Updates chan *commandExecution
	Mutex   *sync.RWMutex

	metricCollectors []metricCollector.MetricCollector
}

func newMetricExchange(name string) *metricExchange {
	m := &metricExchange{}
	m.Name = name

	m.Updates = make(chan *commandExecution)
	m.Mutex = &sync.RWMutex{}
	m.metricCollectors = metricCollector.Registry.InitializeMetricCollectors(name)
	m.Reset()

	go m.Monitor()

	return m
}

// The Default Collector function will panic if collectors are not setup to specification.
func (m *metricExchange) DefaultCollector() *metricCollector.DefaultMetricCollector {
	if len(m.metricCollectors) < 1 {
		panic("No Metric Collectors Registered.")
	}
	collection, ok := m.metricCollectors[0].(*metricCollector.DefaultMetricCollector)
	if !ok {
		panic("Default metric collector is not registered correctly. The default metric collector must be registered first.")
	}
	return collection
}

func (m *metricExchange) Monitor() {
	for update := range m.Updates {
		// we only grab a read lock to make sure Reset() isn't changing the numbers.
		m.Mutex.RLock()

		totalDuration := time.Now().Sub(update.Start)
		for _, collector := range m.metricCollectors {
			collector.IncrementAttempts()
			if update.Type != "success" {
				collector.IncrementErrors()
			}

			// granular metrics
			if update.Type == "success" {
				collector.IncrementSuccesses()
			}
			if update.Type == "failure" {
				collector.IncrementFailures()
			}
			if update.Type == "rejected" {
				collector.IncrementRejects()
			}
			if update.Type == "short-circuit" {
				collector.IncrementShortCircuits()
			}
			if update.Type == "timeout" {
				collector.IncrementTimeouts()
			}

			// fallback metrics
			if update.Type == "fallback-success" {
				collector.IncrementFallbackSuccesses()
			}
			if update.Type == "fallback-failure" {
				collector.IncrementFallbackFailures()
			}

			collector.UpdateTotalDuration(totalDuration)
			collector.UpdateRunDuration(update.RunDuration)
		}

		m.Mutex.RUnlock()
	}
}

func (m *metricExchange) Reset() {
	m.Mutex.Lock()
	defer m.Mutex.Unlock()

	for _, collector := range m.metricCollectors {
		collector.Reset()
	}
}

func (m *metricExchange) Requests() *rolling.Number {
	m.Mutex.RLock()
	defer m.Mutex.RUnlock()

	return m.DefaultCollector().NumRequests
}

func (m *metricExchange) ErrorPercent(now time.Time) int {
	m.Mutex.RLock()
	defer m.Mutex.RUnlock()

	var errPct float64
	reqs := m.DefaultCollector().NumRequests.Sum(now)
	errs := m.DefaultCollector().Errors.Sum(now)

	if reqs > 0 {
		errPct = (float64(errs) / float64(reqs)) * 100
	}

	return int(errPct + 0.5)
}

func (m *metricExchange) IsHealthy(now time.Time) bool {
	return m.ErrorPercent(now) < getSettings(m.Name).ErrorPercentThreshold
}
<commit_msg>Use unexported requestsLocked method<commit_after>package hystrix

import (
	"sync"
	"time"

	"github.com/afex/hystrix-go/hystrix/metric_collector"
	"github.com/afex/hystrix-go/hystrix/rolling"
)

type commandExecution struct {
	Type        string        `json:"type"`
	Start       time.Time     `json:"start_time"`
	RunDuration time.Duration `json:"run_duration"`
}

type metricExchange struct {
	Name    string
	Updates chan *commandExecution
	Mutex   *sync.RWMutex

	metricCollectors []metricCollector.MetricCollector
}

func newMetricExchange(name string) *metricExchange {
	m := &metricExchange{}
	m.Name = name

	m.Updates = make(chan *commandExecution)
	m.Mutex = &sync.RWMutex{}
	m.metricCollectors = metricCollector.Registry.InitializeMetricCollectors(name)
	m.Reset()

	go m.Monitor()

	return m
}

// The Default Collector function will panic if collectors are not setup to specification.
func (m *metricExchange) DefaultCollector() *metricCollector.DefaultMetricCollector {
	if len(m.metricCollectors) < 1 {
		panic("No Metric Collectors Registered.")
	}
	collection, ok := m.metricCollectors[0].(*metricCollector.DefaultMetricCollector)
	if !ok {
		panic("Default metric collector is not registered correctly. The default metric collector must be registered first.")
	}
	return collection
}

func (m *metricExchange) Monitor() {
	for update := range m.Updates {
		// we only grab a read lock to make sure Reset() isn't changing the numbers.
		m.Mutex.RLock()

		totalDuration := time.Now().Sub(update.Start)
		for _, collector := range m.metricCollectors {
			collector.IncrementAttempts()
			if update.Type != "success" {
				collector.IncrementErrors()
			}

			// granular metrics
			if update.Type == "success" {
				collector.IncrementSuccesses()
			}
			if update.Type == "failure" {
				collector.IncrementFailures()
			}
			if update.Type == "rejected" {
				collector.IncrementRejects()
			}
			if update.Type == "short-circuit" {
				collector.IncrementShortCircuits()
			}
			if update.Type == "timeout" {
				collector.IncrementTimeouts()
			}

			// fallback metrics
			if update.Type == "fallback-success" {
				collector.IncrementFallbackSuccesses()
			}
			if update.Type == "fallback-failure" {
				collector.IncrementFallbackFailures()
			}

			collector.UpdateTotalDuration(totalDuration)
			collector.UpdateRunDuration(update.RunDuration)
		}

		m.Mutex.RUnlock()
	}
}

func (m *metricExchange) Reset() {
	m.Mutex.Lock()
	defer m.Mutex.Unlock()

	for _, collector := range m.metricCollectors {
		collector.Reset()
	}
}

func (m *metricExchange) Requests() *rolling.Number {
	m.Mutex.RLock()
	defer m.Mutex.RUnlock()
	return m.requestsLocked()
}

func (m *metricExchange) requestsLocked() *rolling.Number {
	return m.DefaultCollector().NumRequests
}

func (m *metricExchange) ErrorPercent(now time.Time) int {
	m.Mutex.RLock()
	defer m.Mutex.RUnlock()

	var errPct float64
	reqs := m.requestsLocked().Sum(now)
	errs := m.DefaultCollector().Errors.Sum(now)

	if reqs > 0 {
		errPct = (float64(errs) / float64(reqs)) * 100
	}

	return int(errPct + 0.5)
}

func (m *metricExchange) IsHealthy(now time.Time) bool {
	return m.ErrorPercent(now) < getSettings(m.Name).ErrorPercentThreshold
}
<|endoftext|>


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

package aliyun

import (
	"fmt"
	"github.com/denverdino/aliyungo/common"
	"github.com/denverdino/aliyungo/ecs"
	"github.com/golang/glog"
	"kubeup.com/archon/pkg/cluster"
	"kubeup.com/archon/pkg/util"
	"net"
)

type AliyunNetwork struct {
	VPC           string `k8s:"vpc-id"`
	VSwitch       string `k8s:"vswitch-id"`
	SecurityGroup string `k8s:"security-group-id"`
	RouteTable    string `k8s:"route-table-id"`
	Router        string `k8s:"router-id"`
	VPCIPRange    string `k8s:"vpc-ip-range"`
}

func (p *aliyunCloud) EnsureNetwork(clusterName string, network *cluster.Network) (status *cluster.NetworkStatus, err error) {
	an := &AliyunNetwork{}
	defer func() {
		if err != nil {
			err2 := p.deleteNetwork(network, an)
			if err2 != nil {
				glog.Errorf("Unable to rollback network creation: %v", err2)
			}
		}
	}()
	if network.Annotations == nil {
		network.Annotations = make(map[string]string)
	}

	err = util.MapToStruct(network.Annotations, an, AliyunAnnotationPrefix)
	if err != nil {
		return
	}

	if an.VPC == "" {
		err = p.createVPC(clusterName, network, an)
		if err != nil {
			return
		}
	}

	if an.VSwitch == "" {
		err = p.createVSwitch(clusterName, network, an)
		if err != nil {
			return
		}
	}

	err = util.StructToMap(an, network.Annotations, AliyunAnnotationPrefix)
	if err != nil {
		return
	}

	status = &cluster.NetworkStatus{
		Phase: cluster.NetworkRunning,
	}

	return
}

func (p *aliyunCloud) EnsureNetworkDeleted(clusterName string, network *cluster.Network) (err error) {
	an := &AliyunNetwork{}
	if network.Annotations == nil {
		return nil
	}

	err = util.MapToStruct(network.Annotations, an, AliyunAnnotationPrefix)
	if err != nil {
		return
	}

	return p.deleteNetwork(network, an)
}

func (p *aliyunCloud) deleteNetwork(network *cluster.Network, an *AliyunNetwork) (err error) {
	if an.VSwitch != "" {
		err = p.deleteVSwitch(an)
		if err != nil {
			if isNotFound(err) {
				err = nil
			} else {
				return
			}
		}
	}

	if an.VPC != "" {
		err = p.deleteVPC(network, an)
		if err != nil {
			if isNotFound(err) {
				err = nil
			} else {
				return
			}
		}
	}

	return nil
}

func (p *aliyunCloud) AddNetworkAnnotation(clusterName string, instance *cluster.Instance, network *cluster.Network) error {
	if instance == nil || network == nil {
		return fmt.Errorf("instance or network is nil")
	}

	if network.Annotations == nil {
		return fmt.Errorf("Aliyun network is not ready")
	}

	if instance.Annotations == nil {
		instance.Annotations = make(map[string]string)
	}

	// Network Spec
	err := util.StructToMap(network.Spec, instance.Annotations, cluster.AnnotationPrefix)
	if err != nil {
		return err
	}

	aliyunNetwork := AliyunNetwork{}
	err = util.MapToStruct(network.Annotations, &aliyunNetwork, AliyunAnnotationPrefix)
	if err != nil {
		return err
	}

	return util.StructToMap(aliyunNetwork, instance.Annotations, AliyunAnnotationPrefix)
}

func (p *aliyunCloud) createVPC(clusterName string, network *cluster.Network, an *AliyunNetwork) (err error) {
	if an.VPCIPRange == "" {
		return fmt.Errorf("vpn-ip-range should not be empty")
	}
	region := common.Region(network.Spec.Region)

	glog.V(4).Infof("Creating VPC in aliyun for %s (%v)", clusterName, network.Spec)
	resp, err := p.ecs.CreateVpc(&ecs.CreateVpcArgs{
		RegionId:    region,
		CidrBlock:   an.VPCIPRange,
		VpcName:     clusterName + "-vpc",
		Description: "Archon managed vpc",
		ClientToken: util.RandNano(),
	})
	if err != nil {
		err = aliyunSafeError(err)
		return
	}

	an.VPC = resp.VpcId
	an.Router = resp.VRouterId
	an.RouteTable = resp.RouteTableId

	err2 := p.ecs.WaitForVpcAvailable(region, an.VPC, 0)
	if err2 != nil {
		err2 = aliyunSafeError(err)
		glog.V(4).Infof("Warning: waiting for vpc to be available time out: %v. Ignored", err2)
	}

	// Create sg
	sg, err := p.ecs.CreateSecurityGroup(&ecs.CreateSecurityGroupArgs{
		RegionId:          region,
		SecurityGroupName: clusterName + "-sg",
		VpcId:             an.VPC,
		ClientToken:       util.RandNano(),
	})
	if err != nil {
		return aliyunSafeError(err)
	}
	an.SecurityGroup = sg

	err = p.ecs.AuthorizeSecurityGroup(&ecs.AuthorizeSecurityGroupArgs{
		SecurityGroupId: an.SecurityGroup,
		RegionId:        region,
		NicType:         ecs.NicTypeIntranet,
		IpProtocol:      ecs.IpProtocolTCP,
		Policy:          ecs.PermissionPolicyAccept,
		PortRange:       "1/65535",
		SourceCidrIp:    "0.0.0.0/0",
	})
	if err != nil {
		return aliyunSafeError(err)
	}

	return
}

func (p *aliyunCloud) createVSwitch(clusterName string, network *cluster.Network, an *AliyunNetwork) (err error) {
	_, sub, _ := net.ParseCIDR(network.Spec.Subnet)
	sub2 := util.FromIPNet(sub)

	args := &ecs.CreateVSwitchArgs{
		ZoneId:      network.Spec.Zone,
		CidrBlock:   sub2.String(),
		VpcId:       an.VPC,
		VSwitchName: clusterName + "-vswitch",
	}
	vswitch, err := p.ecs.CreateVSwitch(args)
	if err != nil {
		return aliyunSafeError(err)
	}
	an.VSwitch = vswitch
	return
}

func (p *aliyunCloud) deleteVSwitch(an *AliyunNetwork) (err error) {
	err = p.ecs.DeleteVSwitch(an.VSwitch)
	if err != nil && isNotFound(err) {
		err = nil
	}

	return err
}

func (p *aliyunCloud) deleteVPC(network *cluster.Network, an *AliyunNetwork) (err error) {
	region := common.Region(network.Spec.Region)
	err = p.ecs.DeleteSecurityGroup(region, an.SecurityGroup)
	if err != nil && !isNotFound(err) {
		return aliyunSafeError(err)
	}

	err = p.ecs.DeleteVpc(an.VPC)
	if err != nil && !isNotFound(err) {
		return aliyunSafeError(err)
	}

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

package aliyun

import (
	"fmt"
	"github.com/denverdino/aliyungo/common"
	"github.com/denverdino/aliyungo/ecs"
	"github.com/golang/glog"
	"kubeup.com/archon/pkg/cluster"
	"kubeup.com/archon/pkg/util"
	"net"
)

type AliyunNetwork struct {
	VPC           string `k8s:"vpc-id"`
	VSwitch       string `k8s:"vswitch-id"`
	SecurityGroup string `k8s:"security-group-id"`
	RouteTable    string `k8s:"route-table-id"`
	Router        string `k8s:"router-id"`
	VPCIPRange    string `k8s:"vpc-ip-range"`
}

func (p *aliyunCloud) EnsureNetwork(clusterName string, network *cluster.Network) (status *cluster.NetworkStatus, err error) {
	an := &AliyunNetwork{}
	defer func() {
		if err != nil {
			err2 := p.deleteNetwork(network, an)
			if err2 != nil {
				glog.Errorf("Unable to rollback network creation: %v", err2)
			}
		}
	}()
	if network.Annotations == nil {
		network.Annotations = make(map[string]string)
	}

	err = util.MapToStruct(network.Annotations, an, AliyunAnnotationPrefix)
	if err != nil {
		return
	}

	if an.VPC == "" {
		err = p.createVPC(clusterName, network, an)
		if err != nil {
			return
		}
	}

	if an.VSwitch == "" {
		err = p.createVSwitch(clusterName, network, an)
		if err != nil {
			return
		}
	}

	err = util.StructToMap(an, network.Annotations, AliyunAnnotationPrefix)
	if err != nil {
		return
	}

	status = &cluster.NetworkStatus{
		Phase: cluster.NetworkRunning,
	}

	return
}

func (p *aliyunCloud) EnsureNetworkDeleted(clusterName string, network *cluster.Network) (err error) {
	an := &AliyunNetwork{}
	if network.Annotations == nil {
		return nil
	}

	err = util.MapToStruct(network.Annotations, an, AliyunAnnotationPrefix)
	if err != nil {
		return
	}

	return p.deleteNetwork(network, an)
}

func (p *aliyunCloud) deleteNetwork(network *cluster.Network, an *AliyunNetwork) (err error) {
	if an.VSwitch != "" {
		err = p.deleteVSwitch(an)
		if err != nil {
			if isNotFound(err) {
				err = nil
			} else {
				return
			}
		}
	}

	if an.VPC != "" {
		err = p.deleteVPC(network, an)
		if err != nil {
			if isNotFound(err) {
				err = nil
			} else {
				return
			}
		}
	}

	return nil
}

func (p *aliyunCloud) AddNetworkAnnotation(clusterName string, instance *cluster.Instance, network *cluster.Network) error {
	if instance == nil || network == nil {
		return fmt.Errorf("instance or network is nil")
	}

	if network.Annotations == nil {
		return fmt.Errorf("Aliyun network is not ready")
	}

	if instance.Annotations == nil {
		instance.Annotations = make(map[string]string)
	}

	// Network Spec
	err := util.StructToMap(network.Spec, instance.Annotations, cluster.AnnotationPrefix)
	if err != nil {
		return err
	}

	aliyunNetwork := AliyunNetwork{}
	err = util.MapToStruct(network.Annotations, &aliyunNetwork, AliyunAnnotationPrefix)
	if err != nil {
		return err
	}

	return util.StructToMap(aliyunNetwork, instance.Annotations, AliyunAnnotationPrefix)
}

func (p *aliyunCloud) createVPC(clusterName string, network *cluster.Network, an *AliyunNetwork) (err error) {
	if an.VPCIPRange == "" {
		return fmt.Errorf("vpn-ip-range should not be empty")
	}
	region := common.Region(network.Spec.Region)

	glog.V(4).Infof("Creating VPC in aliyun for %s (%v)", clusterName, network.Spec)
	resp, err := p.ecs.CreateVpc(&ecs.CreateVpcArgs{
		RegionId:    region,
		CidrBlock:   an.VPCIPRange,
		VpcName:     clusterName + "-vpc",
		Description: "Archon managed vpc",
		ClientToken: util.RandNano(),
	})
	if err != nil {
		err = aliyunSafeError(err)
		return
	}

	an.VPC = resp.VpcId
	an.Router = resp.VRouterId
	an.RouteTable = resp.RouteTableId

	err2 := p.ecs.WaitForVpcAvailable(region, an.VPC, 0)
	if err2 != nil {
		err2 = aliyunSafeError(err)
		glog.V(4).Infof("Warning: waiting for vpc to be available time out: %v. Ignored", err2)
	}

	// Create sg
	sg, err := p.ecs.CreateSecurityGroup(&ecs.CreateSecurityGroupArgs{
		RegionId:          region,
		SecurityGroupName: clusterName + "-sg",
		VpcId:             an.VPC,
		ClientToken:       util.RandNano(),
	})
	if err != nil {
		return aliyunSafeError(err)
	}
	an.SecurityGroup = sg

	err = p.ecs.AuthorizeSecurityGroup(&ecs.AuthorizeSecurityGroupArgs{
		SecurityGroupId: an.SecurityGroup,
		RegionId:        region,
		NicType:         ecs.NicTypeIntranet,
		IpProtocol:      ecs.IpProtocolTCP,
		Policy:          ecs.PermissionPolicyAccept,
		PortRange:       "1/65535",
		SourceCidrIp:    "0.0.0.0/0",
	})
	if err != nil {
		return aliyunSafeError(err)
	}

	err = p.ecs.AuthorizeSecurityGroup(&ecs.AuthorizeSecurityGroupArgs{
		SecurityGroupId: an.SecurityGroup,
		RegionId:        region,
		NicType:         ecs.NicTypeIntranet,
		IpProtocol:      ecs.IpProtocolAll,
		Policy:          ecs.PermissionPolicyAccept,
		PortRange:       "1/65535",
		SourceCidrIp:    an.VPCIPRange,
	})
	if err != nil {
		return aliyunSafeError(err)
	}
	return
}

func (p *aliyunCloud) createVSwitch(clusterName string, network *cluster.Network, an *AliyunNetwork) (err error) {
	_, sub, _ := net.ParseCIDR(network.Spec.Subnet)
	sub2 := util.FromIPNet(sub)

	args := &ecs.CreateVSwitchArgs{
		ZoneId:      network.Spec.Zone,
		CidrBlock:   sub2.String(),
		VpcId:       an.VPC,
		VSwitchName: clusterName + "-vswitch",
	}
	vswitch, err := p.ecs.CreateVSwitch(args)
	if err != nil {
		return aliyunSafeError(err)
	}
	an.VSwitch = vswitch
	return
}

func (p *aliyunCloud) deleteVSwitch(an *AliyunNetwork) (err error) {
	err = p.ecs.DeleteVSwitch(an.VSwitch)
	if err != nil && isNotFound(err) {
		err = nil
	}

	return err
}

func (p *aliyunCloud) deleteVPC(network *cluster.Network, an *AliyunNetwork) (err error) {
	region := common.Region(network.Spec.Region)
	err = p.ecs.DeleteSecurityGroup(region, an.SecurityGroup)
	if err != nil && !isNotFound(err) {
		return aliyunSafeError(err)
	}

	err = p.ecs.DeleteVpc(an.VPC)
	if err != nil && !isNotFound(err) {
		return aliyunSafeError(err)
	}

	return nil
}
<|endoftext|>


<commit_before>package input

import (
	"bytes"
	"fmt"
	"os"
	"os/signal"
	"strconv"
)

func (i *UI) Select(query string, list []string, opts *Options) (string, error) {

	// Set the default writer & reader if not provided
	wr, rd := i.Writer, i.Reader
	if wr == nil {
		wr = defaultWriter
	}
	if rd == nil {
		rd = defaultReader
	}

	// Find default index which opts.Default indicates
	defaultIndex := -1
	defaultVal := opts.Default
	if defaultVal != "" {
		for i, item := range list {
			if item == defaultVal {
				defaultIndex = i
			}
		}

		// DefaultVal is set but does'nt exist in list
		if defaultIndex == -1 {
			// This error message is not for user
			// Should be found while development
			return "", fmt.Errorf("opt.Default is specied but does not exst in list")
		}
	}

	// Construct the query to the user
	var buf bytes.Buffer
	buf.WriteString(fmt.Sprintf("%s\n\n", query))
	for i, item := range list {
		buf.WriteString(fmt.Sprintf("%d. %s\n", i+1, item))
	}

	// Prompt the query
	buf.WriteString("\n")
	fmt.Fprintf(wr, buf.String())

	resultCh := make(chan string, 1)
	errCh := make(chan error, 1)
	go func() {
		// Loop only when error by invalid user input and opts.Loop is true.
		for {
			// Construct the asking line to input
			var buf bytes.Buffer
			buf.WriteString("Enter a number")

			// Add default val if provided
			if defaultIndex >= 0 {
				buf.WriteString(fmt.Sprintf(" (Default is %d)", defaultIndex+1))
			}

			buf.WriteString(": ")
			fmt.Fprintf(wr, buf.String())

			// Read user input from reader.
			var line string
			if _, err := fmt.Fscanln(rd, &line); err != nil {
				// Handle error if it's not `unexpected newline`
				if err.Error() != "unexpected newline" {
					errCh <- fmt.Errorf("failed to read the input: %s", err)
					break
				}
			}

			// execSelect selects a item from list by user input.
			result, err := execSelect(list, line, defaultIndex)
			if err != nil {

				// Don't loop and just return error if Loop is false
				if !opts.Loop {
					errCh <- err
					return
				}

				// Check error and if it's possible to ask again to user
				// then provide appropriate message and run loop again
				switch err {
				case ErrEmpty:
					fmt.Fprintf(wr, "Input must not be empty. Answer by a number.\n\n")
					continue
				case ErrNotNumber:
					fmt.Fprintf(wr,
						"%q is not a valid input. Answer by a number.\n\n", line)
					continue
				case ErrOutOfRange:
					fmt.Fprintf(wr,
						"%q is not a valid choice. Choose a number from 1 to %d.\n\n",
						line, len(list))
					continue
				default:
					// If other error is returned, it means asking again is
					// impossible
					errCh <- err
					return
				}
			}

			resultCh <- result
			return
		}
	}()

	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, os.Interrupt)
	defer signal.Stop(sigCh)

	select {
	case result := <-resultCh:
		// Insert the new line for next output
		fmt.Fprintf(wr, "\n")
		return result, nil
	case err := <-errCh:
		// Insert the new line for next output
		fmt.Fprintf(wr, "\n")
		return "", err
	case <-sigCh:
		// Insert the new line for next output
		fmt.Fprintf(wr, "\n")
		return "", ErrInterrupted
	}
}

// execSelect selects a item from list by user input.
// It checks input meets the condition to choose answer from list and if not
// returns appropriate error. See more about error in `error.go` file.
func execSelect(list []string, input string, defaultIndex int) (string, error) {
	if input == "" {
		if defaultIndex >= 0 {
			return list[defaultIndex], nil
		}
		return "", ErrEmpty
	}

	// Convert user input string to int val
	n, err := strconv.Atoi(input)
	if err != nil {
		return "", ErrNotNumber
	}

	// Check answer is in range of list
	if n < 1 || len(list) < n {
		return "", ErrOutOfRange
	}

	return list[n-1], nil
}
<commit_msg>Refactoring<commit_after>package input

import (
	"bytes"
	"fmt"
	"os"
	"os/signal"
	"strconv"
)

func (i *UI) Select(query string, list []string, opts *Options) (string, error) {

	// Set the default writer & reader if not provided
	wr, rd := i.Writer, i.Reader
	if wr == nil {
		wr = defaultWriter
	}
	if rd == nil {
		rd = defaultReader
	}

	// Find default index which opts.Default indicates
	defaultIndex := -1
	defaultVal := opts.Default
	if defaultVal != "" {
		for i, item := range list {
			if item == defaultVal {
				defaultIndex = i
			}
		}

		// DefaultVal is set but does'nt exist in list
		if defaultIndex == -1 {
			// This error message is not for user
			// Should be found while development
			return "", fmt.Errorf("opt.Default is specied but does not exst in list")
		}
	}

	// Construct the query to the user
	var buf bytes.Buffer
	buf.WriteString(fmt.Sprintf("%s\n\n", query))
	for i, item := range list {
		buf.WriteString(fmt.Sprintf("%d. %s\n", i+1, item))
	}

	// Prompt the query
	buf.WriteString("\n")
	fmt.Fprintf(wr, buf.String())

	// resultCh is channel receives result string from user input.
	resultCh := make(chan string, 1)

	// errCh is channel receives error while reading user input.
	errCh := make(chan error, 1)

	// sigCh is channel which is watch Interruptted signal (SIGINT)
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, os.Interrupt)
	defer signal.Stop(sigCh)

	go func() {
		// Loop only when error by invalid user input and opts.Loop is true.
		for {
			// Construct the asking line to input
			var buf bytes.Buffer
			buf.WriteString("Enter a number")

			// Add default val if provided
			if defaultIndex >= 0 {
				buf.WriteString(fmt.Sprintf(" (Default is %d)", defaultIndex+1))
			}

			buf.WriteString(": ")
			fmt.Fprintf(wr, buf.String())

			// Read user input from reader.
			var line string
			if _, err := fmt.Fscanln(rd, &line); err != nil {
				// Handle error if it's not `unexpected newline`
				if err.Error() != "unexpected newline" {
					errCh <- fmt.Errorf("failed to read the input: %s", err)
					break
				}
			}

			// execSelect selects a item from list by user input.
			result, err := execSelect(list, line, defaultIndex)
			if err != nil {

				// Don't loop and just return error if Loop is false
				if !opts.Loop {
					errCh <- err
					return
				}

				// Check error and if it's possible to ask again to user
				// then provide appropriate message and run loop again
				switch err {
				case ErrEmpty:
					fmt.Fprintf(wr, "Input must not be empty. Answer by a number.\n\n")
					continue
				case ErrNotNumber:
					fmt.Fprintf(wr,
						"%q is not a valid input. Answer by a number.\n\n", line)
					continue
				case ErrOutOfRange:
					fmt.Fprintf(wr,
						"%q is not a valid choice. Choose a number from 1 to %d.\n\n",
						line, len(list))
					continue
				default:
					// If other error is returned, it means asking again is
					// impossible
					errCh <- err
					return
				}
			}

			resultCh <- result
			return
		}
	}()

	select {
	case result := <-resultCh:
		// Insert the new line for next output
		fmt.Fprintf(wr, "\n")
		return result, nil
	case err := <-errCh:
		// Insert the new line for next output
		fmt.Fprintf(wr, "\n")
		return "", err
	case <-sigCh:
		// Insert the new line for next output
		fmt.Fprintf(wr, "\n")
		return "", ErrInterrupted
	}
}

// execSelect selects a item from list by user input.
// It checks input meets the condition to choose answer from list and if not
// returns appropriate error. See more about error in `error.go` file.
func execSelect(list []string, input string, defaultIndex int) (string, error) {
	if input == "" {
		if defaultIndex >= 0 {
			return list[defaultIndex], nil
		}
		return "", ErrEmpty
	}

	// Convert user input string to int val
	n, err := strconv.Atoi(input)
	if err != nil {
		return "", ErrNotNumber
	}

	// Check answer is in range of list
	if n < 1 || len(list) < n {
		return "", ErrOutOfRange
	}

	return list[n-1], nil
}
<|endoftext|>


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

package caddyconfig

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io"
	"net/http"
	"os"
	"time"

	"github.com/caddyserver/caddy/v2"
)

func init() {
	caddy.RegisterModule(HTTPLoader{})
}

// HTTPLoader can load Caddy configs over HTTP(S). It can adapt the config
// based on the Content-Type header of the HTTP response.
type HTTPLoader struct {
	// The method for the request. Default: GET
	Method string `json:"method,omitempty"`

	// The URL of the request.
	URL string `json:"url,omitempty"`

	// HTTP headers to add to the request.
	Headers http.Header `json:"header,omitempty"`

	// Maximum time allowed for a complete connection and request.
	Timeout caddy.Duration `json:"timeout,omitempty"`

	TLS *struct {
		// Present this instance's managed remote identity credentials to the server.
		UseServerIdentity bool `json:"use_server_identity,omitempty"`

		// PEM-encoded client certificate filename to present to the server.
		ClientCertificateFile string `json:"client_certificate_file,omitempty"`

		// PEM-encoded key to use with the client certificate.
		ClientCertificateKeyFile string `json:"client_certificate_key_file,omitempty"`

		// List of PEM-encoded CA certificate files to add to the same trust
		// store as RootCAPool (or root_ca_pool in the JSON).
		RootCAPEMFiles []string `json:"root_ca_pem_files,omitempty"`
	} `json:"tls,omitempty"`
}

// CaddyModule returns the Caddy module information.
func (HTTPLoader) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		ID:  "caddy.config_loaders.http",
		New: func() caddy.Module { return new(HTTPLoader) },
	}
}

// LoadConfig loads a Caddy config.
func (hl HTTPLoader) LoadConfig(ctx caddy.Context) ([]byte, error) {
	client, err := hl.makeClient(ctx)
	if err != nil {
		return nil, err
	}

	method := hl.Method
	if method == "" {
		method = http.MethodGet
	}

	req, err := http.NewRequest(method, hl.URL, nil)
	if err != nil {
		return nil, err
	}
	req.Header = hl.Headers

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode >= 400 {
		return nil, fmt.Errorf("server responded with HTTP %d", resp.StatusCode)
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	result, warnings, err := adaptByContentType(resp.Header.Get("Content-Type"), body)
	if err != nil {
		return nil, err
	}
	for _, warn := range warnings {
		ctx.Logger(hl).Warn(warn.String())
	}

	return result, nil
}

func (hl HTTPLoader) makeClient(ctx caddy.Context) (*http.Client, error) {
	client := &http.Client{
		Timeout: time.Duration(hl.Timeout),
	}

	if hl.TLS != nil {
		var tlsConfig *tls.Config

		// client authentication
		if hl.TLS.UseServerIdentity {
			certs, err := ctx.IdentityCredentials(ctx.Logger(hl))
			if err != nil {
				return nil, fmt.Errorf("getting server identity credentials: %v", err)
			}
			if tlsConfig == nil {
				tlsConfig = new(tls.Config)
			}
			tlsConfig.Certificates = certs
		} else if hl.TLS.ClientCertificateFile != "" && hl.TLS.ClientCertificateKeyFile != "" {
			cert, err := tls.LoadX509KeyPair(hl.TLS.ClientCertificateFile, hl.TLS.ClientCertificateKeyFile)
			if err != nil {
				return nil, err
			}
			if tlsConfig == nil {
				tlsConfig = new(tls.Config)
			}
			tlsConfig.Certificates = []tls.Certificate{cert}
		}

		// trusted server certs
		if len(hl.TLS.RootCAPEMFiles) > 0 {
			rootPool := x509.NewCertPool()
			for _, pemFile := range hl.TLS.RootCAPEMFiles {
				pemData, err := os.ReadFile(pemFile)
				if err != nil {
					return nil, fmt.Errorf("failed reading ca cert: %v", err)
				}
				rootPool.AppendCertsFromPEM(pemData)
			}
			if tlsConfig == nil {
				tlsConfig = new(tls.Config)
			}
			tlsConfig.RootCAs = rootPool
		}

		client.Transport = &http.Transport{TLSClientConfig: tlsConfig}
	}

	return client, nil
}

var _ caddy.ConfigLoader = (*HTTPLoader)(nil)
<commit_msg>caddyconfig: Support placeholders in HTTP loader<commit_after>// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package caddyconfig

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io"
	"net/http"
	"os"
	"time"

	"github.com/caddyserver/caddy/v2"
)

func init() {
	caddy.RegisterModule(HTTPLoader{})
}

// HTTPLoader can load Caddy configs over HTTP(S). It can adapt the config
// based on the Content-Type header of the HTTP response.
type HTTPLoader struct {
	// The method for the request. Default: GET
	Method string `json:"method,omitempty"`

	// The URL of the request.
	URL string `json:"url,omitempty"`

	// HTTP headers to add to the request.
	Headers http.Header `json:"header,omitempty"`

	// Maximum time allowed for a complete connection and request.
	Timeout caddy.Duration `json:"timeout,omitempty"`

	TLS *struct {
		// Present this instance's managed remote identity credentials to the server.
		UseServerIdentity bool `json:"use_server_identity,omitempty"`

		// PEM-encoded client certificate filename to present to the server.
		ClientCertificateFile string `json:"client_certificate_file,omitempty"`

		// PEM-encoded key to use with the client certificate.
		ClientCertificateKeyFile string `json:"client_certificate_key_file,omitempty"`

		// List of PEM-encoded CA certificate files to add to the same trust
		// store as RootCAPool (or root_ca_pool in the JSON).
		RootCAPEMFiles []string `json:"root_ca_pem_files,omitempty"`
	} `json:"tls,omitempty"`
}

// CaddyModule returns the Caddy module information.
func (HTTPLoader) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		ID:  "caddy.config_loaders.http",
		New: func() caddy.Module { return new(HTTPLoader) },
	}
}

// LoadConfig loads a Caddy config.
func (hl HTTPLoader) LoadConfig(ctx caddy.Context) ([]byte, error) {
	repl := caddy.NewReplacer()

	client, err := hl.makeClient(ctx)
	if err != nil {
		return nil, err
	}

	method := repl.ReplaceAll(hl.Method, "")
	if method == "" {
		method = http.MethodGet
	}

	url := repl.ReplaceAll(hl.URL, "")
	req, err := http.NewRequest(method, url, nil)
	if err != nil {
		return nil, err
	}
	for key, vals := range hl.Headers {
		for _, val := range vals {
			req.Header.Add(repl.ReplaceAll(key, ""), repl.ReplaceKnown(val, ""))
		}
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode >= 400 {
		return nil, fmt.Errorf("server responded with HTTP %d", resp.StatusCode)
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	result, warnings, err := adaptByContentType(resp.Header.Get("Content-Type"), body)
	if err != nil {
		return nil, err
	}
	for _, warn := range warnings {
		ctx.Logger(hl).Warn(warn.String())
	}

	return result, nil
}

func (hl HTTPLoader) makeClient(ctx caddy.Context) (*http.Client, error) {
	client := &http.Client{
		Timeout: time.Duration(hl.Timeout),
	}

	if hl.TLS != nil {
		var tlsConfig *tls.Config

		// client authentication
		if hl.TLS.UseServerIdentity {
			certs, err := ctx.IdentityCredentials(ctx.Logger(hl))
			if err != nil {
				return nil, fmt.Errorf("getting server identity credentials: %v", err)
			}
			if tlsConfig == nil {
				tlsConfig = new(tls.Config)
			}
			tlsConfig.Certificates = certs
		} else if hl.TLS.ClientCertificateFile != "" && hl.TLS.ClientCertificateKeyFile != "" {
			cert, err := tls.LoadX509KeyPair(hl.TLS.ClientCertificateFile, hl.TLS.ClientCertificateKeyFile)
			if err != nil {
				return nil, err
			}
			if tlsConfig == nil {
				tlsConfig = new(tls.Config)
			}
			tlsConfig.Certificates = []tls.Certificate{cert}
		}

		// trusted server certs
		if len(hl.TLS.RootCAPEMFiles) > 0 {
			rootPool := x509.NewCertPool()
			for _, pemFile := range hl.TLS.RootCAPEMFiles {
				pemData, err := os.ReadFile(pemFile)
				if err != nil {
					return nil, fmt.Errorf("failed reading ca cert: %v", err)
				}
				rootPool.AppendCertsFromPEM(pemData)
			}
			if tlsConfig == nil {
				tlsConfig = new(tls.Config)
			}
			tlsConfig.RootCAs = rootPool
		}

		client.Transport = &http.Transport{TLSClientConfig: tlsConfig}
	}

	return client, nil
}

var _ caddy.ConfigLoader = (*HTTPLoader)(nil)
<|endoftext|>


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

package scriptable_disk

import (
	"encoding/base64"
	"fmt"
	"os/exec"
	"strings"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/volume"
	"github.com/cnaize/kubernetes/pkg/api"
	"github.com/cnaize/kubernetes/pkg/types"
	"github.com/golang/glog"
)

// This is the primary entrypoint for volume plugins.
func ProbeVolumePlugins() []volume.VolumePlugin {
	return []volume.VolumePlugin{&scriptableDiskPlugin{nil}}
}

type scriptableDiskPlugin struct {
	host volume.VolumeHost
}

var _ volume.VolumePlugin = &scriptableDiskPlugin{}

const (
	scriptableDiskPluginName = "kubernetes.io/scriptable-disk"
)

func (plugin *scriptableDiskPlugin) Init(host volume.VolumeHost) {
	plugin.host = host
}

func (plugin *scriptableDiskPlugin) Name() string {
	return scriptableDiskPluginName
}

func (plugin *scriptableDiskPlugin) CanSupport(spec *api.Volume) bool {
	if spec.VolumeSource.ScriptableDisk != nil {
		return true
	}

	return false
}

func (plugin *scriptableDiskPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference) (volume.Builder, error) {
	return &scriptableDisk{
		script:  spec.VolumeSource.ScriptableDisk.Script,
		params:  spec.VolumeSource.ScriptableDisk.Params,
		podRef:  podRef,
		volName: spec.Name,
		plugin:  plugin,
	}, nil
}

func (plugin *scriptableDiskPlugin) NewCleaner(volName string, podUID types.UID) (volume.Cleaner, error) {
	return &scriptableDisk{
		podRef:  &api.ObjectReference{UID: podUID},
		volName: volName,
		plugin:  plugin,
	}, nil
}

type scriptableDisk struct {
	script  string
	params  string
	volName string
	podRef  *api.ObjectReference
	plugin  *scriptableDiskPlugin
}

func (sd *scriptableDisk) SetUp() error {
	return sd.SetUpAt(sd.GetPath())
}

func (sd *scriptableDisk) SetUpAt(dir string) error {
	glog.Infoln("SCIPTABLE DISK SETUPING")

	scriptParams, err := base64.StdEncoding.DecodeString(sd.params)
	if err != nil {
		return err
	}

	params := []string{"-c", sd.script}
	params = append(params, strings.Split(string(scriptParams), ";")...)

	if out, err := exec.Command("sh", params...).Output(); err != nil {
		return fmt.Errorf("can't execute script: %v\n", err)
	} else {
		glog.Infof("script finished: %v\n", string(out))
	}

	return nil
}

func (sd *scriptableDisk) GetPath() string {
	name := scriptableDiskPluginName
	return sd.plugin.host.GetPodVolumeDir(sd.podRef.UID, util.EscapeQualifiedNameForDisk(name), sd.volName)
}

func (sd *scriptableDisk) TearDown() error {
	return sd.TearDownAt(sd.GetPath())
}

func (sd *scriptableDisk) TearDownAt(dir string) error {
	return nil
}
<commit_msg>deleted log message<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package scriptable_disk

import (
	"encoding/base64"
	"fmt"
	"os/exec"
	"strings"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/volume"
	"github.com/cnaize/kubernetes/pkg/api"
	"github.com/cnaize/kubernetes/pkg/types"
	"github.com/golang/glog"
)

// This is the primary entrypoint for volume plugins.
func ProbeVolumePlugins() []volume.VolumePlugin {
	return []volume.VolumePlugin{&scriptableDiskPlugin{nil}}
}

type scriptableDiskPlugin struct {
	host volume.VolumeHost
}

var _ volume.VolumePlugin = &scriptableDiskPlugin{}

const (
	scriptableDiskPluginName = "kubernetes.io/scriptable-disk"
)

func (plugin *scriptableDiskPlugin) Init(host volume.VolumeHost) {
	plugin.host = host
}

func (plugin *scriptableDiskPlugin) Name() string {
	return scriptableDiskPluginName
}

func (plugin *scriptableDiskPlugin) CanSupport(spec *api.Volume) bool {
	if spec.VolumeSource.ScriptableDisk != nil {
		return true
	}

	return false
}

func (plugin *scriptableDiskPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference) (volume.Builder, error) {
	return &scriptableDisk{
		script:  spec.VolumeSource.ScriptableDisk.Script,
		params:  spec.VolumeSource.ScriptableDisk.Params,
		podRef:  podRef,
		volName: spec.Name,
		plugin:  plugin,
	}, nil
}

func (plugin *scriptableDiskPlugin) NewCleaner(volName string, podUID types.UID) (volume.Cleaner, error) {
	return &scriptableDisk{
		podRef:  &api.ObjectReference{UID: podUID},
		volName: volName,
		plugin:  plugin,
	}, nil
}

type scriptableDisk struct {
	script  string
	params  string
	volName string
	podRef  *api.ObjectReference
	plugin  *scriptableDiskPlugin
}

func (sd *scriptableDisk) SetUp() error {
	return sd.SetUpAt(sd.GetPath())
}

func (sd *scriptableDisk) SetUpAt(dir string) error {
	scriptParams, err := base64.StdEncoding.DecodeString(sd.params)
	if err != nil {
		return err
	}

	params := []string{"-c", sd.script}
	params = append(params, strings.Split(string(scriptParams), ";")...)

	if out, err := exec.Command("sh", params...).Output(); err != nil {
		return fmt.Errorf("can't execute script: %v\n", err)
	} else {
		glog.Infof("script finished: %v\n", string(out))
	}

	return nil
}

func (sd *scriptableDisk) GetPath() string {
	name := scriptableDiskPluginName
	return sd.plugin.host.GetPodVolumeDir(sd.podRef.UID, util.EscapeQualifiedNameForDisk(name), sd.volName)
}

func (sd *scriptableDisk) TearDown() error {
	return sd.TearDownAt(sd.GetPath())
}

func (sd *scriptableDisk) TearDownAt(dir string) error {
	return nil
}
<|endoftext|>


<commit_before>/*
 * Datadog API for Go
 *
 * Please see the included LICENSE file for licensing information.
 *
 * Copyright 2013 by authors and contributors.
 */

package datadog

import "strconv"

// DataPoint is a tuple of [UNIX timestamp, value]. This has to use floats
// because the value could be non-integer.
type DataPoint [2]float64

// Metric represents a collection of data points that we might send or receive
// on one single metric line.
type Metric struct {
	Metric *string     `json:"metric,omitempty"`
	Points []DataPoint `json:"points,omitempty"`
	Type   *string     `json:"type,omitempty"`
	Host   *string     `json:"host,omitempty"`
	Tags   []string    `json:"tags,omitempty"`
	Unit   *string     `json:"unit,omitempty"`
}

// Series represents a collection of data points we get when we query for timeseries data
type Series struct {
	Metric      *string     `json:"metric,omitempty"`
	DisplayName *string     `json:"display_name,omitempty"`
	Points      []DataPoint `json:"pointlist,omitempty"`
	Start       *float64    `json:"start,omitempty"`
	End         *float64    `json:"end,omitempty"`
	Interval    *int        `json:"interval,omitempty"`
	Aggr        *string     `json:"aggr,omitempty"`
	Length      *int        `json:"length,omitempty"`
	Scope       *string     `json:"scope,omitempty"`
	Expression  *string     `json:"expression,omitempty"`
}

// reqPostSeries from /api/v1/series
type reqPostSeries struct {
	Series []Metric `json:"series,omitempty"`
}

// reqMetrics is the container for receiving metric results.
type reqMetrics struct {
	Series []Series `json:"series,omitempty"`
}

// PostMetrics takes as input a slice of metrics and then posts them up to the
// server for posting data.
func (client *Client) PostMetrics(series []Metric) error {
	return client.doJsonRequest("POST", "/v1/series",
		reqPostSeries{Series: series}, nil)
}

// QueryMetrics takes as input from, to (seconds from Unix Epoch) and query string and then requests
// timeseries data for that time peried
func (client *Client) QueryMetrics(from, to int64, query string) ([]Series, error) {
	var out reqMetrics
	if err := client.doJsonRequest("GET", "/v1/query?from="+strconv.FormatInt(from, 10)+"&to="+strconv.FormatInt(to, 10)+"&query="+query,
		nil, &out); err != nil {
		return nil, err
	}
	return out.Series, nil
}
<commit_msg>Url-encode query param for metrics query<commit_after>/*
 * Datadog API for Go
 *
 * Please see the included LICENSE file for licensing information.
 *
 * Copyright 2013 by authors and contributors.
 */

package datadog

import (
	"net/url"
	"strconv"
)

// DataPoint is a tuple of [UNIX timestamp, value]. This has to use floats
// because the value could be non-integer.
type DataPoint [2]float64

// Metric represents a collection of data points that we might send or receive
// on one single metric line.
type Metric struct {
	Metric *string     `json:"metric,omitempty"`
	Points []DataPoint `json:"points,omitempty"`
	Type   *string     `json:"type,omitempty"`
	Host   *string     `json:"host,omitempty"`
	Tags   []string    `json:"tags,omitempty"`
	Unit   *string     `json:"unit,omitempty"`
}

// Series represents a collection of data points we get when we query for timeseries data
type Series struct {
	Metric      *string     `json:"metric,omitempty"`
	DisplayName *string     `json:"display_name,omitempty"`
	Points      []DataPoint `json:"pointlist,omitempty"`
	Start       *float64    `json:"start,omitempty"`
	End         *float64    `json:"end,omitempty"`
	Interval    *int        `json:"interval,omitempty"`
	Aggr        *string     `json:"aggr,omitempty"`
	Length      *int        `json:"length,omitempty"`
	Scope       *string     `json:"scope,omitempty"`
	Expression  *string     `json:"expression,omitempty"`
}

// reqPostSeries from /api/v1/series
type reqPostSeries struct {
	Series []Metric `json:"series,omitempty"`
}

// reqMetrics is the container for receiving metric results.
type reqMetrics struct {
	Series []Series `json:"series,omitempty"`
}

// PostMetrics takes as input a slice of metrics and then posts them up to the
// server for posting data.
func (client *Client) PostMetrics(series []Metric) error {
	return client.doJsonRequest("POST", "/v1/series",
		reqPostSeries{Series: series}, nil)
}

// QueryMetrics takes as input from, to (seconds from Unix Epoch) and query string and then requests
// timeseries data for that time peried
func (client *Client) QueryMetrics(from, to int64, query string) ([]Series, error) {
	v := url.Values{}
	v.Add("from", strconv.FormatInt(from, 10))
	v.Add("to", strconv.FormatInt(to, 10))
	v.Add("query", query)

	var out reqMetrics
	err := client.doJsonRequest("GET", "/v1/query?"+v.Encode(), nil, &out)
	if err != nil {
		return nil, err
	}
	return out.Series, nil
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"math"
	"regexp"
	"time"
)

var (
	FORMAT_UNIT_REGEXP = regexp.MustCompile("%[sMHdmy]")
	FORMAT_UNIT_SCORE  = map[string]int{
		"s": 1,
		"M": 60,
		"H": 60 * 60,
		"d": 60 * 60 * 24,
		"m": 60 * 60 * 24 * 30,
		"y": 60 * 60 * 24 * 365,
	}
)

//
// Detect which series to use
//
func DetectSeries(format string, t0 time.Time) Series {
	if FORMAT_UNIT_REGEXP.MatchString(format) {
		return NewPeriodSeriesWithGuess(format, t0)
	}
	return NewConstSeries(format)
}

/*
 * Series is a infinite time.Time list that generated from strftime's pattern sucn as:
 * "hoge-%S" => ["hoge-13", "hoge-14", "hoge-15", ...]
 * "%y-%m-%d" => ["2016-2-1", "2016-2-2", ...]
 * Iteration starts at `t0`.
 */
type Series interface {
	Current() time.Time
	Next() Series
	Sub(t time.Time) time.Duration
}

type PeriodSeries struct {
	time   time.Time
	format string
	period time.Duration
}

func NewPeriodSeriesWithGuess(format string, t0 time.Time) Series {
	period, err := guessPeriod(format)
	if err != nil {
		panic("todo impl") //TODO
	}
	return PeriodSeries{t0, format, period}
}

func NewPeriodSeries(format string, t0 time.Time, period time.Duration) Series {
	return PeriodSeries{t0, format, period}
}

func (s PeriodSeries) Current() time.Time {
	return s.time
}

func (s PeriodSeries) Next() Series {
	return PeriodSeries{
		time:   s.time.Add(s.period),
		format: s.format,
		period: s.period,
	}
}

func (s PeriodSeries) Sub(t time.Time) time.Duration {
	return s.Current().Sub(t)
}

type ConstSeries struct {
	format string
}

func NewConstSeries(format string) Series {
	return ConstSeries{format}
}

func (s ConstSeries) Next() Series {
	return NewConstSeries(s.format)
}

func (s ConstSeries) Current() time.Time {
	return time.Now()
}

func (s ConstSeries) Sub(t time.Time) time.Duration {
	return time.Duration(math.MaxInt64)
}

/*
 * Guess period in second from the argument.
 * In general, the most smallest pattern letter is used.
 */
func guessPeriod(format string) (time.Duration, error) {
	re := FORMAT_UNIT_REGEXP
	units := re.FindAllString(format, -1)
	score := FORMAT_UNIT_SCORE
	// find minimum unit
	minScore := math.MaxInt32
	for _, u := range units {
		// u[1:] is truncation '%'
		value, ok := score[u[1:]]
		if ok && value < minScore {
			minScore = value
		}
	}
	if minScore == math.MaxInt32 {
		return time.Duration(0), errors.New("guessPeriod: fail to period detection from '" + format + "'")
	}
	// convert sec to nanosec
	return time.Duration(minScore * 1000 * 1000 * 1000), nil
}
<commit_msg>fix format specifier<commit_after>package main

import (
	"errors"
	"math"
	"regexp"
	"time"
)

var (
	FORMAT_UNIT_REGEXP = regexp.MustCompile("%[SMHdmy]")
	FORMAT_UNIT_SCORE  = map[string]int{
		"S": 1,
		"M": 60,
		"H": 60 * 60,
		"d": 60 * 60 * 24,
		"m": 60 * 60 * 24 * 30,
		"y": 60 * 60 * 24 * 365,
	}
)

//
// Detect which series to use
//
func DetectSeries(format string, t0 time.Time) Series {
	if FORMAT_UNIT_REGEXP.MatchString(format) {
		return NewPeriodSeriesWithGuess(format, t0)
	}
	return NewConstSeries(format)
}

/*
 * Series is a infinite time.Time list that generated from strftime's pattern sucn as:
 * "hoge-%S" => ["hoge-13", "hoge-14", "hoge-15", ...]
 * "%y-%m-%d" => ["2016-2-1", "2016-2-2", ...]
 * Iteration starts at `t0`.
 */
type Series interface {
	Current() time.Time
	Next() Series
	Sub(t time.Time) time.Duration
}

type PeriodSeries struct {
	time   time.Time
	format string
	period time.Duration
}

func NewPeriodSeriesWithGuess(format string, t0 time.Time) Series {
	period, err := guessPeriod(format)
	if err != nil {
		panic("todo impl") //TODO
	}
	return PeriodSeries{t0, format, period}
}

func NewPeriodSeries(format string, t0 time.Time, period time.Duration) Series {
	return PeriodSeries{t0, format, period}
}

func (s PeriodSeries) Current() time.Time {
	return s.time
}

func (s PeriodSeries) Next() Series {
	return PeriodSeries{
		time:   s.time.Add(s.period),
		format: s.format,
		period: s.period,
	}
}

func (s PeriodSeries) Sub(t time.Time) time.Duration {
	return s.Current().Sub(t)
}

type ConstSeries struct {
	format string
}

func NewConstSeries(format string) Series {
	return ConstSeries{format}
}

func (s ConstSeries) Next() Series {
	return NewConstSeries(s.format)
}

func (s ConstSeries) Current() time.Time {
	return time.Now()
}

func (s ConstSeries) Sub(t time.Time) time.Duration {
	return time.Duration(math.MaxInt64)
}

/*
 * Guess period in second from the argument.
 * In general, the most smallest pattern letter is used.
 */
func guessPeriod(format string) (time.Duration, error) {
	re := FORMAT_UNIT_REGEXP
	units := re.FindAllString(format, -1)
	score := FORMAT_UNIT_SCORE
	// find minimum unit
	minScore := math.MaxInt32
	for _, u := range units {
		// u[1:] is truncation '%'
		value, ok := score[u[1:]]
		if ok && value < minScore {
			minScore = value
		}
	}
	if minScore == math.MaxInt32 {
		return time.Duration(0), errors.New("guessPeriod: fail to period detection from '" + format + "'")
	}
	// convert sec to nanosec
	return time.Duration(minScore * 1000 * 1000 * 1000), nil
}
<|endoftext|>


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

package sse

import (
	"encoding/base64"
	"sync"
	"time"
)

// DefaultBufferSize size of the queue that holds the streams messages.
const DefaultBufferSize = 1024

// Server Is our main struct
type Server struct {
	// Specifies the size of the message buffer for each stream
	BufferSize int
	// Enables creation of a stream when a client connects
	AutoStream bool
	// Enables automatic replay for each new subscriber that connects
	AutoReplay bool
	// Encodes all data as base64
	EncodeBase64 bool
	// Sets a ttl that prevents old events from being transmitted
	EventTTL time.Duration
	// Splits an events data into multiple data: entries
	SplitData bool
	Streams   map[string]*Stream
	Headers   map[string]string
	mu        sync.Mutex
}

// New will create a server and setup defaults
func New() *Server {
	return &Server{
		BufferSize: DefaultBufferSize,
		AutoStream: false,
		AutoReplay: true,
		Streams:    make(map[string]*Stream),
		Headers: map[string]string{
			"Access-Control-Allow-Origin": "*",
		},
	}
}

// Close shuts down the server, closes all of the streams and connections
func (s *Server) Close() {
	s.mu.Lock()
	defer s.mu.Unlock()

	for id := range s.Streams {
		s.Streams[id].quit <- true
		delete(s.Streams, id)
	}
}

// CreateStream will create a new stream and register it
func (s *Server) CreateStream(id string) *Stream {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.Streams[id] != nil {
		return s.Streams[id]
	}

	str := newStream(s.BufferSize, s.AutoReplay)
	str.run()

	s.Streams[id] = str

	return str
}

// RemoveStream will remove a stream
func (s *Server) RemoveStream(id string) {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.Streams[id] != nil {
		s.Streams[id].close()
		delete(s.Streams, id)
	}
}

// StreamExists checks whether a stream by a given id exists
func (s *Server) StreamExists(id string) bool {
	s.mu.Lock()
	defer s.mu.Unlock()

	return s.Streams[id] != nil
}

// Publish sends a mesage to every client in a streamID
func (s *Server) Publish(id string, event *Event) {
	s.mu.Lock()
	defer s.mu.Unlock()
	if s.Streams[id] != nil {
		s.Streams[id].event <- s.process(event)
	}
}

func (s *Server) getStream(id string) *Stream {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.Streams[id]
}

func (s *Server) process(event *Event) *Event {
	if s.EncodeBase64 {
		output := make([]byte, base64.StdEncoding.EncodedLen(len(event.Data)))
		base64.StdEncoding.Encode(output, event.Data)
		event.Data = output
	}
	return event
}
<commit_msg>Remove "Access-Control-Allow-Origin" from NewServer Headers (#99)<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package sse

import (
	"encoding/base64"
	"sync"
	"time"
)

// DefaultBufferSize size of the queue that holds the streams messages.
const DefaultBufferSize = 1024

// Server Is our main struct
type Server struct {
	// Specifies the size of the message buffer for each stream
	BufferSize int
	// Enables creation of a stream when a client connects
	AutoStream bool
	// Enables automatic replay for each new subscriber that connects
	AutoReplay bool
	// Encodes all data as base64
	EncodeBase64 bool
	// Sets a ttl that prevents old events from being transmitted
	EventTTL time.Duration
	// Splits an events data into multiple data: entries
	SplitData bool
	Streams   map[string]*Stream
	Headers   map[string]string
	mu        sync.Mutex
}

// New will create a server and setup defaults
func New() *Server {
	return &Server{
		BufferSize: DefaultBufferSize,
		AutoStream: false,
		AutoReplay: true,
		Streams:    make(map[string]*Stream),
		Headers: map[string]string{},
	}
}

// Close shuts down the server, closes all of the streams and connections
func (s *Server) Close() {
	s.mu.Lock()
	defer s.mu.Unlock()

	for id := range s.Streams {
		s.Streams[id].quit <- true
		delete(s.Streams, id)
	}
}

// CreateStream will create a new stream and register it
func (s *Server) CreateStream(id string) *Stream {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.Streams[id] != nil {
		return s.Streams[id]
	}

	str := newStream(s.BufferSize, s.AutoReplay)
	str.run()

	s.Streams[id] = str

	return str
}

// RemoveStream will remove a stream
func (s *Server) RemoveStream(id string) {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.Streams[id] != nil {
		s.Streams[id].close()
		delete(s.Streams, id)
	}
}

// StreamExists checks whether a stream by a given id exists
func (s *Server) StreamExists(id string) bool {
	s.mu.Lock()
	defer s.mu.Unlock()

	return s.Streams[id] != nil
}

// Publish sends a mesage to every client in a streamID
func (s *Server) Publish(id string, event *Event) {
	s.mu.Lock()
	defer s.mu.Unlock()
	if s.Streams[id] != nil {
		s.Streams[id].event <- s.process(event)
	}
}

func (s *Server) getStream(id string) *Stream {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.Streams[id]
}

func (s *Server) process(event *Event) *Event {
	if s.EncodeBase64 {
		output := make([]byte, base64.StdEncoding.EncodedLen(len(event.Data)))
		base64.StdEncoding.Encode(output, event.Data)
		event.Data = output
	}
	return event
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"fmt"
	"net"
	"regexp"
	"strings"
	"sync"
	"syscall"
	"time"

	"golang.org/x/crypto/ssh"
)

const MAX_NAME_LENGTH = 32
const HISTORY_LEN = 20

var RE_STRIP_TEXT = regexp.MustCompile("[^0-9A-Za-z_]")

type Clients map[string]*Client

type Server struct {
	sshConfig *ssh.ServerConfig
	done      chan struct{}
	clients   Clients
	lock      sync.Mutex
	count     int
	history   *History
	admins    map[string]struct{}   // fingerprint lookup
	bannedPk  map[string]*time.Time // fingerprint lookup
	bannedIp  map[net.Addr]*time.Time
}

func NewServer(privateKey []byte) (*Server, error) {
	signer, err := ssh.ParsePrivateKey(privateKey)
	if err != nil {
		return nil, err
	}

	server := Server{
		done:     make(chan struct{}),
		clients:  Clients{},
		count:    0,
		history:  NewHistory(HISTORY_LEN),
		admins:   map[string]struct{}{},
		bannedPk: map[string]*time.Time{},
		bannedIp: map[net.Addr]*time.Time{},
	}

	config := ssh.ServerConfig{
		NoClientAuth: false,
		// Auth-related things should be constant-time to avoid timing attacks.
		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
			fingerprint := Fingerprint(key)
			if server.IsBanned(fingerprint) {
				return nil, fmt.Errorf("Banned.")
			}
			ip := strings.Split(conn.RemoteAddr().String(), ":")[0]
			if ip == "73.3.250.197" {
				// Can't believe I'm doing this...
				return nil, fmt.Errorf("Banned.")
			}
			perm := &ssh.Permissions{Extensions: map[string]string{"fingerprint": fingerprint}}
			return perm, nil
		},
	}
	config.AddHostKey(signer)

	server.sshConfig = &config

	return &server, nil
}

func (s *Server) Len() int {
	return len(s.clients)
}

func (s *Server) Broadcast(msg string, except *Client) {
	logger.Debugf("Broadcast to %d: %s", s.Len(), msg)
	s.history.Add(msg)

	for _, client := range s.clients {
		if except != nil && client == except {
			continue
		}
		/* Add an ascii BEL to ding clients when they're mentioned */
		if strings.Contains(msg, client.Name) {
			client.Msg <- msg + "\007"
		} else {
			client.Msg <- msg
		}

		client.Msg <- msg
	}
}

func (s *Server) Add(client *Client) {
	go func() {
		client.WriteLines(s.history.Get(10))
		client.Write(fmt.Sprintf("-> Welcome to ssh-chat. Enter /help for more."))
	}()

	s.lock.Lock()
	s.count++

	newName, err := s.proposeName(client.Name)
	if err != nil {
		client.Msg <- fmt.Sprintf("-> Your name '%s' is not available, renamed to '%s'. Use /nick <name> to change it.", client.ColoredName(), ColorString(client.Color, newName))
	}

	client.Rename(newName)
	s.clients[client.Name] = client
	num := len(s.clients)
	s.lock.Unlock()

	s.Broadcast(fmt.Sprintf("* %s joined. (Total connected: %d)", client.ColoredName(), num), client)
}

func (s *Server) Remove(client *Client) {
	s.lock.Lock()
	delete(s.clients, client.Name)
	s.lock.Unlock()

	s.Broadcast(fmt.Sprintf("* %s left.", client.ColoredName()), nil)
}

func (s *Server) proposeName(name string) (string, error) {
	// Assumes caller holds lock.
	var err error
	name = RE_STRIP_TEXT.ReplaceAllString(name, "")

	if len(name) > MAX_NAME_LENGTH {
		name = name[:MAX_NAME_LENGTH]
	} else if len(name) == 0 {
		name = fmt.Sprintf("Guest%d", s.count)
	}

	_, collision := s.clients[name]
	if collision {
		err = fmt.Errorf("%s is not available.", name)
		name = fmt.Sprintf("Guest%d", s.count)
	}

	return name, err
}

func (s *Server) Rename(client *Client, newName string) {
	s.lock.Lock()

	newName, err := s.proposeName(newName)
	if err != nil {
		client.Msg <- fmt.Sprintf("-> %s", err)
		s.lock.Unlock()
		return
	}

	// TODO: Use a channel/goroutine for adding clients, rathern than locks?
	delete(s.clients, client.Name)
	oldName := client.Name
	client.Rename(newName)
	s.clients[client.Name] = client
	s.lock.Unlock()

	s.Broadcast(fmt.Sprintf("* %s is now known as %s.", oldName, newName), nil)
}

func (s *Server) List(prefix *string) []string {
	r := []string{}

	for name, _ := range s.clients {
		if prefix != nil && !strings.HasPrefix(name, *prefix) {
			continue
		}
		r = append(r, name)
	}

	return r
}

func (s *Server) Who(name string) *Client {
	return s.clients[name]
}

func (s *Server) Op(fingerprint string) {
	logger.Infof("Adding admin: %s", fingerprint)
	s.lock.Lock()
	s.admins[fingerprint] = struct{}{}
	s.lock.Unlock()
}

func (s *Server) IsOp(client *Client) bool {
	_, r := s.admins[client.Fingerprint()]
	return r
}

func (s *Server) IsBanned(fingerprint string) bool {
	ban, hasBan := s.bannedPk[fingerprint]
	if !hasBan {
		return false
	}
	if ban == nil {
		return true
	}
	if ban.Before(time.Now()) {
		s.Unban(fingerprint)
		return false
	}
	return true
}

func (s *Server) Ban(fingerprint string, duration *time.Duration) {
	var until *time.Time
	s.lock.Lock()
	if duration != nil {
		when := time.Now().Add(*duration)
		until = &when
	}
	s.bannedPk[fingerprint] = until
	s.lock.Unlock()
}

func (s *Server) Unban(fingerprint string) {
	s.lock.Lock()
	delete(s.bannedPk, fingerprint)
	s.lock.Unlock()
}

func (s *Server) Start(laddr string) error {
	// Once a ServerConfig has been configured, connections can be
	// accepted.
	socket, err := net.Listen("tcp", laddr)
	if err != nil {
		return err
	}

	logger.Infof("Listening on %s", laddr)

	go func() {
		defer socket.Close()
		for {
			conn, err := socket.Accept()

			if err != nil {
				logger.Errorf("Failed to accept connection: %v", err)
				if err == syscall.EINVAL {
					// TODO: Handle shutdown more gracefully?
					return
				}
			}

			// Goroutineify to resume accepting sockets early.
			go func() {
				// From a standard TCP connection to an encrypted SSH connection
				sshConn, channels, requests, err := ssh.NewServerConn(conn, s.sshConfig)
				if err != nil {
					logger.Errorf("Failed to handshake: %v", err)
					return
				}

				version := RE_STRIP_TEXT.ReplaceAllString(string(sshConn.ClientVersion()), "")
				if len(version) > 100 {
					version = "Evil Jerk with a superlong string"
				}
				logger.Infof("Connection #%d from: %s, %s, %s", s.count+1, sshConn.RemoteAddr(), sshConn.User(), version)

				go ssh.DiscardRequests(requests)

				client := NewClient(s, sshConn)
				go client.handleChannels(channels)
			}()
		}
	}()

	go func() {
		<-s.done
		socket.Close()
	}()

	return nil
}

func (s *Server) Stop() {
	for _, client := range s.clients {
		client.Conn.Close()
	}

	close(s.done)
}

func Fingerprint(k ssh.PublicKey) string {
	hash := md5.Sum(k.Marshal())
	r := fmt.Sprintf("% x", hash)
	return strings.Replace(r, " ", ":", -1)
}
<commit_msg>Better SSH versions<commit_after>package main

import (
	"crypto/md5"
	"fmt"
	"net"
	"regexp"
	"strings"
	"sync"
	"syscall"
	"time"

	"golang.org/x/crypto/ssh"
)

const MAX_NAME_LENGTH = 32
const HISTORY_LEN = 20

var RE_STRIP_TEXT = regexp.MustCompile("[^0-9A-Za-z_.-]")

type Clients map[string]*Client

type Server struct {
	sshConfig *ssh.ServerConfig
	done      chan struct{}
	clients   Clients
	lock      sync.Mutex
	count     int
	history   *History
	admins    map[string]struct{}   // fingerprint lookup
	bannedPk  map[string]*time.Time // fingerprint lookup
	bannedIp  map[net.Addr]*time.Time
}

func NewServer(privateKey []byte) (*Server, error) {
	signer, err := ssh.ParsePrivateKey(privateKey)
	if err != nil {
		return nil, err
	}

	server := Server{
		done:     make(chan struct{}),
		clients:  Clients{},
		count:    0,
		history:  NewHistory(HISTORY_LEN),
		admins:   map[string]struct{}{},
		bannedPk: map[string]*time.Time{},
		bannedIp: map[net.Addr]*time.Time{},
	}

	config := ssh.ServerConfig{
		NoClientAuth: false,
		// Auth-related things should be constant-time to avoid timing attacks.
		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
			fingerprint := Fingerprint(key)
			if server.IsBanned(fingerprint) {
				return nil, fmt.Errorf("Banned.")
			}
			ip := strings.Split(conn.RemoteAddr().String(), ":")[0]
			if ip == "73.3.250.197" {
				// Can't believe I'm doing this...
				return nil, fmt.Errorf("Banned.")
			}
			perm := &ssh.Permissions{Extensions: map[string]string{"fingerprint": fingerprint}}
			return perm, nil
		},
	}
	config.AddHostKey(signer)

	server.sshConfig = &config

	return &server, nil
}

func (s *Server) Len() int {
	return len(s.clients)
}

func (s *Server) Broadcast(msg string, except *Client) {
	logger.Debugf("Broadcast to %d: %s", s.Len(), msg)
	s.history.Add(msg)

	for _, client := range s.clients {
		if except != nil && client == except {
			continue
		}
		/* Add an ascii BEL to ding clients when they're mentioned */
		if strings.Contains(msg, client.Name) {
			client.Msg <- msg + "\007"
		} else {
			client.Msg <- msg
		}
	}
}

func (s *Server) Add(client *Client) {
	go func() {
		client.WriteLines(s.history.Get(10))
		client.Write(fmt.Sprintf("-> Welcome to ssh-chat. Enter /help for more."))
	}()

	s.lock.Lock()
	s.count++

	newName, err := s.proposeName(client.Name)
	if err != nil {
		client.Msg <- fmt.Sprintf("-> Your name '%s' is not available, renamed to '%s'. Use /nick <name> to change it.", client.ColoredName(), ColorString(client.Color, newName))
	}

	client.Rename(newName)
	s.clients[client.Name] = client
	num := len(s.clients)
	s.lock.Unlock()

	s.Broadcast(fmt.Sprintf("* %s joined. (Total connected: %d)", client.ColoredName(), num), client)
}

func (s *Server) Remove(client *Client) {
	s.lock.Lock()
	delete(s.clients, client.Name)
	s.lock.Unlock()

	s.Broadcast(fmt.Sprintf("* %s left.", client.ColoredName()), nil)
}

func (s *Server) proposeName(name string) (string, error) {
	// Assumes caller holds lock.
	var err error
	name = RE_STRIP_TEXT.ReplaceAllString(name, "")

	if len(name) > MAX_NAME_LENGTH {
		name = name[:MAX_NAME_LENGTH]
	} else if len(name) == 0 {
		name = fmt.Sprintf("Guest%d", s.count)
	}

	_, collision := s.clients[name]
	if collision {
		err = fmt.Errorf("%s is not available.", name)
		name = fmt.Sprintf("Guest%d", s.count)
	}

	return name, err
}

func (s *Server) Rename(client *Client, newName string) {
	s.lock.Lock()

	newName, err := s.proposeName(newName)
	if err != nil {
		client.Msg <- fmt.Sprintf("-> %s", err)
		s.lock.Unlock()
		return
	}

	// TODO: Use a channel/goroutine for adding clients, rathern than locks?
	delete(s.clients, client.Name)
	oldName := client.Name
	client.Rename(newName)
	s.clients[client.Name] = client
	s.lock.Unlock()

	s.Broadcast(fmt.Sprintf("* %s is now known as %s.", oldName, newName), nil)
}

func (s *Server) List(prefix *string) []string {
	r := []string{}

	for name, _ := range s.clients {
		if prefix != nil && !strings.HasPrefix(name, *prefix) {
			continue
		}
		r = append(r, name)
	}

	return r
}

func (s *Server) Who(name string) *Client {
	return s.clients[name]
}

func (s *Server) Op(fingerprint string) {
	logger.Infof("Adding admin: %s", fingerprint)
	s.lock.Lock()
	s.admins[fingerprint] = struct{}{}
	s.lock.Unlock()
}

func (s *Server) IsOp(client *Client) bool {
	_, r := s.admins[client.Fingerprint()]
	return r
}

func (s *Server) IsBanned(fingerprint string) bool {
	ban, hasBan := s.bannedPk[fingerprint]
	if !hasBan {
		return false
	}
	if ban == nil {
		return true
	}
	if ban.Before(time.Now()) {
		s.Unban(fingerprint)
		return false
	}
	return true
}

func (s *Server) Ban(fingerprint string, duration *time.Duration) {
	var until *time.Time
	s.lock.Lock()
	if duration != nil {
		when := time.Now().Add(*duration)
		until = &when
	}
	s.bannedPk[fingerprint] = until
	s.lock.Unlock()
}

func (s *Server) Unban(fingerprint string) {
	s.lock.Lock()
	delete(s.bannedPk, fingerprint)
	s.lock.Unlock()
}

func (s *Server) Start(laddr string) error {
	// Once a ServerConfig has been configured, connections can be
	// accepted.
	socket, err := net.Listen("tcp", laddr)
	if err != nil {
		return err
	}

	logger.Infof("Listening on %s", laddr)

	go func() {
		defer socket.Close()
		for {
			conn, err := socket.Accept()

			if err != nil {
				logger.Errorf("Failed to accept connection: %v", err)
				if err == syscall.EINVAL {
					// TODO: Handle shutdown more gracefully?
					return
				}
			}

			// Goroutineify to resume accepting sockets early.
			go func() {
				// From a standard TCP connection to an encrypted SSH connection
				sshConn, channels, requests, err := ssh.NewServerConn(conn, s.sshConfig)
				if err != nil {
					logger.Errorf("Failed to handshake: %v", err)
					return
				}

				version := RE_STRIP_TEXT.ReplaceAllString(string(sshConn.ClientVersion()), "")
				if len(version) > 100 {
					version = "Evil Jerk with a superlong string"
				}
				logger.Infof("Connection #%d from: %s, %s, %s", s.count+1, sshConn.RemoteAddr(), sshConn.User(), version)

				go ssh.DiscardRequests(requests)

				client := NewClient(s, sshConn)
				go client.handleChannels(channels)
			}()
		}
	}()

	go func() {
		<-s.done
		socket.Close()
	}()

	return nil
}

func (s *Server) Stop() {
	for _, client := range s.clients {
		client.Conn.Close()
	}

	close(s.done)
}

func Fingerprint(k ssh.PublicKey) string {
	hash := md5.Sum(k.Marshal())
	r := fmt.Sprintf("% x", hash)
	return strings.Replace(r, " ", ":", -1)
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package fuse

import (
	"fmt"
	"io"
	"log"

	"github.com/jacobsa/gcsfuse/timeutil"
	"golang.org/x/net/context"

	bazilfuse "bazil.org/fuse"
)

// An object that terminates one end of the userspace <-> FUSE VFS connection.
type server struct {
	logger *log.Logger
	clock  timeutil.Clock
	fs     FileSystem
}

// Create a server that relays requests to the supplied file system.
func newServer(fs FileSystem) (s *server, err error) {
	s = &server{
		logger: getLogger(),
		clock:  timeutil.RealClock(),
		fs:     fs,
	}

	return
}

// Serve the fuse connection by repeatedly reading requests from the supplied
// FUSE connection, responding as dictated by the file system. Return when the
// connection is closed or an unexpected error occurs.
func (s *server) Serve(c *bazilfuse.Conn) (err error) {
	// Read a message at a time, dispatching to goroutines doing the actual
	// processing.
	for {
		var fuseReq bazilfuse.Request
		fuseReq, err = c.ReadRequest()

		// ReadRequest returns EOF when the connection has been closed.
		//
		// TODO(jacobsa): Remove this and verify it's actually needed.
		if err == io.EOF {
			err = nil
			return
		}

		// Otherwise, forward on errors.
		if err != nil {
			err = fmt.Errorf("Conn.ReadRequest: %v", err)
			return
		}

		go s.handleFuseRequest(fuseReq)
	}
}

func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
	// Log the request.
	s.logger.Println("Received:", fuseReq)

	// TODO(jacobsa): Support cancellation when interrupted, if we can coax the
	// system into reproducing such requests.
	ctx := context.Background()

	// Attempt to handle it.
	switch typed := fuseReq.(type) {
	case *bazilfuse.InitRequest:
		// Responding to this is required to make mounting work, at least on OS X.
		// We don't currently expose the capability for the file system to
		// intercept this.
		fuseResp := &bazilfuse.InitResponse{}
		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.StatfsRequest:
		// Responding to this is required to make mounting work, at least on OS X.
		// We don't currently expose the capability for the file system to
		// intercept this.
		fuseResp := &bazilfuse.StatfsResponse{}
		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.GetattrRequest:
		// Convert the request.
		req := &GetInodeAttributesRequest{
			Inode: InodeID(typed.Header.Node),
		}

		// Call the file system.
		resp, err := s.fs.GetInodeAttributes(ctx, req)
		if err != nil {
			s.logger.Print("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.GetattrResponse{
			Attr:      convertAttributes(req.Inode, resp.Attributes),
			AttrValid: resp.AttributesExpiration.Sub(s.clock.Now()),
		}

		s.logger.Print("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.OpenRequest:
		// We support only directories at this point.
		if !typed.Dir {
			s.logger.Println("We don't yet support files. Returning ENOSYS.")
			typed.RespondError(ENOSYS)
			return
		}

		// Convert the request.
		req := &OpenDirRequest{
			Inode: InodeID(typed.Header.Node),
			Flags: typed.Flags,
		}

		// Call the file system.
		resp, err := s.fs.OpenDir(ctx, req)
		if err != nil {
			s.logger.Print("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.OpenResponse{
			Handle: bazilfuse.HandleID(resp.Handle),
		}

		s.logger.Print("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.ReadRequest:
		// We support only directories at this point.
		if !typed.Dir {
			s.logger.Println("We don't yet support files. Returning ENOSYS.")
			typed.RespondError(ENOSYS)
			return
		}

		// Convert the request.
		req := &ReadDirRequest{
			Inode:  InodeID(typed.Header.Node),
			Handle: HandleID(typed.Handle),
			Offset: DirOffset(typed.Offset),
			Size:   typed.Size,
		}

		// Call the file system.
		resp, err := s.fs.ReadDir(ctx, req)
		if err != nil {
			s.logger.Print("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.ReadResponse{
			Data: resp.Data,
		}

		s.logger.Print("Responding:", fuseResp)
		typed.Respond(fuseResp)

	default:
		s.logger.Println("Unhandled type. Returning ENOSYS.")
		typed.RespondError(ENOSYS)
	}
}

func convertAttributes(inode InodeID, attr InodeAttributes) bazilfuse.Attr {
	return bazilfuse.Attr{
		Inode: uint64(inode),
		Size:  attr.Size,
		Mode:  attr.Mode,
	}
}
<commit_msg>Added server support for lookup.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package fuse

import (
	"fmt"
	"io"
	"log"

	"github.com/jacobsa/gcsfuse/timeutil"
	"golang.org/x/net/context"

	bazilfuse "bazil.org/fuse"
)

// An object that terminates one end of the userspace <-> FUSE VFS connection.
type server struct {
	logger *log.Logger
	clock  timeutil.Clock
	fs     FileSystem
}

// Create a server that relays requests to the supplied file system.
func newServer(fs FileSystem) (s *server, err error) {
	s = &server{
		logger: getLogger(),
		clock:  timeutil.RealClock(),
		fs:     fs,
	}

	return
}

// Serve the fuse connection by repeatedly reading requests from the supplied
// FUSE connection, responding as dictated by the file system. Return when the
// connection is closed or an unexpected error occurs.
func (s *server) Serve(c *bazilfuse.Conn) (err error) {
	// Read a message at a time, dispatching to goroutines doing the actual
	// processing.
	for {
		var fuseReq bazilfuse.Request
		fuseReq, err = c.ReadRequest()

		// ReadRequest returns EOF when the connection has been closed.
		//
		// TODO(jacobsa): Remove this and verify it's actually needed.
		if err == io.EOF {
			err = nil
			return
		}

		// Otherwise, forward on errors.
		if err != nil {
			err = fmt.Errorf("Conn.ReadRequest: %v", err)
			return
		}

		go s.handleFuseRequest(fuseReq)
	}
}

func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
	// Log the request.
	s.logger.Println("Received:", fuseReq)

	// TODO(jacobsa): Support cancellation when interrupted, if we can coax the
	// system into reproducing such requests.
	ctx := context.Background()

	// Attempt to handle it.
	switch typed := fuseReq.(type) {
	case *bazilfuse.InitRequest:
		// Responding to this is required to make mounting work, at least on OS X.
		// We don't currently expose the capability for the file system to
		// intercept this.
		fuseResp := &bazilfuse.InitResponse{}
		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.StatfsRequest:
		// Responding to this is required to make mounting work, at least on OS X.
		// We don't currently expose the capability for the file system to
		// intercept this.
		fuseResp := &bazilfuse.StatfsResponse{}
		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.LookupRequest:
		// Convert the request.
		req := &LookUpInodeRequest{
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
		}

		// Call the file system.
		resp, err := s.fs.LookUpInode(ctx, req)
		if err != nil {
			s.logger.Print("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.LookupResponse{
			Node:       bazilfuse.NodeID(resp.Child),
			Generation: uint64(resp.Generation),
			Attr:       convertAttributes(resp.Child, resp.Attributes),
			AttrValid:  resp.AttributesExpiration.Sub(s.clock.Now()),
			EntryValid: resp.EntryExpiration.Sub(s.clock.Now()),
		}

		s.logger.Print("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.GetattrRequest:
		// Convert the request.
		req := &GetInodeAttributesRequest{
			Inode: InodeID(typed.Header.Node),
		}

		// Call the file system.
		resp, err := s.fs.GetInodeAttributes(ctx, req)
		if err != nil {
			s.logger.Print("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.GetattrResponse{
			Attr:      convertAttributes(req.Inode, resp.Attributes),
			AttrValid: resp.AttributesExpiration.Sub(s.clock.Now()),
		}

		s.logger.Print("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.OpenRequest:
		// We support only directories at this point.
		if !typed.Dir {
			s.logger.Println("We don't yet support files. Returning ENOSYS.")
			typed.RespondError(ENOSYS)
			return
		}

		// Convert the request.
		req := &OpenDirRequest{
			Inode: InodeID(typed.Header.Node),
			Flags: typed.Flags,
		}

		// Call the file system.
		resp, err := s.fs.OpenDir(ctx, req)
		if err != nil {
			s.logger.Print("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.OpenResponse{
			Handle: bazilfuse.HandleID(resp.Handle),
		}

		s.logger.Print("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.ReadRequest:
		// We support only directories at this point.
		if !typed.Dir {
			s.logger.Println("We don't yet support files. Returning ENOSYS.")
			typed.RespondError(ENOSYS)
			return
		}

		// Convert the request.
		req := &ReadDirRequest{
			Inode:  InodeID(typed.Header.Node),
			Handle: HandleID(typed.Handle),
			Offset: DirOffset(typed.Offset),
			Size:   typed.Size,
		}

		// Call the file system.
		resp, err := s.fs.ReadDir(ctx, req)
		if err != nil {
			s.logger.Print("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.ReadResponse{
			Data: resp.Data,
		}

		s.logger.Print("Responding:", fuseResp)
		typed.Respond(fuseResp)

	default:
		s.logger.Println("Unhandled type. Returning ENOSYS.")
		typed.RespondError(ENOSYS)
	}
}

func convertAttributes(inode InodeID, attr InodeAttributes) bazilfuse.Attr {
	return bazilfuse.Attr{
		Inode: uint64(inode),
		Size:  attr.Size,
		Mode:  attr.Mode,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"net"
	"io/ioutil"
	"time"
	"regexp"
	"strings"
	"strconv"
	"bufio"
)

type Server struct {
	conf Config
	mon *Monitoring
	lg log.Logger
	ch chan string
	chS chan string
	chA chan string
}

func (s Server) combineMetricsWithSameName(metric string, metrics []Metric) []Metric {
	split := regexp.MustCompile("\\s").Split(metric, 3)

	value, err := strconv.ParseFloat(split[1], 64)
	if err != nil {s.lg.Println("Can not parse value of metric " + split[0] + ": " + split[1]) ; return metrics}
	timestamp, err := strconv.ParseInt(split[2], 10, 64)
	if err != nil {s.lg.Println("Can not parse timestamp of metric " + split[0] + ": " + split[2]) ; return metrics}

	/*
	Go through existing metrics and search for the same name of metric
	If there is no same metric - append it as a new
	 */
	for i,_ := range metrics {
		if metrics[i].name == split[0] {
			metrics[i].amount++
			metrics[i].value += value
			metrics[i].timestamp += timestamp
			return metrics
		}
	}
	metrics = append(metrics, Metric{split[0], 1, value, timestamp})
	return metrics
}
// Sum metrics with prefix
func (s Server) sumMetricsWithPrefix() {
	for ;; time.Sleep(time.Duration(s.conf.SumInterval)*time.Second) {
		var working_list[] Metric
		chanSize := len(s.chS)
		for i := 0; i < chanSize; i++ {
			working_list = s.combineMetricsWithSameName(strings.Replace(<-s.chS, s.conf.SumPrefix, "", -1), working_list)
		}
		for _,val := range working_list {
			s.ch <- val.name + " " +
				strconv.FormatFloat(val.value, 'f', 2, 32) + " " + strconv.FormatInt(val.timestamp/val.amount, 10)
		}
	}
}

// AVG metrics with prefix
func (s Server) avgMetricsWithPrefix() {
	for ;; time.Sleep(time.Duration(s.conf.AvgInterval)*time.Second) {
		var working_list[] Metric
		chanSize := len(s.chA)
		for i := 0; i < chanSize; i++ {
			working_list = s.combineMetricsWithSameName(strings.Replace(<-s.chA, s.conf.AvgPrefix, "", -1), working_list)
		}
		for _,val := range working_list {
			s.ch <- val.name + " " +
				strconv.FormatFloat(val.value/float64(val.amount), 'f', 2, 32) + " " + strconv.FormatInt(val.timestamp/val.amount, 10)
		}
	}
}

// Function checks and removed bad data and sorts it by SUM prefix
func (s Server)cleanAndUseIncomingData(metrics []string) {
	for _,metric := range metrics {
		if validateMetric(metric, s.conf.AllowedMetrics) {
			if strings.HasPrefix(metric, s.conf.SumPrefix) {
				if len(s.chS) < s.conf.SumsPerSecond*s.conf.SumInterval{
					s.chS <- metric
				} else {
					s.mon.dropped++
				}
			} else if strings.HasPrefix(metric, s.conf.AvgPrefix) {
				if len(s.chA) < s.conf.AvgsPerSecond*s.conf.AvgInterval{
					s.chA <- metric
				} else {
					s.mon.dropped++
				}
			} else {
				if len(s.ch) < s.conf.MaxMetrics*s.conf.ClientSendInterval {
					s.ch <- metric
				} else {
					s.mon.dropped++
				}
			}
		} else {
			if metric != "" {
				s.mon.dropped++
				s.lg.Println("Removing bad metric \"" + metric + "\" from the list")
			}
		}
	}
	metrics = nil
}

// Handles incoming requests.
func (s Server)handleRequest(conn net.Conn) {
	connbuf := bufio.NewReader(conn)
	var results_list []string
	amount := 0
	for ;; amount++ {
		metric, err := connbuf.ReadString('\n')
		if err!= nil {
			break
		}
		if amount < s.conf.MaxMetrics {
			results_list = append(results_list, strings.Replace(strings.Replace(metric, "\r", "", -1), "\n", "", -1))
		} else {
			s.mon.dropped++
		}
	}
	conn.Close()
	s.mon.got.net += amount
	s.cleanAndUseIncomingData(results_list)

	results_list = nil
}

// Reading metrics from files in folder. This is a second way how to send metrics, except direct connection
func (s Server)handleDirMetrics() {
	for ;; time.Sleep(time.Duration(s.conf.ClientSendInterval)*time.Second) {
		files, err := ioutil.ReadDir(s.conf.MetricDir)
		if err != nil {
			panic(err.Error())
		}
		for _, f := range files {
			results_list := readMetricsFromFile(s.conf.MetricDir+"/"+f.Name())
			s.mon.got.dir += len(results_list)
			s.cleanAndUseIncomingData(results_list)
		}

	}
}

func (s Server)runServer() {
	// Listen for incoming connections.
	l, err := net.Listen("tcp", s.conf.LocalBind)
	if err != nil {
		s.lg.Println("Failed to run server:", err.Error())
		os.Exit(1)
	} else {
		s.lg.Println("Server is running")
	}
	// Close the listener when the application closes.
	defer l.Close()

	// Run goroutine for reading metrics from metricDir
	go s.handleDirMetrics()
	// Run goroutine for sum metrics with prefix
	go s.sumMetricsWithPrefix()
	// Run goroutine for avg metrics with prefix
	go s.avgMetricsWithPrefix()

	for {
		// Listen for an incoming connection.
		conn, err := l.Accept()
		if err != nil {
			s.lg.Println("Error accepting: ", err.Error())
			os.Exit(1)
		}
		// Handle connections in a new goroutine.
		go s.handleRequest(conn)
	}
}<commit_msg>Precess metrics even if connection from client was not closed<commit_after>package main

import (
	"log"
	"os"
	"net"
	"io/ioutil"
	"time"
	"regexp"
	"strings"
	"strconv"
	"bufio"
)

type Server struct {
	conf Config
	mon *Monitoring
	lg log.Logger
	ch chan string
	chS chan string
	chA chan string
}

func (s Server) combineMetricsWithSameName(metric string, metrics []Metric) []Metric {
	split := regexp.MustCompile("\\s").Split(metric, 3)

	value, err := strconv.ParseFloat(split[1], 64)
	if err != nil {s.lg.Println("Can not parse value of metric " + split[0] + ": " + split[1]) ; return metrics}
	timestamp, err := strconv.ParseInt(split[2], 10, 64)
	if err != nil {s.lg.Println("Can not parse timestamp of metric " + split[0] + ": " + split[2]) ; return metrics}

	/*
	Go through existing metrics and search for the same name of metric
	If there is no same metric - append it as a new
	 */
	for i,_ := range metrics {
		if metrics[i].name == split[0] {
			metrics[i].amount++
			metrics[i].value += value
			metrics[i].timestamp += timestamp
			return metrics
		}
	}
	metrics = append(metrics, Metric{split[0], 1, value, timestamp})
	return metrics
}
// Sum metrics with prefix
func (s Server) sumMetricsWithPrefix() {
	for ;; time.Sleep(time.Duration(s.conf.SumInterval)*time.Second) {
		var working_list[] Metric
		chanSize := len(s.chS)
		for i := 0; i < chanSize; i++ {
			working_list = s.combineMetricsWithSameName(strings.Replace(<-s.chS, s.conf.SumPrefix, "", -1), working_list)
		}
		for _,val := range working_list {
			s.ch <- val.name + " " +
				strconv.FormatFloat(val.value, 'f', 2, 32) + " " + strconv.FormatInt(val.timestamp/val.amount, 10)
		}
	}
}

// AVG metrics with prefix
func (s Server) avgMetricsWithPrefix() {
	for ;; time.Sleep(time.Duration(s.conf.AvgInterval)*time.Second) {
		var working_list[] Metric
		chanSize := len(s.chA)
		for i := 0; i < chanSize; i++ {
			working_list = s.combineMetricsWithSameName(strings.Replace(<-s.chA, s.conf.AvgPrefix, "", -1), working_list)
		}
		for _,val := range working_list {
			s.ch <- val.name + " " +
				strconv.FormatFloat(val.value/float64(val.amount), 'f', 2, 32) + " " + strconv.FormatInt(val.timestamp/val.amount, 10)
		}
	}
}

// Function checks and removed bad data and sorts it by SUM prefix
func (s Server)cleanAndUseIncomingData(metrics []string) {
	for _,metric := range metrics {
		if validateMetric(metric, s.conf.AllowedMetrics) {
			if strings.HasPrefix(metric, s.conf.SumPrefix) {
				if len(s.chS) < s.conf.SumsPerSecond*s.conf.SumInterval{
					s.chS <- metric
				} else {
					s.mon.dropped++
				}
			} else if strings.HasPrefix(metric, s.conf.AvgPrefix) {
				if len(s.chA) < s.conf.AvgsPerSecond*s.conf.AvgInterval{
					s.chA <- metric
				} else {
					s.mon.dropped++
				}
			} else {
				if len(s.ch) < s.conf.MaxMetrics*s.conf.ClientSendInterval {
					s.ch <- metric
				} else {
					s.mon.dropped++
				}
			}
		} else {
			if metric != "" {
				s.mon.dropped++
				s.lg.Println("Removing bad metric \"" + metric + "\" from the list")
			}
		}
	}
	metrics = nil
}

// Reading metrics from network
func (s Server)handleRequest(conn net.Conn) {

	connbuf := bufio.NewReader(conn)
	for ;; {
		s.mon.got.net++
		metric, err := connbuf.ReadString('\n')
		if err!= nil {
			break
		}
		if s.mon.got.net < s.conf.MaxMetrics {
			s.cleanAndUseIncomingData([]string{strings.Replace(strings.Replace(metric, "\r", "", -1), "\n", "", -1)})
		} else {
			s.mon.dropped++
		}
	}
	conn.Close()
}

// Reading metrics from files in folder. This is a second way how to send metrics, except network
func (s Server)handleDirMetrics() {
	for ;; time.Sleep(time.Duration(s.conf.ClientSendInterval)*time.Second) {
		files, err := ioutil.ReadDir(s.conf.MetricDir)
		if err != nil {
			panic(err.Error())
		}
		for _, f := range files {
			results_list := readMetricsFromFile(s.conf.MetricDir+"/"+f.Name())
			s.mon.got.dir += len(results_list)
			s.cleanAndUseIncomingData(results_list)
		}

	}
}

func (s Server)runServer() {
	// Listen for incoming connections.
	l, err := net.Listen("tcp", s.conf.LocalBind)
	if err != nil {
		s.lg.Println("Failed to run server:", err.Error())
		os.Exit(1)
	} else {
		s.lg.Println("Server is running")
	}
	// Close the listener when the application closes.
	defer l.Close()

	// Run goroutine for reading metrics from metricDir
	go s.handleDirMetrics()
	// Run goroutine for sum metrics with prefix
	go s.sumMetricsWithPrefix()
	// Run goroutine for avg metrics with prefix
	go s.avgMetricsWithPrefix()

	for {
		// Listen for an incoming connection.
		conn, err := l.Accept()
		if err != nil {
			s.lg.Println("Error accepting: ", err.Error())
			os.Exit(1)
		}
		// Handle connections in a new goroutine.
		go s.handleRequest(conn)
	}
}<|endoftext|>