<commit_before>package agent

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"log"
	"net"
	"net/http"
	"net/http/pprof"
	"strconv"
	"time"

	"github.com/NYTimes/gziphandler"
	"github.com/hashicorp/nomad/helper/tlsutil"
	"github.com/hashicorp/nomad/nomad/structs"
	"github.com/ugorji/go/codec"
)

const (
	// ErrInvalidMethod is used if the HTTP method is not supported
	ErrInvalidMethod = "Invalid method"

	// scadaHTTPAddr is the address associated with the
	// HTTPServer. When populating an ACL token for a request,
	// this is checked to switch between the ACLToken and
	// AtlasACLToken
	scadaHTTPAddr = "SCADA"
)

var (
	// jsonHandle and jsonHandlePretty are the codec handles to JSON encode
	// structs. The pretty handle will add indents for easier human consumption.
	jsonHandle = &codec.JsonHandle{
		HTMLCharsAsIs: true,
	}
	jsonHandlePretty = &codec.JsonHandle{
		HTMLCharsAsIs: true,
		Indent:        4,
	}
)

// HTTPServer is used to wrap an Agent and expose it over an HTTP interface
type HTTPServer struct {
	agent    *Agent
	mux      *http.ServeMux
	listener net.Listener
	logger   *log.Logger
	addr     string
}

// NewHTTPServer starts new HTTP server over the agent
func NewHTTPServer(agent *Agent, config *Config) (*HTTPServer, error) {
	// Start the listener
	lnAddr, err := net.ResolveTCPAddr("tcp", config.normalizedAddrs.HTTP)
	if err != nil {
		return nil, err
	}
	ln, err := config.Listener("tcp", lnAddr.IP.String(), lnAddr.Port)
	if err != nil {
		return nil, fmt.Errorf("failed to start HTTP listener: %v", err)
	}

	// If TLS is enabled, wrap the listener with a TLS listener
	if config.TLSConfig.EnableHTTP {
		tlsConf := &tlsutil.Config{
			VerifyIncoming:       false,
			VerifyOutgoing:       true,
			VerifyServerHostname: config.TLSConfig.VerifyServerHostname,
			CAFile:               config.TLSConfig.CAFile,
			CertFile:             config.TLSConfig.CertFile,
			KeyFile:              config.TLSConfig.KeyFile,
		}
		tlsConfig, err := tlsConf.IncomingTLSConfig()
		if err != nil {
			return nil, err
		}
		ln = tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, tlsConfig)
	}

	// Create the mux
	mux := http.NewServeMux()

	// Create the server
	srv := &HTTPServer{
		agent:    agent,
		mux:      mux,
		listener: ln,
		logger:   agent.logger,
		addr:     ln.Addr().String(),
	}
	srv.registerHandlers(config.EnableDebug)

	// Start the server
	go http.Serve(ln, gziphandler.GzipHandler(mux))
	return srv, nil
}

// newScadaHttp creates a new HTTP server wrapping the SCADA
// listener such that HTTP calls can be sent from the brokers.
func newScadaHttp(agent *Agent, list net.Listener) *HTTPServer {
	// Create the mux
	mux := http.NewServeMux()

	// Create the server
	srv := &HTTPServer{
		agent:    agent,
		mux:      mux,
		listener: list,
		logger:   agent.logger,
		addr:     scadaHTTPAddr,
	}
	srv.registerHandlers(false) // Never allow debug for SCADA

	// Start the server
	go http.Serve(list, gziphandler.GzipHandler(mux))
	return srv
}

// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections. It's used by NewHttpServer so
// dead TCP connections eventually go away.
type tcpKeepAliveListener struct {
	*net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
	tc, err := ln.AcceptTCP()
	if err != nil {
		return
	}
	tc.SetKeepAlive(true)
	tc.SetKeepAlivePeriod(30 * time.Second)
	return tc, nil
}

// Shutdown is used to shutdown the HTTP server
func (s *HTTPServer) Shutdown() {
	if s != nil {
		s.logger.Printf("[DEBUG] http: Shutting down http server")
		s.listener.Close()
	}
}

// registerHandlers is used to attach our handlers to the mux
func (s *HTTPServer) registerHandlers(enableDebug bool) {
	s.mux.HandleFunc("/v1/jobs", s.wrap(s.JobsRequest))
	s.mux.HandleFunc("/v1/job/", s.wrap(s.JobSpecificRequest))

	s.mux.HandleFunc("/v1/nodes", s.wrap(s.NodesRequest))
	s.mux.HandleFunc("/v1/node/", s.wrap(s.NodeSpecificRequest))

	s.mux.HandleFunc("/v1/allocations", s.wrap(s.AllocsRequest))
	s.mux.HandleFunc("/v1/allocation/", s.wrap(s.AllocSpecificRequest))

	s.mux.HandleFunc("/v1/evaluations", s.wrap(s.EvalsRequest))
	s.mux.HandleFunc("/v1/evaluation/", s.wrap(s.EvalSpecificRequest))

	s.mux.HandleFunc("/v1/client/fs/", s.wrap(s.FsRequest))
	s.mux.HandleFunc("/v1/client/stats", s.wrap(s.ClientStatsRequest))
	s.mux.HandleFunc("/v1/client/allocation/", s.wrap(s.ClientAllocRequest))
	s.mux.HandleFunc("/v1/client/gc", s.wrap(s.ClientGCRequest))

	s.mux.HandleFunc("/v1/agent/self", s.wrap(s.AgentSelfRequest))
	s.mux.HandleFunc("/v1/agent/join", s.wrap(s.AgentJoinRequest))
	s.mux.HandleFunc("/v1/agent/members", s.wrap(s.AgentMembersRequest))
	s.mux.HandleFunc("/v1/agent/force-leave", s.wrap(s.AgentForceLeaveRequest))
	s.mux.HandleFunc("/v1/agent/servers", s.wrap(s.AgentServersRequest))
	s.mux.HandleFunc("/v1/agent/keyring/", s.wrap(s.KeyringOperationRequest))

	s.mux.HandleFunc("/v1/validate/job", s.wrap(s.ValidateJobRequest))

	s.mux.HandleFunc("/v1/regions", s.wrap(s.RegionListRequest))

	s.mux.HandleFunc("/v1/status/leader", s.wrap(s.StatusLeaderRequest))
	s.mux.HandleFunc("/v1/status/peers", s.wrap(s.StatusPeersRequest))

	s.mux.HandleFunc("/v1/operator/", s.wrap(s.OperatorRequest))

	s.mux.HandleFunc("/v1/system/gc", s.wrap(s.GarbageCollectRequest))
	s.mux.HandleFunc("/v1/system/reconcile/summaries", s.wrap(s.ReconcileJobSummaries))

	if enableDebug {
		s.mux.HandleFunc("/debug/pprof/", pprof.Index)
		s.mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
		s.mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
		s.mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
	}
}

// HTTPCodedError is used to provide the HTTP error code
type HTTPCodedError interface {
	error
	Code() int
}

func CodedError(c int, s string) HTTPCodedError {
	return &codedError{s, c}
}

type codedError struct {
	s    string
	code int
}

func (e *codedError) Error() string {
	return e.s
}

func (e *codedError) Code() int {
	return e.code
}

// wrap is used to wrap functions to make them more convenient
func (s *HTTPServer) wrap(handler func(resp http.ResponseWriter, req *http.Request) (interface{}, error)) func(resp http.ResponseWriter, req *http.Request) {
	f := func(resp http.ResponseWriter, req *http.Request) {
		setHeaders(resp, s.agent.config.HTTPAPIResponseHeaders)
		// Invoke the handler
		reqURL := req.URL.String()
		start := time.Now()
		defer func() {
			s.logger.Printf("[DEBUG] http: Request %v (%v)", reqURL, time.Now().Sub(start))
		}()
		obj, err := handler(resp, req)

		// Check for an error
	HAS_ERR:
		if err != nil {
			s.logger.Printf("[ERR] http: Request %v, error: %v", reqURL, err)
			code := 500
			if http, ok := err.(HTTPCodedError); ok {
				code = http.Code()
			}
			resp.WriteHeader(code)
			resp.Write([]byte(err.Error()))
			return
		}

		prettyPrint := false
		if v, ok := req.URL.Query()["pretty"]; ok {
			if len(v) > 0 && (len(v[0]) == 0 || v[0] != "0") {
				prettyPrint = true
			}
		}

		// Write out the JSON object
		if obj != nil {
			var buf bytes.Buffer
			if prettyPrint {
				enc := codec.NewEncoder(&buf, jsonHandlePretty)
				err = enc.Encode(obj)
				if err == nil {
					buf.Write([]byte("\n"))
				}
			} else {
				enc := codec.NewEncoder(&buf, jsonHandle)
				err = enc.Encode(obj)
			}
			if err != nil {
				goto HAS_ERR
			}
			resp.Header().Set("Content-Type", "application/json")
			resp.Write(buf.Bytes())
		}
	}
	return f
}

// decodeBody is used to decode a JSON request body
func decodeBody(req *http.Request, out interface{}) error {
	dec := json.NewDecoder(req.Body)
	return dec.Decode(&out)
}

// setIndex is used to set the index response header
func setIndex(resp http.ResponseWriter, index uint64) {
	resp.Header().Set("X-Nomad-Index", strconv.FormatUint(index, 10))
}

// setKnownLeader is used to set the known leader header
func setKnownLeader(resp http.ResponseWriter, known bool) {
	s := "true"
	if !known {
		s = "false"
	}
	resp.Header().Set("X-Nomad-KnownLeader", s)
}

// setLastContact is used to set the last contact header
func setLastContact(resp http.ResponseWriter, last time.Duration) {
	lastMsec := uint64(last / time.Millisecond)
	resp.Header().Set("X-Nomad-LastContact", strconv.FormatUint(lastMsec, 10))
}

// setMeta is used to set the query response meta data
func setMeta(resp http.ResponseWriter, m *structs.QueryMeta) {
	setIndex(resp, m.Index)
	setLastContact(resp, m.LastContact)
	setKnownLeader(resp, m.KnownLeader)
}

// setHeaders is used to set canonical response header fields
func setHeaders(resp http.ResponseWriter, headers map[string]string) {
	for field, value := range headers {
		resp.Header().Set(http.CanonicalHeaderKey(field), value)
	}
}

// parseWait is used to parse the ?wait and ?index query params
// Returns true on error
func parseWait(resp http.ResponseWriter, req *http.Request, b *structs.QueryOptions) bool {
	query := req.URL.Query()
	if wait := query.Get("wait"); wait != "" {
		dur, err := time.ParseDuration(wait)
		if err != nil {
			resp.WriteHeader(400)
			resp.Write([]byte("Invalid wait time"))
			return true
		}
		b.MaxQueryTime = dur
	}
	if idx := query.Get("index"); idx != "" {
		index, err := strconv.ParseUint(idx, 10, 64)
		if err != nil {
			resp.WriteHeader(400)
			resp.Write([]byte("Invalid index"))
			return true
		}
		b.MinQueryIndex = index
	}
	return false
}

// parseConsistency is used to parse the ?stale query params.
func parseConsistency(req *http.Request, b *structs.QueryOptions) {
	query := req.URL.Query()
	if _, ok := query["stale"]; ok {
		b.AllowStale = true
	}
}

// parsePrefix is used to parse the ?prefix query param
func parsePrefix(req *http.Request, b *structs.QueryOptions) {
	query := req.URL.Query()
	if prefix := query.Get("prefix"); prefix != "" {
		b.Prefix = prefix
	}
}

// parseRegion is used to parse the ?region query param
func (s *HTTPServer) parseRegion(req *http.Request, r *string) {
	if other := req.URL.Query().Get("region"); other != "" {
		*r = other
	} else if *r == "" {
		*r = s.agent.config.Region
	}
}

// parse is a convenience method for endpoints that need to parse multiple flags
func (s *HTTPServer) parse(resp http.ResponseWriter, req *http.Request, r *string, b *structs.QueryOptions) bool {
	s.parseRegion(req, r)
	parseConsistency(req, b)
	parsePrefix(req, b)
	return parseWait(resp, req, b)
}
<commit_msg>Copy TLSConfig verification flags in server create<commit_after>package agent

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"log"
	"net"
	"net/http"
	"net/http/pprof"
	"strconv"
	"time"

	"github.com/NYTimes/gziphandler"
	"github.com/hashicorp/nomad/helper/tlsutil"
	"github.com/hashicorp/nomad/nomad/structs"
	"github.com/ugorji/go/codec"
)

const (
	// ErrInvalidMethod is used if the HTTP method is not supported
	ErrInvalidMethod = "Invalid method"

	// scadaHTTPAddr is the address associated with the
	// HTTPServer. When populating an ACL token for a request,
	// this is checked to switch between the ACLToken and
	// AtlasACLToken
	scadaHTTPAddr = "SCADA"
)

