<commit_before>package main

import (
	"bytes"
	"fmt"
	"os"
	"strings"
	"text/template"
)

// Property represents an AWS CloudFormation resource property
type Property struct {

	// Documentation - A link to the AWS CloudFormation User Guide that provides information about the property.
	Documentation string `json:"Documentation"`

	// DuplicatesAllowed - If the value of the Type field is List, indicates whether AWS CloudFormation allows duplicate values.
	// If the value is true, AWS CloudFormation ignores duplicate values. If the value is false,
	// AWS CloudFormation returns an error if you submit duplicate values.
	DuplicatesAllowed bool `json:"DuplicatesAllowed"`

	// ItemType - If the value of the Type field is List or Map, indicates the type of list or map if they contain
	// non-primitive types. Otherwise, this field is omitted. For lists or maps that contain primitive
	// types, the PrimitiveItemType property indicates the valid value type.
	//
	// A subproperty name is a valid item type. For example, if the type value is List and the item type
	//  value is PortMapping, you can specify a list of port mapping properties.
	ItemType string `json:"ItemType"`

	// PrimitiveItemType - If the value of the Type field is List or Map, indicates the type of list or map
	// if they contain primitive types. Otherwise, this field is omitted. For lists or maps that contain
	// non-primitive types, the ItemType property indicates the valid value type.
	// The valid primitive types for lists and maps are String, Long, Integer, Double, Boolean, or Timestamp.
	// For example, if the type value is List and the item type value is String, you can specify a list of strings
	// for the property. If the type value is Map and the item type value is Boolean, you can specify a string
	// to Boolean mapping for the property.
	PrimitiveItemType string `json:"PrimitiveItemType"`

	// PrimitiveType - For primitive values, the valid primitive type for the property. A primitive type is a
	// basic data type for resource property values.
	// The valid primitive types are String, Long, Integer, Double, Boolean, Timestamp or Json.
	// If valid values are a non-primitive type, this field is omitted and the Type field indicates the valid value type.
	PrimitiveType string `json:"PrimitiveType"`

	// Required indicates whether the property is required.
	Required bool `json:"Required"`

	// Type - For non-primitive types, valid values for the property. The valid types are a subproperty name,
	// List or Map. If valid values are a primitive type, this field is omitted and the PrimitiveType field
	// indicates the valid value type. A list is a comma-separated list of values. A map is a set of key-value pairs,
	// where the keys are always strings. The value type for lists and maps are indicated by the ItemType
	// or PrimitiveItemType field.
	Type string `json:"Type"`

	// UpdateType - During a stack update, the update behavior when you add, remove, or modify the property.
	// AWS CloudFormation replaces the resource when you change Immutable properties. AWS CloudFormation doesn't
	// replace the resource when you change mutable properties. Conditional updates can be mutable or immutable,
	// depending on, for example, which other properties you updated. For more information, see the relevant
	// resource type documentation.
	UpdateType string `json:"UpdateType"`

	// Types - if a property can be different types, they will be listed here
	PrimitiveTypes     []string `json:"PrimitiveTypes"`
	PrimitiveItemTypes []string `json:"PrimitiveItemTypes"`
	ItemTypes          []string `json:"ItemTypes"`
	Types              []string `json:"Types"`
}

// Schema returns a JSON Schema for the resource (as a string)
func (p Property) Schema(name, parent string) string {

	// Open the schema template and setup a counter function that will
	// available in the template to be used to detect when trailing commas
	// are required in the JSON when looping through maps
	tmpl, err := template.New("schema-property.template").Funcs(template.FuncMap{
		"counter":           counter,
		"convertToJSONType": convertTypeToJSON,
	}).ParseFiles("generate/templates/schema-property.template")

	var buf bytes.Buffer
	parentpaths := strings.Split(parent, ".")

	templateData := struct {
		Name     string
		Parent   string
		Property Property
	}{
		Name:     name,
		Parent:   parentpaths[0],
		Property: p,
	}

	// Execute the template, writing it to the buffer
	err = tmpl.Execute(&buf, templateData)
	if err != nil {
		fmt.Printf("Error: Failed to generate property %s\n%s\n", name, err)
		os.Exit(1)
	}

	return buf.String()

}

// IsPolymorphic checks whether a property can be multiple different types
func (p Property) IsPolymorphic() bool {
	return len(p.PrimitiveTypes) > 0 || len(p.PrimitiveItemTypes) > 0 || len(p.PrimitiveItemTypes) > 0 || len(p.ItemTypes) > 0 || len(p.Types) > 0
}

// IsPrimitive checks whether a property is a primitive type
func (p Property) IsPrimitive() bool {
	return p.PrimitiveType != ""
}

// IsMap checks whether a property should be a map (map[string]...)
func (p Property) IsMap() bool {
	return p.Type == "Map"
}

// IsMapOfPrimitives checks whether a map contains primitive values
func (p Property) IsMapOfPrimitives() bool {
	return p.IsMap() && p.PrimitiveItemType != ""
}

// IsList checks whether a property should be a list ([]...)
func (p Property) IsList() bool {
	return p.Type == "List"
}

// IsListOfPrimitives checks whether a list containers primitive values
func (p Property) IsListOfPrimitives() bool {
	return p.IsList() && p.PrimitiveItemType != ""
}

// IsCustomType checks wither a property is a custom type
func (p Property) IsCustomType() bool {
	return p.PrimitiveType == "" && p.ItemType == "" && p.PrimitiveItemType == ""
}

// GoType returns the correct type for this property
// within a Go struct. For example, []string or map[string]AWSLambdaFunction_VpcConfig
func (p Property) GoType(basename string, name string) string {

	if p.IsPolymorphic() {

		types := append([]string{}, p.PrimitiveTypes...)
		types = append(types, p.Types...)

		for _, t := range p.PrimitiveItemTypes {
			types = append(types, "ListOf"+t)
		}

		for _, t := range p.ItemTypes {
			types = append(types, "ListOf"+t)
		}

		generatePolymorphicProperty(basename+"_"+name, p)
		return basename + "_" + name

	}

	if p.IsMap() {

		if p.IsMapOfPrimitives() {
			return "map[string]" + convertTypeToGo(p.PrimitiveItemType)
		}

		if p.ItemType == "Tag" {
			return "map[string]Tag"
		}

		return "map[string]" + basename + "_" + p.ItemType

	}

	if p.IsList() {

		if p.IsListOfPrimitives() {
			return "[]" + convertTypeToGo(p.PrimitiveItemType)
		}

		if p.ItemType == "Tag" {
			return "[]Tag"
		}

		return "[]" + basename + "_" + p.ItemType

	}

	if p.IsCustomType() {
		return basename + "_" + p.Type
	}

	// Must be a primitive value
	return convertTypeToGo(p.PrimitiveType)

}

// GetJSONPrimitiveType returns the correct primitive property type for a JSON Schema.
// If the property is a list/map, then it will return the type of the items.
func (p Property) GetJSONPrimitiveType() string {

	if p.IsPrimitive() {
		return convertTypeToJSON(p.PrimitiveType)
	}

	if p.IsMap() && p.IsMapOfPrimitives() {
		return convertTypeToJSON(p.PrimitiveItemType)
	}

	if p.IsList() && p.IsListOfPrimitives() {
		return convertTypeToJSON(p.PrimitiveItemType)
	}

	return "unknown"

}

func convertTypeToGo(pt string) string {
	switch pt {
	case "String":
		return "string"
	case "Long":
		return "int64"
	case "Integer":
		return "int"
	case "Double":
		return "float64"
	case "Boolean":
		return "bool"
	case "Timestamp":
		return "string"
	case "Json":
		return "interface{}"
	default:
		return pt
	}
}

func convertTypeToJSON(name string) string {
	switch name {
	case "String":
		return "string"
	case "Long":
		return "number"
	case "Integer":
		return "number"
	case "Double":
		return "number"
	case "Boolean":
		return "boolean"
	case "Timestamp":
		return "string"
	case "Json":
		return "object"
	default:
		return name
	}
}
<commit_msg>Remove superflous code.<commit_after>package main

import (
	"bytes"
	"fmt"
	"os"
	"strings"
	"text/template"
)

// Property represents an AWS CloudFormation resource property
type Property struct {

	// Documentation - A link to the AWS CloudFormation User Guide that provides information about the property.
	Documentation string `json:"Documentation"`

	// DuplicatesAllowed - If the value of the Type field is List, indicates whether AWS CloudFormation allows duplicate values.
	// If the value is true, AWS CloudFormation ignores duplicate values. If the value is false,
	// AWS CloudFormation returns an error if you submit duplicate values.
	DuplicatesAllowed bool `json:"DuplicatesAllowed"`

	// ItemType - If the value of the Type field is List or Map, indicates the type of list or map if they contain
	// non-primitive types. Otherwise, this field is omitted. For lists or maps that contain primitive
	// types, the PrimitiveItemType property indicates the valid value type.
	//
	// A subproperty name is a valid item type. For example, if the type value is List and the item type
	//  value is PortMapping, you can specify a list of port mapping properties.
	ItemType string `json:"ItemType"`

	// PrimitiveItemType - If the value of the Type field is List or Map, indicates the type of list or map
	// if they contain primitive types. Otherwise, this field is omitted. For lists or maps that contain
	// non-primitive types, the ItemType property indicates the valid value type.
	// The valid primitive types for lists and maps are String, Long, Integer, Double, Boolean, or Timestamp.
	// For example, if the type value is List and the item type value is String, you can specify a list of strings
	// for the property. If the type value is Map and the item type value is Boolean, you can specify a string
	// to Boolean mapping for the property.
	PrimitiveItemType string `json:"PrimitiveItemType"`

	// PrimitiveType - For primitive values, the valid primitive type for the property. A primitive type is a
	// basic data type for resource property values.
	// The valid primitive types are String, Long, Integer, Double, Boolean, Timestamp or Json.
	// If valid values are a non-primitive type, this field is omitted and the Type field indicates the valid value type.
	PrimitiveType string `json:"PrimitiveType"`

	// Required indicates whether the property is required.
	Required bool `json:"Required"`

	// Type - For non-primitive types, valid values for the property. The valid types are a subproperty name,
	// List or Map. If valid values are a primitive type, this field is omitted and the PrimitiveType field
	// indicates the valid value type. A list is a comma-separated list of values. A map is a set of key-value pairs,
	// where the keys are always strings. The value type for lists and maps are indicated by the ItemType
	// or PrimitiveItemType field.
	Type string `json:"Type"`

	// UpdateType - During a stack update, the update behavior when you add, remove, or modify the property.
	// AWS CloudFormation replaces the resource when you change Immutable properties. AWS CloudFormation doesn't
	// replace the resource when you change mutable properties. Conditional updates can be mutable or immutable,
	// depending on, for example, which other properties you updated. For more information, see the relevant
	// resource type documentation.
	UpdateType string `json:"UpdateType"`

	// Types - if a property can be different types, they will be listed here
	PrimitiveTypes     []string `json:"PrimitiveTypes"`
	PrimitiveItemTypes []string `json:"PrimitiveItemTypes"`
	ItemTypes          []string `json:"ItemTypes"`
	Types              []string `json:"Types"`
}

// Schema returns a JSON Schema for the resource (as a string)
func (p Property) Schema(name, parent string) string {

	// Open the schema template and setup a counter function that will
	// available in the template to be used to detect when trailing commas
	// are required in the JSON when looping through maps
	tmpl, err := template.New("schema-property.template").Funcs(template.FuncMap{
		"counter":           counter,
		"convertToJSONType": convertTypeToJSON,
	}).ParseFiles("generate/templates/schema-property.template")

	var buf bytes.Buffer
	parentpaths := strings.Split(parent, ".")

	templateData := struct {
		Name     string
		Parent   string
		Property Property
	}{
		Name:     name,
		Parent:   parentpaths[0],
		Property: p,
	}

	// Execute the template, writing it to the buffer
	err = tmpl.Execute(&buf, templateData)
	if err != nil {
		fmt.Printf("Error: Failed to generate property %s\n%s\n", name, err)
		os.Exit(1)
	}

	return buf.String()

}

// IsPolymorphic checks whether a property can be multiple different types
func (p Property) IsPolymorphic() bool {
	return len(p.PrimitiveTypes) > 0 || len(p.PrimitiveItemTypes) > 0 || len(p.PrimitiveItemTypes) > 0 || len(p.ItemTypes) > 0 || len(p.Types) > 0
}

// IsPrimitive checks whether a property is a primitive type
func (p Property) IsPrimitive() bool {
	return p.PrimitiveType != ""
}

// IsMap checks whether a property should be a map (map[string]...)
func (p Property) IsMap() bool {
	return p.Type == "Map"
}

// IsMapOfPrimitives checks whether a map contains primitive values
func (p Property) IsMapOfPrimitives() bool {
	return p.IsMap() && p.PrimitiveItemType != ""
}