var (
	// jsonHandle and jsonHandlePretty are the codec handles to JSON encode
	// structs. The pretty handle will add indents for easier human consumption.
	jsonHandle = &codec.JsonHandle{
		HTMLCharsAsIs: true,
	}
	jsonHandlePretty = &codec.JsonHandle{
		HTMLCharsAsIs: true,
		Indent:        4,
	}
)

// HTTPServer is used to wrap an Agent and expose it over an HTTP interface
type HTTPServer struct {
	agent    *Agent
	mux      *http.ServeMux
	listener net.Listener
	logger   *log.Logger
	addr     string
}

// NewHTTPServer starts new HTTP server over the agent
func NewHTTPServer(agent *Agent, config *Config) (*HTTPServer, error) {
	// Start the listener
	lnAddr, err := net.ResolveTCPAddr("tcp", config.normalizedAddrs.HTTP)
	if err != nil {
		return nil, err
	}
	ln, err := config.Listener("tcp", lnAddr.IP.String(), lnAddr.Port)
	if err != nil {
		return nil, fmt.Errorf("failed to start HTTP listener: %v", err)
	}

	// If TLS is enabled, wrap the listener with a TLS listener
	if config.TLSConfig.EnableHTTP {
		tlsConf := &tlsutil.Config{
			VerifyIncoming:       config.TLSConfig.VerifyIncoming,
			VerifyOutgoing:       config.TLSConfig.VerifyOutgoing,
			VerifyServerHostname: config.TLSConfig.VerifyServerHostname,
			CAFile:               config.TLSConfig.CAFile,
			CertFile:             config.TLSConfig.CertFile,
			KeyFile:              config.TLSConfig.KeyFile,
		}
		tlsConfig, err := tlsConf.IncomingTLSConfig()
		if err != nil {
			return nil, err
		}
		ln = tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, tlsConfig)
	}

	// Create the mux
	mux := http.NewServeMux()

	// Create the server
	srv := &HTTPServer{
		agent:    agent,
		mux:      mux,
		listener: ln,
		logger:   agent.logger,
		addr:     ln.Addr().String(),
	}
	srv.registerHandlers(config.EnableDebug)

	// Start the server
	go http.Serve(ln, gziphandler.GzipHandler(mux))
	return srv, nil
}

// newScadaHttp creates a new HTTP server wrapping the SCADA
// listener such that HTTP calls can be sent from the brokers.
func newScadaHttp(agent *Agent, list net.Listener) *HTTPServer {
	// Create the mux
	mux := http.NewServeMux()

	// Create the server
	srv := &HTTPServer{
		agent:    agent,
		mux:      mux,
		listener: list,
		logger:   agent.logger,
		addr:     scadaHTTPAddr,
	}
	srv.registerHandlers(false) // Never allow debug for SCADA

	// Start the server
	go http.Serve(list, gziphandler.GzipHandler(mux))
	return srv
}

// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections. It's used by NewHttpServer so
// dead TCP connections eventually go away.
type tcpKeepAliveListener struct {
	*net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
	tc, err := ln.AcceptTCP()
	if err != nil {
		return
	}
	tc.SetKeepAlive(true)
	tc.SetKeepAlivePeriod(30 * time.Second)
	return tc, nil
}

// Shutdown is used to shutdown the HTTP server
func (s *HTTPServer) Shutdown() {
	if s != nil {
		s.logger.Printf("[DEBUG] http: Shutting down http server")
		s.listener.Close()
	}
}

// registerHandlers is used to attach our handlers to the mux
func (s *HTTPServer) registerHandlers(enableDebug bool) {
	s.mux.HandleFunc("/v1/jobs", s.wrap(s.JobsRequest))
	s.mux.HandleFunc("/v1/job/", s.wrap(s.JobSpecificRequest))

	s.mux.HandleFunc("/v1/nodes", s.wrap(s.NodesRequest))
	s.mux.HandleFunc("/v1/node/", s.wrap(s.NodeSpecificRequest))

	s.mux.HandleFunc("/v1/allocations", s.wrap(s.AllocsRequest))
	s.mux.HandleFunc("/v1/allocation/", s.wrap(s.AllocSpecificRequest))

	s.mux.HandleFunc("/v1/evaluations", s.wrap(s.EvalsRequest))
	s.mux.HandleFunc("/v1/evaluation/", s.wrap(s.EvalSpecificRequest))

	s.mux.HandleFunc("/v1/client/fs/", s.wrap(s.FsRequest))
	s.mux.HandleFunc("/v1/client/stats", s.wrap(s.ClientStatsRequest))
	s.mux.HandleFunc("/v1/client/allocation/", s.wrap(s.ClientAllocRequest))
	s.mux.HandleFunc("/v1/client/gc", s.wrap(s.ClientGCRequest))

	s.mux.HandleFunc("/v1/agent/self", s.wrap(s.AgentSelfRequest))
	s.mux.HandleFunc("/v1/agent/join", s.wrap(s.AgentJoinRequest))
	s.mux.HandleFunc("/v1/agent/members", s.wrap(s.AgentMembersRequest))
	s.mux.HandleFunc("/v1/agent/force-leave", s.wrap(s.AgentForceLeaveRequest))
	s.mux.HandleFunc("/v1/agent/servers", s.wrap(s.AgentServersRequest))
	s.mux.HandleFunc("/v1/agent/keyring/", s.wrap(s.KeyringOperationRequest))

	s.mux.HandleFunc("/v1/validate/job", s.wrap(s.ValidateJobRequest))

	s.mux.HandleFunc("/v1/regions", s.wrap(s.RegionListRequest))

	s.mux.HandleFunc("/v1/status/leader", s.wrap(s.StatusLeaderRequest))
	s.mux.HandleFunc("/v1/status/peers", s.wrap(s.StatusPeersRequest))

	s.mux.HandleFunc("/v1/operator/", s.wrap(s.OperatorRequest))

	s.mux.HandleFunc("/v1/system/gc", s.wrap(s.GarbageCollectRequest))
	s.mux.HandleFunc("/v1/system/reconcile/summaries", s.wrap(s.ReconcileJobSummaries))

	if enableDebug {
		s.mux.HandleFunc("/debug/pprof/", pprof.Index)
		s.mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
		s.mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
		s.mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
	}
}

// HTTPCodedError is used to provide the HTTP error code
type HTTPCodedError interface {
	error
	Code() int
}

func CodedError(c int, s string) HTTPCodedError {
	return &codedError{s, c}
}

type codedError struct {
	s    string
	code int
}

func (e *codedError) Error() string {
	return e.s
}

func (e *codedError) Code() int {
	return e.code
}

// wrap is used to wrap functions to make them more convenient
func (s *HTTPServer) wrap(handler func(resp http.ResponseWriter, req *http.Request) (interface{}, error)) func(resp http.ResponseWriter, req *http.Request) {
	f := func(resp http.ResponseWriter, req *http.Request) {
		setHeaders(resp, s.agent.config.HTTPAPIResponseHeaders)
		// Invoke the handler
		reqURL := req.URL.String()
		start := time.Now()
		defer func() {
			s.logger.Printf("[DEBUG] http: Request %v (%v)", reqURL, time.Now().Sub(start))
		}()
		obj, err := handler(resp, req)

		// Check for an error
	HAS_ERR:
		if err != nil {
			s.logger.Printf("[ERR] http: Request %v, error: %v", reqURL, err)
			code := 500
			if http, ok := err.(HTTPCodedError); ok {
				code = http.Code()
			}
			resp.WriteHeader(code)
			resp.Write([]byte(err.Error()))
			return
		}

		prettyPrint := false
		if v, ok := req.URL.Query()["pretty"]; ok {
			if len(v) > 0 && (len(v[0]) == 0 || v[0] != "0") {
				prettyPrint = true
			}
		}

		// Write out the JSON object
		if obj != nil {
			var buf bytes.Buffer
			if prettyPrint {
				enc := codec.NewEncoder(&buf, jsonHandlePretty)
				err = enc.Encode(obj)
				if err == nil {
					buf.Write([]byte("\n"))
				}
			} else {
				enc := codec.NewEncoder(&buf, jsonHandle)
				err = enc.Encode(obj)
			}
			if err != nil {
				goto HAS_ERR
			}
			resp.Header().Set("Content-Type", "application/json")
			resp.Write(buf.Bytes())
		}
	}
	return f
}

// decodeBody is used to decode a JSON request body
func decodeBody(req *http.Request, out interface{}) error {
	dec := json.NewDecoder(req.Body)
	return dec.Decode(&out)
}

// setIndex is used to set the index response header
func setIndex(resp http.ResponseWriter, index uint64) {
	resp.Header().Set("X-Nomad-Index", strconv.FormatUint(index, 10))
}

// setKnownLeader is used to set the known leader header
func setKnownLeader(resp http.ResponseWriter, known bool) {
	s := "true"
	if !known {
		s = "false"
	}
	resp.Header().Set("X-Nomad-KnownLeader", s)
}

// setLastContact is used to set the last contact header
func setLastContact(resp http.ResponseWriter, last time.Duration) {
	lastMsec := uint64(last / time.Millisecond)
	resp.Header().Set("X-Nomad-LastContact", strconv.FormatUint(lastMsec, 10))
}

// setMeta is used to set the query response meta data
func setMeta(resp http.ResponseWriter, m *structs.QueryMeta) {
	setIndex(resp, m.Index)
	setLastContact(resp, m.LastContact)
	setKnownLeader(resp, m.KnownLeader)
}

// setHeaders is used to set canonical response header fields
func setHeaders(resp http.ResponseWriter, headers map[string]string) {
	for field, value := range headers {
		resp.Header().Set(http.CanonicalHeaderKey(field), value)
	}
}

// parseWait is used to parse the ?wait and ?index query params
// Returns true on error
func parseWait(resp http.ResponseWriter, req *http.Request, b *structs.QueryOptions) bool {
	query := req.URL.Query()
	if wait := query.Get("wait"); wait != "" {
		dur, err := time.ParseDuration(wait)
		if err != nil {
			resp.WriteHeader(400)
			resp.Write([]byte("Invalid wait time"))
			return true
		}
		b.MaxQueryTime = dur
	}
	if idx := query.Get("index"); idx != "" {
		index, err := strconv.ParseUint(idx, 10, 64)
		if err != nil {
			resp.WriteHeader(400)
			resp.Write([]byte("Invalid index"))
			return true
		}
		b.MinQueryIndex = index
	}
	return false
}

// parseConsistency is used to parse the ?stale query params.
func parseConsistency(req *http.Request, b *structs.QueryOptions) {
	query := req.URL.Query()
	if _, ok := query["stale"]; ok {
		b.AllowStale = true
	}
}

// parsePrefix is used to parse the ?prefix query param
func parsePrefix(req *http.Request, b *structs.QueryOptions) {
	query := req.URL.Query()
	if prefix := query.Get("prefix"); prefix != "" {
		b.Prefix = prefix
	}
}

// parseRegion is used to parse the ?region query param
func (s *HTTPServer) parseRegion(req *http.Request, r *string) {
	if other := req.URL.Query().Get("region"); other != "" {
		*r = other
	} else if *r == "" {
		*r = s.agent.config.Region
	}
}

// parse is a convenience method for endpoints that need to parse multiple flags
func (s *HTTPServer) parse(resp http.ResponseWriter, req *http.Request, r *string, b *structs.QueryOptions) bool {
	s.parseRegion(req, r)
	parseConsistency(req, b)
	parsePrefix(req, b)
	return parseWait(resp, req, b)
}
<|endoftext|>


<commit_before>package command

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"strings"

	yaml "gopkg.in/yaml.v2"
)

type VsmCreateCommand struct {
	// To control this CLI's display
	M Meta
	// OS command to execute; <optional>
	Cmd     *exec.Cmd
	vsmname string
	size    string
}

type VsmSpec struct {
	Kind       string `yaml:"kind"`
	APIVersion string `yaml:"apiVersion"`
	Metadata   struct {
		Name string `yaml:"name"`
	} `yaml:"metadata"`
	Spec struct {
		AccessModes []string `yaml:"accessModes"`
		Resources   struct {
			Requests struct {
				Storage string `yaml:"storage"`
			} `yaml:"requests"`
		} `yaml:"resources"`
	} `yaml:"spec"`
}

func (c *VsmCreateCommand) Help() string {
	helpText := `
Usage: maya vsm-create [options] <path>

  Creates a new VSM using the specification located at <path>.

  On successful vsm creation submission and scheduling, exit code 0 will be
  returned. If there are placement issues encountered
  (unsatisfiable constraints, resource exhaustion, etc), then the
  exit code will be 2. Any other errors, including client connection
  issues or internal errors, are indicated by exit code 1.

VSM Create Options:
  -name
    Name of the vsm
  -size
    Provisioning size of the vsm(defualt is 5G)
`
	return strings.TrimSpace(helpText)
}

func (c *VsmCreateCommand) Synopsis() string {
	return "Creates a new VSM"
}

// The logic of this function can be understood by understanding
// the help text defined earlier.
func (c *VsmCreateCommand) Run(args []string) int {

	var op int

	flags := c.M.FlagSet("vsm-create", FlagSetClient)
	flags.Usage = func() { c.M.Ui.Output(c.Help()) }
	flags.StringVar(&c.vsmname, "name", "", "")
	flags.StringVar(&c.size, "size", "5", "")

	if err := flags.Parse(args); err != nil {
		return 1
	}

	// specs file is mandatory
	args = flags.Args()
	if len(args) != 1 && len(strings.TrimSpace(c.vsmname)) == 0 {
		c.M.Ui.Error(c.Help())
		return 1
	}
	if len(args) == 1 {
		if c.Cmd == nil {
			// sub command
			args = append([]string{string(NomadRun)}, args...)

			// main command; append sub cmd to main cmd
			c.Cmd = exec.Command(string(ExecNomad), args...)
		}

		ic := &InternalCommand{
			Cmd: c.Cmd,
			Ui:  c.M.Ui,
		}

		if op = ic.Execute(); 0 != op {
			c.M.Ui.Error("Error creating vsm")
			return op
		}
		return 1
	}
	if c.vsmname != " " {
		err := CreateApiVsm(c.vsmname, c.size)
		if err != nil {
			fmt.Println("Error Creating Vsm")
		}
	}
	return op
}

// Function to create the Vsm through a API call to m-apiserver
func CreateApiVsm(vname string, size string) error {

	var vs VsmSpec

	addr := os.Getenv("MAPI_ADDR")
	if addr == "" {
		err := errors.New("MAPI_ADDR environment variable not set")
		fmt.Println(err)
		return err
	}
	url := addr + "/latest/volumes/"

	vs.Metadata.Name = vname
	vs.Spec.Resources.Requests.Storage = size

	//Marshal serializes the value provided into a YAML document
	yamlValue, _ := yaml.Marshal(vs)

	req, err := http.NewRequest("POST", url, bytes.NewBuffer(yamlValue))

	req.Header.Add("Content-Type", "application/yaml")

	c := &http.Client{
		Timeout: timeout,
	}
	resp, err := c.Do(req)
	if err != nil {
		fmt.Printf("http.Do() error: %v\n", err)
		return err
	}
	defer resp.Body.Close()

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Printf("ioutil.ReadAll() error: %v\n", err)
		return err
	}

	fmt.Printf("VSM Successfully Created:\n%v\n", string(data))

	return err
}
<commit_msg>fixing typo<commit_after>package command

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"strings"

	yaml "gopkg.in/yaml.v2"
)

type VsmCreateCommand struct {
	// To control this CLI's display
	M Meta
	// OS command to execute; <optional>
	Cmd     *exec.Cmd
	vsmname string
	size    string
}

type VsmSpec struct {
	Kind       string `yaml:"kind"`
	APIVersion string `yaml:"apiVersion"`
	Metadata   struct {
		Name string `yaml:"name"`
	} `yaml:"metadata"`
	Spec struct {
		AccessModes []string `yaml:"accessModes"`
		Resources   struct {
			Requests struct {
				Storage string `yaml:"storage"`
			} `yaml:"requests"`
		} `yaml:"resources"`
	} `yaml:"spec"`
}

func (c *VsmCreateCommand) Help() string {
	helpText := `
Usage: maya vsm-create [options] <path>

  Creates a new VSM using the specification located at <path>.

  On successful vsm creation submission and scheduling, exit code 0 will be
  returned. If there are placement issues encountered
  (unsatisfiable constraints, resource exhaustion, etc), then the
  exit code will be 2. Any other errors, including client connection
  issues or internal errors, are indicated by exit code 1.

VSM Create Options:
  -name
    Name of the vsm
  -size
    Provisioning size of the vsm(default is 5G)
`
	return strings.TrimSpace(helpText)
}

func (c *VsmCreateCommand) Synopsis() string {
	return "Creates a new VSM"
}

// The logic of this function can be understood by understanding
// the help text defined earlier.
func (c *VsmCreateCommand) Run(args []string) int {

	var op int

	flags := c.M.FlagSet("vsm-create", FlagSetClient)
	flags.Usage = func() { c.M.Ui.Output(c.Help()) }
	flags.StringVar(&c.vsmname, "name", "", "")
	flags.StringVar(&c.size, "size", "5", "")

	if err := flags.Parse(args); err != nil {
		return 1
	}

	// specs file is mandatory
	args = flags.Args()
	if len(args) != 1 && len(strings.TrimSpace(c.vsmname)) == 0 {
		c.M.Ui.Error(c.Help())
		return 1
	}
	if len(args) == 1 {
		if c.Cmd == nil {
			// sub command
			args = append([]string{string(NomadRun)}, args...)

			// main command; append sub cmd to main cmd
			c.Cmd = exec.Command(string(ExecNomad), args...)
		}

		ic := &InternalCommand{
			Cmd: c.Cmd,
			Ui:  c.M.Ui,
		}

		if op = ic.Execute(); 0 != op {
			c.M.Ui.Error("Error creating vsm")
			return op
		}
		return 1
	}
	if c.vsmname != " " {
		err := CreateApiVsm(c.vsmname, c.size)
		if err != nil {
			fmt.Println("Error Creating Vsm")
		}
	}
	return op
}

// Function to create the Vsm through a API call to m-apiserver
func CreateApiVsm(vname string, size string) error {

	var vs VsmSpec

	addr := os.Getenv("MAPI_ADDR")
	if addr == "" {
		err := errors.New("MAPI_ADDR environment variable not set")
		fmt.Println(err)
		return err
	}
	url := addr + "/latest/volumes/"

	vs.Metadata.Name = vname
	vs.Spec.Resources.Requests.Storage = size

	//Marshal serializes the value provided into a YAML document
	yamlValue, _ := yaml.Marshal(vs)

	req, err := http.NewRequest("POST", url, bytes.NewBuffer(yamlValue))

	req.Header.Add("Content-Type", "application/yaml")

	c := &http.Client{
		Timeout: timeout,
	}
	resp, err := c.Do(req)
	if err != nil {
		fmt.Printf("http.Do() error: %v\n", err)
		return err
	}
	defer resp.Body.Close()

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Printf("ioutil.ReadAll() error: %v\n", err)
		return err
	}

	fmt.Printf("VSM Successfully Created:\n%v\n", string(data))

	return err
}
<|endoftext|>


<commit_before>//create: 2015/09/18 11:12:13 change: 2015/09/18 13:51:03 author:lijiao
package virtio

import(
	"io"
	"sync"
	"time"
)

type VirtReader struct{
	mu       sync.Mutex
	Index    int
	Capaticy int
	Step     int
	Delay    time.Duration
}

func (r *VirtReader) Read(p []byte)(n int, err error){
	time.Sleep(r.Delay)
	r.mu.Lock()
	defer r.mu.Unlock()
	if r.Index < r. Capaticy - r.Step {
		r.Index = r.Index + r.Step
		return r.Step, nil
	}else if r.Index >= r.Capaticy{
		return 0, io.EOF
	}
	n = r.Capaticy - r.Index
	r.Index = r.Capaticy
	return  n, io.EOF
}

<commit_msg>format<commit_after>//create: 2015/09/18 11:12:13 Change: 2019/01/18 16:20:58 author:lijiao
package virtio

import (
	"io"
	"sync"
	"time"
)

type VirtReader struct {
	mu       sync.Mutex
	Index    int
	Capaticy int
	Step     int
	Delay    time.Duration
}

func (r *VirtReader) Read(p []byte) (n int, err error) {
	time.Sleep(r.Delay)
	r.mu.Lock()
	defer r.mu.Unlock()
	if r.Index < r.Capaticy-r.Step {
		r.Index = r.Index + r.Step
		return r.Step, nil
	} else if r.Index >= r.Capaticy {
		return 0, io.EOF
	}
	n = r.Capaticy - r.Index
	r.Index = r.Capaticy
	return n, io.EOF
}
<|endoftext|>


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

package packet

import (
	"encoding/binary"
	"fmt"
	"strconv"
)

// Code represents the source or destination of a packet.
type Code int16

const (
	CodeServices Code = -1
)

func (code Code) String() string {
	switch {
	case code >= 0:
		return fmt.Sprintf("service[%d]", code)

	case code == CodeServices:
		return "services"

	default:
		return fmt.Sprintf("<invalid code %d>", code)
	}
}

type Domain uint8

const (
	DomainCall Domain = iota
	DomainInfo
	DomainFlow
	DomainData
)

func (d Domain) String() string {
	switch d {
	case DomainCall:
		return "call"

	case DomainInfo:
		return "info"

	case DomainFlow:
		return "flow"

	case DomainData:
		return "data"

	default:
		return "<invalid domain>"
	}
}

const (
	Alignment = 8

	// Packet header
	OffsetSize     = 0
	OffsetCode     = 4
	OffsetDomain   = 6
	offsetReserved = 7
	HeaderSize     = 8

	// Services packet header
	OffsetServicesCount = HeaderSize + 0
	ServicesHeaderSize  = HeaderSize + 2

	// Flow packet header
	FlowHeaderSize = HeaderSize

	// Data packet header
	OffsetDataID   = HeaderSize + 0
	OffsetDataNote = HeaderSize + 4
	DataHeaderSize = HeaderSize + 8
)

const (
	flowOffsetID        = 0
	flowOffsetIncrement = 4
	flowSize            = 8
)

// Align packet length up to a multiple of packet alignment.
func Align(length int) int {
	return (length + (Alignment - 1)) &^ (Alignment - 1)
}

// Buf holds a packet.
type Buf []byte

func Make(code Code, domain Domain, packetSize int) Buf {
	b := Buf(make([]byte, packetSize))
	binary.LittleEndian.PutUint16(b[OffsetCode:], uint16(code))
	b[OffsetDomain] = byte(domain)
	return b
}

func MakeCall(code Code, contentSize int) Buf {
	return Make(code, DomainCall, HeaderSize+contentSize)
}

func MakeFlow(code Code, id int32, increment int32) Buf {
	b := MakeFlows(code, 1)
	b.Set(0, id, increment)
	return Buf(b)
}

// Code is the program instance-specific service identifier.
func (b Buf) Code() Code {
	return Code(binary.LittleEndian.Uint16(b[OffsetCode:]))
}

func (b Buf) Domain() Domain {
	return Domain(b[OffsetDomain])
}

// Content of a received packet, or buffer for initializing sent packet.
func (b Buf) Content() []byte {
	return b[HeaderSize:]
}

func (b Buf) String() (s string) {
	var (
		size     string
		reserved string
	)

	if n := binary.LittleEndian.Uint32(b); n == 0 || n == uint32(len(b)) {
		size = strconv.Itoa(len(b))
	} else {
		size = fmt.Sprintf("%d/%d", n, len(b))
	}

	if x := b[offsetReserved]; x != 0 {
		reserved = fmt.Sprintf(" reserved=0x%02x", x)
	}

	s = fmt.Sprintf("size=%s code=%s domain=%s%s", size, b.Code(), b.Domain(), reserved)

	switch b.Domain() {
	case DomainFlow:
		s += FlowBuf(b).string()

	case DomainData:
		s += DataBuf(b).string()
	}
	return
}

// Split a packet into two parts.  The headerSize parameter determins how many
// bytes are initialized in the second part: the header is copied from the
// first part.  The length of the first part is given as the prefixLen
// parameter.  If the buffer is too short for the second part, the length of
// the second buffer will be zero.
func (b Buf) Split(headerSize, prefixLen int) (prefix, unused Buf) {
	prefixCap := Align(prefixLen)
	if prefixCap > len(b) {
		prefixCap = len(b)
	}

	prefix = b[:prefixLen:prefixCap]
	unused = b[prefixCap:]

	if len(unused) < headerSize {
		unused = unused[0:]
		return
	}

	copy(unused, prefix[:headerSize])
	return
}

// FlowBuf holds a flow packet.
type FlowBuf Buf

func MakeFlows(code Code, count int) FlowBuf {
	b := Make(code, DomainFlow, FlowHeaderSize+count*flowSize)
	return FlowBuf(b)
}

func (b FlowBuf) Num() int {
	return (len(b) - FlowHeaderSize) / flowSize
}

func (b FlowBuf) Get(i int) (id int32, increment int32) {
	flow := b[FlowHeaderSize+i*flowSize:]
	id = int32(binary.LittleEndian.Uint32(flow[flowOffsetID:]))
	increment = int32(binary.LittleEndian.Uint32(flow[flowOffsetIncrement:]))
	return
}

func (b FlowBuf) Set(i int, id int32, increment int32) {
	flow := b[FlowHeaderSize+i*flowSize:]
	binary.LittleEndian.PutUint32(flow[flowOffsetID:], uint32(id))
	binary.LittleEndian.PutUint32(flow[flowOffsetIncrement:], uint32(increment))
}

func (b FlowBuf) String() string {
	return Buf(b).String() + b.string()
}