// IsList checks whether a property should be a list ([]...)
func (p Property) IsList() bool {
	return p.Type == "List"
}

// IsListOfPrimitives checks whether a list containers primitive values
func (p Property) IsListOfPrimitives() bool {
	return p.IsList() && p.PrimitiveItemType != ""
}

// IsCustomType checks wither a property is a custom type
func (p Property) IsCustomType() bool {
	return p.PrimitiveType == "" && p.ItemType == "" && p.PrimitiveItemType == ""
}

// GoType returns the correct type for this property
// within a Go struct. For example, []string or map[string]AWSLambdaFunction_VpcConfig
func (p Property) GoType(basename string, name string) string {

	if p.IsPolymorphic() {

		generatePolymorphicProperty(basename+"_"+name, p)
		return basename + "_" + name

	}

	if p.IsMap() {

		if p.IsMapOfPrimitives() {
			return "map[string]" + convertTypeToGo(p.PrimitiveItemType)
		}

		if p.ItemType == "Tag" {
			return "map[string]Tag"
		}

		return "map[string]" + basename + "_" + p.ItemType

	}

	if p.IsList() {

		if p.IsListOfPrimitives() {
			return "[]" + convertTypeToGo(p.PrimitiveItemType)
		}

		if p.ItemType == "Tag" {
			return "[]Tag"
		}

		return "[]" + basename + "_" + p.ItemType

	}

	if p.IsCustomType() {
		return basename + "_" + p.Type
	}

	// Must be a primitive value
	return convertTypeToGo(p.PrimitiveType)

}

// GetJSONPrimitiveType returns the correct primitive property type for a JSON Schema.
// If the property is a list/map, then it will return the type of the items.
func (p Property) GetJSONPrimitiveType() string {

	if p.IsPrimitive() {
		return convertTypeToJSON(p.PrimitiveType)
	}

	if p.IsMap() && p.IsMapOfPrimitives() {
		return convertTypeToJSON(p.PrimitiveItemType)
	}

	if p.IsList() && p.IsListOfPrimitives() {
		return convertTypeToJSON(p.PrimitiveItemType)
	}

	return "unknown"

}

func convertTypeToGo(pt string) string {
	switch pt {
	case "String":
		return "string"
	case "Long":
		return "int64"
	case "Integer":
		return "int"
	case "Double":
		return "float64"
	case "Boolean":
		return "bool"
	case "Timestamp":
		return "string"
	case "Json":
		return "interface{}"
	default:
		return pt
	}
}

func convertTypeToJSON(name string) string {
	switch name {
	case "String":
		return "string"
	case "Long":
		return "number"
	case "Integer":
		return "number"
	case "Double":
		return "number"
	case "Boolean":
		return "boolean"
	case "Timestamp":
		return "string"
	case "Json":
		return "object"
	default:
		return name
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"net/http"
	"runtime"
	"sync/atomic"
	"time"

	"github.com/lomik/graphite-clickhouse/config"
	"github.com/lomik/graphite-clickhouse/find"
	"github.com/lomik/graphite-clickhouse/render"
	"github.com/lomik/zapwriter"
	"github.com/uber-go/zap"

	_ "net/http/pprof"
)

// Version of graphite-clickhouse
const Version = "0.2"

type LogResponseWriter struct {
	http.ResponseWriter
	status int
}

func (w *LogResponseWriter) WriteHeader(status int) {
	w.status = status
	w.ResponseWriter.WriteHeader(status)
}

func (w *LogResponseWriter) Status() int {
	if w.status == 0 {
		return http.StatusOK
	}
	return w.status
}

func WrapResponseWriter(w http.ResponseWriter) *LogResponseWriter {
	if wrapped, ok := w.(*LogResponseWriter); ok {
		return wrapped
	}
	return &LogResponseWriter{ResponseWriter: w}
}

func Handler(logger zap.Logger, handler http.Handler) http.Handler {
	var requestCounter uint32
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		writer := WrapResponseWriter(w)

		requestID := r.Header.Get("X-Request-Id")
		if requestID == "" {
			requestID = fmt.Sprintf("%d", atomic.AddUint32(&requestCounter, 1))
		}

		logger := logger.With(zap.String("requestID", requestID))

		r = r.WithContext(context.WithValue(r.Context(), "logger", logger))

		start := time.Now()
		handler.ServeHTTP(w, r)
		logger.Info("access",
			zap.Int("time_ms", int(time.Since(start)/time.Millisecond)),
			zap.String("method", r.Method),
			zap.String("url", r.URL.String()),
			zap.String("peer", r.RemoteAddr),
			zap.Int("status", writer.Status()),
		)
	})
}

func main() {
	var err error

	/* CONFIG start */

	configFile := flag.String("config", "/etc/graphite-clickhouse/graphite-clickhouse.conf", "Filename of config")
	printDefaultConfig := flag.Bool("config-print-default", false, "Print default config")
	checkConfig := flag.Bool("check-config", false, "Check config and exit")

	printVersion := flag.Bool("version", false, "Print version")

	flag.Parse()

	if *printVersion {
		fmt.Print(Version)
		return
	}

	if *printDefaultConfig {
		if err = config.Print(config.New()); err != nil {
			log.Fatal(err)
		}
		return
	}

	cfg := config.New()
	if err := config.Parse(*configFile, cfg); err != nil {
		log.Fatal(err)
	}

	// config parsed successfully. Exit in check-only mode
	if *checkConfig {
		return
	}
	runtime.GOMAXPROCS(cfg.Common.MaxCPU)

	zapOutput, err := zapwriter.New(cfg.Logging.File)
	if err != nil {
		log.Fatal(err)
	}

	var logLevel zap.Level
	if err = logLevel.UnmarshalText([]byte(cfg.Logging.Level)); err != nil {
		log.Fatal(err)
	}

	dynamicLevel := zap.DynamicLevel()
	dynamicLevel.SetLevel(logLevel)

	logger := zap.New(
		zapwriter.NewMixedEncoder(),
		zap.AddCaller(),
		zap.Output(zapOutput),
		dynamicLevel,
	)

	/* CONFIG end */

	http.Handle("/metrics/find/", Handler(logger, find.NewHandler(cfg)))
	http.Handle("/render/", Handler(logger, render.NewHandler(cfg)))

	http.Handle("/", Handler(logger, http.HandlerFunc(http.NotFound)))

	log.Fatal(http.ListenAndServe(cfg.Common.Listen, nil))
}
<commit_msg>version 0.2.1<commit_after>package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"net/http"
	"runtime"
	"sync/atomic"
	"time"

	"github.com/lomik/graphite-clickhouse/config"
	"github.com/lomik/graphite-clickhouse/find"
	"github.com/lomik/graphite-clickhouse/render"
	"github.com/lomik/zapwriter"
	"github.com/uber-go/zap"

	_ "net/http/pprof"
)

// Version of graphite-clickhouse
const Version = "0.2.1"

type LogResponseWriter struct {
	http.ResponseWriter
	status int
}

func (w *LogResponseWriter) WriteHeader(status int) {
	w.status = status
	w.ResponseWriter.WriteHeader(status)
}

func (w *LogResponseWriter) Status() int {
	if w.status == 0 {
		return http.StatusOK
	}
	return w.status
}

func WrapResponseWriter(w http.ResponseWriter) *LogResponseWriter {
	if wrapped, ok := w.(*LogResponseWriter); ok {
		return wrapped
	}
	return &LogResponseWriter{ResponseWriter: w}
}

func Handler(logger zap.Logger, handler http.Handler) http.Handler {
	var requestCounter uint32
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		writer := WrapResponseWriter(w)

		requestID := r.Header.Get("X-Request-Id")
		if requestID == "" {
			requestID = fmt.Sprintf("%d", atomic.AddUint32(&requestCounter, 1))
		}

		logger := logger.With(zap.String("requestID", requestID))

		r = r.WithContext(context.WithValue(r.Context(), "logger", logger))

		start := time.Now()
		handler.ServeHTTP(w, r)
		logger.Info("access",
			zap.Int("time_ms", int(time.Since(start)/time.Millisecond)),
			zap.String("method", r.Method),
			zap.String("url", r.URL.String()),
			zap.String("peer", r.RemoteAddr),
			zap.Int("status", writer.Status()),
		)
	})
}

func main() {
	var err error

	/* CONFIG start */

	configFile := flag.String("config", "/etc/graphite-clickhouse/graphite-clickhouse.conf", "Filename of config")
	printDefaultConfig := flag.Bool("config-print-default", false, "Print default config")
	checkConfig := flag.Bool("check-config", false, "Check config and exit")

	printVersion := flag.Bool("version", false, "Print version")

	flag.Parse()

	if *printVersion {
		fmt.Print(Version)
		return
	}

	if *printDefaultConfig {
		if err = config.Print(config.New()); err != nil {
			log.Fatal(err)
		}
		return
	}

	cfg := config.New()
	if err := config.Parse(*configFile, cfg); err != nil {
		log.Fatal(err)
	}

	// config parsed successfully. Exit in check-only mode
	if *checkConfig {
		return
	}
	runtime.GOMAXPROCS(cfg.Common.MaxCPU)

	zapOutput, err := zapwriter.New(cfg.Logging.File)
	if err != nil {
		log.Fatal(err)
	}

	var logLevel zap.Level
	if err = logLevel.UnmarshalText([]byte(cfg.Logging.Level)); err != nil {
		log.Fatal(err)
	}

	dynamicLevel := zap.DynamicLevel()
	dynamicLevel.SetLevel(logLevel)

	logger := zap.New(
		zapwriter.NewMixedEncoder(),
		zap.AddCaller(),
		zap.Output(zapOutput),
		dynamicLevel,
	)

	/* CONFIG end */

	http.Handle("/metrics/find/", Handler(logger, find.NewHandler(cfg)))
	http.Handle("/render/", Handler(logger, render.NewHandler(cfg)))

	http.Handle("/", Handler(logger, http.HandlerFunc(http.NotFound)))

	log.Fatal(http.ListenAndServe(cfg.Common.Listen, nil))
}
<|endoftext|>


<commit_before>package lib

import (
	"fmt"
	"os"
	"strings"
	"subutai/config"
	"subutai/lib/net"
	"subutai/lib/net/p2p"
	"subutai/log"
)

func P2P(c, d, u, l, p bool, args []string) {
	if c {
		if len(args) > 8 {
			p2p.Create(args[4], args[5], args[6], args[7], args[8])
		}
	} else if u {
		if len(args) > 6 {
			p2p.UpdateKey(args[4], args[5], args[6])
		}
	} else if d {
		if len(args) > 4 {
			p2p.Remove(args[4])
		}
	} else if p {
		p2p.Peers(args[4])
	} else if l {
		p2p.Print()
	}
}

func LxcManagementNetwork(args []string) {
	if len(args) < 3 {
		log.Error("Not enough arguments")
	}
	switch args[2] {
	case "-D", "--deletegateway":
		net.DeleteGateway(args[3])
	case "-v", "--listvnimap":
		listVNIMap()
	case "-r", "--removetunnel":
		removeTunnel(args[3])
	case "-T", "--creategateway":
		net.CreateGateway(args[3], args[4])
	case "-M", "--removevni":
		delVNI(args[3], args[4], args[5])
	case "-E", "--reservvni":
		reservVNI(args[3], args[4], args[5])
	case "-m", "--createvnimap":
		createVNIMap(args[3], args[4], args[5], args[6])
	case "-c", "--createtunnel":
		log.Check(log.FatalLevel, "create tunnel", createTunnel(args[3], args[4], args[5]))
	case "-l", "--listtunnel":
		liste := listTunnel()
		fmt.Println("List of Tunnels\n--------")
		for _, v := range liste {
			fmt.Println(string(v))
		}
	case "-Z", "--vniop":
		switch args[3] {
		case "deleteall":
			net.DeleteAllVNI(args[4])
			net.DeleteGateway(args[4])
		case "delete":
			net.DeleteVNI(args[4], args[5], args[6])
		case "list":
			net.ListVNI()
		}
	}
}