func (b FlowBuf) string() (s string) {
	for i := 0; i < b.Num(); i++ {
		id, inc := b.Get(i)
		s += fmt.Sprintf(" stream[%d]+=%d", id, inc)
	}
	return
}

// DataBuf holds a data packet.
type DataBuf Buf

func MakeData(code Code, id int32, dataSize int) DataBuf {
	b := Make(code, DomainData, DataHeaderSize+dataSize)
	binary.LittleEndian.PutUint32(b[OffsetDataID:], uint32(id))
	return DataBuf(b)
}

func (b DataBuf) ID() int32 {
	return int32(binary.LittleEndian.Uint32(b[OffsetDataID:]))
}

// Note is a value associated with a data packet.  Each service interface
// specifies its semantics separately.
func (b DataBuf) Note() int32 {
	return int32(binary.LittleEndian.Uint32(b[OffsetDataNote:]))
}

// SetNote value.  It defaults to zero.
func (b DataBuf) SetNote(value int32) {
	binary.LittleEndian.PutUint32(b[OffsetDataNote:], uint32(value))
}

func (b DataBuf) Data() []byte {
	return b[DataHeaderSize:]
}

func (b DataBuf) DataLen() int {
	return len(b) - DataHeaderSize
}

func (b DataBuf) Split(dataLen int) (prefix Buf, unused DataBuf) {
	prefix, unusedBuf := Buf(b).Split(DataHeaderSize, DataHeaderSize+dataLen)
	unused = DataBuf(unusedBuf)
	return
}

func (b DataBuf) String() string {
	return Buf(b).String() + b.string()
}

func (b DataBuf) string() (s string) {
	s = fmt.Sprintf(" id=%d", b.ID())
	if n := b.DataLen(); n > 0 {
		s += fmt.Sprintf(" datalen=%d", n)
	}
	if x := b.Note(); x != 0 {
		s += fmt.Sprintf(" note=%d", x)
	}
	return
}
<commit_msg>packet: MakeInfo helper<commit_after>// Copyright (c) 2017 Timo Savola. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package packet

import (
	"encoding/binary"
	"fmt"
	"strconv"
)

// Code represents the source or destination of a packet.
type Code int16

const (
	CodeServices Code = -1
)

func (code Code) String() string {
	switch {
	case code >= 0:
		return fmt.Sprintf("service[%d]", code)

	case code == CodeServices:
		return "services"

	default:
		return fmt.Sprintf("<invalid code %d>", code)
	}
}

type Domain uint8

const (
	DomainCall Domain = iota
	DomainInfo
	DomainFlow
	DomainData
)

func (d Domain) String() string {
	switch d {
	case DomainCall:
		return "call"

	case DomainInfo:
		return "info"

	case DomainFlow:
		return "flow"

	case DomainData:
		return "data"

	default:
		return "<invalid domain>"
	}
}

const (
	Alignment = 8

	// Packet header
	OffsetSize     = 0
	OffsetCode     = 4
	OffsetDomain   = 6
	offsetReserved = 7
	HeaderSize     = 8

	// Services packet header
	OffsetServicesCount = HeaderSize + 0
	ServicesHeaderSize  = HeaderSize + 2

	// Flow packet header
	FlowHeaderSize = HeaderSize

	// Data packet header
	OffsetDataID   = HeaderSize + 0
	OffsetDataNote = HeaderSize + 4
	DataHeaderSize = HeaderSize + 8
)

const (
	flowOffsetID        = 0
	flowOffsetIncrement = 4
	flowSize            = 8
)

// Align packet length up to a multiple of packet alignment.
func Align(length int) int {
	return (length + (Alignment - 1)) &^ (Alignment - 1)
}

// Buf holds a packet.
type Buf []byte

func Make(code Code, domain Domain, packetSize int) Buf {
	b := Buf(make([]byte, packetSize))
	binary.LittleEndian.PutUint16(b[OffsetCode:], uint16(code))
	b[OffsetDomain] = byte(domain)
	return b
}

func MakeCall(code Code, contentSize int) Buf {
	return Make(code, DomainCall, HeaderSize+contentSize)
}

func MakeInfo(code Code, contentSize int) Buf {
	return Make(code, DomainInfo, HeaderSize+contentSize)
}

func MakeFlow(code Code, id int32, increment int32) Buf {
	b := MakeFlows(code, 1)
	b.Set(0, id, increment)
	return Buf(b)
}

// Code is the program instance-specific service identifier.
func (b Buf) Code() Code {
	return Code(binary.LittleEndian.Uint16(b[OffsetCode:]))
}

func (b Buf) Domain() Domain {
	return Domain(b[OffsetDomain])
}

// Content of a received packet, or buffer for initializing sent packet.
func (b Buf) Content() []byte {
	return b[HeaderSize:]
}

func (b Buf) String() (s string) {
	var (
		size     string
		reserved string
	)

	if n := binary.LittleEndian.Uint32(b); n == 0 || n == uint32(len(b)) {
		size = strconv.Itoa(len(b))
	} else {
		size = fmt.Sprintf("%d/%d", n, len(b))
	}

	if x := b[offsetReserved]; x != 0 {
		reserved = fmt.Sprintf(" reserved=0x%02x", x)
	}

	s = fmt.Sprintf("size=%s code=%s domain=%s%s", size, b.Code(), b.Domain(), reserved)

	switch b.Domain() {
	case DomainFlow:
		s += FlowBuf(b).string()

	case DomainData:
		s += DataBuf(b).string()
	}
	return
}

// Split a packet into two parts.  The headerSize parameter determins how many
// bytes are initialized in the second part: the header is copied from the
// first part.  The length of the first part is given as the prefixLen
// parameter.  If the buffer is too short for the second part, the length of
// the second buffer will be zero.
func (b Buf) Split(headerSize, prefixLen int) (prefix, unused Buf) {
	prefixCap := Align(prefixLen)
	if prefixCap > len(b) {
		prefixCap = len(b)
	}

	prefix = b[:prefixLen:prefixCap]
	unused = b[prefixCap:]

	if len(unused) < headerSize {
		unused = unused[0:]
		return
	}

	copy(unused, prefix[:headerSize])
	return
}

// FlowBuf holds a flow packet.
type FlowBuf Buf

func MakeFlows(code Code, count int) FlowBuf {
	b := Make(code, DomainFlow, FlowHeaderSize+count*flowSize)
	return FlowBuf(b)
}

func (b FlowBuf) Num() int {
	return (len(b) - FlowHeaderSize) / flowSize
}

func (b FlowBuf) Get(i int) (id int32, increment int32) {
	flow := b[FlowHeaderSize+i*flowSize:]
	id = int32(binary.LittleEndian.Uint32(flow[flowOffsetID:]))
	increment = int32(binary.LittleEndian.Uint32(flow[flowOffsetIncrement:]))
	return
}

func (b FlowBuf) Set(i int, id int32, increment int32) {
	flow := b[FlowHeaderSize+i*flowSize:]
	binary.LittleEndian.PutUint32(flow[flowOffsetID:], uint32(id))
	binary.LittleEndian.PutUint32(flow[flowOffsetIncrement:], uint32(increment))
}

func (b FlowBuf) String() string {
	return Buf(b).String() + b.string()
}

func (b FlowBuf) string() (s string) {
	for i := 0; i < b.Num(); i++ {
		id, inc := b.Get(i)
		s += fmt.Sprintf(" stream[%d]+=%d", id, inc)
	}
	return
}

// DataBuf holds a data packet.
type DataBuf Buf

func MakeData(code Code, id int32, dataSize int) DataBuf {
	b := Make(code, DomainData, DataHeaderSize+dataSize)
	binary.LittleEndian.PutUint32(b[OffsetDataID:], uint32(id))
	return DataBuf(b)
}

func (b DataBuf) ID() int32 {
	return int32(binary.LittleEndian.Uint32(b[OffsetDataID:]))
}

// Note is a value associated with a data packet.  Each service interface
// specifies its semantics separately.
func (b DataBuf) Note() int32 {
	return int32(binary.LittleEndian.Uint32(b[OffsetDataNote:]))
}

// SetNote value.  It defaults to zero.
func (b DataBuf) SetNote(value int32) {
	binary.LittleEndian.PutUint32(b[OffsetDataNote:], uint32(value))
}

func (b DataBuf) Data() []byte {
	return b[DataHeaderSize:]
}

func (b DataBuf) DataLen() int {
	return len(b) - DataHeaderSize
}

func (b DataBuf) Split(dataLen int) (prefix Buf, unused DataBuf) {
	prefix, unusedBuf := Buf(b).Split(DataHeaderSize, DataHeaderSize+dataLen)
	unused = DataBuf(unusedBuf)
	return
}

func (b DataBuf) String() string {
	return Buf(b).String() + b.string()
}

func (b DataBuf) string() (s string) {
	s = fmt.Sprintf(" id=%d", b.ID())
	if n := b.DataLen(); n > 0 {
		s += fmt.Sprintf(" datalen=%d", n)
	}
	if x := b.Note(); x != 0 {
		s += fmt.Sprintf(" note=%d", x)
	}
	return
}
<|endoftext|>


<commit_before>package modbusone

import (
	"io"
	"math/rand"
	"time"

	"github.com/xiegeo/modbusone/crc"
)

var _ = rand.Int63n

type rtuPacketReader struct {
	r        SerialContext //the underlining reader
	isClient bool
	last     []byte
}

//NewRTUPacketReader create a Reader that attempt to read full packets.
//
//
func NewRTUPacketReader(r SerialContext, isClient bool) io.Reader {
	return &rtuPacketReader{r, isClient, nil}
}

func (s *rtuPacketReader) Read(p []byte) (int, error) {
	s.r.Stats().ReadPackets++
	expected := smallestRTUSize
	read := 0
	for read < expected {
		if len(s.last) != 0 {
			read += copy(p, s.last)
			s.last = s.last[:0]
		} else {
			time.Sleep(time.Duration(rand.Int63n(int64(time.Second))))
			n, err := s.r.Read(p[read:])
			debugf("RTUPacketReader read (%v+%v)/%v %v, expcted %v", read, n, len(p), err, expected)
			read += n
			if err != nil || read == len(p) {
				return read, err
			}
		}
		if read < expected {
			//lets read more
			continue
		}
		//lets see if there is more to read
		expected = GetRTUSizeFromHeader(p[:read], s.isClient)
		debugf("RTUPacketReader new expected size %v", expected)
		if expected > read-1 {
			time.Sleep(s.r.BytesDelay(expected - read))
		}
	}
	if read > expected {
		if crc.Validate(p[:expected]) {
			s.r.Stats().LongReadWarnings++
			s.last = append(s.last[:0], p[expected:read]...)
			debugf("long read warning %v / %v", expected, read)
			return expected, nil
		}
		if crc.Validate(p[:read]) {
			s.r.Stats().FormateWarnings++
		}
	}
	return read, nil
}

//GetPDUSizeFromHeader returns the expected sized of a pdu packet with the given
//PDU header, if not enough info is in the header, then it returns the shortest possible.
//isClient is true if a client/master is reading the packet.
func GetPDUSizeFromHeader(header []byte, isClient bool) int {
	if len(header) < 2 {
		return 2
	}
	ec, f := FunctionCode(header[0]).SeparateError()
	if ec || !f.Valid() {
		return 2
	}
	if isClient == f.IsWriteToServer() {
		//all packets without data: fc, address, and count
		return 5
	}
	if isClient {
		//all data replies: fc, data bytes, data
		return 2 + int(header[1])
	}
	if f.IsSingle() {
		//fc, address, one data
		return 5
	}
	//fc, adress, count, data bytes, data
	if len(header) < 6 {
		return 6
	}
	if OverSizeSupport {
		n := int(header[3])*256 + int(header[4])
		if f.IsUint16() {
			return 6 + n*2
		}
		return 6 + (n-1)/8 + 1
	}
	return 6 + int(header[5])
}

//GetRTUSizeFromHeader returns the expected sized of a rtu packet with the given
//RTU header, if not enough info is in the header, then it returns the shortest possible.
//isClient is true if a client/master is reading the packet.
func GetRTUSizeFromHeader(header []byte, isClient bool) int {
	if len(header) < 3 {
		return 3
	}
	return GetPDUSizeFromHeader(header[1:], isClient) + 3
}
<commit_msg>oops, forgot to disable random sleeping test monkey<commit_after>package modbusone

import (
	"io"
	"math/rand"
	"time"

	"github.com/xiegeo/modbusone/crc"
)

var _ = rand.Int63n

type rtuPacketReader struct {
	r        SerialContext //the underlining reader
	isClient bool
	last     []byte
}

//NewRTUPacketReader create a Reader that attempt to read full packets.
//
//
func NewRTUPacketReader(r SerialContext, isClient bool) io.Reader {
	return &rtuPacketReader{r, isClient, nil}
}

func (s *rtuPacketReader) Read(p []byte) (int, error) {
	s.r.Stats().ReadPackets++
	expected := smallestRTUSize
	read := 0
	for read < expected {
		if len(s.last) != 0 {
			read += copy(p, s.last)
			s.last = s.last[:0]
		} else {
			//time.Sleep(time.Duration(rand.Int63n(int64(time.Second))))
			n, err := s.r.Read(p[read:])
			debugf("RTUPacketReader read (%v+%v)/%v %v, expcted %v", read, n, len(p), err, expected)
			read += n
			if err != nil || read == len(p) {
				return read, err
			}
		}
		if read < expected {
			//lets read more
			continue
		}
		//lets see if there is more to read
		expected = GetRTUSizeFromHeader(p[:read], s.isClient)
		debugf("RTUPacketReader new expected size %v", expected)
		if expected > read-1 {
			time.Sleep(s.r.BytesDelay(expected - read))
		}
	}
	if read > expected {
		if crc.Validate(p[:expected]) {
			s.r.Stats().LongReadWarnings++
			s.last = append(s.last[:0], p[expected:read]...)
			debugf("long read warning %v / %v", expected, read)
			return expected, nil
		}
		if crc.Validate(p[:read]) {
			s.r.Stats().FormateWarnings++
		}
	}
	return read, nil
}

//GetPDUSizeFromHeader returns the expected sized of a pdu packet with the given
//PDU header, if not enough info is in the header, then it returns the shortest possible.
//isClient is true if a client/master is reading the packet.
func GetPDUSizeFromHeader(header []byte, isClient bool) int {
	if len(header) < 2 {
		return 2
	}
	ec, f := FunctionCode(header[0]).SeparateError()
	if ec || !f.Valid() {
		return 2
	}
	if isClient == f.IsWriteToServer() {
		//all packets without data: fc, address, and count
		return 5
	}
	if isClient {
		//all data replies: fc, data bytes, data
		return 2 + int(header[1])
	}
	if f.IsSingle() {
		//fc, address, one data
		return 5
	}
	//fc, adress, count, data bytes, data
	if len(header) < 6 {
		return 6
	}
	if OverSizeSupport {
		n := int(header[3])*256 + int(header[4])
		if f.IsUint16() {
			return 6 + n*2
		}
		return 6 + (n-1)/8 + 1
	}
	return 6 + int(header[5])
}

//GetRTUSizeFromHeader returns the expected sized of a rtu packet with the given
//RTU header, if not enough info is in the header, then it returns the shortest possible.
//isClient is true if a client/master is reading the packet.
func GetRTUSizeFromHeader(header []byte, isClient bool) int {
	if len(header) < 3 {
		return 3
	}
	return GetPDUSizeFromHeader(header[1:], isClient) + 3
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net/http"
	"net/http/httptest"
)

func newTestRequest(method, path string) *httptest.ResponseRecorder {
	request, err := http.NewRequest(method, path, nil)
	if err != nil {
		log.Fatal(err)
	}

	recorder := httptest.NewRecorder()
	router.ServeHTTP(recorder, request)

	return recorder
}
<commit_msg>don't need this<commit_after><|endoftext|>


<commit_before>// Copyright 2015 Davis Webb
// Copyright 2015 Luke Shumaker

package store

import (
	//"database/sql"
	he "httpentity"
	"time"
)

var _ he.NetEntity = &Session{}
var fileSession he.Entity = newFileSession()

// Model /////////////////////////////////////////////////////////////

type Session struct {
	id        string
	user_id   string
	last_used time.Time
}

func NewSession(username string, password string) *Session {
	panic("not implemented")
}

func GetSessionById(id string) *Session {
	panic("not implemented")
}

func (o *Session) Delete() {
	panic("not implemented")
}

// View //////////////////////////////////////////////////////////////

func (o *Session) Encoders() map[string]he.Encoder {
	panic("not implemented")
}

// File ("Controller") ///////////////////////////////////////////////

type t_fileSession struct {
	methods map[string]he.Handler
}

func newFileSession() t_fileSession {
	r := t_fileSession{}
	r.methods = map[string]he.Handler{
		"POST": func(req he.Request) he.Response {
			username := "" /*TODO*/
			password := "" /*TODO*/
			sess := NewSession(username, password)
			if sess == nil {
				return req.StatusUnauthorized(he.NetString("Incorrect username/password"))
			} else {
				return req.StatusOK(sess)
			}
		},
		"DELETE": func(req he.Request) he.Response {
			sess := GetSessionById("" /*TODO*/)
			if sess != nil {
				sess.Delete()
			}
			return req.StatusNoContent()
		},
	}
	return r
}

func (d t_fileSession) Methods() map[string]he.Handler {
	return d.methods
}

func (d t_fileSession) Subentity(name string, request he.Request) he.Entity {
	return nil
}
<commit_msg>implement the session HTTP methods<commit_after>// Copyright 2015 Davis Webb
// Copyright 2015 Luke Shumaker

package store

import (
	//"database/sql"
	he "httpentity"
	"time"
)

var _ he.NetEntity = &Session{}
var fileSession he.Entity = newFileSession()

// Model /////////////////////////////////////////////////////////////

type Session struct {
	id        string
	user_id   string
	last_used time.Time
}

func NewSession(username string, password string) *Session {
	panic("not implemented")
}

func GetSessionById(id string) *Session {
	panic("not implemented")
}

func (o *Session) Delete() {
	panic("not implemented")
}

// View //////////////////////////////////////////////////////////////

func (o *Session) Encoders() map[string]he.Encoder {
	panic("not implemented")
}

// File ("Controller") ///////////////////////////////////////////////

type t_fileSession struct {
	methods map[string]he.Handler
}

func newFileSession() t_fileSession {
	r := t_fileSession{}
	r.methods = map[string]he.Handler{
		"POST": func(req he.Request) he.Response {
			badbody := req.StatusBadRequest("submitted body not what expected")
			hash    , ok := req.Entity.(map[string]interface{}); if !ok { return badbody }
			username, ok := hash["username"].(string)          ; if !ok { return badbody }
			password, ok := hash["password"].(string)          ; if !ok { return badbody }
			if len(hash) != 2                                           { return badbody }

			sess := NewSession(username, password)
			if sess == nil {
				return req.StatusUnauthorized(he.NetString("Incorrect username/password"))
			} else {
				ret := req.StatusOK(sess)
				// TODO: set the session_id cookie (in ret.Headers) to sess.Id
				return ret
			}
		},
		"DELETE": func(req he.Request) he.Response {
			sess := req.Things["session"].(*Session)
			if sess != nil {
				sess.Delete()
			}
			return req.StatusNoContent()
		},
	}
	return r
}

func (d t_fileSession) Methods() map[string]he.Handler {
	return d.methods
}

func (d t_fileSession) Subentity(name string, request he.Request) he.Entity {
	return nil
}
<|endoftext|>


<commit_before>package common

import (
	"net"
	"os"
	"testing"
)

const (
	confFile = "../bin/quantum-test.yml"
)

var (
	testPacket []byte
)

func init() {
	testPacket = make([]byte, 18)
	// IP (1.1.1.1)
	testPacket[0] = 1
	testPacket[1] = 1
	testPacket[2] = 1
	testPacket[3] = 1

	// Nonce
	testPacket[4] = 2
	testPacket[5] = 2
	testPacket[6] = 2
	testPacket[7] = 2
	testPacket[8] = 2
	testPacket[9] = 2
	testPacket[10] = 2
	testPacket[11] = 2
	testPacket[12] = 2
	testPacket[13] = 2
	testPacket[14] = 2
	testPacket[15] = 2

	// Packet data
	testPacket[16] = 3
	testPacket[17] = 3
}

func testEq(a, b []byte) bool {

	if a == nil && b == nil {
		return true
	}

	if a == nil || b == nil {
		return false
	}

	if len(a) != len(b) {
		return false
	}

	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}

	return true
}

func TestArrayEquals(t *testing.T) {
	if !ArrayEquals(nil, nil) {
		t.Fatal("ArrayEquals returned false comparing nil/nil")
	}
	if ArrayEquals([]byte{0}, nil) {
		t.Fatal("ArrayEquals returned true comparing nil/non-nil")
	}
	if ArrayEquals([]byte{0, 1}, []byte{0}) {
		t.Fatal("ArrayEquals returned true comparing mismatched lengths")
	}
	if !ArrayEquals([]byte{0, 1}, []byte{0, 1}) {
		t.Fatal("ArrayEquals returned false for equal arrays")
	}
}

func TestIPtoInt(t *testing.T) {
	var expected uint32
	actual := IPtoInt(net.ParseIP("0.0.0.0"))
	if expected != actual {
		t.Fatalf("IPtoInt did not return the right value, got: %d, expected: %d", actual, expected)
	}
}

func TestIncrementIP(t *testing.T) {
	expected := net.ParseIP("10.0.0.1")

	actual := net.ParseIP("10.0.0.0")
	IncrementIP(actual)

	if !testEq(expected, actual) {
		t.Fatalf("IncrementIP did not return the right value, got: %s, expected: %s", actual, expected)
	}
}

func TestNewConfig(t *testing.T) {
	os.Setenv("QUANTUM_DEVICE_NAME", "different")
	os.Setenv("QUANTUM_LISTEN_PORT", "1")
	os.Setenv("QUANTUM_CONF_FILE", confFile)
	os.Setenv("QUANTUM_PID_FILE", "../quantum.pid")

	cfg, err := NewConfig()
	if err != nil {
		t.Fatalf("NewConfig returned an error, %s", err)
	}
	t.Log(cfg)
	if cfg == nil {
		t.Fatal("NewConfig returned a blank config")
	}
	if cfg.DeviceName != "different" {
		t.Fatalf("NewConfig didn't pick up the environment variable replacement for DeviceName")
	}
	if cfg.ListenPort != 1 {
		t.Fatalf("NewConfig didn't pick up the environment variable replacement for ListenPort")
	}
	if cfg.Password != "Password1" {
		t.Fatalf("NewConfig didn't pick up the config file replacement for Password")
	}
}

func TestEcdh(t *testing.T) {
	pub, priv := GenerateECKeyPair()
	if len(pub) != keyLength {
		t.Fatalf("GenerateECKeyPair did not return the right length for the public key,\nactual: %d, expected: %d", len(pub), keyLength)
	}
	if len(priv) != keyLength {
		t.Fatalf("GenerateECKeyPair did not return the right length for the private key,\nactual: %d, expected: %d", len(priv), keyLength)
	}
	if testEq(pub, priv) {
		t.Fatalf("GenerateECKeyPair returned identical pub/priv keys this can't possibly happen:\npub: %v, priv: %v", pub, priv)
	}
	secret := GenerateSharedSecret(pub, priv)
	if len(secret) != keyLength {
		t.Fatalf("GenerateECKeyPair did not return the right length for the shared secret,\nactual: %d, expected: %d", len(secret), keyLength)
	}
	if testEq(secret, pub) || testEq(secret, priv) {
		t.Fatalf("GenerateECKeyPair returned identical secret and pub/priv keys this can't possibly happen:\npub: %v, priv: %v, secret: %v", pub, priv, secret)
	}
}

func TestNewMapping(t *testing.T) {
	privateIP := net.ParseIP("0.0.0.0")
	publicip := net.ParseIP("1.1.1.1")
	publicip6 := net.ParseIP("dead::beef")
	publicport := 80
	publicKey := make([]byte, 32)
	machineID := "123456"

	actual := NewMapping(machineID, privateIP, publicip, publicip6, publicport, publicKey)
	if !testEq(actual.IPv4, publicip) || !testEq(actual.IPv6, publicip6) || actual.Port != publicport || !testEq(actual.PrivateIP, privateIP) || !testEq(actual.PublicKey, publicKey) {
		t.Fatalf("NewMapping did not return the right value, got: %v", actual)
	}
}

func TestParseMapping(t *testing.T) {
	privateIP := net.ParseIP("0.0.0.0")
	publicip := net.ParseIP("1.1.1.1")
	publicip6 := net.ParseIP("dead::beef")
	publicport := 80
	publicKey := make([]byte, 32)
	machineID := "123456"

	expected := NewMapping(machineID, privateIP, publicip, publicip6, publicport, publicKey)
	actual, err := ParseMapping(expected.String(), make([]byte, 32))
	if err != nil {
		t.Fatalf("Error occured during test: %s", err)
	}
	if !testEq(actual.IPv4, expected.IPv4) || actual.Port != expected.Port || !testEq(actual.PrivateIP, expected.PrivateIP) || !testEq(actual.PublicKey, expected.PublicKey) {
		t.Fatalf("ParseMapping did not return the right value, got: %v, expected: %v", actual, expected)
	}
}

func TestParseNetworkConfig(t *testing.T) {
	actual, err := ParseNetworkConfig(DefaultNetworkConfig.Bytes())
	if err != nil {
		t.Fatal("ParseNetworkConfig returned an error:", err)
	}
	if actual.Network != DefaultNetworkConfig.Network || actual.LeaseTime != DefaultNetworkConfig.LeaseTime {
		t.Fatalf("ParseNetworkConfig returned the wrong value, got: %v, expected: %v", actual, DefaultNetworkConfig)
	}
}