func createTunnel(tunnelPortName, tunnelIPAddress, tunnelType string) error {
	log.Info("tunnel port name: " + tunnelPortName)
	log.Info("tunnel IP address: " + tunnelIPAddress)
	log.Info("tunnel type: " + tunnelType)
	log.Check(log.FatalLevel, "check tunnel validity ", net.CheckTunnelPortNameValidity(tunnelPortName))
	log.Check(log.FatalLevel, "check ip validity "+tunnelIPAddress, net.CheckIPValidity(listTunnel(), tunnelIPAddress))
	if tunnelType == "vxlan" || tunnelType == "gre" {
		log.Check(log.FatalLevel, "create tunnel ", net.CreateTunnel(listTunnel(), tunnelPortName, tunnelIPAddress, tunnelType))
	} else {
		log.Error("Tunnel type must be vxlan or gre")
	}
	return nil
}
func listTunnel() []string { // we need a list when ip address checking.
	var returnArr []string
	list := net.ListTunnels()
	listA := strings.Split(string(list), "\n")

	for k, v := range listA {
		if strings.Contains(string(v), "remote_ip") {
			devInt := strings.Fields(listA[k-2])
			strLine := strings.Trim(devInt[1], "\"")
			devIP := strings.Fields(v)
			strLine = strLine + "-" + strings.Trim(strings.Trim(devIP[2], "remote_ip="), "\"}")
			returnArr = append(returnArr, strLine)
		}
	}
	return returnArr
}
func removeTunnel(tunnelPortName string) {
	retVal := net.CheckTunnelPortNameValidity(tunnelPortName)
	log.Check(log.WarnLevel, " remove "+tunnelPortName+"_vni_vlan",
		os.Remove(config.Agent.DataPrefix+"var/subutai-network/"+tunnelPortName+"_vni_vlan"))

	// basically it return err if given tunnelPortName exits.
	if retVal != nil {
		log.Info(tunnelPortName + " found in system.")
		log.Check(log.FatalLevel, "remove tunnel", net.RemovePort(tunnelPortName))
		log.Info(tunnelPortName + " removed")
	} else {
		log.Info(tunnelPortName + " not exists in system so NOT to remove")
	}

}

func createVNIMap(tunnelPortName, vni, vlan, envid string) {
	// check: if there is vni file
	if _, err := os.Stat(config.Agent.DataPrefix + "/var/subutai-network/vni_reserve"); os.IsNotExist(err) {
		log.Error("Do Reserve first. No reserved VNIs, not exist file for reserved VNI")
	}
	net.CreateVNIFile(tunnelPortName + "_vni_vlan")
	// check: control if there is such entry in nvi_reserv file.
	ret, _ := net.CheckVNIFile(tunnelPortName+"_vni_vlan", vni, vlan, envid)
	if ret[0] == true {
		log.Info("vni found")
	}
	if ret[1] == true {
		log.Info("vlanid found")
	}
	if ret[2] == true {
		log.Info("envid found")
	}
	if ret[3] == true {
		log.Info("reservation found")
	}

	net.MakeVNIMap(tunnelPortName, vni, vlan, envid)
	log.Info("vni map created: " + vni + " " + vlan + " " + envid)
}

func listVNIMap() {
	if _, err := os.Stat(config.Agent.DataPrefix + "/var/subutai-network/"); os.IsNotExist(err) {
		log.Error("folder not found" + err.Error())
	}
	net.DisplayVNIMap()
}

func delVNI(tunnelPortName, vni, vlan string) {
	if _, err := os.Stat(config.Agent.DataPrefix + "/var/subutai-network/"); os.IsNotExist(err) {
		log.Error("folder not found" + err.Error())
	}
	net.DelVNI(tunnelPortName, vni, vlan)
	log.Info(vni + " " + vlan + " deleted from " + tunnelPortName)
}

func reservVNI(vni, vlan, envid string) {
	// check: create vni file
	net.CreateVNIFile("vni_reserve")
	net.MakeReservation(vni, vlan, envid)
	log.Info(vni + " " + vlan + " " + envid + " is reserved")

}
<commit_msg>Added number of arguments check. SS-4101<commit_after>package lib

import (
	"fmt"
	"os"
	"strings"
	"subutai/config"
	"subutai/lib/net"
	"subutai/lib/net/p2p"
	"subutai/log"
)

func P2P(c, d, u, l, p bool, args []string) {
	if c {
		if len(args) > 8 {
			p2p.Create(args[4], args[5], args[6], args[7], args[8])
		} else {
			fmt.Println("Wrong usage")
		}
	} else if u {
		if len(args) > 6 {
			p2p.UpdateKey(args[4], args[5], args[6])
		} else {
			fmt.Println("Wrong usage")
		}
	} else if d {
		if len(args) > 4 {
			p2p.Remove(args[4])
		} else {
			fmt.Println("Wrong usage")
		}
	} else if p {
		if len(args) > 4 {
			p2p.Peers(args[4])
		} else {
			fmt.Println("Wrong usage")
		}
	} else if l {
		p2p.Print()
	}
}

func LxcManagementNetwork(args []string) {
	if len(args) < 3 {
		log.Error("Not enough arguments")
	}
	switch args[2] {
	case "-D", "--deletegateway":
		net.DeleteGateway(args[3])
	case "-v", "--listvnimap":
		listVNIMap()
	case "-r", "--removetunnel":
		removeTunnel(args[3])
	case "-T", "--creategateway":
		net.CreateGateway(args[3], args[4])
	case "-M", "--removevni":
		delVNI(args[3], args[4], args[5])
	case "-E", "--reservvni":
		reservVNI(args[3], args[4], args[5])
	case "-m", "--createvnimap":
		createVNIMap(args[3], args[4], args[5], args[6])
	case "-c", "--createtunnel":
		log.Check(log.FatalLevel, "create tunnel", createTunnel(args[3], args[4], args[5]))
	case "-l", "--listtunnel":
		liste := listTunnel()
		fmt.Println("List of Tunnels\n--------")
		for _, v := range liste {
			fmt.Println(string(v))
		}
	case "-Z", "--vniop":
		switch args[3] {
		case "deleteall":
			net.DeleteAllVNI(args[4])
			net.DeleteGateway(args[4])
		case "delete":
			net.DeleteVNI(args[4], args[5], args[6])
		case "list":
			net.ListVNI()
		}
	}
}

func createTunnel(tunnelPortName, tunnelIPAddress, tunnelType string) error {
	log.Info("tunnel port name: " + tunnelPortName)
	log.Info("tunnel IP address: " + tunnelIPAddress)
	log.Info("tunnel type: " + tunnelType)
	log.Check(log.FatalLevel, "check tunnel validity ", net.CheckTunnelPortNameValidity(tunnelPortName))
	log.Check(log.FatalLevel, "check ip validity "+tunnelIPAddress, net.CheckIPValidity(listTunnel(), tunnelIPAddress))
	if tunnelType == "vxlan" || tunnelType == "gre" {
		log.Check(log.FatalLevel, "create tunnel ", net.CreateTunnel(listTunnel(), tunnelPortName, tunnelIPAddress, tunnelType))
	} else {
		log.Error("Tunnel type must be vxlan or gre")
	}
	return nil
}
func listTunnel() []string { // we need a list when ip address checking.
	var returnArr []string
	list := net.ListTunnels()
	listA := strings.Split(string(list), "\n")

	for k, v := range listA {
		if strings.Contains(string(v), "remote_ip") {
			devInt := strings.Fields(listA[k-2])
			strLine := strings.Trim(devInt[1], "\"")
			devIP := strings.Fields(v)
			strLine = strLine + "-" + strings.Trim(strings.Trim(devIP[2], "remote_ip="), "\"}")
			returnArr = append(returnArr, strLine)
		}
	}
	return returnArr
}
func removeTunnel(tunnelPortName string) {
	retVal := net.CheckTunnelPortNameValidity(tunnelPortName)
	log.Check(log.WarnLevel, " remove "+tunnelPortName+"_vni_vlan",
		os.Remove(config.Agent.DataPrefix+"var/subutai-network/"+tunnelPortName+"_vni_vlan"))

	// basically it return err if given tunnelPortName exits.
	if retVal != nil {
		log.Info(tunnelPortName + " found in system.")
		log.Check(log.FatalLevel, "remove tunnel", net.RemovePort(tunnelPortName))
		log.Info(tunnelPortName + " removed")
	} else {
		log.Info(tunnelPortName + " not exists in system so NOT to remove")
	}

}

func createVNIMap(tunnelPortName, vni, vlan, envid string) {
	// check: if there is vni file
	if _, err := os.Stat(config.Agent.DataPrefix + "/var/subutai-network/vni_reserve"); os.IsNotExist(err) {
		log.Error("Do Reserve first. No reserved VNIs, not exist file for reserved VNI")
	}
	net.CreateVNIFile(tunnelPortName + "_vni_vlan")
	// check: control if there is such entry in nvi_reserv file.
	ret, _ := net.CheckVNIFile(tunnelPortName+"_vni_vlan", vni, vlan, envid)
	if ret[0] == true {
		log.Info("vni found")
	}
	if ret[1] == true {
		log.Info("vlanid found")
	}
	if ret[2] == true {
		log.Info("envid found")
	}
	if ret[3] == true {
		log.Info("reservation found")
	}

	net.MakeVNIMap(tunnelPortName, vni, vlan, envid)
	log.Info("vni map created: " + vni + " " + vlan + " " + envid)
}

func listVNIMap() {
	if _, err := os.Stat(config.Agent.DataPrefix + "/var/subutai-network/"); os.IsNotExist(err) {
		log.Error("folder not found" + err.Error())
	}
	net.DisplayVNIMap()
}

func delVNI(tunnelPortName, vni, vlan string) {
	if _, err := os.Stat(config.Agent.DataPrefix + "/var/subutai-network/"); os.IsNotExist(err) {
		log.Error("folder not found" + err.Error())
	}
	net.DelVNI(tunnelPortName, vni, vlan)
	log.Info(vni + " " + vlan + " deleted from " + tunnelPortName)
}

func reservVNI(vni, vlan, envid string) {
	// check: create vni file
	net.CreateVNIFile("vni_reserve")
	net.MakeReservation(vni, vlan, envid)
	log.Info(vni + " " + vlan + " " + envid + " is reserved")

}
<|endoftext|>


<commit_before>//
// Copyright (c) 2012-2017 Codenvy, S.A.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// Contributors:
//   Codenvy, S.A. - initial API and implementation
//

package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"time"

	"regexp"

	"github.com/eclipse/che/agents/exec/auth"
	"github.com/eclipse/che/agents/exec/process"
	"github.com/eclipse/che/agents/exec/rest"
	"github.com/eclipse/che/agents/exec/rpc"
	"github.com/eclipse/che/agents/exec/term"
	"github.com/julienschmidt/httprouter"
)

var (
	AppHttpRoutes = []rest.RoutesGroup{
		process.HttpRoutes,
		rpc.HttpRoutes,
		term.HttpRoutes,
	}

	AppOpRoutes = []rpc.RoutesGroup{
		process.RpcRoutes,
	}

	serverAddress string
	staticDir     string
	basePath      string
	apiEndpoint   string

	authEnabled                      bool
	tokensExpirationTimeoutInMinutes uint

	processCleanupThresholdInMinutes int
	processCleanupPeriodInMinutes    int
)

func init() {
	// server configuration
	flag.StringVar(
		&serverAddress,
		"addr",
		":9000",
		"IP:PORT or :PORT the address to start the server on",
	)
	flag.StringVar(
		&staticDir,
		"static",
		"./static/",
		"path to the directory where static content is located",
	)
	flag.StringVar(
		&basePath,
		"path",
		"",
		`the base path for all the rpc & rest routes, so route paths are treated not
	as 'server_address + route_path' but 'server_address + path + route_path'.
	For example for the server address 'localhost:9000', route path '/connect' and
	configured path '/api/' exec-agent server will serve the following route:
	'localhost:9000/api/connect'.
	Regexp syntax is supported`,
	)

	// terminal configuration
	flag.StringVar(
		&term.Cmd,
		"cmd",
		"/bin/bash",
		"shell interpreter and command to execute on slave side of the pty",
	)
	process.ShellInterpreter = term.Cmd

	// workspace master server configuration
	flag.StringVar(
		&apiEndpoint,
		"api-endpoint",
		os.Getenv("CHE_API"),
		`api-endpoint used by exec-agent modules(such as activity checker or authentication)
	to request workspace master. By default the value from 'CHE_API' environment variable is used`,
	)

	// auth configuration
	flag.BoolVar(
		&authEnabled,
		"enable-auth",
		false,
		"whether authenicate requests on workspace master before allowing them to proceed",
	)
	flag.UintVar(
		&tokensExpirationTimeoutInMinutes,
		"tokens-expiration-timeout",
		auth.DefaultTokensExpirationTimeoutInMinutes,
		"how much time machine tokens stay in cache(if auth is enabled)",
	)

	// terminal configuration
	flag.BoolVar(
		&term.ActivityTrackingEnabled,
		"enable-activity-tracking",
		false,
		"whether workspace master will be notified about terminal activity",
	)

	// process executor configuration
	flag.IntVar(
		&processCleanupPeriodInMinutes,
		"process-cleanup-period",
		-1,
		"how often processs cleanup job will be executed(in minutes)",
	)
	flag.IntVar(&processCleanupThresholdInMinutes,
		"process-cleanup-threshold",
		-1,
		`how much time will dead and unused process stay(in minutes),
	if -1 passed then processes won't be cleaned at all. Please note that the time
	of real cleanup is between configured threshold and threshold + process-cleanup-period.`,
	)
	curDir, _ := os.Getwd()
	curDir += string(os.PathSeparator) + "logs"
	flag.StringVar(
		&process.LogsDir,
		"logs-dir",
		curDir,
		"base directory for process logs",
	)
}

func main() {
	flag.Parse()

	log.SetOutput(os.Stdout)

	// print configuration
	fmt.Println("Exec-agent configuration")
	fmt.Println("  Server")
	fmt.Printf("    - Address: %s\n", serverAddress)
	fmt.Printf("    - Static content: %s\n", staticDir)
	fmt.Printf("    - Base path: '%s'\n", basePath)
	fmt.Println("  Terminal")
	fmt.Printf("    - Slave command: '%s'\n", term.Cmd)
	fmt.Printf("    - Activity tracking enabled: %t\n", term.ActivityTrackingEnabled)
	if authEnabled {
		fmt.Println("  Authentication")
		fmt.Printf("    - Enabled: %t\n", authEnabled)
		fmt.Printf("    - Tokens expiration timeout: %dm\n", tokensExpirationTimeoutInMinutes)
	}
	fmt.Println("  Process executor")
	fmt.Printf("    - Logs dir: %s\n", process.LogsDir)
	if processCleanupPeriodInMinutes > 0 {
		fmt.Printf("    - Cleanup job period: %dm\n", processCleanupPeriodInMinutes)
		fmt.Printf("    - Not used & dead processes stay for: %dm\n", processCleanupThresholdInMinutes)
	}
	if authEnabled || term.ActivityTrackingEnabled {
		fmt.Println("  Workspace master server")
		fmt.Printf("    - API endpoint: %s\n", apiEndpoint)
	}
	fmt.Println()

	term.ApiEndpoint = apiEndpoint

	// process configuration
	if err := os.RemoveAll(process.LogsDir); err != nil {
		log.Fatal(err)
	}

	if processCleanupPeriodInMinutes > 0 {
		if processCleanupThresholdInMinutes < 0 {
			log.Fatal("Expected process cleanup threshold to be non negative value")
		}
		cleaner := process.NewCleaner(processCleanupPeriodInMinutes, processCleanupThresholdInMinutes)
		cleaner.CleanPeriodically()
	}

	// terminal configuration
	if term.ActivityTrackingEnabled {
		go term.Activity.StartTracking()
	}

	// register routes and http handlers
	router := httprouter.New()
	router.NotFound = http.FileServer(http.Dir(staticDir))

	fmt.Print("⇩ Registered HttpRoutes:\n\n")
	for _, routesGroup := range AppHttpRoutes {
		fmt.Printf("%s:\n", routesGroup.Name)
		for _, route := range routesGroup.Items {
			router.Handle(
				route.Method,
				route.Path,
				toHandle(route.HandleFunc),
			)
			fmt.Printf("✓ %s\n", &route)
		}
		fmt.Println()
	}

	fmt.Print("\n⇩ Registered RpcRoutes:\n\n")
	for _, routesGroup := range AppOpRoutes {
		fmt.Printf("%s:\n", routesGroup.Name)
		for _, route := range routesGroup.Items {
			fmt.Printf("✓ %s\n", route.Method)
			rpc.RegisterRoute(route)
		}
	}

	var handler http.Handler = router

	// required authentication for all the requests, if it is configured
	if authEnabled {
		cache := auth.NewCache(time.Minute*time.Duration(tokensExpirationTimeoutInMinutes), time.Minute*5)

		handler = auth.Handler{
			Delegate:    handler,
			ApiEndpoint: apiEndpoint,
			Cache:       cache,
			UnauthorizedHandler: func(w http.ResponseWriter, req *http.Request) {
				dropChannelsWithExpiredToken(req.URL.Query().Get("token"))
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
			},
		}
	}

	// cut base path on requests, if it is configured
	if basePath != "" {
		if rx, err := regexp.Compile(basePath); err == nil {
			handler = basePathChopper{rx, handler}
		} else {
			log.Fatal(err)
		}
	}

	http.Handle("/", handler)

	server := &http.Server{
		Handler:      handler,
		Addr:         serverAddress,
		WriteTimeout: 10 * time.Second,
		ReadTimeout:  10 * time.Second,
	}
	log.Fatal(server.ListenAndServe())
}

func dropChannelsWithExpiredToken(token string) {
	for _, c := range rpc.GetChannels() {
		u, err := url.ParseRequestURI(c.RequestURI)
		if err != nil {
			log.Printf("Couldn't parse the RequestURI '%s' of channel '%s'", c.RequestURI, c.Id)
		} else if u.Query().Get("token") == token {
			log.Printf("Token for channel '%s' is expired, trying to drop the channel", c.Id)
			rpc.DropChannel(c.Id)
		}
	}
}

type routerParamsAdapter struct {
	params httprouter.Params
}

func (pa routerParamsAdapter) Get(param string) string {
	return pa.params.ByName(param)
}

func toHandle(f rest.HttpRouteHandlerFunc) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
		if err := f(w, r, routerParamsAdapter{params: p}); err != nil {
			rest.WriteError(w, err)
		}
	}
}

type basePathChopper struct {
	pattern  *regexp.Regexp
	delegate http.Handler
}

func (c basePathChopper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// if request path starts with given base path
	if idx := c.pattern.FindStringSubmatchIndex(r.URL.Path); len(idx) != 0 && idx[0] == 0 {
		r.URL.Path = r.URL.Path[idx[1]:]
		r.RequestURI = r.RequestURI[idx[1]:]
	}
	c.delegate.ServeHTTP(w, r)
}
<commit_msg>Reuse terminal slave command by exec agent<commit_after>//
// Copyright (c) 2012-2017 Codenvy, S.A.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// Contributors:
//   Codenvy, S.A. - initial API and implementation
//

package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"time"

	"regexp"

	"github.com/eclipse/che/agents/exec/auth"
	"github.com/eclipse/che/agents/exec/process"
	"github.com/eclipse/che/agents/exec/rest"
	"github.com/eclipse/che/agents/exec/rpc"
	"github.com/eclipse/che/agents/exec/term"
	"github.com/julienschmidt/httprouter"
)

var (
	AppHttpRoutes = []rest.RoutesGroup{
		process.HttpRoutes,
		rpc.HttpRoutes,
		term.HttpRoutes,
	}

	AppOpRoutes = []rpc.RoutesGroup{
		process.RpcRoutes,
	}

	serverAddress string
	staticDir     string
	basePath      string
	apiEndpoint   string

	authEnabled                      bool
	tokensExpirationTimeoutInMinutes uint

	processCleanupThresholdInMinutes int
	processCleanupPeriodInMinutes    int
)

func init() {
	// server configuration
	flag.StringVar(
		&serverAddress,
		"addr",
		":9000",
		"IP:PORT or :PORT the address to start the server on",
	)
	flag.StringVar(
		&staticDir,
		"static",
		"./static/",
		"path to the directory where static content is located",
	)
	flag.StringVar(
		&basePath,
		"path",
		"",
		`the base path for all the rpc & rest routes, so route paths are treated not
	as 'server_address + route_path' but 'server_address + path + route_path'.
	For example for the server address 'localhost:9000', route path '/connect' and
	configured path '/api/' exec-agent server will serve the following route:
	'localhost:9000/api/connect'.
	Regexp syntax is supported`,
	)

	// terminal configuration
	flag.StringVar(
		&term.Cmd,
		"cmd",
		"/bin/bash",
		"shell interpreter and command to execute on slave side of the pty",
	)

	// workspace master server configuration
	flag.StringVar(
		&apiEndpoint,
		"api-endpoint",
		os.Getenv("CHE_API"),
		`api-endpoint used by exec-agent modules(such as activity checker or authentication)
	to request workspace master. By default the value from 'CHE_API' environment variable is used`,
	)

	// auth configuration
	flag.BoolVar(
		&authEnabled,
		"enable-auth",
		false,
		"whether authenicate requests on workspace master before allowing them to proceed",
	)
	flag.UintVar(
		&tokensExpirationTimeoutInMinutes,
		"tokens-expiration-timeout",
		auth.DefaultTokensExpirationTimeoutInMinutes,
		"how much time machine tokens stay in cache(if auth is enabled)",
	)

	// terminal configuration
	flag.BoolVar(
		&term.ActivityTrackingEnabled,
		"enable-activity-tracking",
		false,
		"whether workspace master will be notified about terminal activity",
	)

	// process executor configuration
	flag.IntVar(
		&processCleanupPeriodInMinutes,
		"process-cleanup-period",
		-1,
		"how often processs cleanup job will be executed(in minutes)",
	)
	flag.IntVar(&processCleanupThresholdInMinutes,
		"process-cleanup-threshold",
		-1,
		`how much time will dead and unused process stay(in minutes),
	if -1 passed then processes won't be cleaned at all. Please note that the time
	of real cleanup is between configured threshold and threshold + process-cleanup-period.`,
	)
	curDir, _ := os.Getwd()
	curDir += string(os.PathSeparator) + "logs"
	flag.StringVar(
		&process.LogsDir,
		"logs-dir",
		curDir,
		"base directory for process logs",
	)
}

func main() {
	flag.Parse()

	log.SetOutput(os.Stdout)

	// print configuration
	fmt.Println("Exec-agent configuration")
	fmt.Println("  Server")
	fmt.Printf("    - Address: %s\n", serverAddress)
	fmt.Printf("    - Static content: %s\n", staticDir)
	fmt.Printf("    - Base path: '%s'\n", basePath)
	fmt.Println("  Terminal")
	fmt.Printf("    - Slave command: '%s'\n", term.Cmd)
	fmt.Printf("    - Activity tracking enabled: %t\n", term.ActivityTrackingEnabled)
	if authEnabled {
		fmt.Println("  Authentication")
		fmt.Printf("    - Enabled: %t\n", authEnabled)
		fmt.Printf("    - Tokens expiration timeout: %dm\n", tokensExpirationTimeoutInMinutes)
	}
	fmt.Println("  Process executor")
	fmt.Printf("    - Logs dir: %s\n", process.LogsDir)
	if processCleanupPeriodInMinutes > 0 {
		fmt.Printf("    - Cleanup job period: %dm\n", processCleanupPeriodInMinutes)
		fmt.Printf("    - Not used & dead processes stay for: %dm\n", processCleanupThresholdInMinutes)
	}
	if authEnabled || term.ActivityTrackingEnabled {
		fmt.Println("  Workspace master server")
		fmt.Printf("    - API endpoint: %s\n", apiEndpoint)
	}
	fmt.Println()

	term.ApiEndpoint = apiEndpoint

	// process configuration
	if err := os.RemoveAll(process.LogsDir); err != nil {
		log.Fatal(err)
	}

	if processCleanupPeriodInMinutes > 0 {
		if processCleanupThresholdInMinutes < 0 {
			log.Fatal("Expected process cleanup threshold to be non negative value")
		}
		cleaner := process.NewCleaner(processCleanupPeriodInMinutes, processCleanupThresholdInMinutes)
		cleaner.CleanPeriodically()
	}
	process.ShellInterpreter = term.Cmd

	// terminal configuration
	if term.ActivityTrackingEnabled {
		go term.Activity.StartTracking()
	}

	// register routes and http handlers
	router := httprouter.New()
	router.NotFound = http.FileServer(http.Dir(staticDir))

	fmt.Print("⇩ Registered HttpRoutes:\n\n")
	for _, routesGroup := range AppHttpRoutes {
		fmt.Printf("%s:\n", routesGroup.Name)
		for _, route := range routesGroup.Items {
			router.Handle(
				route.Method,
				route.Path,
				toHandle(route.HandleFunc),
			)
			fmt.Printf("✓ %s\n", &route)
		}
		fmt.Println()
	}

	fmt.Print("\n⇩ Registered RpcRoutes:\n\n")
	for _, routesGroup := range AppOpRoutes {
		fmt.Printf("%s:\n", routesGroup.Name)
		for _, route := range routesGroup.Items {
			fmt.Printf("✓ %s\n", route.Method)
			rpc.RegisterRoute(route)
		}
	}

	var handler http.Handler = router

	// required authentication for all the requests, if it is configured
	if authEnabled {
		cache := auth.NewCache(time.Minute*time.Duration(tokensExpirationTimeoutInMinutes), time.Minute*5)

		handler = auth.Handler{
			Delegate:    handler,
			ApiEndpoint: apiEndpoint,
			Cache:       cache,
			UnauthorizedHandler: func(w http.ResponseWriter, req *http.Request) {
				dropChannelsWithExpiredToken(req.URL.Query().Get("token"))
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
			},
		}
	}

	// cut base path on requests, if it is configured
	if basePath != "" {
		if rx, err := regexp.Compile(basePath); err == nil {
			handler = basePathChopper{rx, handler}
		} else {
			log.Fatal(err)
		}
	}

	http.Handle("/", handler)

	server := &http.Server{
		Handler:      handler,
		Addr:         serverAddress,
		WriteTimeout: 10 * time.Second,
		ReadTimeout:  10 * time.Second,
	}
	log.Fatal(server.ListenAndServe())
}