func TestNewTunPayload(t *testing.T) {
	payload := NewTunPayload(testPacket, 2)
	for i := 0; i < 4; i++ {
		if payload.IPAddress[i] != 1 {
			t.Fatal("NewTunPayload returned an incorrect IP address mapping.")
		}
	}

	for i := 0; i < 12; i++ {
		if payload.Nonce[i] != 2 {
			t.Fatal("NewTunPayload returned an incorrect Nonce mapping.")
		}
	}

	for i := 0; i < 2; i++ {
		if payload.Packet[i] != 3 {
			t.Fatal("NewTunPayload returned an incorrect Packet mapping.")
		}
	}
}

func TestNewSockPayload(t *testing.T) {
	payload := NewSockPayload(testPacket, 18)
	for i := 0; i < 4; i++ {
		if payload.IPAddress[i] != 1 {
			t.Fatal("NewTunPayload returned an incorrect IP address mapping.")
		}
	}

	for i := 0; i < 12; i++ {
		if payload.Nonce[i] != 2 {
			t.Fatal("NewTunPayload returned an incorrect Nonce mapping.")
		}
	}

	for i := 0; i < 2; i++ {
		if payload.Packet[i] != 3 {
			t.Fatal("NewTunPayload returned an incorrect Packet mapping.")
		}
	}
}

func TestNewStats(t *testing.T) {
	stats := NewStats(1)
	if stats.Packets != 0 {
		t.Fatalf("NewStats did not return the correct default for Packets, got: %d, expected: %d", stats.Packets, 0)
	}
	if stats.Bytes != 0 {
		t.Fatalf("NewStats did not return the correct default for Bytes, got: %d, expected: %d", stats.Bytes, 0)
	}
	if stats.Links == nil {
		t.Fatalf("NewStats did not return the correct default for Links, got: %v, expected: %v", stats.Links, make(map[string]*Stats))
	}
	str := stats.String()
	if str == "" {
		t.Fatalf("String didn't return the correct value.")
	}
}

func TestNewLogger(t *testing.T) {
	log := NewLogger()
	if log.Error == nil {
		t.Fatal("NewLogger returned a nil Error log.")
	}
	if log.Info == nil {
		t.Fatal("NewLogger returned a nil Error log.")
	}
}

func TestGenerateLocalMapping(t *testing.T) {
	cfg := &Config{
		PrivateIP:     net.ParseIP("10.10.0.1"),
		PublicIPv4:    net.ParseIP("8.8.8.8"),
		PublicIPv6:    net.ParseIP("::"),
		ListenPort:    1099,
		PublicKey:     make([]byte, 32),
		NetworkConfig: DefaultNetworkConfig,
		MachineID:     "123",
	}

	mappings := make(map[uint32]*Mapping)
	mapping, err := GenerateLocalMapping(cfg, mappings)
	if err != nil {
		t.Fatal(err)
	}

	if !testEq(mapping.PrivateIP.To4(), cfg.PrivateIP.To4()) {
		t.Fatal("GenerateLocalMapping created the wrong mapping.")
	}

	mappings[IPtoInt(cfg.PrivateIP)] = mapping

	_, err = GenerateLocalMapping(cfg, mappings)
	if err != nil {
		t.Fatal(err)
	}

	mapping.MachineID = "456"

	_, err = GenerateLocalMapping(cfg, mappings)
	if err == nil {
		t.Fatal("GenerateLocalMapping failed to properly handle an existing ip address")
	}

	cfg.PrivateIP = nil
	mapping.MachineID = "123"

	_, err = GenerateLocalMapping(cfg, mappings)
	if err != nil {
		t.Fatal(err)
	}
}
<commit_msg>Added some more unit tests for the ip handler module<commit_after>package common

import (
	"net"
	"os"
	"testing"
)

const (
	confFile = "../bin/quantum-test.yml"
)

var (
	testPacket []byte
)

func init() {
	testPacket = make([]byte, 18)
	// IP (1.1.1.1)
	testPacket[0] = 1
	testPacket[1] = 1
	testPacket[2] = 1
	testPacket[3] = 1

	// Nonce
	testPacket[4] = 2
	testPacket[5] = 2
	testPacket[6] = 2
	testPacket[7] = 2
	testPacket[8] = 2
	testPacket[9] = 2
	testPacket[10] = 2
	testPacket[11] = 2
	testPacket[12] = 2
	testPacket[13] = 2
	testPacket[14] = 2
	testPacket[15] = 2

	// Packet data
	testPacket[16] = 3
	testPacket[17] = 3
}

func testEq(a, b []byte) bool {

	if a == nil && b == nil {
		return true
	}

	if a == nil || b == nil {
		return false
	}

	if len(a) != len(b) {
		return false
	}

	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}

	return true
}

func TestArrayEquals(t *testing.T) {
	if !ArrayEquals(nil, nil) {
		t.Fatal("ArrayEquals returned false comparing nil/nil")
	}
	if ArrayEquals([]byte{0}, nil) {
		t.Fatal("ArrayEquals returned true comparing nil/non-nil")
	}
	if ArrayEquals([]byte{0, 1}, []byte{0}) {
		t.Fatal("ArrayEquals returned true comparing mismatched lengths")
	}
	if !ArrayEquals([]byte{0, 1}, []byte{0, 1}) {
		t.Fatal("ArrayEquals returned false for equal arrays")
	}
}

func TestIPtoInt(t *testing.T) {
	var expected uint32
	actual := IPtoInt(net.ParseIP("0.0.0.0"))
	if expected != actual {
		t.Fatalf("IPtoInt did not return the right value, got: %d, expected: %d", actual, expected)
	}
}

func TestIncrementIP(t *testing.T) {
	expected := net.ParseIP("10.0.0.1")

	actual := net.ParseIP("10.0.0.0")
	IncrementIP(actual)

	if !testEq(expected, actual) {
		t.Fatalf("IncrementIP did not return the right value, got: %s, expected: %s", actual, expected)
	}
}

func TestNewConfig(t *testing.T) {
	os.Setenv("QUANTUM_DEVICE_NAME", "different")
	os.Setenv("QUANTUM_LISTEN_PORT", "1")
	os.Setenv("QUANTUM_CONF_FILE", confFile)
	os.Setenv("QUANTUM_PID_FILE", "../quantum.pid")

	cfg, err := NewConfig()
	if err != nil {
		t.Fatalf("NewConfig returned an error, %s", err)
	}
	t.Log(cfg)
	if cfg == nil {
		t.Fatal("NewConfig returned a blank config")
	}
	if cfg.DeviceName != "different" {
		t.Fatalf("NewConfig didn't pick up the environment variable replacement for DeviceName")
	}
	if cfg.ListenPort != 1 {
		t.Fatalf("NewConfig didn't pick up the environment variable replacement for ListenPort")
	}
	if cfg.Password != "Password1" {
		t.Fatalf("NewConfig didn't pick up the config file replacement for Password")
	}
}

func TestEcdh(t *testing.T) {
	pub, priv := GenerateECKeyPair()
	if len(pub) != keyLength {
		t.Fatalf("GenerateECKeyPair did not return the right length for the public key,\nactual: %d, expected: %d", len(pub), keyLength)
	}
	if len(priv) != keyLength {
		t.Fatalf("GenerateECKeyPair did not return the right length for the private key,\nactual: %d, expected: %d", len(priv), keyLength)
	}
	if testEq(pub, priv) {
		t.Fatalf("GenerateECKeyPair returned identical pub/priv keys this can't possibly happen:\npub: %v, priv: %v", pub, priv)
	}
	secret := GenerateSharedSecret(pub, priv)
	if len(secret) != keyLength {
		t.Fatalf("GenerateECKeyPair did not return the right length for the shared secret,\nactual: %d, expected: %d", len(secret), keyLength)
	}
	if testEq(secret, pub) || testEq(secret, priv) {
		t.Fatalf("GenerateECKeyPair returned identical secret and pub/priv keys this can't possibly happen:\npub: %v, priv: %v, secret: %v", pub, priv, secret)
	}
}

func TestNewMapping(t *testing.T) {
	privateIP := net.ParseIP("0.0.0.0")
	publicip := net.ParseIP("1.1.1.1")
	publicip6 := net.ParseIP("dead::beef")
	publicport := 80
	publicKey := make([]byte, 32)
	machineID := "123456"

	actual := NewMapping(machineID, privateIP, publicip, publicip6, publicport, publicKey)
	if !testEq(actual.IPv4, publicip) || !testEq(actual.IPv6, publicip6) || actual.Port != publicport || !testEq(actual.PrivateIP, privateIP) || !testEq(actual.PublicKey, publicKey) {
		t.Fatalf("NewMapping did not return the right value, got: %v", actual)
	}
}

func TestParseMapping(t *testing.T) {
	privateIP := net.ParseIP("0.0.0.0")
	publicip := net.ParseIP("1.1.1.1")
	publicip6 := net.ParseIP("dead::beef")
	publicport := 80
	publicKey := make([]byte, 32)
	machineID := "123456"

	expected := NewMapping(machineID, privateIP, publicip, publicip6, publicport, publicKey)
	actual, err := ParseMapping(expected.String(), make([]byte, 32))
	if err != nil {
		t.Fatalf("Error occured during test: %s", err)
	}
	if !testEq(actual.IPv4, expected.IPv4) || actual.Port != expected.Port || !testEq(actual.PrivateIP, expected.PrivateIP) || !testEq(actual.PublicKey, expected.PublicKey) {
		t.Fatalf("ParseMapping did not return the right value, got: %v, expected: %v", actual, expected)
	}
}

func TestParseNetworkConfig(t *testing.T) {
	actual, err := ParseNetworkConfig(DefaultNetworkConfig.Bytes())
	if err != nil {
		t.Fatal("ParseNetworkConfig returned an error:", err)
	}
	if actual.Network != DefaultNetworkConfig.Network || actual.LeaseTime != DefaultNetworkConfig.LeaseTime {
		t.Fatalf("ParseNetworkConfig returned the wrong value, got: %v, expected: %v", actual, DefaultNetworkConfig)
	}
}

func TestNewTunPayload(t *testing.T) {
	payload := NewTunPayload(testPacket, 2)
	for i := 0; i < 4; i++ {
		if payload.IPAddress[i] != 1 {
			t.Fatal("NewTunPayload returned an incorrect IP address mapping.")
		}
	}

	for i := 0; i < 12; i++ {
		if payload.Nonce[i] != 2 {
			t.Fatal("NewTunPayload returned an incorrect Nonce mapping.")
		}
	}

	for i := 0; i < 2; i++ {
		if payload.Packet[i] != 3 {
			t.Fatal("NewTunPayload returned an incorrect Packet mapping.")
		}
	}
}

func TestNewSockPayload(t *testing.T) {
	payload := NewSockPayload(testPacket, 18)
	for i := 0; i < 4; i++ {
		if payload.IPAddress[i] != 1 {
			t.Fatal("NewTunPayload returned an incorrect IP address mapping.")
		}
	}

	for i := 0; i < 12; i++ {
		if payload.Nonce[i] != 2 {
			t.Fatal("NewTunPayload returned an incorrect Nonce mapping.")
		}
	}

	for i := 0; i < 2; i++ {
		if payload.Packet[i] != 3 {
			t.Fatal("NewTunPayload returned an incorrect Packet mapping.")
		}
	}
}

func TestNewStats(t *testing.T) {
	stats := NewStats(1)
	if stats.Packets != 0 {
		t.Fatalf("NewStats did not return the correct default for Packets, got: %d, expected: %d", stats.Packets, 0)
	}
	if stats.Bytes != 0 {
		t.Fatalf("NewStats did not return the correct default for Bytes, got: %d, expected: %d", stats.Bytes, 0)
	}
	if stats.Links == nil {
		t.Fatalf("NewStats did not return the correct default for Links, got: %v, expected: %v", stats.Links, make(map[string]*Stats))
	}
	str := stats.String()
	if str == "" {
		t.Fatalf("String didn't return the correct value.")
	}
}

func TestNewLogger(t *testing.T) {
	log := NewLogger()
	if log.Error == nil {
		t.Fatal("NewLogger returned a nil Error log.")
	}
	if log.Info == nil {
		t.Fatal("NewLogger returned a nil Error log.")
	}
}