func dropChannelsWithExpiredToken(token string) {
	for _, c := range rpc.GetChannels() {
		u, err := url.ParseRequestURI(c.RequestURI)
		if err != nil {
			log.Printf("Couldn't parse the RequestURI '%s' of channel '%s'", c.RequestURI, c.Id)
		} else if u.Query().Get("token") == token {
			log.Printf("Token for channel '%s' is expired, trying to drop the channel", c.Id)
			rpc.DropChannel(c.Id)
		}
	}
}

type routerParamsAdapter struct {
	params httprouter.Params
}

func (pa routerParamsAdapter) Get(param string) string {
	return pa.params.ByName(param)
}

func toHandle(f rest.HttpRouteHandlerFunc) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
		if err := f(w, r, routerParamsAdapter{params: p}); err != nil {
			rest.WriteError(w, err)
		}
	}
}

type basePathChopper struct {
	pattern  *regexp.Regexp
	delegate http.Handler
}

func (c basePathChopper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// if request path starts with given base path
	if idx := c.pattern.FindStringSubmatchIndex(r.URL.Path); len(idx) != 0 && idx[0] == 0 {
		r.URL.Path = r.URL.Path[idx[1]:]
		r.RequestURI = r.RequestURI[idx[1]:]
	}
	c.delegate.ServeHTTP(w, r)
}
<|endoftext|>


<commit_before>package tests

import (
	"fmt"
	"io/ioutil"
	"os"

	"github.com/ghodss/yaml"
	. "github.com/onsi/ginkgo"
	"github.com/onsi/ginkgo/extensions/table"
	. "github.com/onsi/gomega"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"kubevirt.io/containerized-data-importer/tests/framework"
	"kubevirt.io/containerized-data-importer/tests/utils"
)

const (
	dataVolumeName     = "test-dv"
	pvcName            = "test-pvc"
	validURL           = "http://www.example.com/example.img"
	invalidURLFormat   = "invalidURL"
	datavolumeTestFile = "manifests/datavolume.yaml"
	destinationFile    = "/var/tmp/datavolume_test.yaml"
)

var _ = Describe("[rfe_id:1130][crit:medium][vendor:cnv-qe@redhat.com][level:component]Validation tests", func() {
	f := framework.NewFrameworkOrDie("api-validation-func-test")

	Describe("[posneg:negative]Verify DataVolume validation", func() {
		Context("when creating Datavolume", func() {
			dv := map[string]interface{}{}

			AfterEach(func() {
				err := os.Remove(destinationFile)
				Expect(err).ToNot(HaveOccurred())
			})

			table.DescribeTable("with Datavolume source validation should", func(sourceType string, args ...string) {

				By("Reading yaml file from: " + datavolumeTestFile)
				err := yamlFiletoStruct(datavolumeTestFile, &dv)
				Expect(err).ToNot(HaveOccurred())

				switch sourceType {
				case "http":
					url := args[0]
					dv["spec"].(map[string]interface{})["source"] = map[string]interface{}{"http": map[string]interface{}{"url": url}}

				case "s3":
					url := args[0]
					dv["spec"].(map[string]interface{})["source"] = map[string]interface{}{"s3": map[string]interface{}{"url": url}}
				case "pvc":
					namespace := args[0]
					name := args[1]
					dv["spec"].(map[string]interface{})["source"] = map[string]interface{}{
						"pvc": map[string]interface{}{
							"namespace": namespace,
							"name":      name}}
				}

				err = structToYamlFile(destinationFile, dv)
				Expect(err).ToNot(HaveOccurred())

				By(fmt.Sprint("Verifying kubectl create"))
				Eventually(func() bool {
					_, err := RunKubectlCommand(f, "create", "-f", destinationFile, "-n", f.Namespace.Name)
					if err != nil {
						return true
					}
					return false
				}, timeout, pollingInterval).Should(BeTrue())

			},
				table.Entry("[test_id:1321]fail with http source with invalid url format", "http", invalidURLFormat),
				table.Entry("[test_id:1322]fail with http source with empty url", "http", ""),
				table.Entry("[test_id:1323][crit:low]fail with s3 source with invalid url format", "s3", invalidURLFormat),
				table.Entry("[test_id:1324][crit:low]fail with s3 source with empty url", "s3", ""),
				table.Entry("[test_id:1325]fail with empty PVC source namespace", "pvc", "", "test-pvc"),
				table.Entry("[test_id:1326]fail with empty PVC source name", "pvc", "test", ""),
			)

			table.DescribeTable("with Datavolume PVC size should", func(size string) {

				By("Reading yaml file from: " + datavolumeTestFile)
				err := yamlFiletoStruct(datavolumeTestFile, &dv)
				Expect(err).ToNot(HaveOccurred())

				dv["spec"].(map[string]interface{})["pvc"].(map[string]interface{})["resources"].(map[string]interface{})["requests"].(map[string]interface{})["storage"] = size
				err = structToYamlFile(destinationFile, dv)
				Expect(err).ToNot(HaveOccurred())

				By(fmt.Sprint("Verifying kubectl apply"))
				Eventually(func() bool {
					_, err := RunKubectlCommand(f, "create", "-f", destinationFile, "-n", f.Namespace.Name)
					if err != nil {
						return true
					}
					return false
				}, timeout, pollingInterval).Should(BeTrue())
			},
				table.Entry("[test_id:1033]fail with zero PVC size", "0"),
				table.Entry("[test_id:1327]fail with negative PVC size", "-500m"),
				table.Entry("[test_id:1328]fail with invalid PVC size", "invalid_size"),
			)

		})
	})

	Context("DataVolume Already Exists", func() {
		BeforeEach(func() {
			dataVolume := utils.NewDataVolumeWithHTTPImport(dataVolumeName, "500Mi", validURL)

			dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
			Expect(err).ToNot(HaveOccurred())
		})

		AfterEach(func() {
			err := utils.DeleteDataVolume(f.CdiClient, f.Namespace.Name, dataVolumeName)
			Expect(err).ToNot(HaveOccurred())
		})
		It("[test_id:1030]should fail creating an already existing DataVolume", func() {
			By(fmt.Sprint("Verifying kubectl create"))
			Eventually(func() bool {

				_, err := RunKubectlCommand(f, "create", "-f", datavolumeTestFile, "-n", f.Namespace.Name)
				if err != nil {
					return true
				}
				return false
			}, timeout, pollingInterval).Should(BeTrue())

		})
	})

	Context("DataVolume destination PVC", func() {
		BeforeEach(func() {
			pvc := utils.NewPVCDefinition(dataVolumeName, "50Mi", nil, nil)

			pvc, err := utils.CreatePVCFromDefinition(f.K8sClient, f.Namespace.Name, pvc)
			Expect(err).ToNot(HaveOccurred())
		})

		AfterEach(func() {
			pvc, err := f.K8sClient.CoreV1().PersistentVolumeClaims(f.Namespace.Name).Get(dataVolumeName, metav1.GetOptions{})
			Expect(err).ToNot(HaveOccurred())
			err = utils.DeletePVC(f.K8sClient, f.Namespace.Name, pvc)
			Expect(err).ToNot(HaveOccurred())
		})
		It("should fail creating a DataVolume with already existing destination pvc", func() {
			By(fmt.Sprint("Verifying kubectl create"))
			Eventually(func() bool {

				_, err := RunKubectlCommand(f, "create", "-f", datavolumeTestFile, "-n", f.Namespace.Name)
				if err != nil {
					return true
				}
				return false
			}, timeout, pollingInterval).Should(BeTrue())

		})
	})

	Context("when creating data volumes from manual manifests", func() {
		table.DescribeTable("with manifests Datavolume should", func(destinationFile string, expectError bool) {
			By(fmt.Sprint("Verifying kubectl apply"))
			_, err := RunKubectlCommand(f, "create", "-f", destinationFile, "-n", f.Namespace.Name)
			if expectError {
				Expect(err).To(HaveOccurred())
			} else {
				Expect(err).ToNot(HaveOccurred())
			}
		},
			table.Entry("fail with blank image source and contentType archive", "manifests/dvBlankArchive.yaml", true),
			table.Entry("fail with invalid contentType", "manifests/dvInvalidContentType.yaml", true),
			table.Entry("fail with missing source", "manifests/dvMissingSource.yaml", true),
			table.Entry("fail with multiple sources", "manifests/dvMultiSource.yaml", true),
			table.Entry("fail with invalid URL for http source", "manifests/dvInvalidURL.yaml", true),
			table.Entry("fail with invalid source PVC", "manifests/dvInvalidSourcePVC.yaml", true),
			table.Entry("succeed with valid source http", "manifests/datavolume.yaml", false),
			table.Entry("fail with missing PVC spec", "manifests/dvMissingPVCSpec.yaml", true),
			table.Entry("fail with missing resources spec", "manifests/dvMissingResourcesSpec.yaml", true),
			table.Entry("fail with 0 size PVC", "manifests/dv0SizePVC.yaml", true),
		)

	})
})

func yamlFiletoStruct(fileName string, o *map[string]interface{}) error {
	yamlFile, err := ioutil.ReadFile(fileName)
	if err != nil {
		return err
	}
	err = yaml.Unmarshal(yamlFile, o)
	if err != nil {
		return err
	}
	return nil

}