func TestGenerateLocalMapping(t *testing.T) {
	cfg := &Config{
		PrivateIP:     net.ParseIP("10.10.0.1"),
		PublicIPv4:    net.ParseIP("8.8.8.8"),
		PublicIPv6:    net.ParseIP("::"),
		ListenPort:    1099,
		PublicKey:     make([]byte, 32),
		NetworkConfig: DefaultNetworkConfig,
		MachineID:     "123",
	}

	mappings := make(map[uint32]*Mapping)
	mapping, err := GenerateLocalMapping(cfg, mappings)
	if err != nil {
		t.Fatal(err)
	}

	if !testEq(mapping.PrivateIP.To4(), cfg.PrivateIP.To4()) {
		t.Fatal("GenerateLocalMapping created the wrong mapping.")
	}

	mappings[IPtoInt(cfg.PrivateIP)] = mapping

	_, err = GenerateLocalMapping(cfg, mappings)
	if err != nil {
		t.Fatal(err)
	}

	mapping.MachineID = "456"

	_, err = GenerateLocalMapping(cfg, mappings)
	if err == nil {
		t.Fatal("GenerateLocalMapping failed to properly handle an existing ip address")
	}

	cfg.PrivateIP = nil
	mapping.MachineID = "123"

	_, err = GenerateLocalMapping(cfg, mappings)
	if err != nil {
		t.Fatal(err)
	}

	delete(mappings, IPtoInt(net.ParseIP("10.10.0.1")))

	_, err = GenerateLocalMapping(cfg, mappings)
	if err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package worker

import (
	"fmt"
	"strings"

	"github.com/kyma-project/kyma/components/binding/internal"
	"github.com/kyma-project/kyma/components/binding/pkg/apis/v1alpha1"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
)

type KindManager interface {
	AddLabel(*v1alpha1.Binding) error
	RemoveLabel(*v1alpha1.Binding) error
	LabelExist(*v1alpha1.Binding) (bool, error)
	RemoveOldAddNewLabel(*v1alpha1.Binding) error
}

type BindingWorker struct {
	kindManager KindManager
}

func NewBindingWorker(km KindManager) *BindingWorker {
	return &BindingWorker{
		kindManager: km,
	}
}

func (b *BindingWorker) RemoveProcess(binding *v1alpha1.Binding, log log.FieldLogger) (*v1alpha1.Binding, error) {
	log.Info("start Binding removing process")

	labelExist, err := b.kindManager.LabelExist(binding)
	if err != nil {
		errStatus := binding.Status.Failed()
		if errStatus != nil {
			return binding, errors.Wrapf(errStatus, "while set Binding phase to %s", v1alpha1.BindingFailed)
		}
		binding.Status.Message = fmt.Sprintf(internal.BindingRemovingFailed, err)
		return binding, errors.Wrap(err, "while checking if label exist")
	}

	if !labelExist {
		log.Info("label does not exist, remove process finished")
		return b.removeFinalizer(binding), nil
	}

	err = b.kindManager.RemoveLabel(binding)
	if err != nil {
		errStatus := binding.Status.Failed()
		if errStatus != nil {
			return binding, errors.Wrapf(errStatus, "while set Binding phase to %s", v1alpha1.BindingFailed)
		}
		binding.Status.Message = fmt.Sprintf(internal.BindingRemovingFailed, err)
		return binding, errors.Wrap(err, "while removing label")
	}

	return b.removeFinalizer(binding), nil
}

func (b *BindingWorker) Process(binding *v1alpha1.Binding, log log.FieldLogger) (*v1alpha1.Binding, error) {
	log.Info("start Binding process")

	if binding.Status.IsEmpty() {
		log.Info("binding status is empty. Binding initialization.")
		err := binding.Status.Init()
		if err != nil {
			return binding, errors.Wrap(err, "while init Binding phase")
		}
		binding.Status.Target = fmt.Sprintf("%s/%s", binding.Spec.Target.Kind, binding.Spec.Target.Name)
		binding.Status.Source = fmt.Sprintf("%s/%s", binding.Spec.Source.Kind, binding.Spec.Source.Name)
		binding.Status.Message = internal.BindingInitialization
		return binding, nil
	}
	if _, ok := binding.Labels[v1alpha1.BindingValidatedLabelKey]; !ok {
		if !binding.Status.IsFailed() {
			errStatus := binding.Status.Failed()
			if errStatus != nil {
				return binding, errors.Wrapf(errStatus, "while set Binding phase to %s", v1alpha1.BindingFailed)
			}
			binding.Status.Message = internal.BindingValidationFailed
		}
		return binding, errors.New(internal.BindingValidationFailed)
	}

	switch binding.Status.Phase {
	case v1alpha1.BindingPending:
		return b.pendingPhase(binding, log)
	case v1alpha1.BindingReady:
		return b.readyPhase(binding, log)
	case v1alpha1.BindingFailed:
		return b.failedPhase(binding, log)
	}

	return binding, errors.Errorf("status phase %s not supported", binding.Status.Phase)
}

// pendingPhase adds label to Target; marks Binding as Ready
func (b *BindingWorker) pendingPhase(binding *v1alpha1.Binding, log log.FieldLogger) (*v1alpha1.Binding, error) {
	log.Infof("set labels to the target: %s - %s", binding.Spec.Target.Kind, binding.Spec.Target.Name)
	err := b.kindManager.AddLabel(binding)
	if err != nil {
		errStatus := binding.Status.Failed()
		if errStatus != nil {
			return binding, errors.Wrapf(errStatus, "while set Binding phase to %s", v1alpha1.BindingFailed)
		}
		binding.Status.Message = fmt.Sprintf(internal.BindingTargetFailed, err)
		return binding, errors.Wrapf(err, "while adding label to target (phase: %s)", v1alpha1.BindingPending)
	}

	log.Info("Binding process successfully completed")
	err = binding.Status.Ready()
	if err != nil {
		return binding, errors.Wrapf(err, "while set Binding phase to %s", v1alpha1.BindingReady)
	}
	binding.Status.Message = internal.BindingReady

	return binding, nil
}

// readyPhase checks if Target was changed; if yes remove label from old Target
// checks if Source was changed; if yes removes old label from Target and adds new
// checks if label in Target exist, if not adds label to Target
func (b *BindingWorker) readyPhase(binding *v1alpha1.Binding, log log.FieldLogger) (*v1alpha1.Binding, error) {
	if binding.Status.Target != fmt.Sprintf("%s/%s", binding.Spec.Target.Kind, binding.Spec.Target.Name) {
		log.Info("target was changed, removing label from old target")
		bindingCopy := binding.DeepCopy()
		bindingCopy.Spec.Target.Kind = strings.Split(binding.Status.Target, "/")[0]
		bindingCopy.Spec.Target.Name = strings.Split(binding.Status.Target, "/")[1]
		err := b.kindManager.RemoveLabel(bindingCopy)
		if err != nil {
			return binding, errors.Wrap(err, "while removing label from old target")
		}
		binding.Status.Target = fmt.Sprintf("%s/%s", binding.Spec.Target.Kind, binding.Spec.Target.Name)
	}

	if binding.Status.Source != fmt.Sprintf("%s/%s", binding.Spec.Source.Kind, binding.Spec.Source.Name) {
		log.Info("source was changed, removing old label and add new")
		err := b.kindManager.RemoveOldAddNewLabel(binding)
		if err != nil {
			return binding, errors.Wrap(err, "while removing old label and adding new in target")
		}
		binding.Status.Source = fmt.Sprintf("%s/%s", binding.Spec.Source.Kind, binding.Spec.Source.Name)
	}
	labelExist, err := b.kindManager.LabelExist(binding)
	if err != nil {
		return binding, errors.Wrap(err, "while checking if label exist in target")
	}
	if !labelExist {
		log.Infof("Binding has %s state but label not exist in target, adding new label", v1alpha1.BindingReady)
		err := b.kindManager.AddLabel(binding)
		if err != nil {
			return binding, errors.Wrapf(err, "while adding label to target (phase: %s)", v1alpha1.BindingReady)
		}
	}

	return binding, nil
}

// failedPhase check if label on target exist; if yes removes old label, adds new and marks Binding as Ready
// if not triggers pending process for Binding
func (b *BindingWorker) failedPhase(binding *v1alpha1.Binding, log log.FieldLogger) (*v1alpha1.Binding, error) {
	labelExist, err := b.kindManager.LabelExist(binding)
	if err != nil {
		return binding, errors.Wrap(err, "while checking if label exist in target")
	}

	if labelExist {
		log.Infof("Binding with phase %s has label on target, removing label", v1alpha1.BindingFailed)
		err := b.kindManager.RemoveLabel(binding)
		if err != nil {
			return binding, errors.Wrap(err, "while removing old label and adding new in target")
		}
	}

	err = binding.Status.Pending()
	if err != nil {
		return binding, errors.Wrapf(err, "while set Binding phase to %s", v1alpha1.BindingPending)
	}
	binding.Status.Message = internal.BindingPendingFromFailed

	return binding, nil
}

func (b *BindingWorker) removeFinalizer(binding *v1alpha1.Binding) *v1alpha1.Binding {
	if binding.Finalizers == nil {
		return binding
	}

	updatedFinalizers := make([]string, 0)
	for _, finalizer := range binding.Finalizers {
		if finalizer == v1alpha1.BindingFinalizer {
			continue
		}
		updatedFinalizers = append(updatedFinalizers, finalizer)
	}

	binding.Finalizers = updatedFinalizers
	return binding
}
<commit_msg>Fix binding reconcile logic (#9906)<commit_after>package worker

import (
	"fmt"
	"strings"

	"github.com/kyma-project/kyma/components/binding/internal"
	"github.com/kyma-project/kyma/components/binding/pkg/apis/v1alpha1"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
)

type KindManager interface {
	AddLabel(*v1alpha1.Binding) error
	RemoveLabel(*v1alpha1.Binding) error
	LabelExist(*v1alpha1.Binding) (bool, error)
	RemoveOldAddNewLabel(*v1alpha1.Binding) error
}

type BindingWorker struct {
	kindManager KindManager
}

func NewBindingWorker(km KindManager) *BindingWorker {
	return &BindingWorker{
		kindManager: km,
	}
}

func (b *BindingWorker) RemoveProcess(binding *v1alpha1.Binding, log log.FieldLogger) (*v1alpha1.Binding, error) {
	log.Info("start Binding removing process")

	labelExist, err := b.kindManager.LabelExist(binding)
	if err != nil {
		errStatus := binding.Status.Failed()
		if errStatus != nil {
			return binding, errors.Wrapf(errStatus, "while set Binding phase to %s", v1alpha1.BindingFailed)
		}
		binding.Status.Message = fmt.Sprintf(internal.BindingRemovingFailed, err)
		return binding, errors.Wrap(err, "while checking if label exist")
	}

	if !labelExist {
		log.Info("label does not exist, remove process finished")
		return b.removeFinalizer(binding), nil
	}

	err = b.kindManager.RemoveLabel(binding)
	if err != nil {
		errStatus := binding.Status.Failed()
		if errStatus != nil {
			return binding, errors.Wrapf(errStatus, "while set Binding phase to %s", v1alpha1.BindingFailed)
		}
		binding.Status.Message = fmt.Sprintf(internal.BindingRemovingFailed, err)
		return binding, errors.Wrap(err, "while removing label")
	}

	return b.removeFinalizer(binding), nil
}

func (b *BindingWorker) Process(binding *v1alpha1.Binding, log log.FieldLogger) (*v1alpha1.Binding, error) {
	log.Info("start Binding process")

	if binding.Status.IsEmpty() {
		log.Info("binding status is empty. Binding initialization.")
		err := binding.Status.Init()
		if err != nil {
			return binding, errors.Wrap(err, "while init Binding phase")
		}
		binding.Status.Target = fmt.Sprintf("%s/%s", binding.Spec.Target.Kind, binding.Spec.Target.Name)
		binding.Status.Source = fmt.Sprintf("%s/%s", binding.Spec.Source.Kind, binding.Spec.Source.Name)
		binding.Status.Message = internal.BindingInitialization
		return binding, nil
	}
	if _, ok := binding.Labels[v1alpha1.BindingValidatedLabelKey]; !ok {
		if binding.Status.IsFailed() {
			return binding, errors.New(internal.BindingValidationFailed)
		}
		errStatus := binding.Status.Failed()
		if errStatus != nil {
			return binding, errors.Wrapf(errStatus, "while set Binding phase to %s", v1alpha1.BindingFailed)
		}
		binding.Status.Message = internal.BindingValidationFailed
		return binding, nil
	}

	switch binding.Status.Phase {
	case v1alpha1.BindingPending:
		return b.pendingPhase(binding, log)
	case v1alpha1.BindingReady:
		return b.readyPhase(binding, log)
	case v1alpha1.BindingFailed:
		return b.failedPhase(binding, log)
	}

	return binding, errors.Errorf("status phase %s not supported", binding.Status.Phase)
}

// pendingPhase adds label to Target; marks Binding as Ready
func (b *BindingWorker) pendingPhase(binding *v1alpha1.Binding, log log.FieldLogger) (*v1alpha1.Binding, error) {
	log.Infof("set labels to the target: %s - %s", binding.Spec.Target.Kind, binding.Spec.Target.Name)
	err := b.kindManager.AddLabel(binding)
	if err != nil {
		errStatus := binding.Status.Failed()
		if errStatus != nil {
			return binding, errors.Wrapf(errStatus, "while set Binding phase to %s", v1alpha1.BindingFailed)
		}
		binding.Status.Message = fmt.Sprintf(internal.BindingTargetFailed, err)
		return binding, errors.Wrapf(err, "while adding label to target (phase: %s)", v1alpha1.BindingPending)
	}

	log.Info("Binding process successfully completed")
	err = binding.Status.Ready()
	if err != nil {
		return binding, errors.Wrapf(err, "while set Binding phase to %s", v1alpha1.BindingReady)
	}
	binding.Status.Message = internal.BindingReady

	return binding, nil
}

// readyPhase checks if Target was changed; if yes remove label from old Target
// checks if Source was changed; if yes removes old label from Target and adds new
// checks if label in Target exist, if not adds label to Target
func (b *BindingWorker) readyPhase(binding *v1alpha1.Binding, log log.FieldLogger) (*v1alpha1.Binding, error) {
	if binding.Status.Target != fmt.Sprintf("%s/%s", binding.Spec.Target.Kind, binding.Spec.Target.Name) {
		log.Info("target was changed, removing label from old target")
		bindingCopy := binding.DeepCopy()
		bindingCopy.Spec.Target.Kind = strings.Split(binding.Status.Target, "/")[0]
		bindingCopy.Spec.Target.Name = strings.Split(binding.Status.Target, "/")[1]
		err := b.kindManager.RemoveLabel(bindingCopy)
		if err != nil {
			return binding, errors.Wrap(err, "while removing label from old target")
		}
		binding.Status.Target = fmt.Sprintf("%s/%s", binding.Spec.Target.Kind, binding.Spec.Target.Name)
	}

	if binding.Status.Source != fmt.Sprintf("%s/%s", binding.Spec.Source.Kind, binding.Spec.Source.Name) {
		log.Info("source was changed, removing old label and add new")
		err := b.kindManager.RemoveOldAddNewLabel(binding)
		if err != nil {
			return binding, errors.Wrap(err, "while removing old label and adding new in target")
		}
		binding.Status.Source = fmt.Sprintf("%s/%s", binding.Spec.Source.Kind, binding.Spec.Source.Name)
	}
	labelExist, err := b.kindManager.LabelExist(binding)
	if err != nil {
		return binding, errors.Wrap(err, "while checking if label exist in target")
	}
	if !labelExist {
		log.Infof("Binding has %s state but label not exist in target, adding new label", v1alpha1.BindingReady)
		err := b.kindManager.AddLabel(binding)
		if err != nil {
			return binding, errors.Wrapf(err, "while adding label to target (phase: %s)", v1alpha1.BindingReady)
		}
	}

	return binding, nil
}

// failedPhase check if label on target exist; if yes removes old label, adds new and marks Binding as Ready
// if not triggers pending process for Binding
func (b *BindingWorker) failedPhase(binding *v1alpha1.Binding, log log.FieldLogger) (*v1alpha1.Binding, error) {
	labelExist, err := b.kindManager.LabelExist(binding)
	if err != nil {
		return binding, errors.Wrap(err, "while checking if label exist in target")
	}

	if labelExist {
		log.Infof("Binding with phase %s has label on target, removing label", v1alpha1.BindingFailed)
		err := b.kindManager.RemoveLabel(binding)
		if err != nil {
			return binding, errors.Wrap(err, "while removing old label and adding new in target")
		}
	}

	err = binding.Status.Pending()
	if err != nil {
		return binding, errors.Wrapf(err, "while set Binding phase to %s", v1alpha1.BindingPending)
	}
	binding.Status.Message = internal.BindingPendingFromFailed

	return binding, nil
}

func (b *BindingWorker) removeFinalizer(binding *v1alpha1.Binding) *v1alpha1.Binding {
	if binding.Finalizers == nil {
		return binding
	}

	updatedFinalizers := make([]string, 0)
	for _, finalizer := range binding.Finalizers {
		if finalizer == v1alpha1.BindingFinalizer {
			continue
		}
		updatedFinalizers = append(updatedFinalizers, finalizer)
	}

	binding.Finalizers = updatedFinalizers
	return binding
}
<|endoftext|>


<commit_before>package hosts

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"sync"

	"github.com/prometheus/client_golang/prometheus"
)

// Exporter collects Rancher stats from machine of a specified user and exports them using
// the prometheus metrics package.
type Exporter struct {
	rancherURL string
	accessKey  string
	secretKey  string
	mutex      sync.RWMutex
	gaugeVecs  map[string]*prometheus.GaugeVec
}

// HostsData is used to store data from the hosts endpoint in the API
type HostsData struct {
	Data []struct {
		Hostname string `json:"hostname"`
		State    string `json:"state"`
	} `json:"data"`
}

// NewExporter returns an initialized Exporter.
func NewExporter(rancherURL string, accessKey string, secretKey string) *Exporter {

	gaugeVecs := make(map[string]*prometheus.GaugeVec)

	gaugeVecs["HostState"] = prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Namespace: "rancher",
			Name:      ("host_state_activating"),
			Help:      "State of defined host as reported by the Rancher API",
		}, []string{"rancherURL", "name", "state"})

	return &Exporter{
		gaugeVecs:  gaugeVecs,
		rancherURL: rancherURL,
		accessKey:  accessKey,
		secretKey:  secretKey,
	}
}