func structToYamlFile(fileName string, o interface{}) error {
	yamlOutput, err := yaml.Marshal(o)
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(fileName, yamlOutput, 0644)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Update api_validation_test.go<commit_after>package tests

import (
	"fmt"
	"io/ioutil"
	"os"

	"github.com/ghodss/yaml"
	. "github.com/onsi/ginkgo"
	"github.com/onsi/ginkgo/extensions/table"
	. "github.com/onsi/gomega"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"kubevirt.io/containerized-data-importer/tests/framework"
	"kubevirt.io/containerized-data-importer/tests/utils"
)

const (
	dataVolumeName     = "test-dv"
	pvcName            = "test-pvc"
	validURL           = "http://www.example.com/example.img"
	invalidURLFormat   = "invalidURL"
	datavolumeTestFile = "manifests/datavolume.yaml"
	destinationFile    = "/var/tmp/datavolume_test.yaml"
)

var _ = Describe("[rfe_id:1130][crit:medium][vendor:cnv-qe@redhat.com][level:component]Validation tests", func() {
	f := framework.NewFrameworkOrDie("api-validation-func-test")

	Describe("[posneg:negative]Verify DataVolume validation", func() {
		Context("when creating Datavolume", func() {
			dv := map[string]interface{}{}

			AfterEach(func() {
				err := os.Remove(destinationFile)
				Expect(err).ToNot(HaveOccurred())
			})

			table.DescribeTable("with Datavolume source validation should", func(sourceType string, args ...string) {

				By("Reading yaml file from: " + datavolumeTestFile)
				err := yamlFiletoStruct(datavolumeTestFile, &dv)
				Expect(err).ToNot(HaveOccurred())

				switch sourceType {
				case "http":
					url := args[0]
					dv["spec"].(map[string]interface{})["source"] = map[string]interface{}{"http": map[string]interface{}{"url": url}}

				case "s3":
					url := args[0]
					dv["spec"].(map[string]interface{})["source"] = map[string]interface{}{"s3": map[string]interface{}{"url": url}}
				case "pvc":
					namespace := args[0]
					name := args[1]
					dv["spec"].(map[string]interface{})["source"] = map[string]interface{}{
						"pvc": map[string]interface{}{
							"namespace": namespace,
							"name":      name}}
				}

				err = structToYamlFile(destinationFile, dv)
				Expect(err).ToNot(HaveOccurred())

				By(fmt.Sprint("Verifying kubectl create"))
				Eventually(func() bool {
					_, err := RunKubectlCommand(f, "create", "-f", destinationFile, "-n", f.Namespace.Name)
					if err != nil {
						return true
					}
					return false
				}, timeout, pollingInterval).Should(BeTrue())

			},
				table.Entry("[test_id:1321]fail with http source with invalid url format", "http", invalidURLFormat),
				table.Entry("[test_id:1322]fail with http source with empty url", "http", ""),
				table.Entry("[test_id:1323][crit:low]fail with s3 source with invalid url format", "s3", invalidURLFormat),
				table.Entry("[test_id:1324][crit:low]fail with s3 source with empty url", "s3", ""),
				table.Entry("[test_id:1325]fail with empty PVC source namespace", "pvc", "", "test-pvc"),
				table.Entry("[test_id:1326]fail with empty PVC source name", "pvc", "test", ""),
			)

			table.DescribeTable("with Datavolume PVC size should", func(size string) {

				By("Reading yaml file from: " + datavolumeTestFile)
				err := yamlFiletoStruct(datavolumeTestFile, &dv)
				Expect(err).ToNot(HaveOccurred())

				dv["spec"].(map[string]interface{})["pvc"].(map[string]interface{})["resources"].(map[string]interface{})["requests"].(map[string]interface{})["storage"] = size
				err = structToYamlFile(destinationFile, dv)
				Expect(err).ToNot(HaveOccurred())

				By(fmt.Sprint("Verifying kubectl apply"))
				Eventually(func() bool {
					_, err := RunKubectlCommand(f, "create", "-f", destinationFile, "-n", f.Namespace.Name)
					if err != nil {
						return true
					}
					return false
				}, timeout, pollingInterval).Should(BeTrue())
			},
				table.Entry("[test_id:1033]fail with zero PVC size", "0"),
				table.Entry("[test_id:1327]fail with negative PVC size", "-500m"),
				table.Entry("[test_id:1328]fail with invalid PVC size", "invalid_size"),
			)

		})
	})

	Context("DataVolume Already Exists", func() {
		BeforeEach(func() {
			dataVolume := utils.NewDataVolumeWithHTTPImport(dataVolumeName, "500Mi", validURL)

			dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
			Expect(err).ToNot(HaveOccurred())
		})

		AfterEach(func() {
			err := utils.DeleteDataVolume(f.CdiClient, f.Namespace.Name, dataVolumeName)
			Expect(err).ToNot(HaveOccurred())
		})
		It("[test_id:1030]should fail creating an already existing DataVolume", func() {
			By(fmt.Sprint("Verifying kubectl create"))
			Eventually(func() bool {

				_, err := RunKubectlCommand(f, "create", "-f", datavolumeTestFile, "-n", f.Namespace.Name)
				if err != nil {
					return true
				}
				return false
			}, timeout, pollingInterval).Should(BeTrue())

		})
	})

	Context("DataVolume destination PVC", func() {
		BeforeEach(func() {
			pvc := utils.NewPVCDefinition(dataVolumeName, "50Mi", nil, nil)

			pvc, err := utils.CreatePVCFromDefinition(f.K8sClient, f.Namespace.Name, pvc)
			Expect(err).ToNot(HaveOccurred())
		})

		AfterEach(func() {
			pvc, err := f.K8sClient.CoreV1().PersistentVolumeClaims(f.Namespace.Name).Get(dataVolumeName, metav1.GetOptions{})
			Expect(err).ToNot(HaveOccurred())
			err = utils.DeletePVC(f.K8sClient, f.Namespace.Name, pvc)
			Expect(err).ToNot(HaveOccurred())
		})
		It("should fail creating a DataVolume with already existing destination pvc", func() {
			By(fmt.Sprint("Verifying kubectl create"))
			Eventually(func() bool {

				_, err := RunKubectlCommand(f, "create", "-f", datavolumeTestFile, "-n", f.Namespace.Name)
				if err != nil {
					return true
				}
				return false
			}, timeout, pollingInterval).Should(BeTrue())

		})
	})

	Context("when creating data volumes from manual manifests", func() {
		table.DescribeTable("with manifests Datavolume should", func(destinationFile string, expectError bool) {
			By(fmt.Sprint("Verifying kubectl apply"))
			_, err := RunKubectlCommand(f, "create", "-f", destinationFile, "-n", f.Namespace.Name)
			if expectError {
				Expect(err).To(HaveOccurred())
			} else {
				Expect(err).ToNot(HaveOccurred())
			}
		},
			table.Entry("fail with blank image source and contentType archive", "manifests/dvBlankArchive.yaml", true),
			table.Entry("fail with invalid contentType", "manifests/dvInvalidContentType.yaml", true),
			table.Entry("fail with missing source", "manifests/dvMissingSource.yaml", true),
			table.Entry("fail with multiple sources", "manifests/dvMultiSource.yaml", true),
			table.Entry("fail with invalid URL for http source", "manifests/dvInvalidURL.yaml", true),
			table.Entry("fail with invalid source PVC", "manifests/dvInvalidSourcePVC.yaml", true),
			table.Entry("[posneg:positive]succeed with valid source http", "manifests/datavolume.yaml", false),
			table.Entry("fail with missing PVC spec", "manifests/dvMissingPVCSpec.yaml", true),
			table.Entry("fail with missing resources spec", "manifests/dvMissingResourcesSpec.yaml", true),
			table.Entry("fail with 0 size PVC", "manifests/dv0SizePVC.yaml", true),
		)

	})
})

func yamlFiletoStruct(fileName string, o *map[string]interface{}) error {
	yamlFile, err := ioutil.ReadFile(fileName)
	if err != nil {
		return err
	}
	err = yaml.Unmarshal(yamlFile, o)
	if err != nil {
		return err
	}
	return nil

}

func structToYamlFile(fileName string, o interface{}) error {
	yamlOutput, err := yaml.Marshal(o)
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(fileName, yamlOutput, 0644)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package strconvutil

import (
	"testing"

	"github.com/grokify/gotilla/math/mathutil"
)

var changeToXoXPctTests = []struct {
	v    float64
	want float64
}{
	{1.15, 15.0},
	{1.1, 10.0},
	{1.0, 0.0},
	{0.9, -10.0},
	{0.87, -13.0},
}

func TestChangeToXoXPctTests(t *testing.T) {
	for _, tt := range changeToXoXPctTests {
		// without math.Round, we end up with:
		// Error: with [0.9], want [-10], got [-9.999999999999998]
		try := mathutil.Round(ChangeToXoXPct(tt.v), 0.5, 0.0)
		if try != tt.want {
			t.Errorf("strconvutil.ChangeToXoXPct() Error: with [%v], want [%v], got [%v]",
				tt.v, tt.want, try)
		}
	}
}

var changeToFunnelPctTests = []struct {
	v    float64
	want float64
}{
	{2.0, 200.0},
	{1.5, 150.0},
	{1.15, 115.0},
	{1.1, 110.0},
	{1.0, 100.0},
	{0.9, 90.0},
	{0.87, 87.0},
	{0.5, 50.0},
	{0.25, 25.0},
}

func TestChangeToFunnelPctTests(t *testing.T) {
	for _, tt := range changeToFunnelPctTests {
		// without math.Round, we end up with:
		// Error: with [0.9], want [-10], got [-9.999999999999998]
		try := mathutil.Round(ChangeToFunnelPct(tt.v), 0.5, 0.0)
		if try != tt.want {
			t.Errorf("strconvutil.ChangeToFunnelPct() Error: with [%v], want [%v], got [%v]",
				tt.v, tt.want, try)
		}
	}
}
<commit_msg>update strconv test.<commit_after>package strconvutil

import (
	"testing"

	"github.com/grokify/gotilla/math/mathutil"
)

var changeToXoXPctTests = []struct {
	v    float64
	want float64
}{
	{1.15, 15.0},
	{1.1, 10.0},
	{1.0, 0.0},
	{0.9, -10.0},
	{0.87, -13.0},
}

func TestChangeToXoXPctTests(t *testing.T) {
	for _, tt := range changeToXoXPctTests {
		// without math.Round, we end up with:
		// Error: with [0.9], want [-10], got [-9.999999999999998]
		try := mathutil.RoundMore(ChangeToXoXPct(tt.v), 0.5, 0.0)
		// try := mathutil.Round(ChangeToXoXPct(tt.v))
		if try != tt.want {
			t.Errorf("strconvutil.ChangeToXoXPct() Error: with [%v], want [%v], got [%v]",
				tt.v, tt.want, try)
		}
	}
}

var changeToFunnelPctTests = []struct {
	v    float64
	want float64
}{
	{2.0, 200.0},
	{1.5, 150.0},
	{1.15, 115.0},
	{1.1, 110.0},
	{1.0, 100.0},
	{0.9, 90.0},
	{0.87, 87.0},
	{0.5, 50.0},
	{0.25, 25.0},
}

func TestChangeToFunnelPctTests(t *testing.T) {
	for _, tt := range changeToFunnelPctTests {
		// without math.Round, we end up with:
		// Error: with [0.9], want [-10], got [-9.999999999999998]
		try := mathutil.RoundMore(ChangeToFunnelPct(tt.v), 0.5, 0.0)
		// try := mathutil.Round(ChangeToXoXPct(tt.v))
		if try != tt.want {
			t.Errorf("strconvutil.ChangeToFunnelPct() Error: with [%v], want [%v], got [%v]",
				tt.v, tt.want, try)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"net/url"
	"os"
	"time"

	"github.com/gorilla/websocket"
	influx "github.com/influxdata/influxdb/client/v2"
	"github.com/mohae/autofact"
	"github.com/mohae/autofact/client"
	"github.com/mohae/autofact/db"
	"github.com/mohae/autofact/message"
	"github.com/mohae/autofact/sysinfo"
)

// server is the container for a server's information and everything that it
// is tracking/serving.
type server struct {
	// ID of the server
	ID uint32
	// URL of the server
	url.URL
	// Period between pings
	PingPeriod time.Duration
	// How long to wait for a pong response before timing out
	PongWait time.Duration
	// Flatbuffers serialized default client config
	ClientCfg []byte
	// A map of clients, by ID
	Inventory inventory
	// TODO: add handling to prevent the same client from connecting
	// more than once:  this requires detection of reconnect of an
	// existing client vs an existing client maintaining multiple
	// con-current connections
	DB db.Bolt
	// InfluxDB client
	*InfluxClient
}

func newServer(id uint32) server {
	return server{
		ID:        id,
		Inventory: newInventory(),
	}
}

// LoadInventory populates the server's inventory from the database.  This
// is a cached list of clients.
func (s *server) LoadInventory() (int, error) {
	var n int
	ids, err := s.DB.ClientIDs()
	if err != nil {
		return n, err
	}
	for i, id := range ids {
		c := newClient(id)
		c.InfluxClient = s.InfluxClient
		s.Inventory.AddClient(id, c)
		n = i
	}
	return n, nil
}

// connects to InfluxDB
func (s *server) connectToInfluxDB() error {
	var err error
	s.InfluxClient, err = newInfluxClient(influxDBName, influxAddress, influxUser, influxPassword)
	return err
}

// Client checks the inventory to see if the client exists
func (s *server) Client(id uint32) (*Client, bool) {
	return s.Inventory.Client(id)
}

// NewClient creates a new Client, adds it to the server's inventory and
// returns the client to the caller.   If the save of the Client's info to
// the database results in an error, it will be returned.
func (s *server) NewClient() (*Client, error) {
	// get a new client
	cl := s.Inventory.NewClient()
	cl.InfluxClient = s.InfluxClient
	// save the client info to the db
	err := s.DB.SaveClient(cl.ID)
	return cl, err
}

// Client holds the client's configuration, the websocket connection to the
// client node, and its connection state.
type Client struct {
	ID uint32
	client.Cfg
	WS *websocket.Conn
	*InfluxClient
	isConnected bool
}

func newClient(id uint32) *Client {
	return &Client{
		ID: id,
		Cfg: client.Cfg{
			HealthbeatInterval:   clientCfg.HealthbeatInterval,
			HealthbeatPushPeriod: clientCfg.HealthbeatPushPeriod,
			PingPeriod:           clientCfg.PingPeriod,
			PongWait:             clientCfg.PongWait,
			SaveInterval:         clientCfg.SaveInterval,
			WriteWait:            clientCfg.WriteWait,
		},
	}
}

// PingHandler is the handler for Pings.
func (c *Client) PingHandler(msg string) error {
	fmt.Printf("ping: %s\n", msg)
	return c.WS.WriteMessage(websocket.PongMessage, []byte("ping"))
}

// PongHandler is the handler for pongs.
func (c *Client) PongHandler(msg string) error {
	fmt.Printf("pong: %s\n", msg)
	return c.WS.WriteMessage(websocket.PingMessage, []byte("pong"))
}

// Listen listens for messages and handles them accordingly.  Binary messages
// are expected to be  Flatbuffer serialized bytes containing a Message.
func (c *Client) Listen(doneCh chan struct{}) {
	// loop until there's a done signal
	defer close(doneCh)
	for {
		typ, p, err := c.WS.ReadMessage()
		if err != nil {
			fmt.Fprintf(os.Stderr, "error reading message: %s\n", err)
			if _, ok := err.(*websocket.CloseError); !ok {
				return
			}
			fmt.Println("client closed connection...waiting for reconnect")
			return
		}
		switch typ {
		case websocket.TextMessage:
			fmt.Printf("textmessage: %s\n", p)
			if bytes.Equal(p, autofact.AckMsg) {
				// if this is an acknowledgement message, do nothing
				continue
			}
			err := c.WS.WriteMessage(websocket.TextMessage, autofact.AckMsg)
			if err != nil {
				if _, ok := err.(*websocket.CloseError); !ok {
					return
				}
				fmt.Println("client closed connection...waiting for reconnect")
				return
			}
		case websocket.BinaryMessage:
			err = c.WS.WriteMessage(websocket.TextMessage, autofact.AckMsg)
			if err != nil {
				fmt.Fprintf(os.Stderr, "error writing binary message: %s\n", err)
				if _, ok := err.(*websocket.CloseError); !ok {
					return
				}
				fmt.Println("client closed connection...waiting for reconnect")
				return
			}
			c.processBinaryMessage(p)
		case websocket.CloseMessage:
			fmt.Printf("closemessage: %x\n", p)
			fmt.Println("client closed connection...waiting for reconnect")
			return
		}
	}
}

// WriteBinaryMessage serializes a message and writes it to the socket as
// a binary message.
func (c *Client) WriteBinaryMessage(k message.Kind, p []byte) {
	c.WS.WriteMessage(websocket.BinaryMessage, message.Serialize(c.ID, k, p))
}

// binary messages are expected to be flatbuffer encoding of message.Message.
func (c *Client) processBinaryMessage(p []byte) error {
	// unmarshal the message
	msg := message.GetRootAsMessage(p, 0)
	// process according to kind
	k := message.Kind(msg.Kind())
	switch k {
	case message.CPUData:
		cpu := sysinfo.GetRootAsCPUData(msg.DataBytes(), 0)
		tags := map[string]string{"cpu": "cpu-total"}
		fields := map[string]interface{}{
			"user":   float32(cpu.Usr()) / 100.0,
			"sys":    float32(cpu.Sys()) / 100.0,
			"iowait": float32(cpu.IOWait()) / 100.0,
			"idle":   float32(cpu.Idle()) / 100.0,
		}
		// TODO: use the timestamp in the data instead of server time
		pt, err := influx.NewPoint("cpu_usage", tags, fields, time.Unix(0, cpu.Timestamp()).UTC())
		c.InfluxClient.seriesCh <- Series{Data: []*influx.Point{pt}, err: err}
		return nil
	case message.MemData:
		fmt.Println(sysinfo.UnmarshalMemDataToString(msg.DataBytes()))
	default:
		fmt.Println("unknown message kind")
		fmt.Println(string(p))
	}
	return nil
}
<commit_msg>add sending memstats to influxdb<commit_after>package main

import (
	"bytes"
	"fmt"
	"net/url"
	"os"
	"strconv"
	"time"

	"github.com/gorilla/websocket"
	influx "github.com/influxdata/influxdb/client/v2"
	"github.com/mohae/autofact"
	"github.com/mohae/autofact/client"
	"github.com/mohae/autofact/db"
	"github.com/mohae/autofact/message"
	"github.com/mohae/autofact/sysinfo"
)

// server is the container for a server's information and everything that it
// is tracking/serving.
type server struct {
	// ID of the server
	ID uint32
	// URL of the server
	url.URL
	// Period between pings
	PingPeriod time.Duration
	// How long to wait for a pong response before timing out
	PongWait time.Duration
	// Flatbuffers serialized default client config
	ClientCfg []byte
	// A map of clients, by ID
	Inventory inventory
	// TODO: add handling to prevent the same client from connecting
	// more than once:  this requires detection of reconnect of an
	// existing client vs an existing client maintaining multiple
	// con-current connections
	DB db.Bolt
	// InfluxDB client
	*InfluxClient
}

func newServer(id uint32) server {
	return server{
		ID:        id,
		Inventory: newInventory(),
	}
}

// LoadInventory populates the server's inventory from the database.  This
// is a cached list of clients.
func (s *server) LoadInventory() (int, error) {
	var n int
	ids, err := s.DB.ClientIDs()
	if err != nil {
		return n, err
	}
	for i, id := range ids {
		c := newClient(id)
		c.InfluxClient = s.InfluxClient
		s.Inventory.AddClient(id, c)
		n = i
	}
	return n, nil
}

// connects to InfluxDB
func (s *server) connectToInfluxDB() error {
	var err error
	s.InfluxClient, err = newInfluxClient(influxDBName, influxAddress, influxUser, influxPassword)
	return err
}

// Client checks the inventory to see if the client exists
func (s *server) Client(id uint32) (*Client, bool) {
	return s.Inventory.Client(id)
}

// NewClient creates a new Client, adds it to the server's inventory and
// returns the client to the caller.   If the save of the Client's info to
// the database results in an error, it will be returned.
func (s *server) NewClient() (*Client, error) {
	// get a new client
	cl := s.Inventory.NewClient()
	cl.InfluxClient = s.InfluxClient
	// save the client info to the db
	err := s.DB.SaveClient(cl.ID)
	return cl, err
}

// Client holds the client's configuration, the websocket connection to the
// client node, and its connection state.
type Client struct {
	ID     uint32
	Name   string
	Region string
	client.Cfg
	WS *websocket.Conn
	*InfluxClient
	isConnected bool
}

// TODO: add region support (hardcoded for now for dev purposes)
func newClient(id uint32) *Client {
	return &Client{
		ID:     id,
		Name:   strconv.FormatUint(uint64(id), 10),
		Region: "region1",
		Cfg: client.Cfg{
			HealthbeatInterval:   clientCfg.HealthbeatInterval,
			HealthbeatPushPeriod: clientCfg.HealthbeatPushPeriod,
			PingPeriod:           clientCfg.PingPeriod,
			PongWait:             clientCfg.PongWait,
			SaveInterval:         clientCfg.SaveInterval,
			WriteWait:            clientCfg.WriteWait,
		},
	}
}

// PingHandler is the handler for Pings.
func (c *Client) PingHandler(msg string) error {
	fmt.Printf("ping: %s\n", msg)
	return c.WS.WriteMessage(websocket.PongMessage, []byte("ping"))
}

// PongHandler is the handler for pongs.
func (c *Client) PongHandler(msg string) error {
	fmt.Printf("pong: %s\n", msg)
	return c.WS.WriteMessage(websocket.PingMessage, []byte("pong"))
}

// Listen listens for messages and handles them accordingly.  Binary messages
// are expected to be  Flatbuffer serialized bytes containing a Message.
func (c *Client) Listen(doneCh chan struct{}) {
	// loop until there's a done signal
	defer close(doneCh)
	for {
		typ, p, err := c.WS.ReadMessage()
		if err != nil {
			fmt.Fprintf(os.Stderr, "error reading message: %s\n", err)
			if _, ok := err.(*websocket.CloseError); !ok {
				return
			}
			fmt.Println("client closed connection...waiting for reconnect")
			return
		}
		switch typ {
		case websocket.TextMessage:
			fmt.Printf("textmessage: %s\n", p)
			if bytes.Equal(p, autofact.AckMsg) {
				// if this is an acknowledgement message, do nothing
				continue
			}
			err := c.WS.WriteMessage(websocket.TextMessage, autofact.AckMsg)
			if err != nil {
				if _, ok := err.(*websocket.CloseError); !ok {
					return
				}
				fmt.Println("client closed connection...waiting for reconnect")
				return
			}
		case websocket.BinaryMessage:
			err = c.WS.WriteMessage(websocket.TextMessage, autofact.AckMsg)
			if err != nil {
				fmt.Fprintf(os.Stderr, "error writing binary message: %s\n", err)
				if _, ok := err.(*websocket.CloseError); !ok {
					return
				}
				fmt.Println("client closed connection...waiting for reconnect")
				return
			}
			c.processBinaryMessage(p)
		case websocket.CloseMessage:
			fmt.Printf("closemessage: %x\n", p)
			fmt.Println("client closed connection...waiting for reconnect")
			return
		}
	}
}

// WriteBinaryMessage serializes a message and writes it to the socket as
// a binary message.
func (c *Client) WriteBinaryMessage(k message.Kind, p []byte) {
	c.WS.WriteMessage(websocket.BinaryMessage, message.Serialize(c.ID, k, p))
}

// binary messages are expected to be flatbuffer encoding of message.Message.
func (c *Client) processBinaryMessage(p []byte) error {
	// unmarshal the message
	msg := message.GetRootAsMessage(p, 0)
	// process according to kind
	k := message.Kind(msg.Kind())
	switch k {
	case message.CPUData:
		cpu := sysinfo.GetRootAsCPUData(msg.DataBytes(), 0)
		tags := map[string]string{"host": c.Name, "region": c.Region, "cpu": string(cpu.CPUID())}
		fields := map[string]interface{}{
			"user":   float32(cpu.Usr()) / 100.0,
			"sys":    float32(cpu.Sys()) / 100.0,
			"iowait": float32(cpu.IOWait()) / 100.0,
			"idle":   float32(cpu.Idle()) / 100.0,
		}
		pt, err := influx.NewPoint("cpu_usage", tags, fields, time.Unix(0, cpu.Timestamp()).UTC())
		c.InfluxClient.seriesCh <- Series{Data: []*influx.Point{pt}, err: err}
		return nil
	case message.MemData:
		mem := sysinfo.GetRootAsMemData(msg.DataBytes(), 0)
		tags := map[string]string{"client": c.Name, "region": c.Region}
		fields := map[string]interface{}{
			"mem-total":   float32(mem.MemTotal()) / 100.0,
			"mem-used":    float32(mem.MemUsed()) / 100.0,
			"mem-free":    float32(mem.MemFree()) / 100.0,
			"mem-shared":  float32(mem.MemShared()) / 100.0,
			"mem-buffers": float32(mem.MemBuffers()) / 100.0,
			"cache-used":  float32(mem.CacheUsed()) / 100.0,
			"cache-free":  float32(mem.CacheFree()) / 100.0,
			"swap-total":  float32(mem.SwapTotal()) / 100.0,
			"swap-used":   float32(mem.SwapUsed()) / 100.0,
			"swap-free":   float32(mem.SwapFree()) / 100.0,
		}
		pt, err := influx.NewPoint("memory", tags, fields, time.Unix(0, mem.Timestamp()).UTC())
		c.InfluxClient.seriesCh <- Series{Data: []*influx.Point{pt}, err: err}
		return nil
	default:
		fmt.Println("unknown message kind")
		fmt.Println(string(p))
	}
	return nil
}
<|endoftext|>


<commit_before>package persist

import (
	"bufio"
	"encoding/json"
	"entity"
	"errors"
	//"fmt"
	"log"
	"os"
	"sync"
)

type PersistJSON struct {
	*sync.RWMutex
}

func NewPersistJSON() *PersistJSON {
	return &PersistJSON{
		RWMutex: &sync.RWMutex{},
	}
}

type profileSet struct {
	Profiles []*entity.Profile
}

func (self *PersistJSON) GetAllProfiles() ([]*entity.Profile, error) {
	self.RLock()
	defer self.RUnlock()

	f, err := os.Open("data/data.json")
	if err != nil {
		return nil, err
	}
	defer f.Close()
	log.Printf("PersistJSON.GetAllProfiles(): data/data.json opened")

	decoder := json.NewDecoder(bufio.NewReader(f))
	log.Printf("PersistJSON.GetAllProfiles(): made decoder")

	ps := profileSet{}
	err = decoder.Decode(&ps)
	if err != nil {
		return nil, err
	}
	log.Printf("PersistJSON.GetAllProfiles(): decoded ProfileSet: %v", ps)

	return ps.Profiles, nil
}

func (self *PersistJSON) GetProfileById(version entity.Version) (*entity.Profile, error) {
	self.RLock()
	defer self.RUnlock()

	return nil, errors.New("PersistJSON.GetAllProfiles(): not implemented")
}

func (self *PersistJSON) AddProfile(profile *entity.Profile) error {
	profs, err := self.GetAllProfiles()
	log.Printf("PersistJSON.AddProfile(): profs: %v", profs)
	if err != nil {
		return err
	}

	// BUG(mistone): dedup!
	profs = append(profs, profile)

	// BUG(mistone): race!
	self.Lock()
	defer self.Unlock()

	f, err := os.OpenFile("data/data.json", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0)
	if err != nil {
		return err
	}
	defer f.Close()
	log.Printf("PersistJSON.AddProfile(): data/data.json opened for write")

	encoder := json.NewEncoder(bufio.NewWriter(f))
	log.Printf("PersistJSON.AddProfile(): made encoder")

	ps := profileSet{
		Profiles: profs,
	}
	err = encoder.Encode(&ps)
	if err != nil {
		return err
	}
	log.Printf("PersistJSON.AddProfile(): encoded ProfileSet: %v", ps)

	return nil
}

func (self *PersistJSON) GetAllReviews() ([]*entity.Review, error) {
	self.RLock()
	defer self.RUnlock()

	return nil, errors.New("PersistJSON.GetAllProfiles(): not implemented")
}

func (self *PersistJSON) GetReviewById(version entity.Version) (*entity.Review, error) {
	self.RLock()
	defer self.RUnlock()

	return nil, errors.New("PersistJSON.GetAllProfiles(): not implemented")
}

func (self *PersistJSON) AddReview(review *entity.Review) error {
	self.Lock()
	defer self.Unlock()

	return errors.New("PersistJSON.GetAllProfiles(): not implemented")
}
<commit_msg>Implement PersistJSON.GetProfileById(), based on PersistMem.GetProfileById().<commit_after>package persist

import (
	"bufio"
	"encoding/json"
	"entity"
	"errors"
	"fmt"
	"log"
	"os"
	"sync"
)

type PersistJSON struct {
	*sync.RWMutex
}

func NewPersistJSON() *PersistJSON {
	return &PersistJSON{
		RWMutex: &sync.RWMutex{},
	}
}

type profileSet struct {
	Profiles []*entity.Profile
}

func (self *PersistJSON) GetAllProfiles() ([]*entity.Profile, error) {
	self.RLock()
	defer self.RUnlock()

	f, err := os.Open("data/data.json")
	if err != nil {
		return nil, err
	}
	defer f.Close()
	log.Printf("PersistJSON.GetAllProfiles(): data/data.json opened")

	decoder := json.NewDecoder(bufio.NewReader(f))
	log.Printf("PersistJSON.GetAllProfiles(): made decoder")

	ps := profileSet{}
	err = decoder.Decode(&ps)
	if err != nil {
		return nil, err
	}
	log.Printf("PersistJSON.GetAllProfiles(): decoded ProfileSet: %v", ps)

	return ps.Profiles, nil
}

func (self *PersistJSON) GetProfileById(version entity.Version) (*entity.Profile, error) {
	self.RLock()
	defer self.RUnlock()

	profiles, err := self.GetAllProfiles()
	if err != nil {
		return nil, err
	}
	for _, prof := range profiles {
		if prof.Version == version {
			return prof, nil
		}
	}
	return nil, errors.New(fmt.Sprintf("PersistJSON.GetProfileById(): profile version '%v' not found", version))
}

func (self *PersistJSON) AddProfile(profile *entity.Profile) error {
	profs, err := self.GetAllProfiles()
	log.Printf("PersistJSON.AddProfile(): profs: %v", profs)
	if err != nil {
		return err
	}

	// BUG(mistone): dedup!
	profs = append(profs, profile)

	// BUG(mistone): race!
	self.Lock()
	defer self.Unlock()

	f, err := os.OpenFile("data/data.json", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0)
	if err != nil {
		return err
	}
	defer f.Close()
	log.Printf("PersistJSON.AddProfile(): data/data.json opened for write")

	encoder := json.NewEncoder(bufio.NewWriter(f))
	log.Printf("PersistJSON.AddProfile(): made encoder")

	ps := profileSet{
		Profiles: profs,
	}
	err = encoder.Encode(&ps)
	if err != nil {
		return err
	}
	log.Printf("PersistJSON.AddProfile(): encoded ProfileSet: %v", ps)

	return nil
}

func (self *PersistJSON) GetAllReviews() ([]*entity.Review, error) {
	self.RLock()
	defer self.RUnlock()

	return nil, errors.New("PersistJSON.GetAllProfiles(): not implemented")
}

func (self *PersistJSON) GetReviewById(version entity.Version) (*entity.Review, error) {
	self.RLock()
	defer self.RUnlock()

	return nil, errors.New("PersistJSON.GetAllProfiles(): not implemented")
}

func (self *PersistJSON) AddReview(review *entity.Review) error {
	self.Lock()
	defer self.Unlock()

	return errors.New("PersistJSON.GetAllProfiles(): not implemented")
}
<|endoftext|>


<commit_before>package command

import (
	"encoding/json"
	"flag"
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"

	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gocli"
	"github.com/funkygao/golib/gofmt"
	"github.com/funkygao/golib/top"
	"github.com/ryanuber/columnize"
)

type Haproxy struct {
	Ui  cli.Ui
	Cmd string

	zone string
}

func (this *Haproxy) Run(args []string) (exitCode int) {
	var topMode bool
	cmdFlags := flag.NewFlagSet("haproxy", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&this.zone, "z", ctx.DefaultZone(), "")
	cmdFlags.BoolVar(&topMode, "top", true, "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	zone := ctx.Zone(this.zone)
	if topMode {
		header, _ := this.getStats(zone.HaProxyStatsUri[0])
		t := top.New(header, "%8s %4s %21s %21s %9s %6s %8s %12s %8s %8s %7s %7s %14s %6s")
		go func() {
			for {
				rows := make([]string, 0)
				for _, uri := range zone.HaProxyStatsUri {
					_, r := this.getStats(uri)
					rows = append(rows, r...)
				}
				t.Refresh(rows)

				time.Sleep(time.Second * 3)
			}
		}()
		if err := t.Start(); err != nil {
			panic(err)
		}
	} else {
		for _, uri := range zone.HaProxyStatsUri {
			this.fetchStats(uri)
		}
	}

	return
}

func (*Haproxy) Synopsis() string {
	return "Query ehaproxy cluster for load stats"
}

func (this *Haproxy) getStats(statsUri string) (header string, rows []string) {
	client := http.Client{Timeout: time.Second * 30}
	resp, err := client.Get(statsUri)
	swallow(err)
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		swallow(fmt.Errorf("fetch[%s] stats got status: %d", resp.StatusCode))
	}

	var records map[string]map[string]int64
	reader := json.NewDecoder(resp.Body)
	err = reader.Decode(&records)
	swallow(err)

	u, err := url.Parse(statsUri)
	swallow(err)
	var shortHostname string
	if strings.Contains(u.Host, ":") {
		u.Host = u.Host[:strings.Index(u.Host, ":")]
	}
	tuples := strings.SplitN(u.Host, ".", 4)
	if len(tuples) < 4 {
		shortHostname = u.Host
	} else {
		shortHostname = tuples[3]
	}
	if len(shortHostname) > 8 {
		shortHostname = shortHostname[:8]
	}

	sortedSvcs := make([]string, 0)
	for svc, _ := range records {
		sortedSvcs = append(sortedSvcs, svc)
	}
	sort.Strings(sortedSvcs)

	sortedCols := make([]string, 0)
	for k, _ := range records["pub"] {
		sortedCols = append(sortedCols, k)
	}
	sort.Strings(sortedCols)

	header = strings.Join(append([]string{"host", "svc"}, sortedCols...), "|")
	for _, svc := range sortedSvcs {
		stats := records[svc]

		var vals = []string{shortHostname, svc}
		for _, k := range sortedCols {
			v := stats[k]

			vals = append(vals, gofmt.Comma(v))
		}

		rows = append(rows, strings.Join(vals, "|"))
	}

	return
}

func (this *Haproxy) fetchStats(statsUri string) {
	client := http.Client{Timeout: time.Second * 30}
	resp, err := client.Get(statsUri)
	swallow(err)
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		swallow(fmt.Errorf("fetch[%s] stats got status: %d", resp.StatusCode))
	}

	var records map[string]map[string]int64
	reader := json.NewDecoder(resp.Body)
	err = reader.Decode(&records)
	swallow(err)

	u, err := url.Parse(statsUri)
	swallow(err)
	this.Ui.Info(u.Host)

	sortedSvcs := make([]string, 0)
	for svc, _ := range records {
		sortedSvcs = append(sortedSvcs, svc)
	}
	sort.Strings(sortedSvcs)

	sortedCols := make([]string, 0)
	for k, _ := range records["pub"] {
		sortedCols = append(sortedCols, k)
	}
	sort.Strings(sortedCols)

	lines := []string{strings.Join(append([]string{"svc"}, sortedCols...), "|")}
	for _, svc := range sortedSvcs {
		stats := records[svc]

		var vals = []string{svc}
		for _, k := range sortedCols {
			v := stats[k]

			vals = append(vals, gofmt.Comma(v))
		}

		lines = append(lines, strings.Join(vals, "|"))
	}

	this.Ui.Output(columnize.SimpleFormat(lines))
}

func (this *Haproxy) Help() string {
	help := fmt.Sprintf(`
Usage: %s haproxy [options]

    %s

Options:

    -z zone

    -top
      Top mode

`, this.Cmd, this.Synopsis())
	return strings.TrimSpace(help)
}
<commit_msg>filter by svc name<commit_after>package command

import (
	"encoding/json"
	"flag"
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"

	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gocli"
	"github.com/funkygao/golib/gofmt"
	"github.com/funkygao/golib/top"
	"github.com/ryanuber/columnize"
)

type Haproxy struct {
	Ui  cli.Ui
	Cmd string

	zone string
	svc  string
}

func (this *Haproxy) Run(args []string) (exitCode int) {
	var topMode bool
	cmdFlags := flag.NewFlagSet("haproxy", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&this.zone, "z", ctx.DefaultZone(), "")
	cmdFlags.StringVar(&this.svc, "svc", "", "")
	cmdFlags.BoolVar(&topMode, "top", true, "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	zone := ctx.Zone(this.zone)
	if topMode {
		header, _ := this.getStats(zone.HaProxyStatsUri[0])
		t := top.New(header, "%8s %4s %21s %21s %9s %6s %8s %12s %8s %8s %7s %7s %14s %6s")
		go func() {
			for {
				rows := make([]string, 0)
				for _, uri := range zone.HaProxyStatsUri {
					_, r := this.getStats(uri)
					rows = append(rows, r...)
				}
				t.Refresh(rows)

				time.Sleep(time.Second * 3)
			}
		}()
		if err := t.Start(); err != nil {
			panic(err)
		}
	} else {
		for _, uri := range zone.HaProxyStatsUri {
			this.fetchStats(uri)
		}
	}

	return
}

func (*Haproxy) Synopsis() string {
	return "Query ehaproxy cluster for load stats"
}

func (this *Haproxy) getStats(statsUri string) (header string, rows []string) {
	client := http.Client{Timeout: time.Second * 30}
	resp, err := client.Get(statsUri)
	swallow(err)
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		swallow(fmt.Errorf("fetch[%s] stats got status: %d", resp.StatusCode))
	}

	var records map[string]map[string]int64
	reader := json.NewDecoder(resp.Body)
	err = reader.Decode(&records)
	swallow(err)

	u, err := url.Parse(statsUri)
	swallow(err)
	var shortHostname string
	if strings.Contains(u.Host, ":") {
		u.Host = u.Host[:strings.Index(u.Host, ":")]
	}
	tuples := strings.SplitN(u.Host, ".", 4)
	if len(tuples) < 4 {
		shortHostname = u.Host
	} else {
		shortHostname = tuples[3]
	}
	if len(shortHostname) > 8 {
		shortHostname = shortHostname[:8]
	}

	sortedSvcs := make([]string, 0)
	for svc, _ := range records {
		sortedSvcs = append(sortedSvcs, svc)
	}
	sort.Strings(sortedSvcs)

	sortedCols := make([]string, 0)
	for k, _ := range records["pub"] {
		sortedCols = append(sortedCols, k)
	}
	sort.Strings(sortedCols)

	header = strings.Join(append([]string{"host", "svc"}, sortedCols...), "|")
	for _, svc := range sortedSvcs {
		if this.svc != "" && this.svc != svc {
			continue
		}

		stats := records[svc]
		var vals = []string{shortHostname, svc}
		for _, k := range sortedCols {
			v := stats[k]

			vals = append(vals, gofmt.Comma(v))
		}

		rows = append(rows, strings.Join(vals, "|"))
	}

	return
}

func (this *Haproxy) fetchStats(statsUri string) {
	client := http.Client{Timeout: time.Second * 30}
	resp, err := client.Get(statsUri)
	swallow(err)
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		swallow(fmt.Errorf("fetch[%s] stats got status: %d", resp.StatusCode))
	}

	var records map[string]map[string]int64
	reader := json.NewDecoder(resp.Body)
	err = reader.Decode(&records)
	swallow(err)

	u, err := url.Parse(statsUri)
	swallow(err)
	this.Ui.Info(u.Host)

	sortedSvcs := make([]string, 0)
	for svc, _ := range records {
		sortedSvcs = append(sortedSvcs, svc)
	}
	sort.Strings(sortedSvcs)

	sortedCols := make([]string, 0)
	for k, _ := range records["pub"] {
		sortedCols = append(sortedCols, k)
	}
	sort.Strings(sortedCols)

	lines := []string{strings.Join(append([]string{"svc"}, sortedCols...), "|")}
	for _, svc := range sortedSvcs {
		stats := records[svc]

		var vals = []string{svc}
		for _, k := range sortedCols {
			v := stats[k]

			vals = append(vals, gofmt.Comma(v))
		}

		lines = append(lines, strings.Join(vals, "|"))
	}

	this.Ui.Output(columnize.SimpleFormat(lines))
}

func (this *Haproxy) Help() string {
	help := fmt.Sprintf(`
Usage: %s haproxy [options]

    %s

Options:

    -z zone

    -svc name
      Filter by svc name

    -top
      Top mode

`, this.Cmd, this.Synopsis())
	return strings.TrimSpace(help)
}
<|endoftext|>