// Describe describes all the metrics ever exported by the Rancher exporter. It
// implements prometheus.Collector.
func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {

	for _, m := range e.gaugeVecs {
		m.Describe(ch)
	}
}

// Gets the JSON response from the API and places it in the struct
func getJSONhosts(rancherURL string, accessKey string, secretKey string) (error, HostsData) {
	pulledData := HostsData{}
	client := &http.Client{}
	req, err := http.NewRequest("GET", rancherURL, nil)
	req.SetBasicAuth(accessKey, secretKey)

	resp, err := client.Do(req)

	if err != nil {
		fmt.Println("Error Collecting JSON from API: ", err)
		panic(err)
	}
	return json.NewDecoder(resp.Body).Decode(&pulledData), pulledData

}

func (e *Exporter) scrapeHosts(rancherURL string, accessKey string, secretKey string, ch chan<- prometheus.Metric) error {

	for _, m := range e.gaugeVecs {
		m.Reset()
	}

	fmt.Println("Scraping: ", rancherURL+"/hosts/")
	err, hostsData := getJSONhosts(rancherURL+"/hosts/", accessKey, secretKey)
	if err != nil {
		return err
	}
	fmt.Println("JSON Fetched for hosts: ", hostsData)

	// Host Metrics
	for _, x := range hostsData.Data {

		// Set all the metrics to 0, unless we get a match

		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "activating"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "active"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "deactivating"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "error"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "erroring"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "inactive"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "provisioned"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "purged"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "purging"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "registering"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "removed"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "removing"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "requested"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "restoring"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "updating_active"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "updating_inactive"}).Set(0)

		// Match states of the API to known values and override our values above.
		if x.State == "activating" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "activating"}).Set(1)
		} else if x.State == "active" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "active"}).Set(1)
		} else if x.State == "deactivating" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "deactivating"}).Set(1)
		} else if x.State == "error" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "error"}).Set(1)
		} else if x.State == "erroring" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "erroring"}).Set(1)
		} else if x.State == "inactive" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "inactive"}).Set(1)
		} else if x.State == "provisioned" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "provisioned"}).Set(1)
		} else if x.State == "purged" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "purged"}).Set(1)
		} else if x.State == "purging" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "purging"}).Set(1)
		} else if x.State == "registering" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "registering"}).Set(1)
		} else if x.State == "removed" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "removed"}).Set(1)
		} else if x.State == "removing" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "removing"}).Set(1)
		} else if x.State == "requested" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "requested"}).Set(1)
		} else if x.State == "restoring" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "restoring"}).Set(1)
		} else if x.State == "updating-active" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "updating_active"}).Set(1)
		} else if x.State == "updating-inactive" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "updating_inactive"}).Set(1)
		}

	}

	return nil

}

// Collect function, called on by Prometheus Client
func (e *Exporter) Collect(ch chan<- prometheus.Metric) {

	e.mutex.Lock() // To protect metrics from concurrent collects.
	defer e.mutex.Unlock()

	if err := e.scrapeHosts(e.rancherURL, e.accessKey, e.secretKey, ch); err != nil {
		log.Printf("Error scraping rancher url: %s", err)
		return
	}

	for _, m := range e.gaugeVecs {
		m.Collect(ch)
	}
}
<commit_msg>Fix error with host state metric label<commit_after>package hosts

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"sync"

	"github.com/prometheus/client_golang/prometheus"
)

// Exporter collects Rancher stats from machine of a specified user and exports them using
// the prometheus metrics package.
type Exporter struct {
	rancherURL string
	accessKey  string
	secretKey  string
	mutex      sync.RWMutex
	gaugeVecs  map[string]*prometheus.GaugeVec
}

// HostsData is used to store data from the hosts endpoint in the API
type HostsData struct {
	Data []struct {
		Hostname string `json:"hostname"`
		State    string `json:"state"`
	} `json:"data"`
}

// NewExporter returns an initialized Exporter.
func NewExporter(rancherURL string, accessKey string, secretKey string) *Exporter {

	gaugeVecs := make(map[string]*prometheus.GaugeVec)

	gaugeVecs["HostState"] = prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Namespace: "rancher",
			Name:      ("host_state"),
			Help:      "State of defined host as reported by the Rancher API",
		}, []string{"rancherURL", "name", "state"})

	return &Exporter{
		gaugeVecs:  gaugeVecs,
		rancherURL: rancherURL,
		accessKey:  accessKey,
		secretKey:  secretKey,
	}
}

// Describe describes all the metrics ever exported by the Rancher exporter. It
// implements prometheus.Collector.
func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {

	for _, m := range e.gaugeVecs {
		m.Describe(ch)
	}
}

// Gets the JSON response from the API and places it in the struct
func getJSONhosts(rancherURL string, accessKey string, secretKey string) (error, HostsData) {
	pulledData := HostsData{}
	client := &http.Client{}
	req, err := http.NewRequest("GET", rancherURL, nil)
	req.SetBasicAuth(accessKey, secretKey)

	resp, err := client.Do(req)

	if err != nil {
		fmt.Println("Error Collecting JSON from API: ", err)
		panic(err)
	}
	return json.NewDecoder(resp.Body).Decode(&pulledData), pulledData

}

func (e *Exporter) scrapeHosts(rancherURL string, accessKey string, secretKey string, ch chan<- prometheus.Metric) error {

	for _, m := range e.gaugeVecs {
		m.Reset()
	}

	fmt.Println("Scraping: ", rancherURL+"/hosts/")
	err, hostsData := getJSONhosts(rancherURL+"/hosts/", accessKey, secretKey)
	if err != nil {
		return err
	}
	fmt.Println("JSON Fetched for hosts: ", hostsData)

	// Host Metrics
	for _, x := range hostsData.Data {

		// Set all the metrics to 0, unless we get a match

		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "activating"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "active"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "deactivating"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "error"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "erroring"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "inactive"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "provisioned"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "purged"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "purging"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "registering"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "removed"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "removing"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "requested"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "restoring"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "updating_active"}).Set(0)
		e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "updating_inactive"}).Set(0)

		// Match states of the API to known values and override our values above.
		if x.State == "activating" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "activating"}).Set(1)
		} else if x.State == "active" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "active"}).Set(1)
		} else if x.State == "deactivating" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "deactivating"}).Set(1)
		} else if x.State == "error" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "error"}).Set(1)
		} else if x.State == "erroring" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "erroring"}).Set(1)
		} else if x.State == "inactive" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "inactive"}).Set(1)
		} else if x.State == "provisioned" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "provisioned"}).Set(1)
		} else if x.State == "purged" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "purged"}).Set(1)
		} else if x.State == "purging" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "purging"}).Set(1)
		} else if x.State == "registering" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "registering"}).Set(1)
		} else if x.State == "removed" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "removed"}).Set(1)
		} else if x.State == "removing" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "removing"}).Set(1)
		} else if x.State == "requested" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "requested"}).Set(1)
		} else if x.State == "restoring" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "restoring"}).Set(1)
		} else if x.State == "updating-active" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "updating_active"}).Set(1)
		} else if x.State == "updating-inactive" {
			e.gaugeVecs["HostState"].With(prometheus.Labels{"rancherURL": rancherURL, "name": x.Hostname, "state": "updating_inactive"}).Set(1)
		}

	}

	return nil

}

// Collect function, called on by Prometheus Client
func (e *Exporter) Collect(ch chan<- prometheus.Metric) {

	e.mutex.Lock() // To protect metrics from concurrent collects.
	defer e.mutex.Unlock()

	if err := e.scrapeHosts(e.rancherURL, e.accessKey, e.secretKey, ch); err != nil {
		log.Printf("Error scraping rancher url: %s", err)
		return
	}

	for _, m := range e.gaugeVecs {
		m.Collect(ch)
	}
}
<|endoftext|>