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

package etcdserver

import (
	"context"
	"fmt"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/coreos/etcd/pkg/netutil"
	"github.com/coreos/etcd/pkg/transport"
	"github.com/coreos/etcd/pkg/types"

	"go.uber.org/zap"
)

// ServerConfig holds the configuration of etcd as taken from the command line or discovery.
type ServerConfig struct {
	Name           string
	DiscoveryURL   string
	DiscoveryProxy string
	ClientURLs     types.URLs
	PeerURLs       types.URLs
	DataDir        string
	// DedicatedWALDir config will make the etcd to write the WAL to the WALDir
	// rather than the dataDir/member/wal.
	DedicatedWALDir     string
	SnapCount           uint64
	MaxSnapFiles        uint
	MaxWALFiles         uint
	InitialPeerURLsMap  types.URLsMap
	InitialClusterToken string
	NewCluster          bool
	PeerTLSInfo         transport.TLSInfo

	CORS map[string]struct{}

	// HostWhitelist lists acceptable hostnames from client requests.
	// If server is insecure (no TLS), server only accepts requests
	// whose Host header value exists in this white list.
	HostWhitelist map[string]struct{}

	TickMs           uint
	ElectionTicks    int
	BootstrapTimeout time.Duration

	AutoCompactionRetention time.Duration
	AutoCompactionMode      string
	QuotaBackendBytes       int64
	MaxTxnOps               uint

	// MaxRequestBytes is the maximum request size to send over raft.
	MaxRequestBytes uint

	StrictReconfigCheck bool

	// ClientCertAuthEnabled is true when cert has been signed by the client CA.
	ClientCertAuthEnabled bool

	AuthToken string

	// InitialCorruptCheck is true to check data corruption on boot
	// before serving any peer/client traffic.
	InitialCorruptCheck bool
	CorruptCheckTime    time.Duration

	// PreVote is true to enable Raft Pre-Vote.
	PreVote bool

	// Logger logs server-side operations.
	// If not nil, it disables "capnslog" and uses the given logger.
	Logger *zap.Logger
	// LoggerConfig is server logger configuration for Raft logger.
	LoggerConfig zap.Config

	Debug bool

	ForceNewCluster bool
}

// VerifyBootstrap sanity-checks the initial config for bootstrap case
// and returns an error for things that should never happen.
func (c *ServerConfig) VerifyBootstrap() error {
	if err := c.hasLocalMember(); err != nil {
		return err
	}
	if err := c.advertiseMatchesCluster(); err != nil {
		return err
	}
	if checkDuplicateURL(c.InitialPeerURLsMap) {
		return fmt.Errorf("initial cluster %s has duplicate url", c.InitialPeerURLsMap)
	}
	if c.InitialPeerURLsMap.String() == "" && c.DiscoveryURL == "" {
		return fmt.Errorf("initial cluster unset and no discovery URL found")
	}
	return nil
}

// VerifyJoinExisting sanity-checks the initial config for join existing cluster
// case and returns an error for things that should never happen.
func (c *ServerConfig) VerifyJoinExisting() error {
	// The member has announced its peer urls to the cluster before starting; no need to
	// set the configuration again.
	if err := c.hasLocalMember(); err != nil {
		return err
	}
	if checkDuplicateURL(c.InitialPeerURLsMap) {
		return fmt.Errorf("initial cluster %s has duplicate url", c.InitialPeerURLsMap)
	}
	if c.DiscoveryURL != "" {
		return fmt.Errorf("discovery URL should not be set when joining existing initial cluster")
	}
	return nil
}

// hasLocalMember checks that the cluster at least contains the local server.
func (c *ServerConfig) hasLocalMember() error {
	if urls := c.InitialPeerURLsMap[c.Name]; urls == nil {
		return fmt.Errorf("couldn't find local name %q in the initial cluster configuration", c.Name)
	}
	return nil
}

// advertiseMatchesCluster confirms peer URLs match those in the cluster peer list.
func (c *ServerConfig) advertiseMatchesCluster() error {
	urls, apurls := c.InitialPeerURLsMap[c.Name], c.PeerURLs.StringSlice()
	urls.Sort()
	sort.Strings(apurls)
	ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second)
	defer cancel()
	ok, err := netutil.URLStringsEqual(ctx, apurls, urls.StringSlice())
	if ok {
		return nil
	}

	initMap, apMap := make(map[string]struct{}), make(map[string]struct{})
	for _, url := range c.PeerURLs {
		apMap[url.String()] = struct{}{}
	}
	for _, url := range c.InitialPeerURLsMap[c.Name] {
		initMap[url.String()] = struct{}{}
	}

	missing := []string{}
	for url := range initMap {
		if _, ok := apMap[url]; !ok {
			missing = append(missing, url)
		}
	}
	if len(missing) > 0 {
		for i := range missing {
			missing[i] = c.Name + "=" + missing[i]
		}
		mstr := strings.Join(missing, ",")
		apStr := strings.Join(apurls, ",")
		return fmt.Errorf("--initial-cluster has %s but missing from --initial-advertise-peer-urls=%s (%v)", mstr, apStr, err)
	}

	for url := range apMap {
		if _, ok := initMap[url]; !ok {
			missing = append(missing, url)
		}
	}
	if len(missing) > 0 {
		mstr := strings.Join(missing, ",")
		umap := types.URLsMap(map[string]types.URLs{c.Name: c.PeerURLs})
		return fmt.Errorf("--initial-advertise-peer-urls has %s but missing from --initial-cluster=%s", mstr, umap.String())
	}

	// resolved URLs from "--initial-advertise-peer-urls" and "--initial-cluster" did not match or failed
	apStr := strings.Join(apurls, ",")
	umap := types.URLsMap(map[string]types.URLs{c.Name: c.PeerURLs})
	return fmt.Errorf("failed to resolve %s to match --initial-cluster=%s (%v)", apStr, umap.String(), err)
}

func (c *ServerConfig) MemberDir() string { return filepath.Join(c.DataDir, "member") }

func (c *ServerConfig) WALDir() string {
	if c.DedicatedWALDir != "" {
		return c.DedicatedWALDir
	}
	return filepath.Join(c.MemberDir(), "wal")
}

func (c *ServerConfig) SnapDir() string { return filepath.Join(c.MemberDir(), "snap") }

func (c *ServerConfig) ShouldDiscover() bool { return c.DiscoveryURL != "" }

// ReqTimeout returns timeout for request to finish.
func (c *ServerConfig) ReqTimeout() time.Duration {
	// 5s for queue waiting, computation and disk IO delay
	// + 2 * election timeout for possible leader election
	return 5*time.Second + 2*time.Duration(c.ElectionTicks*int(c.TickMs))*time.Millisecond
}

func (c *ServerConfig) electionTimeout() time.Duration {
	return time.Duration(c.ElectionTicks*int(c.TickMs)) * time.Millisecond
}

func (c *ServerConfig) peerDialTimeout() time.Duration {
	// 1s for queue wait and election timeout
	return time.Second + time.Duration(c.ElectionTicks*int(c.TickMs))*time.Millisecond
}

func (c *ServerConfig) PrintWithInitial() { c.print(true) }

func (c *ServerConfig) Print() { c.print(false) }

func (c *ServerConfig) print(initial bool) {
	// TODO: remove this after dropping "capnslog"
	if c.Logger == nil {
		plog.Infof("name = %s", c.Name)
		if c.ForceNewCluster {
			plog.Infof("force new cluster")
		}
		plog.Infof("data dir = %s", c.DataDir)
		plog.Infof("member dir = %s", c.MemberDir())
		if c.DedicatedWALDir != "" {
			plog.Infof("dedicated WAL dir = %s", c.DedicatedWALDir)
		}
		plog.Infof("heartbeat = %dms", c.TickMs)
		plog.Infof("election = %dms", c.ElectionTicks*int(c.TickMs))
		plog.Infof("snapshot count = %d", c.SnapCount)
		if len(c.DiscoveryURL) != 0 {
			plog.Infof("discovery URL= %s", c.DiscoveryURL)
			if len(c.DiscoveryProxy) != 0 {
				plog.Infof("discovery proxy = %s", c.DiscoveryProxy)
			}
		}
		plog.Infof("advertise client URLs = %s", c.ClientURLs)
		if initial {
			plog.Infof("initial advertise peer URLs = %s", c.PeerURLs)
			plog.Infof("initial cluster = %s", c.InitialPeerURLsMap)
		}
	} else {
		state := "new"
		if !c.NewCluster {
			state = "existing"
		}
		c.Logger.Info(
			"server configuration",
			zap.String("name", c.Name),
			zap.String("data-dir", c.DataDir),
			zap.String("member-dir", c.MemberDir()),
			zap.String("dedicated-wal-dir", c.DedicatedWALDir),
			zap.Bool("force-new-cluster", c.ForceNewCluster),
			zap.Uint("heartbeat-tick-ms", c.TickMs),
			zap.String("heartbeat-interval", fmt.Sprintf("%v", time.Duration(c.TickMs)*time.Millisecond)),
			zap.Int("election-tick-ms", c.ElectionTicks),
			zap.String("election-timeout", fmt.Sprintf("%v", time.Duration(c.ElectionTicks*int(c.TickMs))*time.Millisecond)),
			zap.Uint64("snapshot-count", c.SnapCount),
			zap.Strings("advertise-client-urls", c.getACURLs()),
			zap.Strings("initial-advertise-peer-urls", c.getAPURLs()),
			zap.Bool("initial", initial),
			zap.String("initial-cluster", c.InitialPeerURLsMap.String()),
			zap.String("initial-cluster-state", state),
			zap.String("initial-cluster-token", c.InitialClusterToken),
			zap.Bool("pre-vote", c.PreVote),
			zap.Bool("initial-corrupt-check", c.InitialCorruptCheck),
			zap.Duration("corrupt-check-time", c.CorruptCheckTime),
			zap.String("auto-compaction-mode", c.AutoCompactionMode),
			zap.Duration("auto-compaction-retention", c.AutoCompactionRetention),
			zap.String("discovery-url", c.DiscoveryURL),
			zap.String("discovery-proxy", c.DiscoveryProxy),
		)
	}
}

func checkDuplicateURL(urlsmap types.URLsMap) bool {
	um := make(map[string]bool)
	for _, urls := range urlsmap {
		for _, url := range urls {
			u := url.String()
			if um[u] {
				return true
			}
			um[u] = true
		}
	}
	return false
}

func (c *ServerConfig) bootstrapTimeout() time.Duration {
	if c.BootstrapTimeout != 0 {
		return c.BootstrapTimeout
	}
	return time.Second
}

func (c *ServerConfig) backendPath() string { return filepath.Join(c.SnapDir(), "db") }

func (c *ServerConfig) getAPURLs() (ss []string) {
	ss = make([]string, len(c.PeerURLs))
	for i := range c.PeerURLs {
		ss[i] = c.PeerURLs[i].String()
	}
	return ss
}

func (c *ServerConfig) getACURLs() (ss []string) {
	ss = make([]string, len(c.ClientURLs))
	for i := range c.ClientURLs {
		ss[i] = c.ClientURLs[i].String()
	}
	return ss
}
<commit_msg>etcdserver: print server configuration duration fields in string<commit_after>// Copyright 2015 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package etcdserver

import (
	"context"
	"fmt"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/coreos/etcd/pkg/netutil"
	"github.com/coreos/etcd/pkg/transport"
	"github.com/coreos/etcd/pkg/types"

	"go.uber.org/zap"
)

// ServerConfig holds the configuration of etcd as taken from the command line or discovery.
type ServerConfig struct {
	Name           string
	DiscoveryURL   string
	DiscoveryProxy string
	ClientURLs     types.URLs
	PeerURLs       types.URLs
	DataDir        string
	// DedicatedWALDir config will make the etcd to write the WAL to the WALDir
	// rather than the dataDir/member/wal.
	DedicatedWALDir     string
	SnapCount           uint64
	MaxSnapFiles        uint
	MaxWALFiles         uint
	InitialPeerURLsMap  types.URLsMap
	InitialClusterToken string
	NewCluster          bool
	PeerTLSInfo         transport.TLSInfo

	CORS map[string]struct{}

	// HostWhitelist lists acceptable hostnames from client requests.
	// If server is insecure (no TLS), server only accepts requests
	// whose Host header value exists in this white list.
	HostWhitelist map[string]struct{}

	TickMs           uint
	ElectionTicks    int
	BootstrapTimeout time.Duration

	AutoCompactionRetention time.Duration
	AutoCompactionMode      string
	QuotaBackendBytes       int64
	MaxTxnOps               uint

	// MaxRequestBytes is the maximum request size to send over raft.
	MaxRequestBytes uint

	StrictReconfigCheck bool

	// ClientCertAuthEnabled is true when cert has been signed by the client CA.
	ClientCertAuthEnabled bool

	AuthToken string

	// InitialCorruptCheck is true to check data corruption on boot
	// before serving any peer/client traffic.
	InitialCorruptCheck bool
	CorruptCheckTime    time.Duration

	// PreVote is true to enable Raft Pre-Vote.
	PreVote bool

	// Logger logs server-side operations.
	// If not nil, it disables "capnslog" and uses the given logger.
	Logger *zap.Logger
	// LoggerConfig is server logger configuration for Raft logger.
	LoggerConfig zap.Config

	Debug bool

	ForceNewCluster bool
}

// VerifyBootstrap sanity-checks the initial config for bootstrap case
// and returns an error for things that should never happen.
func (c *ServerConfig) VerifyBootstrap() error {
	if err := c.hasLocalMember(); err != nil {
		return err
	}
	if err := c.advertiseMatchesCluster(); err != nil {
		return err
	}
	if checkDuplicateURL(c.InitialPeerURLsMap) {
		return fmt.Errorf("initial cluster %s has duplicate url", c.InitialPeerURLsMap)
	}
	if c.InitialPeerURLsMap.String() == "" && c.DiscoveryURL == "" {
		return fmt.Errorf("initial cluster unset and no discovery URL found")
	}
	return nil
}

// VerifyJoinExisting sanity-checks the initial config for join existing cluster
// case and returns an error for things that should never happen.
func (c *ServerConfig) VerifyJoinExisting() error {
	// The member has announced its peer urls to the cluster before starting; no need to
	// set the configuration again.
	if err := c.hasLocalMember(); err != nil {
		return err
	}
	if checkDuplicateURL(c.InitialPeerURLsMap) {
		return fmt.Errorf("initial cluster %s has duplicate url", c.InitialPeerURLsMap)
	}
	if c.DiscoveryURL != "" {
		return fmt.Errorf("discovery URL should not be set when joining existing initial cluster")
	}
	return nil
}

// hasLocalMember checks that the cluster at least contains the local server.
func (c *ServerConfig) hasLocalMember() error {
	if urls := c.InitialPeerURLsMap[c.Name]; urls == nil {
		return fmt.Errorf("couldn't find local name %q in the initial cluster configuration", c.Name)
	}
	return nil
}

// advertiseMatchesCluster confirms peer URLs match those in the cluster peer list.
func (c *ServerConfig) advertiseMatchesCluster() error {
	urls, apurls := c.InitialPeerURLsMap[c.Name], c.PeerURLs.StringSlice()
	urls.Sort()
	sort.Strings(apurls)
	ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second)
	defer cancel()
	ok, err := netutil.URLStringsEqual(ctx, apurls, urls.StringSlice())
	if ok {
		return nil
	}

	initMap, apMap := make(map[string]struct{}), make(map[string]struct{})
	for _, url := range c.PeerURLs {
		apMap[url.String()] = struct{}{}
	}
	for _, url := range c.InitialPeerURLsMap[c.Name] {
		initMap[url.String()] = struct{}{}
	}

	missing := []string{}
	for url := range initMap {
		if _, ok := apMap[url]; !ok {
			missing = append(missing, url)
		}
	}
	if len(missing) > 0 {
		for i := range missing {
			missing[i] = c.Name + "=" + missing[i]
		}
		mstr := strings.Join(missing, ",")
		apStr := strings.Join(apurls, ",")
		return fmt.Errorf("--initial-cluster has %s but missing from --initial-advertise-peer-urls=%s (%v)", mstr, apStr, err)
	}

	for url := range apMap {
		if _, ok := initMap[url]; !ok {
			missing = append(missing, url)
		}
	}
	if len(missing) > 0 {
		mstr := strings.Join(missing, ",")
		umap := types.URLsMap(map[string]types.URLs{c.Name: c.PeerURLs})
		return fmt.Errorf("--initial-advertise-peer-urls has %s but missing from --initial-cluster=%s", mstr, umap.String())
	}

	// resolved URLs from "--initial-advertise-peer-urls" and "--initial-cluster" did not match or failed
	apStr := strings.Join(apurls, ",")
	umap := types.URLsMap(map[string]types.URLs{c.Name: c.PeerURLs})
	return fmt.Errorf("failed to resolve %s to match --initial-cluster=%s (%v)", apStr, umap.String(), err)
}

func (c *ServerConfig) MemberDir() string { return filepath.Join(c.DataDir, "member") }

func (c *ServerConfig) WALDir() string {
	if c.DedicatedWALDir != "" {
		return c.DedicatedWALDir
	}
	return filepath.Join(c.MemberDir(), "wal")
}

func (c *ServerConfig) SnapDir() string { return filepath.Join(c.MemberDir(), "snap") }

func (c *ServerConfig) ShouldDiscover() bool { return c.DiscoveryURL != "" }

// ReqTimeout returns timeout for request to finish.
func (c *ServerConfig) ReqTimeout() time.Duration {
	// 5s for queue waiting, computation and disk IO delay
	// + 2 * election timeout for possible leader election
	return 5*time.Second + 2*time.Duration(c.ElectionTicks*int(c.TickMs))*time.Millisecond
}

func (c *ServerConfig) electionTimeout() time.Duration {
	return time.Duration(c.ElectionTicks*int(c.TickMs)) * time.Millisecond
}

func (c *ServerConfig) peerDialTimeout() time.Duration {
	// 1s for queue wait and election timeout
	return time.Second + time.Duration(c.ElectionTicks*int(c.TickMs))*time.Millisecond
}

func (c *ServerConfig) PrintWithInitial() { c.print(true) }

func (c *ServerConfig) Print() { c.print(false) }

func (c *ServerConfig) print(initial bool) {
	// TODO: remove this after dropping "capnslog"
	if c.Logger == nil {
		plog.Infof("name = %s", c.Name)
		if c.ForceNewCluster {
			plog.Infof("force new cluster")
		}
		plog.Infof("data dir = %s", c.DataDir)
		plog.Infof("member dir = %s", c.MemberDir())
		if c.DedicatedWALDir != "" {
			plog.Infof("dedicated WAL dir = %s", c.DedicatedWALDir)
		}
		plog.Infof("heartbeat = %dms", c.TickMs)
		plog.Infof("election = %dms", c.ElectionTicks*int(c.TickMs))
		plog.Infof("snapshot count = %d", c.SnapCount)
		if len(c.DiscoveryURL) != 0 {
			plog.Infof("discovery URL= %s", c.DiscoveryURL)
			if len(c.DiscoveryProxy) != 0 {
				plog.Infof("discovery proxy = %s", c.DiscoveryProxy)
			}
		}
		plog.Infof("advertise client URLs = %s", c.ClientURLs)
		if initial {
			plog.Infof("initial advertise peer URLs = %s", c.PeerURLs)
			plog.Infof("initial cluster = %s", c.InitialPeerURLsMap)
		}
	} else {
		state := "new"
		if !c.NewCluster {
			state = "existing"
		}
		c.Logger.Info(
			"server configuration",
			zap.String("name", c.Name),
			zap.String("data-dir", c.DataDir),
			zap.String("member-dir", c.MemberDir()),
			zap.String("dedicated-wal-dir", c.DedicatedWALDir),
			zap.Bool("force-new-cluster", c.ForceNewCluster),
			zap.Uint("heartbeat-tick-ms", c.TickMs),
			zap.String("heartbeat-interval", fmt.Sprintf("%v", time.Duration(c.TickMs)*time.Millisecond)),
			zap.Int("election-tick-ms", c.ElectionTicks),
			zap.String("election-timeout", fmt.Sprintf("%v", time.Duration(c.ElectionTicks*int(c.TickMs))*time.Millisecond)),
			zap.Uint64("snapshot-count", c.SnapCount),
			zap.Strings("advertise-client-urls", c.getACURLs()),
			zap.Strings("initial-advertise-peer-urls", c.getAPURLs()),
			zap.Bool("initial", initial),
			zap.String("initial-cluster", c.InitialPeerURLsMap.String()),
			zap.String("initial-cluster-state", state),
			zap.String("initial-cluster-token", c.InitialClusterToken),
			zap.Bool("pre-vote", c.PreVote),
			zap.Bool("initial-corrupt-check", c.InitialCorruptCheck),
			zap.String("corrupt-check-time-interval", c.CorruptCheckTime.String()),
			zap.String("auto-compaction-mode", c.AutoCompactionMode),
			zap.Duration("auto-compaction-retention", c.AutoCompactionRetention),
			zap.String("auto-compaction-interval", c.AutoCompactionRetention.String()),
			zap.String("discovery-url", c.DiscoveryURL),
			zap.String("discovery-proxy", c.DiscoveryProxy),
		)
	}
}

func checkDuplicateURL(urlsmap types.URLsMap) bool {
	um := make(map[string]bool)
	for _, urls := range urlsmap {
		for _, url := range urls {
			u := url.String()
			if um[u] {
				return true
			}
			um[u] = true
		}
	}
	return false
}

func (c *ServerConfig) bootstrapTimeout() time.Duration {
	if c.BootstrapTimeout != 0 {
		return c.BootstrapTimeout
	}
	return time.Second
}

func (c *ServerConfig) backendPath() string { return filepath.Join(c.SnapDir(), "db") }

func (c *ServerConfig) getAPURLs() (ss []string) {
	ss = make([]string, len(c.PeerURLs))
	for i := range c.PeerURLs {
		ss[i] = c.PeerURLs[i].String()
	}
	return ss
}

func (c *ServerConfig) getACURLs() (ss []string) {
	ss = make([]string, len(c.ClientURLs))
	for i := range c.ClientURLs {
		ss[i] = c.ClientURLs[i].String()
	}
	return ss
}
<|endoftext|>


<commit_before>// Example of a daemon with echo service
package main

import (
	"fmt"
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"

	"github.com/takama/daemon"
)

const (

	// name of the service
	name        = "myservice"
	description = "My Echo Service"

	// port which daemon should be listen
	port = ":9977"
)

// dependencies that are NOT required by the service, but might be used
var dependencies = []string{"dummy.service"}

var stdlog, errlog *log.Logger

// Service has embedded daemon
type Service struct {
	daemon.Daemon
}

// Manage by daemon commands or run the daemon
func (service *Service) Manage() (string, error) {

	usage := "Usage: myservice install | remove | start | stop | status"

	// if received any kind of command, do it
	if len(os.Args) > 1 {
		command := os.Args[1]
		switch command {
		case "install":
			return service.Install()
		case "remove":
			return service.Remove()
		case "start":
			return service.Start()
		case "stop":
			return service.Stop()
		case "status":
			return service.Status()
		default:
			return usage, nil
		}
	}

	// Do something, call your goroutines, etc

	// Set up channel on which to send signal notifications.
	// We must use a buffered channel or risk missing the signal
	// if we're not ready to receive when the signal is sent.
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt, os.Kill, syscall.SIGTERM)

	// Set up listener for defined host and port
	listener, err := net.Listen("tcp", port)
	if err != nil {
		return "Possibly was a problem with the port binding", err
	}

	// set up channel on which to send accepted connections
	listen := make(chan net.Conn, 100)
	go acceptConnection(listener, listen)

	// loop work cycle with accept connections or interrupt
	// by system signal
	for {
		select {
		case conn := <-listen:
			go handleClient(conn)
		case killSignal := <-interrupt:
			stdlog.Println("Got signal:", killSignal)
			stdlog.Println("Stoping listening on ", listener.Addr())
			listener.Close()
			if killSignal == os.Interrupt {
				return "Daemon was interruped by system signal", nil
			}
			return "Daemon was killed", nil
		}
	}

	// never happen, but need to complete code
	return usage, nil
}

// Accept a client connection and collect it in a channel
func acceptConnection(listener net.Listener, listen chan<- net.Conn) {
	for {
		conn, err := listener.Accept()
		if err != nil {
			continue
		}
		listen <- conn
	}
}

func handleClient(client net.Conn) {
	for {
		buf := make([]byte, 4096)
		numbytes, err := client.Read(buf)
		if numbytes == 0 || err != nil {
			return
		}
		client.Write(buf)
	}
}

func init() {
	stdlog = log.New(os.Stdout, "", log.Ldate|log.Ltime)
	errlog = log.New(os.Stderr, "", log.Ldate|log.Ltime)
}

func main() {
	srv, err := daemon.New(name, description, dependencies...)
	if err != nil {
		errlog.Println("Error: ", err)
		os.Exit(1)
	}
	service := &Service{srv}
	status, err := service.Manage()
	if err != nil {
		errlog.Println(status, "\nError: ", err)
		os.Exit(1)
	}
	fmt.Println(status)

}
<commit_msg>remove dates from example logging<commit_after>// Example of a daemon with echo service
package main

import (
	"fmt"
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"

	"github.com/takama/daemon"
)

const (

	// name of the service
	name        = "myservice"
	description = "My Echo Service"

	// port which daemon should be listen
	port = ":9977"
)

// dependencies that are NOT required by the service, but might be used
var dependencies = []string{"dummy.service"}

var stdlog, errlog *log.Logger

// Service has embedded daemon
type Service struct {
	daemon.Daemon
}

// Manage by daemon commands or run the daemon
func (service *Service) Manage() (string, error) {

	usage := "Usage: myservice install | remove | start | stop | status"

	// if received any kind of command, do it
	if len(os.Args) > 1 {
		command := os.Args[1]
		switch command {
		case "install":
			return service.Install()
		case "remove":
			return service.Remove()
		case "start":
			return service.Start()
		case "stop":
			return service.Stop()
		case "status":
			return service.Status()
		default:
			return usage, nil
		}
	}

	// Do something, call your goroutines, etc

	// Set up channel on which to send signal notifications.
	// We must use a buffered channel or risk missing the signal
	// if we're not ready to receive when the signal is sent.
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt, os.Kill, syscall.SIGTERM)

	// Set up listener for defined host and port
	listener, err := net.Listen("tcp", port)
	if err != nil {
		return "Possibly was a problem with the port binding", err
	}

	// set up channel on which to send accepted connections
	listen := make(chan net.Conn, 100)
	go acceptConnection(listener, listen)

	// loop work cycle with accept connections or interrupt
	// by system signal
	for {
		select {
		case conn := <-listen:
			go handleClient(conn)
		case killSignal := <-interrupt:
			stdlog.Println("Got signal:", killSignal)
			stdlog.Println("Stoping listening on ", listener.Addr())
			listener.Close()
			if killSignal == os.Interrupt {
				return "Daemon was interruped by system signal", nil
			}
			return "Daemon was killed", nil
		}
	}

	// never happen, but need to complete code
	return usage, nil
}

// Accept a client connection and collect it in a channel
func acceptConnection(listener net.Listener, listen chan<- net.Conn) {
	for {
		conn, err := listener.Accept()
		if err != nil {
			continue
		}
		listen <- conn
	}
}

func handleClient(client net.Conn) {
	for {
		buf := make([]byte, 4096)
		numbytes, err := client.Read(buf)
		if numbytes == 0 || err != nil {
			return
		}
		client.Write(buf)
	}
}

func init() {
	stdlog = log.New(os.Stdout, "", 0)
	errlog = log.New(os.Stderr, "", 0)
}

func main() {
	srv, err := daemon.New(name, description, dependencies...)
	if err != nil {
		errlog.Println("Error: ", err)
		os.Exit(1)
	}
	service := &Service{srv}
	status, err := service.Manage()
	if err != nil {
		errlog.Println(status, "\nError: ", err)
		os.Exit(1)
	}
	fmt.Println(status)

}
<|endoftext|>


<commit_before>package wrapper

import (
	"context"
	"fmt"

	"github.com/grafana/grafana/pkg/components/null"
	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/tsdb"
	"github.com/grafana/grafana_plugin_model/go/datasource"
)

func NewDatasourcePluginWrapper(log log.Logger, plugin datasource.DatasourcePlugin) *DatasourcePluginWrapper {
	return &DatasourcePluginWrapper{DatasourcePlugin: plugin, logger: log}
}

type DatasourcePluginWrapper struct {
	datasource.DatasourcePlugin
	logger log.Logger
}

func (tw *DatasourcePluginWrapper) Query(ctx context.Context, ds *models.DataSource, query *tsdb.TsdbQuery) (*tsdb.Response, error) {
	jsonData, err := ds.JsonData.MarshalJSON()
	if err != nil {
		return nil, err
	}

	pbQuery := &datasource.DatasourceRequest{
		Datasource: &datasource.DatasourceInfo{
			Name:                    ds.Name,
			Type:                    ds.Type,
			Url:                     ds.Url,
			Id:                      ds.Id,
			OrgId:                   ds.OrgId,
			JsonData:                string(jsonData),
			DecryptedSecureJsonData: ds.SecureJsonData.Decrypt(),
		},
		TimeRange: &datasource.TimeRange{
			FromRaw:     query.TimeRange.From,
			ToRaw:       query.TimeRange.To,
			ToEpochMs:   query.TimeRange.GetToAsMsEpoch(),
			FromEpochMs: query.TimeRange.GetFromAsMsEpoch(),
		},
		Queries: []*datasource.Query{},
	}

	for _, q := range query.Queries {
		modelJson, _ := q.Model.MarshalJSON()

		pbQuery.Queries = append(pbQuery.Queries, &datasource.Query{
			ModelJson:     string(modelJson),
			IntervalMs:    q.IntervalMs,
			RefId:         q.RefId,
			MaxDataPoints: q.MaxDataPoints,
		})
	}

	pbres, err := tw.DatasourcePlugin.Query(ctx, pbQuery)

	if err != nil {
		return nil, err
	}

	res := &tsdb.Response{
		Results: map[string]*tsdb.QueryResult{},
	}

	for _, r := range pbres.Results {
		res.Results[r.RefId] = &tsdb.QueryResult{
			RefId:  r.RefId,
			Series: []*tsdb.TimeSeries{},
		}

		for _, s := range r.GetSeries() {
			points := tsdb.TimeSeriesPoints{}

			for _, p := range s.Points {
				po := tsdb.NewTimePoint(null.FloatFrom(p.Value), float64(p.Timestamp))
				points = append(points, po)
			}

			res.Results[r.RefId].Series = append(res.Results[r.RefId].Series, &tsdb.TimeSeries{
				Name:   s.Name,
				Tags:   s.Tags,
				Points: points,
			})
		}

		mappedTables, err := tw.mapTables(r)
		if err != nil {
			return nil, err
		}
		res.Results[r.RefId].Tables = mappedTables
	}

	return res, nil
}
func (tw *DatasourcePluginWrapper) mapTables(r *datasource.QueryResult) ([]*tsdb.Table, error) {
	var tables []*tsdb.Table
	for _, t := range r.GetTables() {
		mappedTable, err := tw.mapTable(t)
		if err != nil {
			return nil, err
		}
		tables = append(tables, mappedTable)
	}
	return tables, nil
}

func (tw *DatasourcePluginWrapper) mapTable(t *datasource.Table) (*tsdb.Table, error) {
	table := &tsdb.Table{}
	for _, c := range t.GetColumns() {
		table.Columns = append(table.Columns, tsdb.TableColumn{
			Text: c.Name,
		})
	}

	for _, r := range t.GetRows() {
		row := tsdb.RowValues{}
		for _, rv := range r.Values {
			mappedRw, err := tw.mapRowValue(rv)
			if err != nil {
				return nil, err
			}

			row = append(row, mappedRw)
		}
		table.Rows = append(table.Rows, row)
	}

	return table, nil
}
func (tw *DatasourcePluginWrapper) mapRowValue(rv *datasource.RowValue) (interface{}, error) {
	switch rv.Kind {
	case datasource.RowValue_TYPE_NULL:
		return nil, nil
	case datasource.RowValue_TYPE_INT64:
		return rv.Int64Value, nil
	case datasource.RowValue_TYPE_BOOL:
		return rv.BoolValue, nil
	case datasource.RowValue_TYPE_STRING:
		return rv.StringValue, nil
	case datasource.RowValue_TYPE_DOUBLE:
		return rv.DoubleValue, nil
	case datasource.RowValue_TYPE_BYTES:
		return rv.BytesValue, nil
	default:
		return nil, fmt.Errorf("Unsupported row value %v from plugin", rv.Kind)
	}
}
<commit_msg>plugins: map error property on query result<commit_after>package wrapper

import (
	"context"
	"errors"
	"fmt"

	"github.com/grafana/grafana/pkg/components/null"
	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/tsdb"
	"github.com/grafana/grafana_plugin_model/go/datasource"
)

func NewDatasourcePluginWrapper(log log.Logger, plugin datasource.DatasourcePlugin) *DatasourcePluginWrapper {
	return &DatasourcePluginWrapper{DatasourcePlugin: plugin, logger: log}
}

type DatasourcePluginWrapper struct {
	datasource.DatasourcePlugin
	logger log.Logger
}

func (tw *DatasourcePluginWrapper) Query(ctx context.Context, ds *models.DataSource, query *tsdb.TsdbQuery) (*tsdb.Response, error) {
	jsonData, err := ds.JsonData.MarshalJSON()
	if err != nil {
		return nil, err
	}

	pbQuery := &datasource.DatasourceRequest{
		Datasource: &datasource.DatasourceInfo{
			Name:                    ds.Name,
			Type:                    ds.Type,
			Url:                     ds.Url,
			Id:                      ds.Id,
			OrgId:                   ds.OrgId,
			JsonData:                string(jsonData),
			DecryptedSecureJsonData: ds.SecureJsonData.Decrypt(),
		},
		TimeRange: &datasource.TimeRange{
			FromRaw:     query.TimeRange.From,
			ToRaw:       query.TimeRange.To,
			ToEpochMs:   query.TimeRange.GetToAsMsEpoch(),
			FromEpochMs: query.TimeRange.GetFromAsMsEpoch(),
		},
		Queries: []*datasource.Query{},
	}

	for _, q := range query.Queries {
		modelJson, _ := q.Model.MarshalJSON()

		pbQuery.Queries = append(pbQuery.Queries, &datasource.Query{
			ModelJson:     string(modelJson),
			IntervalMs:    q.IntervalMs,
			RefId:         q.RefId,
			MaxDataPoints: q.MaxDataPoints,
		})
	}

	pbres, err := tw.DatasourcePlugin.Query(ctx, pbQuery)

	if err != nil {
		return nil, err
	}

	res := &tsdb.Response{
		Results: map[string]*tsdb.QueryResult{},
	}

	for _, r := range pbres.Results {
		qr := &tsdb.QueryResult{
			RefId:       r.RefId,
			Series:      []*tsdb.TimeSeries{},
			Error:       errors.New(r.Error),
			ErrorString: r.Error,
		}

		for _, s := range r.GetSeries() {
			points := tsdb.TimeSeriesPoints{}

			for _, p := range s.Points {
				po := tsdb.NewTimePoint(null.FloatFrom(p.Value), float64(p.Timestamp))
				points = append(points, po)
			}

			qr.Series = append(qr.Series, &tsdb.TimeSeries{
				Name:   s.Name,
				Tags:   s.Tags,
				Points: points,
			})
		}

		mappedTables, err := tw.mapTables(r)
		if err != nil {
			return nil, err
		}
		qr.Tables = mappedTables

		res.Results[r.RefId] = qr
	}

	return res, nil
}
func (tw *DatasourcePluginWrapper) mapTables(r *datasource.QueryResult) ([]*tsdb.Table, error) {
	var tables []*tsdb.Table
	for _, t := range r.GetTables() {
		mappedTable, err := tw.mapTable(t)
		if err != nil {
			return nil, err
		}
		tables = append(tables, mappedTable)
	}
	return tables, nil
}

func (tw *DatasourcePluginWrapper) mapTable(t *datasource.Table) (*tsdb.Table, error) {
	table := &tsdb.Table{}
	for _, c := range t.GetColumns() {
		table.Columns = append(table.Columns, tsdb.TableColumn{
			Text: c.Name,
		})
	}

	for _, r := range t.GetRows() {
		row := tsdb.RowValues{}
		for _, rv := range r.Values {
			mappedRw, err := tw.mapRowValue(rv)
			if err != nil {
				return nil, err
			}

			row = append(row, mappedRw)
		}
		table.Rows = append(table.Rows, row)
	}

	return table, nil
}
func (tw *DatasourcePluginWrapper) mapRowValue(rv *datasource.RowValue) (interface{}, error) {
	switch rv.Kind {
	case datasource.RowValue_TYPE_NULL:
		return nil, nil
	case datasource.RowValue_TYPE_INT64:
		return rv.Int64Value, nil
	case datasource.RowValue_TYPE_BOOL:
		return rv.BoolValue, nil
	case datasource.RowValue_TYPE_STRING:
		return rv.StringValue, nil
	case datasource.RowValue_TYPE_DOUBLE:
		return rv.DoubleValue, nil
	case datasource.RowValue_TYPE_BYTES:
		return rv.BytesValue, nil
	default:
		return nil, fmt.Errorf("Unsupported row value %v from plugin", rv.Kind)
	}
}
<|endoftext|>


<commit_before>package provision

import (
	"bytes"
	"fmt"
	"net/url"
	"strings"
	"text/template"

	"github.com/docker/machine/libmachine/auth"
	"github.com/docker/machine/libmachine/log"
	"github.com/docker/machine/libmachine/swarm"
)

type SwarmCommandContext struct {
	ContainerName string
	Env           []string
	DockerDir     string
	DockerPort    int
	IP            string
	Port          string
	AuthOptions   auth.Options
	SwarmOptions  swarm.Options
	SwarmImage    string
}

// Wrapper function to generate a docker run swarm command (manage or join)
// from a template/context and execute it.
func runSwarmCommandFromTemplate(p Provisioner, cmdTmpl string, swarmCmdContext SwarmCommandContext) error {
	var (
		executedCmdTmpl bytes.Buffer
	)

	parsedMasterCmdTemplate, err := template.New("swarmMasterCmd").Parse(cmdTmpl)
	if err != nil {
		return err
	}

	parsedMasterCmdTemplate.Execute(&executedCmdTmpl, swarmCmdContext)

	log.Debugf("The swarm command being run is: %s", executedCmdTmpl.String())

	if _, err := p.SSHCommand(executedCmdTmpl.String()); err != nil {
		return err
	}

	return nil
}

func configureSwarm(p Provisioner, swarmOptions swarm.Options, authOptions auth.Options) error {
	if !swarmOptions.IsSwarm {
		return nil
	}

	log.Info("Configuring swarm...")

	ip, err := p.GetDriver().GetIP()
	if err != nil {
		return err
	}

	u, err := url.Parse(swarmOptions.Host)
	if err != nil {
		return err
	}

	parts := strings.Split(u.Host, ":")
	port := parts[1]

	dockerDir := p.GetDockerOptionsDir()

	swarmCmdContext := SwarmCommandContext{
		ContainerName: "",
		Env:           swarmOptions.Env,
		DockerDir:     dockerDir,
		DockerPort:    2376,
		IP:            ip,
		Port:          port,
		AuthOptions:   authOptions,
		SwarmOptions:  swarmOptions,
		SwarmImage:    swarmOptions.Image,
	}

	// First things first, get the swarm image.
	if _, err := p.SSHCommand(fmt.Sprintf("sudo docker pull %s", swarmOptions.Image)); err != nil {
		return err
	}

	swarmMasterCmdTemplate := `sudo docker run -d \
--restart=always \
{{range .Env}} -e {{.}}{{end}} \
--name swarm-agent-master \
-p {{.Port}}:{{.Port}} \
-v {{.DockerDir}}:{{.DockerDir}} \
{{.SwarmImage}} \
manage \
--tlsverify \
--tlscacert={{.AuthOptions.CaCertRemotePath}} \
--tlscert={{.AuthOptions.ServerCertRemotePath}} \
--tlskey={{.AuthOptions.ServerKeyRemotePath}} \
-H {{.SwarmOptions.Host}} \
--strategy {{.SwarmOptions.Strategy}} {{range .SwarmOptions.ArbitraryFlags}} --{{.}}{{end}} {{.SwarmOptions.Discovery}}
`

	swarmWorkerCmdTemplate := `sudo docker run -d \
--restart=always \
{{range .Env}} -e {{.}}{{end}} \
--name swarm-agent \
{{.SwarmImage}} \
join --advertise {{.Ip}}:{{.DockerPort}} {{.SwarmOptions.Discovery}}
`

	if swarmOptions.Master {
		log.Debug("Launching swarm master")
		if err := runSwarmCommandFromTemplate(p, swarmMasterCmdTemplate, swarmCmdContext); err != nil {
			return err
		}
	}

	log.Debug("Launch swarm worker")
	if err := runSwarmCommandFromTemplate(p, swarmWorkerCmdTemplate, swarmCmdContext); err != nil {
		return err
	}

	return nil
}
<commit_msg>BugFix: IP in template need as per Struct<commit_after>package provision

import (
	"bytes"
	"fmt"
	"net/url"
	"strings"
	"text/template"

	"github.com/docker/machine/libmachine/auth"
	"github.com/docker/machine/libmachine/log"
	"github.com/docker/machine/libmachine/swarm"
)

type SwarmCommandContext struct {
	ContainerName string
	Env           []string
	DockerDir     string
	DockerPort    int
	IP            string
	Port          string
	AuthOptions   auth.Options
	SwarmOptions  swarm.Options
	SwarmImage    string
}

// Wrapper function to generate a docker run swarm command (manage or join)
// from a template/context and execute it.
func runSwarmCommandFromTemplate(p Provisioner, cmdTmpl string, swarmCmdContext SwarmCommandContext) error {
	var (
		executedCmdTmpl bytes.Buffer
	)

	parsedMasterCmdTemplate, err := template.New("swarmMasterCmd").Parse(cmdTmpl)
	if err != nil {
		return err
	}

	parsedMasterCmdTemplate.Execute(&executedCmdTmpl, swarmCmdContext)

	log.Debugf("The swarm command being run is: %s", executedCmdTmpl.String())

	if _, err := p.SSHCommand(executedCmdTmpl.String()); err != nil {
		return err
	}

	return nil
}

func configureSwarm(p Provisioner, swarmOptions swarm.Options, authOptions auth.Options) error {
	if !swarmOptions.IsSwarm {
		return nil
	}

	log.Info("Configuring swarm...")

	ip, err := p.GetDriver().GetIP()
	if err != nil {
		return err
	}

	u, err := url.Parse(swarmOptions.Host)
	if err != nil {
		return err
	}

	parts := strings.Split(u.Host, ":")
	port := parts[1]

	dockerDir := p.GetDockerOptionsDir()

	swarmCmdContext := SwarmCommandContext{
		ContainerName: "",
		Env:           swarmOptions.Env,
		DockerDir:     dockerDir,
		DockerPort:    2376,
		IP:            ip,
		Port:          port,
		AuthOptions:   authOptions,
		SwarmOptions:  swarmOptions,
		SwarmImage:    swarmOptions.Image,
	}

	// First things first, get the swarm image.
	if _, err := p.SSHCommand(fmt.Sprintf("sudo docker pull %s", swarmOptions.Image)); err != nil {
		return err
	}

	swarmMasterCmdTemplate := `sudo docker run -d \
--restart=always \
{{range .Env}} -e {{.}}{{end}} \
--name swarm-agent-master \
-p {{.Port}}:{{.Port}} \
-v {{.DockerDir}}:{{.DockerDir}} \
{{.SwarmImage}} \
manage \
--tlsverify \
--tlscacert={{.AuthOptions.CaCertRemotePath}} \
--tlscert={{.AuthOptions.ServerCertRemotePath}} \
--tlskey={{.AuthOptions.ServerKeyRemotePath}} \
-H {{.SwarmOptions.Host}} \
--strategy {{.SwarmOptions.Strategy}} {{range .SwarmOptions.ArbitraryFlags}} --{{.}}{{end}} {{.SwarmOptions.Discovery}}
`

	swarmWorkerCmdTemplate := `sudo docker run -d \
--restart=always \
{{range .Env}} -e {{.}}{{end}} \
--name swarm-agent \
{{.SwarmImage}} \
join --advertise {{.IP}}:{{.DockerPort}} {{.SwarmOptions.Discovery}}
`

	if swarmOptions.Master {
		log.Debug("Launching swarm master")
		if err := runSwarmCommandFromTemplate(p, swarmMasterCmdTemplate, swarmCmdContext); err != nil {
			return err
		}
	}

	log.Debug("Launch swarm worker")
	if err := runSwarmCommandFromTemplate(p, swarmWorkerCmdTemplate, swarmCmdContext); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
    "github.com/stretchr/testify/assert"
    "testing"
)

func TestParseApache2Scoreboard( t *testing.T ){
    stub := "Scoreboard: W._SRWKDCLGI...."
    stat := make(map[string]float64)

    err := parseApache2Scoreboard( stub, &stat )
    assert.Nil( t, err )
    assert.Equal( t, stat[ "score-_" ], 1 )
    assert.Equal( t, stat[ "score-S" ], 1 )
    assert.Equal( t, stat[ "score-R" ], 1 )
    assert.Equal( t, stat[ "score-W" ], 2 )
    assert.Equal( t, stat[ "score-K" ], 1 )
    assert.Equal( t, stat[ "score-D" ], 1 )
    assert.Equal( t, stat[ "score-C" ], 1 )
    assert.Equal( t, stat[ "score-L" ], 1 )
    assert.Equal( t, stat[ "score-G" ], 1 )
    assert.Equal( t, stat[ "score-I" ], 1 )
    assert.Equal( t, stat[ "score-." ], 5 )
}

func TestParseApache2Status( t *testing.T ){
    stub := `Total Accesses: 358
Total kBytes: 20
CPULoad: .00117358
Uptime: 102251
ReqPerSec: .00350119
BytesPerSec: .200291
BytesPerReq: 57.2067
BusyWorkers: 1
IdleWorkers: 4
`
    stat := make(map[string]float64)

    err := parseApache2Status( stub, &stat )
    assert.Nil( t, err )
    assert.Equal( t, stat[ "requests" ], 358 )
    assert.Equal( t, stat[ "bytes_sent" ], 20 )
    assert.Equal( t, stat[ "cpu_load" ], 0.00117358 )
    assert.Equal( t, stat[ "busy_workers" ], 1 )
    assert.Equal( t, stat[ "idle_workers" ], 4 )
}

func TestGetApache2Metrics_1( t *testing.T ){
    ret, err := getApache2Metrics( "127.0.0.1", 1080, "/server-status?auto" )
    assert.Nil( t, err, "Please start-up your httpd (127.0.0.1:1080) or unable to connect httpd." )
    assert.NotNil( t, ret, )
    assert.NotEmpty( t, ret )
    assert.Contains( t, ret, "Total Accesses" )
    assert.Contains( t, ret, "Total kBytes" )
    assert.Contains( t, ret, "Uptime" )
    assert.Contains( t, ret, "BusyWorkers" )
    assert.Contains( t, ret, "IdleWorkers" )
    assert.Contains( t, ret, "Scoreboard" )
}
<commit_msg>Change TestGetApache2Metrics_1() is corresponded to httpd stub.<commit_after>package main

import (
    "github.com/stretchr/testify/assert"
    "testing"
    "fmt"
    "net/http"
    "net/http/httptest"
    "regexp"
    "strconv"
)

func TestParseApache2Scoreboard( t *testing.T ){
    stub := "Scoreboard: W._SRWKDCLGI...."
    stat := make(map[string]float64)

    err := parseApache2Scoreboard( stub, &stat )
    assert.Nil( t, err )
    assert.Equal( t, stat[ "score-_" ], 1 )
    assert.Equal( t, stat[ "score-S" ], 1 )
    assert.Equal( t, stat[ "score-R" ], 1 )
    assert.Equal( t, stat[ "score-W" ], 2 )
    assert.Equal( t, stat[ "score-K" ], 1 )
    assert.Equal( t, stat[ "score-D" ], 1 )
    assert.Equal( t, stat[ "score-C" ], 1 )
    assert.Equal( t, stat[ "score-L" ], 1 )
    assert.Equal( t, stat[ "score-G" ], 1 )
    assert.Equal( t, stat[ "score-I" ], 1 )
    assert.Equal( t, stat[ "score-." ], 5 )
}

func TestParseApache2Status( t *testing.T ){
    stub := `Total Accesses: 358
Total kBytes: 20
CPULoad: .00117358
Uptime: 102251
ReqPerSec: .00350119
BytesPerSec: .200291
BytesPerReq: 57.2067
BusyWorkers: 1
IdleWorkers: 4
`
    stat := make(map[string]float64)

    err := parseApache2Status( stub, &stat )
    assert.Nil( t, err )
    assert.Equal( t, stat[ "requests" ], 358 )
    assert.Equal( t, stat[ "bytes_sent" ], 20 )
    assert.Equal( t, stat[ "cpu_load" ], 0.00117358 )
    assert.Equal( t, stat[ "busy_workers" ], 1 )
    assert.Equal( t, stat[ "idle_workers" ], 4 )
}

func TestGetApache2Metrics_1( t *testing.T ){
    stub := `Total Accesses: 668
Total kBytes: 2789
CPULoad: .000599374
Uptime: 171846
ReqPerSec: .0038872
BytesPerSec: 16.6192
BytesPerReq: 4275.35
BusyWorkers: 1
IdleWorkers: 3
Scoreboard: W_.__...........................`

    ts := httptest.NewServer(
        http.HandlerFunc(
            func( w http.ResponseWriter, r *http.Request ){
                fmt.Fprintln( w, stub )
            } ) )
    defer ts.Close()
    re, _   := regexp.Compile( "([a-z]+)://([A-Za-z0-9.]+):([0-9]+)(.*)" )
    found   := re.FindStringSubmatch( ts.URL )
    assert.Equal( t, len( found ), 5, fmt.Sprintf( "Test stub uri format is changed. %s", ts.URL ) )

    host    := found[2]
    port, _ := strconv.Atoi( found[3] )
    path    := found[4]

    ret, err := getApache2Metrics( host, uint16( port ), path )
    assert.Nil( t, err )
    assert.NotNil( t, ret )
    assert.NotEmpty( t, ret )
    assert.Contains( t, ret, "Total Accesses" )
    assert.Contains( t, ret, "Total kBytes" )
    assert.Contains( t, ret, "Uptime" )
    assert.Contains( t, ret, "BusyWorkers" )
    assert.Contains( t, ret, "IdleWorkers" )
    assert.Contains( t, ret, "Scoreboard" )
}
<|endoftext|>


<commit_before>package matchers_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/matchers"
	"time"
)

var _ = Describe("BeNumerically", func() {
	Context("when passed a number", func() {
		It("should support ==", func() {
			Ω(uint32(5)).Should(BeNumerically("==", 5))
			Ω(float64(5.0)).Should(BeNumerically("==", 5))
			Ω(int8(5)).Should(BeNumerically("==", 5))
		})

		It("should not have false positives", func() {
			Ω(5.1).ShouldNot(BeNumerically("==", 5))
			Ω(5).ShouldNot(BeNumerically("==", 5.1))
		})

		It("should support >", func() {
			Ω(uint32(5)).Should(BeNumerically(">", 4))
			Ω(float64(5.0)).Should(BeNumerically(">", 4.9))
			Ω(int8(5)).Should(BeNumerically(">", 4))

			Ω(uint32(5)).ShouldNot(BeNumerically(">", 5))
			Ω(float64(5.0)).ShouldNot(BeNumerically(">", 5.0))
			Ω(int8(5)).ShouldNot(BeNumerically(">", 5))
		})

		It("should support <", func() {
			Ω(uint32(5)).Should(BeNumerically("<", 6))
			Ω(float64(5.0)).Should(BeNumerically("<", 5.1))
			Ω(int8(5)).Should(BeNumerically("<", 6))

			Ω(uint32(5)).ShouldNot(BeNumerically("<", 5))
			Ω(float64(5.0)).ShouldNot(BeNumerically("<", 5.0))
			Ω(int8(5)).ShouldNot(BeNumerically("<", 5))
		})

		It("should support >=", func() {
			Ω(uint32(5)).Should(BeNumerically(">=", 4))
			Ω(float64(5.0)).Should(BeNumerically(">=", 4.9))
			Ω(int8(5)).Should(BeNumerically(">=", 4))

			Ω(uint32(5)).Should(BeNumerically(">=", 5))
			Ω(float64(5.0)).Should(BeNumerically(">=", 5.0))
			Ω(int8(5)).Should(BeNumerically(">=", 5))

			Ω(uint32(5)).ShouldNot(BeNumerically(">=", 6))
			Ω(float64(5.0)).ShouldNot(BeNumerically(">=", 5.1))
			Ω(int8(5)).ShouldNot(BeNumerically(">=", 6))
		})

		It("should support <=", func() {
			Ω(uint32(5)).Should(BeNumerically("<=", 6))
			Ω(float64(5.0)).Should(BeNumerically("<=", 5.1))
			Ω(int8(5)).Should(BeNumerically("<=", 6))

			Ω(uint32(5)).Should(BeNumerically("<=", 5))
			Ω(float64(5.0)).Should(BeNumerically("<=", 5.0))
			Ω(int8(5)).Should(BeNumerically("<=", 5))

			Ω(uint32(5)).ShouldNot(BeNumerically("<=", 4))
			Ω(float64(5.0)).ShouldNot(BeNumerically("<=", 4.9))
			Ω(int8(5)).Should(BeNumerically("<=", 5))
		})

		Context("when passed ~", func() {
			Context("when passed a float", func() {
				Context("and there is no precision parameter", func() {
					It("should default to 1e-8", func() {
						Ω(5.00000001).Should(BeNumerically("~", 5.00000002))
						Ω(5.00000001).ShouldNot(BeNumerically("~", 5.0000001))
					})
				})

				Context("and there is a precision parameter", func() {
					It("should use the precision parameter", func() {
						Ω(5.1).Should(BeNumerically("~", 5.19, 0.1))
						Ω(5.1).Should(BeNumerically("~", 5.01, 0.1))
						Ω(5.1).ShouldNot(BeNumerically("~", 5.22, 0.1))
						Ω(5.1).ShouldNot(BeNumerically("~", 4.98, 0.1))
					})
				})
			})

			Context("when passed an int/uint", func() {
				Context("and there is no precision parameter", func() {
					It("should just do strict equality", func() {
						Ω(5).Should(BeNumerically("~", 5))
						Ω(5).ShouldNot(BeNumerically("~", 6))
						Ω(uint(5)).ShouldNot(BeNumerically("~", 6))
					})
				})

				Context("and there is a precision parameter", func() {
					It("should use precision paramter", func() {
						Ω(5).Should(BeNumerically("~", 6, 2))
						Ω(5).ShouldNot(BeNumerically("~", 8, 2))
						Ω(uint(5)).Should(BeNumerically("~", 6, 1))
					})
				})
			})
		})
	})

	Context("when passed a non-number", func() {
		It("should error", func() {
			success, err := (&BeNumericallyMatcher{Comparator: "==", CompareTo: []interface{}{5}}).Match("foo")
			Ω(success).Should(BeFalse())
			Ω(err).Should(HaveOccurred())

			success, err = (&BeNumericallyMatcher{Comparator: "=="}).Match(5)
			Ω(success).Should(BeFalse())
			Ω(err).Should(HaveOccurred())

			success, err = (&BeNumericallyMatcher{Comparator: "~", CompareTo: []interface{}{3.0, "foo"}}).Match(5.0)
			Ω(success).Should(BeFalse())
			Ω(err).Should(HaveOccurred())

			success, err = (&BeNumericallyMatcher{Comparator: "==", CompareTo: []interface{}{"bar"}}).Match(5)
			Ω(success).Should(BeFalse())
			Ω(err).Should(HaveOccurred())

			success, err = (&BeNumericallyMatcher{Comparator: "==", CompareTo: []interface{}{"bar"}}).Match("foo")
			Ω(success).Should(BeFalse())
			Ω(err).Should(HaveOccurred())

			success, err = (&BeNumericallyMatcher{Comparator: "==", CompareTo: []interface{}{nil}}).Match(0)
			Ω(success).Should(BeFalse())
			Ω(err).Should(HaveOccurred())

			success, err = (&BeNumericallyMatcher{Comparator: "==", CompareTo: []interface{}{0}}).Match(nil)
			Ω(success).Should(BeFalse())
			Ω(err).Should(HaveOccurred())
		})
	})

	Context("when passed an unsupported comparator", func() {
		It("should error", func() {
			success, err := (&BeNumericallyMatcher{Comparator: "!=", CompareTo: []interface{}{5}}).Match(4)
			Ω(success).Should(BeFalse())
			Ω(err).Should(HaveOccurred())
		})
	})
})
<commit_msg>sigh.. fix it again<commit_after>package matchers_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/matchers"
)

var _ = Describe("BeNumerically", func() {
	Context("when passed a number", func() {
		It("should support ==", func() {
			Ω(uint32(5)).Should(BeNumerically("==", 5))
			Ω(float64(5.0)).Should(BeNumerically("==", 5))
			Ω(int8(5)).Should(BeNumerically("==", 5))
		})

		It("should not have false positives", func() {
			Ω(5.1).ShouldNot(BeNumerically("==", 5))
			Ω(5).ShouldNot(BeNumerically("==", 5.1))
		})

		It("should support >", func() {
			Ω(uint32(5)).Should(BeNumerically(">", 4))
			Ω(float64(5.0)).Should(BeNumerically(">", 4.9))
			Ω(int8(5)).Should(BeNumerically(">", 4))

			Ω(uint32(5)).ShouldNot(BeNumerically(">", 5))
			Ω(float64(5.0)).ShouldNot(BeNumerically(">", 5.0))
			Ω(int8(5)).ShouldNot(BeNumerically(">", 5))
		})

		It("should support <", func() {
			Ω(uint32(5)).Should(BeNumerically("<", 6))
			Ω(float64(5.0)).Should(BeNumerically("<", 5.1))
			Ω(int8(5)).Should(BeNumerically("<", 6))

			Ω(uint32(5)).ShouldNot(BeNumerically("<", 5))
			Ω(float64(5.0)).ShouldNot(BeNumerically("<", 5.0))
			Ω(int8(5)).ShouldNot(BeNumerically("<", 5))
		})

		It("should support >=", func() {
			Ω(uint32(5)).Should(BeNumerically(">=", 4))
			Ω(float64(5.0)).Should(BeNumerically(">=", 4.9))
			Ω(int8(5)).Should(BeNumerically(">=", 4))

			Ω(uint32(5)).Should(BeNumerically(">=", 5))
			Ω(float64(5.0)).Should(BeNumerically(">=", 5.0))
			Ω(int8(5)).Should(BeNumerically(">=", 5))

			Ω(uint32(5)).ShouldNot(BeNumerically(">=", 6))
			Ω(float64(5.0)).ShouldNot(BeNumerically(">=", 5.1))
			Ω(int8(5)).ShouldNot(BeNumerically(">=", 6))
		})

		It("should support <=", func() {
			Ω(uint32(5)).Should(BeNumerically("<=", 6))
			Ω(float64(5.0)).Should(BeNumerically("<=", 5.1))
			Ω(int8(5)).Should(BeNumerically("<=", 6))

			Ω(uint32(5)).Should(BeNumerically("<=", 5))
			Ω(float64(5.0)).Should(BeNumerically("<=", 5.0))
			Ω(int8(5)).Should(BeNumerically("<=", 5))

			Ω(uint32(5)).ShouldNot(BeNumerically("<=", 4))
			Ω(float64(5.0)).ShouldNot(BeNumerically("<=", 4.9))
			Ω(int8(5)).Should(BeNumerically("<=", 5))
		})

		Context("when passed ~", func() {
			Context("when passed a float", func() {
				Context("and there is no precision parameter", func() {
					It("should default to 1e-8", func() {
						Ω(5.00000001).Should(BeNumerically("~", 5.00000002))
						Ω(5.00000001).ShouldNot(BeNumerically("~", 5.0000001))
					})
				})

				Context("and there is a precision parameter", func() {
					It("should use the precision parameter", func() {
						Ω(5.1).Should(BeNumerically("~", 5.19, 0.1))
						Ω(5.1).Should(BeNumerically("~", 5.01, 0.1))
						Ω(5.1).ShouldNot(BeNumerically("~", 5.22, 0.1))
						Ω(5.1).ShouldNot(BeNumerically("~", 4.98, 0.1))
					})
				})
			})

			Context("when passed an int/uint", func() {
				Context("and there is no precision parameter", func() {
					It("should just do strict equality", func() {
						Ω(5).Should(BeNumerically("~", 5))
						Ω(5).ShouldNot(BeNumerically("~", 6))
						Ω(uint(5)).ShouldNot(BeNumerically("~", 6))
					})
				})

				Context("and there is a precision parameter", func() {
					It("should use precision paramter", func() {
						Ω(5).Should(BeNumerically("~", 6, 2))
						Ω(5).ShouldNot(BeNumerically("~", 8, 2))
						Ω(uint(5)).Should(BeNumerically("~", 6, 1))
					})
				})
			})
		})
	})

	Context("when passed a non-number", func() {
		It("should error", func() {
			success, err := (&BeNumericallyMatcher{Comparator: "==", CompareTo: []interface{}{5}}).Match("foo")
			Ω(success).Should(BeFalse())
			Ω(err).Should(HaveOccurred())

			success, err = (&BeNumericallyMatcher{Comparator: "=="}).Match(5)
			Ω(success).Should(BeFalse())
			Ω(err).Should(HaveOccurred())

			success, err = (&BeNumericallyMatcher{Comparator: "~", CompareTo: []interface{}{3.0, "foo"}}).Match(5.0)
			Ω(success).Should(BeFalse())
			Ω(err).Should(HaveOccurred())

			success, err = (&BeNumericallyMatcher{Comparator: "==", CompareTo: []interface{}{"bar"}}).Match(5)
			Ω(success).Should(BeFalse())
			Ω(err).Should(HaveOccurred())

			success, err = (&BeNumericallyMatcher{Comparator: "==", CompareTo: []interface{}{"bar"}}).Match("foo")
			Ω(success).Should(BeFalse())
			Ω(err).Should(HaveOccurred())

			success, err = (&BeNumericallyMatcher{Comparator: "==", CompareTo: []interface{}{nil}}).Match(0)
			Ω(success).Should(BeFalse())
			Ω(err).Should(HaveOccurred())

			success, err = (&BeNumericallyMatcher{Comparator: "==", CompareTo: []interface{}{0}}).Match(nil)
			Ω(success).Should(BeFalse())
			Ω(err).Should(HaveOccurred())
		})
	})

	Context("when passed an unsupported comparator", func() {
		It("should error", func() {
			success, err := (&BeNumericallyMatcher{Comparator: "!=", CompareTo: []interface{}{5}}).Match(4)
			Ω(success).Should(BeFalse())
			Ω(err).Should(HaveOccurred())
		})
	})
})
<|endoftext|>


<commit_before>package repository

import (
	"crypto/sha256"
	"io"
	"io/ioutil"
	"os"
	"restic"
	"sync"

	"restic/errors"
	"restic/hashing"

	"restic/crypto"
	"restic/debug"
	"restic/fs"
	"restic/pack"
)

// Saver implements saving data in a backend.
type Saver interface {
	Save(restic.Handle, io.Reader) error
}

// Packer holds a pack.Packer together with a hash writer.
type Packer struct {
	*pack.Packer
	hw      *hashing.Writer
	tmpfile *os.File
}

// packerManager keeps a list of open packs and creates new on demand.
type packerManager struct {
	be      Saver
	key     *crypto.Key
	pm      sync.Mutex
	packers []*Packer

	pool sync.Pool
}

const minPackSize = 4 * 1024 * 1024
const maxPackSize = 16 * 1024 * 1024
const maxPackers = 200

// newPackerManager returns an new packer manager which writes temporary files
// to a temporary directory
func newPackerManager(be Saver, key *crypto.Key) *packerManager {
	return &packerManager{
		be:  be,
		key: key,
		pool: sync.Pool{
			New: func() interface{} {
				return make([]byte, (minPackSize+maxPackSize)/2)
			},
		},
	}
}

// findPacker returns a packer for a new blob of size bytes. Either a new one is
// created or one is returned that already has some blobs.
func (r *packerManager) findPacker(size uint) (packer *Packer, err error) {
	r.pm.Lock()
	defer r.pm.Unlock()

	// search for a suitable packer
	if len(r.packers) > 0 {
		debug.Log("searching packer for %d bytes\n", size)
		for i, p := range r.packers {
			if p.Packer.Size()+size < maxPackSize {
				debug.Log("found packer %v", p)
				// remove from list
				r.packers = append(r.packers[:i], r.packers[i+1:]...)
				return p, nil
			}
		}
	}

	// no suitable packer found, return new
	debug.Log("create new pack for %d bytes", size)
	tmpfile, err := ioutil.TempFile("", "restic-temp-pack-")
	if err != nil {
		return nil, errors.Wrap(err, "ioutil.TempFile")
	}

	hw := hashing.NewWriter(tmpfile, sha256.New())
	p := pack.NewPacker(r.key, hw)
	packer = &Packer{
		Packer:  p,
		hw:      hw,
		tmpfile: tmpfile,
	}

	return packer, nil
}

// insertPacker appends p to s.packs.
func (r *packerManager) insertPacker(p *Packer) {
	r.pm.Lock()
	defer r.pm.Unlock()

	r.packers = append(r.packers, p)
	debug.Log("%d packers\n", len(r.packers))
}

// savePacker stores p in the backend.
func (r *Repository) savePacker(p *Packer) error {
	debug.Log("save packer with %d blobs\n", p.Packer.Count())
	_, err := p.Packer.Finalize()
	if err != nil {
		return err
	}

	f, err := fs.Open(p.tmpfile.Name())
	if err != nil {
		return errors.Wrap(err, "Open")
	}

	id := restic.IDFromHash(p.hw.Sum(nil))
	h := restic.Handle{Type: restic.DataFile, Name: id.String()}

	err = r.be.Save(h, f)
	if err != nil {
		debug.Log("Save(%v) error: %v", h, err)
		return err
	}

	debug.Log("saved as %v", h)

	err = f.Close()
	if err != nil {
		return errors.Wrap(err, "close tempfile")
	}

	err = fs.Remove(p.tmpfile.Name())
	if err != nil {
		return errors.Wrap(err, "Remove")
	}

	// update blobs in the index
	for _, b := range p.Packer.Blobs() {
		debug.Log("  updating blob %v to pack %v", b.ID.Str(), id.Str())
		r.idx.Store(restic.PackedBlob{
			Blob: restic.Blob{
				Type:   b.Type,
				ID:     b.ID,
				Offset: b.Offset,
				Length: uint(b.Length),
			},
			PackID: id,
		})
	}

	return nil
}

// countPacker returns the number of open (unfinished) packers.
func (r *packerManager) countPacker() int {
	r.pm.Lock()
	defer r.pm.Unlock()

	return len(r.packers)
}
<commit_msg>Fix saving pack: close temp file before removing<commit_after>package repository

import (
	"crypto/sha256"
	"io"
	"io/ioutil"
	"os"
	"restic"
	"sync"

	"restic/errors"
	"restic/hashing"

	"restic/crypto"
	"restic/debug"
	"restic/fs"
	"restic/pack"
)

// Saver implements saving data in a backend.
type Saver interface {
	Save(restic.Handle, io.Reader) error
}

// Packer holds a pack.Packer together with a hash writer.
type Packer struct {
	*pack.Packer
	hw      *hashing.Writer
	tmpfile *os.File
}

// packerManager keeps a list of open packs and creates new on demand.
type packerManager struct {
	be      Saver
	key     *crypto.Key
	pm      sync.Mutex
	packers []*Packer

	pool sync.Pool
}

const minPackSize = 4 * 1024 * 1024
const maxPackSize = 16 * 1024 * 1024
const maxPackers = 200

// newPackerManager returns an new packer manager which writes temporary files
// to a temporary directory
func newPackerManager(be Saver, key *crypto.Key) *packerManager {
	return &packerManager{
		be:  be,
		key: key,
		pool: sync.Pool{
			New: func() interface{} {
				return make([]byte, (minPackSize+maxPackSize)/2)
			},
		},
	}
}

// findPacker returns a packer for a new blob of size bytes. Either a new one is
// created or one is returned that already has some blobs.
func (r *packerManager) findPacker(size uint) (packer *Packer, err error) {
	r.pm.Lock()
	defer r.pm.Unlock()

	// search for a suitable packer
	if len(r.packers) > 0 {
		debug.Log("searching packer for %d bytes\n", size)
		for i, p := range r.packers {
			if p.Packer.Size()+size < maxPackSize {
				debug.Log("found packer %v", p)
				// remove from list
				r.packers = append(r.packers[:i], r.packers[i+1:]...)
				return p, nil
			}
		}
	}

	// no suitable packer found, return new
	debug.Log("create new pack for %d bytes", size)
	tmpfile, err := ioutil.TempFile("", "restic-temp-pack-")
	if err != nil {
		return nil, errors.Wrap(err, "ioutil.TempFile")
	}

	hw := hashing.NewWriter(tmpfile, sha256.New())
	p := pack.NewPacker(r.key, hw)
	packer = &Packer{
		Packer:  p,
		hw:      hw,
		tmpfile: tmpfile,
	}

	return packer, nil
}

// insertPacker appends p to s.packs.
func (r *packerManager) insertPacker(p *Packer) {
	r.pm.Lock()
	defer r.pm.Unlock()

	r.packers = append(r.packers, p)
	debug.Log("%d packers\n", len(r.packers))
}

// savePacker stores p in the backend.
func (r *Repository) savePacker(p *Packer) error {
	debug.Log("save packer with %d blobs\n", p.Packer.Count())
	_, err := p.Packer.Finalize()
	if err != nil {
		return err
	}

	_, err = p.tmpfile.Seek(0, 0)
	if err != nil {
		return errors.Wrap(err, "Seek")
	}

	id := restic.IDFromHash(p.hw.Sum(nil))
	h := restic.Handle{Type: restic.DataFile, Name: id.String()}

	err = r.be.Save(h, p.tmpfile)
	if err != nil {
		debug.Log("Save(%v) error: %v", h, err)
		return err
	}

	debug.Log("saved as %v", h)

	err = p.tmpfile.Close()
	if err != nil {
		return errors.Wrap(err, "close tempfile")
	}

	err = fs.Remove(p.tmpfile.Name())
	if err != nil {
		return errors.Wrap(err, "Remove")
	}

	// update blobs in the index
	for _, b := range p.Packer.Blobs() {
		debug.Log("  updating blob %v to pack %v", b.ID.Str(), id.Str())
		r.idx.Store(restic.PackedBlob{
			Blob: restic.Blob{
				Type:   b.Type,
				ID:     b.ID,
				Offset: b.Offset,
				Length: uint(b.Length),
			},
			PackID: id,
		})
	}

	return nil
}

// countPacker returns the number of open (unfinished) packers.
func (r *packerManager) countPacker() int {
	r.pm.Lock()
	defer r.pm.Unlock()

	return len(r.packers)
}
<|endoftext|>


<commit_before>package postgresql

import (
	"github.com/kylelemons/godebug/pretty"
	"github.com/tecsisa/authorizr/api"
	"github.com/tecsisa/authorizr/database"
	"testing"
	"time"
)

func TestPostgresRepo_AddGroup(t *testing.T) {
	now := time.Now().UTC()
	testcases := map[string]struct {
		// Previous data
		previousGroup *api.Group
		// Postgres Repo Args
		groupToCreate *api.Group
		// Expected result
		expectedResponse *api.Group
		expectedError    *database.Error
	}{
		"OkCase": {
			groupToCreate: &api.Group{
				ID:       "GroupID",
				Name:     "Name",
				Path:     "Path",
				Urn:      "urn",
				CreateAt: now,
				Org:      "Org",
			},
			expectedResponse: &api.Group{
				ID:       "GroupID",
				Name:     "Name",
				Path:     "Path",
				Urn:      "urn",
				CreateAt: now,
				Org:      "Org",
			},
		},
		"ErrorCasegroupAlreadyExist": {
			previousGroup: &api.Group{
				ID:       "GroupID",
				Name:     "Name",
				Path:     "Path",
				Urn:      "urn",
				CreateAt: now,
				Org:      "Org",
			},
			groupToCreate: &api.Group{
				ID:       "GroupID",
				Name:     "Name",
				Path:     "Path",
				Urn:      "urn",
				CreateAt: now,
				Org:      "Org",
			},
			expectedError: &database.Error{
				Code:    database.INTERNAL_ERROR,
				Message: "pq: duplicate key value violates unique constraint \"groups_pkey\"",
			},
		},
	}

	for n, test := range testcases {
		// Clean user database
		cleanGroupTable()

		// Insert previous data
		if test.previousGroup != nil {
			insertGroup(test.previousGroup.ID, test.previousGroup.Name, test.previousGroup.Path,
				test.previousGroup.CreateAt.UnixNano(), test.previousGroup.Urn, test.previousGroup.Org)
		}
		// Call to repository to store group
		storedGroup, err := repoDB.AddGroup(*test.groupToCreate)
		if test.expectedError != nil {
			dbError, ok := err.(*database.Error)
			if !ok || dbError == nil {
				t.Errorf("Test %v failed. Unexpected data retrieved from error: %v", n, err)
				continue
			}
			if diff := pretty.Compare(dbError, test.expectedError); diff != "" {
				t.Errorf("Test %v failed. Received different error response (received/wanted) %v", n, diff)
				continue
			}
		} else {
			if err != nil {
				t.Errorf("Test %v failed. Unexpected error: %v", n, err)
				continue
			}
			// Check response
			if diff := pretty.Compare(storedGroup, test.expectedResponse); diff != "" {
				t.Errorf("Test %v failed. Received different responses (received/wanted) %v", n, diff)
				continue
			}
			// Check database
			groupNumber, err := getGroupsCountFiltered(test.groupToCreate.ID, test.groupToCreate.Name, test.groupToCreate.Path,
				test.groupToCreate.CreateAt.UnixNano(), test.groupToCreate.Urn, test.groupToCreate.Org)
			if err != nil {
				t.Errorf("Test %v failed. Unexpected error counting users: %v", n, err)
				continue
			}
			if groupNumber != 1 {
				t.Errorf("Test %v failed. Received different user number: %v", n, groupNumber)
				continue
			}

		}

	}
}
<commit_msg>Test GetGroupByName for postgres connector added<commit_after>package postgresql

import (
	"fmt"
	"github.com/kylelemons/godebug/pretty"
	"github.com/tecsisa/authorizr/api"
	"github.com/tecsisa/authorizr/database"
	"testing"
	"time"
)

func TestPostgresRepo_AddGroup(t *testing.T) {
	now := time.Now().UTC()
	testcases := map[string]struct {
		// Previous data
		previousGroup *api.Group
		// Postgres Repo Args
		groupToCreate *api.Group
		// Expected result
		expectedResponse *api.Group
		expectedError    *database.Error
	}{
		"OkCase": {
			groupToCreate: &api.Group{
				ID:       "GroupID",
				Name:     "Name",
				Path:     "Path",
				Urn:      "urn",
				CreateAt: now,
				Org:      "Org",
			},
			expectedResponse: &api.Group{
				ID:       "GroupID",
				Name:     "Name",
				Path:     "Path",
				Urn:      "urn",
				CreateAt: now,
				Org:      "Org",
			},
		},
		"ErrorCasegroupAlreadyExist": {
			previousGroup: &api.Group{
				ID:       "GroupID",
				Name:     "Name",
				Path:     "Path",
				Urn:      "urn",
				CreateAt: now,
				Org:      "Org",
			},
			groupToCreate: &api.Group{
				ID:       "GroupID",
				Name:     "Name",
				Path:     "Path",
				Urn:      "urn",
				CreateAt: now,
				Org:      "Org",
			},
			expectedError: &database.Error{
				Code:    database.INTERNAL_ERROR,
				Message: "pq: duplicate key value violates unique constraint \"groups_pkey\"",
			},
		},
	}

	for n, test := range testcases {
		// Clean user database
		cleanGroupTable()

		// Insert previous data
		if test.previousGroup != nil {
			err := insertGroup(test.previousGroup.ID, test.previousGroup.Name, test.previousGroup.Path,
				test.previousGroup.CreateAt.UnixNano(), test.previousGroup.Urn, test.previousGroup.Org)
			if err != nil {
				t.Errorf("Test %v failed. Unexpected error inserting previous data: %v", n, err)
				continue
			}
		}
		// Call to repository to store group
		storedGroup, err := repoDB.AddGroup(*test.groupToCreate)
		if test.expectedError != nil {
			dbError, ok := err.(*database.Error)
			if !ok || dbError == nil {
				t.Errorf("Test %v failed. Unexpected data retrieved from error: %v", n, err)
				continue
			}
			if diff := pretty.Compare(dbError, test.expectedError); diff != "" {
				t.Errorf("Test %v failed. Received different error response (received/wanted) %v", n, diff)
				continue
			}
		} else {
			if err != nil {
				t.Errorf("Test %v failed. Unexpected error: %v", n, err)
				continue
			}
			// Check response
			if diff := pretty.Compare(storedGroup, test.expectedResponse); diff != "" {
				t.Errorf("Test %v failed. Received different responses (received/wanted) %v", n, diff)
				continue
			}
			// Check database
			groupNumber, err := getGroupsCountFiltered(test.groupToCreate.ID, test.groupToCreate.Name, test.groupToCreate.Path,
				test.groupToCreate.CreateAt.UnixNano(), test.groupToCreate.Urn, test.groupToCreate.Org)
			if err != nil {
				t.Errorf("Test %v failed. Unexpected error counting users: %v", n, err)
				continue
			}
			if groupNumber != 1 {
				t.Errorf("Test %v failed. Received different user number: %v", n, groupNumber)
				continue
			}

		}

	}
}

func TestPostgresRepo_GetGroupByName(t *testing.T) {
	now := time.Now().UTC()
	testcases := map[string]struct {
		// Previous data
		previousGroup *api.Group
		// Postgres Repo Args
		org  string
		name string
		// Expected result
		expectedResponse *api.Group
		expectedError    *database.Error
	}{
		"OkCase": {
			previousGroup: &api.Group{
				ID:       "GroupID",
				Name:     "Name",
				Path:     "Path",
				Urn:      "Urn",
				CreateAt: now,
				Org:      "Org",
			},
			org:  "Org",
			name: "Name",
			expectedResponse: &api.Group{
				ID:       "GroupID",
				Name:     "Name",
				Path:     "Path",
				Urn:      "Urn",
				CreateAt: now,
				Org:      "Org",
			},
		},
		"ErrorCaseGroupNotExist": {
			previousGroup: &api.Group{
				ID:       "GroupID",
				Name:     "Name",
				Path:     "Path",
				Urn:      "Urn",
				CreateAt: now,
				Org:      "Org",
			},
			org:  "Org",
			name: "NotExist",
			expectedError: &database.Error{
				Code:    database.GROUP_NOT_FOUND,
				Message: fmt.Sprintf("Group with organization Org and name NotExist not found"),
			},
		},
	}

	for n, test := range testcases {
		// Clean group database
		cleanGroupTable()

		// Insert previous data
		if test.previousGroup != nil {
			err := insertGroup(test.previousGroup.ID, test.previousGroup.Name, test.previousGroup.Path,
				test.previousGroup.CreateAt.UnixNano(), test.previousGroup.Urn, test.previousGroup.Org)
			if err != nil {
				t.Errorf("Test %v failed. Unexpected error inserting previous data: %v", n, err)
				continue
			}
		}

		// Call to repository to get group
		receivedGroup, err := repoDB.GetGroupByName(test.org, test.name)
		if test.expectedError != nil {
			dbError, ok := err.(*database.Error)
			if !ok || dbError == nil {
				t.Errorf("Test %v failed. Unexpected data retrieved from error: %v", n, err)
				continue
			}
		} else {
			if err != nil {
				t.Errorf("Test %v failed. Unexpected error: %v", n, err)
				continue
			}
			// Check response
			if diff := pretty.Compare(receivedGroup, test.expectedResponse); diff != "" {
				t.Errorf("Test %v failed. Received different responses (received/wanted) %v", n, diff)
				continue
			}
		}

	}
}
<|endoftext|>


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

package common

import (
	"context"
	"fmt"
	"net"
	"os"
	"strconv"
	"time"

	"github.com/sirupsen/logrus"
	"google.golang.org/grpc"

	"github.com/networkservicemesh/networkservicemesh/controlplane/api/crossconnect"
	local "github.com/networkservicemesh/networkservicemesh/controlplane/api/local/connection"
	remote "github.com/networkservicemesh/networkservicemesh/controlplane/api/remote/connection"
	"github.com/networkservicemesh/networkservicemesh/dataplane/api/dataplane"
	"github.com/networkservicemesh/networkservicemesh/pkg/tools"
	monitor_crossconnect "github.com/networkservicemesh/networkservicemesh/sdk/monitor/crossconnect"
)

type NSMDataplane interface {
	dataplane.DataplaneServer
	dataplane.MechanismsMonitorServer
	Init(*DataplaneConfig) error
}

// TODO Convert all the defaults to properly use NsmBaseDir
const (
	NSMBaseDirKey                        = "NSM_BASEDIR"
	NSMBaseDirDefault                    = "/var/lib/networkservicemesh/"
	DataplaneRegistrarSocketKey          = "DATAPLANE_REGISTRAR_SOCKET"
	DataplaneRegistrarSocketDefault      = "/var/lib/networkservicemesh/nsm.dataplane-registrar.io.sock"
	DataplaneRegistrarSocketTypeKey      = "DATAPLANE_REGISTRAR_SOCKET_TYPE"
	DataplaneRegistrarSocketTypeDefault  = "unix"
	DataplaneMetricsEnabledKey           = "METRICS_COLLECTOR_ENABLED"
	DataplaneMetricsEnabledDefault       = false
	DataplaneMetricsRequestPeriodKey     = "METRICS_COLLECTOR_REQUEST_PERIOD"
	DataplaneMetricsRequestPeriodDefault = time.Second * 2
	DataplaneNameKey                     = "DATAPLANE_NAME"
	DataplaneNameDefault                 = "vppagent"
	DataplaneSocketKey                   = "DATAPLANE_SOCKET"
	DataplaneSocketDefault               = "/var/lib/networkservicemesh/nsm-vppagent.dataplane.sock"
	DataplaneSocketTypeKey               = "DATAPLANE_SOCKET_TYPE"
	DataplaneSocketTypeDefault           = "unix"
	DataplaneSrcIPKey                    = "NSM_DATAPLANE_SRC_IP"
)

// DataplaneConfig keeps the common configuration for a forwarding plane
type DataplaneConfig struct {
	Name                    string
	NSMBaseDir              string
	RegistrarSocket         string
	RegistrarSocketType     string
	DataplaneSocket         string
	DataplaneSocketType     string
	MechanismsUpdateChannel chan *Mechanisms
	Mechanisms              *Mechanisms
	MetricsEnabled          bool
	MetricsPeriod           time.Duration
	SrcIP                   net.IP
	EgressInterface         EgressInterfaceType
	GRPCserver              *grpc.Server
	Monitor                 monitor_crossconnect.MonitorServer
	Listener                net.Listener
}

// Mechanisms is a message used to communicate any changes in operational parameters and constraints
type Mechanisms struct {
	RemoteMechanisms []*remote.Mechanism
	LocalMechanisms  []*local.Mechanism
}

func createDataplaneConfig(dataplaneGoals *DataplaneProbeGoals) *DataplaneConfig {
	cfg := &DataplaneConfig{}
	var ok bool

	cfg.Name, ok = os.LookupEnv(DataplaneNameKey)
	if !ok {
		logrus.Infof("%s not set, using default %s", DataplaneNameKey, DataplaneNameDefault)
		cfg.Name = DataplaneNameDefault
	}
	logrus.Infof("Starting dataplane - %s", cfg.Name)

	cfg.DataplaneSocket, ok = os.LookupEnv(DataplaneSocketKey)
	if !ok {
		logrus.Infof("%s not set, using default %s", DataplaneSocketKey, DataplaneSocketDefault)
		cfg.DataplaneSocket = DataplaneSocketDefault
	}
	logrus.Infof("DataplaneSocket: %s", cfg.DataplaneSocket)

	err := tools.SocketCleanup(cfg.DataplaneSocket)
	if err != nil {
		logrus.Fatalf("Error cleaning up socket %s: %s", cfg.DataplaneSocket, err)
	} else {
		dataplaneGoals.SetSocketCleanReady()
	}

	cfg.DataplaneSocketType, ok = os.LookupEnv(DataplaneSocketTypeKey)
	if !ok {
		logrus.Infof("%s not set, using default %s", DataplaneSocketTypeKey, DataplaneSocketTypeDefault)
		cfg.DataplaneSocketType = DataplaneSocketTypeDefault
	}
	logrus.Infof("DataplaneSocketType: %s", cfg.DataplaneSocketType)

	cfg.NSMBaseDir, ok = os.LookupEnv(NSMBaseDirKey)
	if !ok {
		logrus.Infof("%s not set, using default %s", NSMBaseDirKey, NSMBaseDirDefault)
		cfg.NSMBaseDir = NSMBaseDirDefault
	}
	logrus.Infof("NSMBaseDir: %s", cfg.NSMBaseDir)

	cfg.RegistrarSocket, ok = os.LookupEnv(DataplaneRegistrarSocketKey)
	if !ok {
		logrus.Infof("%s not set, using default %s", DataplaneRegistrarSocketKey, DataplaneRegistrarSocketDefault)
		cfg.RegistrarSocket = DataplaneRegistrarSocketDefault
	}
	logrus.Infof("RegistrarSocket: %s", cfg.RegistrarSocket)

	cfg.RegistrarSocketType, ok = os.LookupEnv(DataplaneRegistrarSocketTypeKey)
	if !ok {
		logrus.Infof("%s not set, using default %s", DataplaneRegistrarSocketTypeKey, DataplaneRegistrarSocketTypeDefault)
		cfg.RegistrarSocketType = DataplaneRegistrarSocketTypeDefault
	}
	logrus.Infof("RegistrarSocketType: %s", cfg.RegistrarSocketType)

	cfg.GRPCserver = tools.NewServer()

	cfg.Monitor = monitor_crossconnect.NewMonitorServer()
	crossconnect.RegisterMonitorCrossConnectServer(cfg.GRPCserver, cfg.Monitor)

	cfg.MetricsEnabled = DataplaneMetricsEnabledDefault
	val, ok := os.LookupEnv(DataplaneMetricsEnabledKey)
	if ok {
		res, err := strconv.ParseBool(val)
		if err == nil {
			cfg.MetricsEnabled = res
		}
	}
	logrus.Infof("MetricsEnabled: %v", cfg.MetricsEnabled)

	if cfg.MetricsEnabled {
		cfg.MetricsPeriod = DataplaneMetricsRequestPeriodDefault
		if val, ok = os.LookupEnv(DataplaneMetricsRequestPeriodKey); ok {
			parsedPeriod, err := time.ParseDuration(val)
			if err == nil {
				cfg.MetricsPeriod = parsedPeriod
			}
		}
		logrus.Infof("MetricsPeriod: %v ", cfg.MetricsPeriod)
	}

	srcIPStr, ok := os.LookupEnv(DataplaneSrcIPKey)
	if !ok {
		logrus.Fatalf("Env variable %s must be set to valid srcIP for use for tunnels from this Pod.  Consider using downward API to do so.", DataplaneSrcIPKey)
	} else {
		dataplaneGoals.SetSrcIPReady()
	}
	cfg.SrcIP = net.ParseIP(srcIPStr)
	if cfg.SrcIP == nil {
		logrus.Fatalf("Env variable %s must be set to a valid IP address, was set to %s", DataplaneSrcIPKey, srcIPStr)
	} else {
		dataplaneGoals.SetValidIPReady()
	}
	cfg.EgressInterface, err = NewEgressInterface(cfg.SrcIP)
	if err != nil {
		logrus.Fatalf("Unable to find egress Interface: %s", err)
	} else {
		dataplaneGoals.SetNewEgressIFReady()
	}
	logrus.Infof("SrcIP: %s, IfaceName: %s, SrcIPNet: %s", cfg.SrcIP, cfg.EgressInterface.Name(), cfg.EgressInterface.SrcIPNet())

	return cfg
}

// CreateDataplane creates new Dataplane Registrar client
func CreateDataplane(dp NSMDataplane, dataplaneGoals *DataplaneProbeGoals) *DataplaneRegistration {
	start := time.Now()
	// Populate common configuration
	config := createDataplaneConfig(dataplaneGoals)

	// Initialize the dataplane
	err := dp.Init(config)
	if err != nil {
		logrus.Fatalf("Dataplane initialization failed: %s ", err)
	}

	// Verify the configuration is populated
	if !sanityCheckConfig(config) {
		logrus.Fatalf("Dataplane configuration sanity check failed: %s ", err)
	}

	// Prepare the gRPC server
	config.Listener, err = net.Listen(config.DataplaneSocketType, config.DataplaneSocket)
	if err != nil {
		logrus.Fatalf("Error listening on socket %s: %s ", config.DataplaneSocket, err)
	} else {
		dataplaneGoals.SetSocketListenReady()
	}
	dataplane.RegisterDataplaneServer(config.GRPCserver, dp)
	dataplane.RegisterMechanismsMonitorServer(config.GRPCserver, dp)

	// Start the server
	logrus.Infof("Creating %s server...", config.Name)
	go func() {
		_ = config.GRPCserver.Serve(config.Listener)
	}()
	logrus.Infof("%s server serving", config.Name)

	logrus.Debugf("Starting the %s dataplane server took: %s", config.Name, time.Since(start))

	logrus.Info("Creating Dataplane Registrar Client...")
	registrar := NewDataplaneRegistrarClient(config.RegistrarSocketType, config.RegistrarSocket)
	registration := registrar.Register(context.Background(), config.Name, config.DataplaneSocket, nil, nil)
	logrus.Info("Registered Dataplane Registrar Client")

	return registration
}

func sanityCheckConfig(dataplaneConfig *DataplaneConfig) bool {
	return len(dataplaneConfig.Name) > 0 &&
		len(dataplaneConfig.NSMBaseDir) > 0 &&
		len(dataplaneConfig.RegistrarSocket) > 0 &&
		len(dataplaneConfig.RegistrarSocketType) > 0 &&
		len(dataplaneConfig.DataplaneSocket) > 0 &&
		len(dataplaneConfig.DataplaneSocketType) > 0
}

// SanityCheckConnectionType checks whether the forwarding plane supports the connection type in the request
func SanityCheckConnectionType(mechanisms *Mechanisms, crossConnect *crossconnect.CrossConnect) error {
	localFound, remoteFound := false, false
	/* Verify local mechanisms */
	for _, mech := range mechanisms.LocalMechanisms {
		if crossConnect.GetLocalSource().GetMechanism().GetType() == mech.GetType() || crossConnect.GetLocalDestination().GetMechanism().GetType() == mech.GetType() {
			localFound = true
			break
		}
	}
	if !localFound {
		return fmt.Errorf("connection type not supported by the forwarding plane - local")
	}
	/* Verify remote mechanisms */
	for _, mech := range mechanisms.RemoteMechanisms {
		if crossConnect.GetRemoteSource().GetMechanism().GetType() == mech.GetType() || crossConnect.GetRemoteDestination().GetMechanism().GetType() == mech.GetType() {
			remoteFound = true
			break
		}
	}
	if !remoteFound {
		return fmt.Errorf("connection type not supported by the forwarding plane - remote")
	}
	return nil
}
<commit_msg>Fix sanity check for supported mechanism types by the forwarding plane (#1552)<commit_after>// Copyright 2019 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package common

import (
	"context"
	"fmt"
	"net"
	"os"
	"strconv"
	"time"

	"github.com/sirupsen/logrus"
	"google.golang.org/grpc"

	"github.com/networkservicemesh/networkservicemesh/controlplane/api/crossconnect"
	local "github.com/networkservicemesh/networkservicemesh/controlplane/api/local/connection"
	remote "github.com/networkservicemesh/networkservicemesh/controlplane/api/remote/connection"
	"github.com/networkservicemesh/networkservicemesh/dataplane/api/dataplane"
	"github.com/networkservicemesh/networkservicemesh/pkg/tools"
	monitor_crossconnect "github.com/networkservicemesh/networkservicemesh/sdk/monitor/crossconnect"
)

type NSMDataplane interface {
	dataplane.DataplaneServer
	dataplane.MechanismsMonitorServer
	Init(*DataplaneConfig) error
}

// TODO Convert all the defaults to properly use NsmBaseDir
const (
	NSMBaseDirKey                        = "NSM_BASEDIR"
	NSMBaseDirDefault                    = "/var/lib/networkservicemesh/"
	DataplaneRegistrarSocketKey          = "DATAPLANE_REGISTRAR_SOCKET"
	DataplaneRegistrarSocketDefault      = "/var/lib/networkservicemesh/nsm.dataplane-registrar.io.sock"
	DataplaneRegistrarSocketTypeKey      = "DATAPLANE_REGISTRAR_SOCKET_TYPE"
	DataplaneRegistrarSocketTypeDefault  = "unix"
	DataplaneMetricsEnabledKey           = "METRICS_COLLECTOR_ENABLED"
	DataplaneMetricsEnabledDefault       = false
	DataplaneMetricsRequestPeriodKey     = "METRICS_COLLECTOR_REQUEST_PERIOD"
	DataplaneMetricsRequestPeriodDefault = time.Second * 2
	DataplaneNameKey                     = "DATAPLANE_NAME"
	DataplaneNameDefault                 = "vppagent"
	DataplaneSocketKey                   = "DATAPLANE_SOCKET"
	DataplaneSocketDefault               = "/var/lib/networkservicemesh/nsm-vppagent.dataplane.sock"
	DataplaneSocketTypeKey               = "DATAPLANE_SOCKET_TYPE"
	DataplaneSocketTypeDefault           = "unix"
	DataplaneSrcIPKey                    = "NSM_DATAPLANE_SRC_IP"
)

// DataplaneConfig keeps the common configuration for a forwarding plane
type DataplaneConfig struct {
	Name                    string
	NSMBaseDir              string
	RegistrarSocket         string
	RegistrarSocketType     string
	DataplaneSocket         string
	DataplaneSocketType     string
	MechanismsUpdateChannel chan *Mechanisms
	Mechanisms              *Mechanisms
	MetricsEnabled          bool
	MetricsPeriod           time.Duration
	SrcIP                   net.IP
	EgressInterface         EgressInterfaceType
	GRPCserver              *grpc.Server
	Monitor                 monitor_crossconnect.MonitorServer
	Listener                net.Listener
}

// Mechanisms is a message used to communicate any changes in operational parameters and constraints
type Mechanisms struct {
	RemoteMechanisms []*remote.Mechanism
	LocalMechanisms  []*local.Mechanism
}

func createDataplaneConfig(dataplaneGoals *DataplaneProbeGoals) *DataplaneConfig {
	cfg := &DataplaneConfig{}
	var ok bool

	cfg.Name, ok = os.LookupEnv(DataplaneNameKey)
	if !ok {
		logrus.Infof("%s not set, using default %s", DataplaneNameKey, DataplaneNameDefault)
		cfg.Name = DataplaneNameDefault
	}
	logrus.Infof("Starting dataplane - %s", cfg.Name)

	cfg.DataplaneSocket, ok = os.LookupEnv(DataplaneSocketKey)
	if !ok {
		logrus.Infof("%s not set, using default %s", DataplaneSocketKey, DataplaneSocketDefault)
		cfg.DataplaneSocket = DataplaneSocketDefault
	}
	logrus.Infof("DataplaneSocket: %s", cfg.DataplaneSocket)

	err := tools.SocketCleanup(cfg.DataplaneSocket)
	if err != nil {
		logrus.Fatalf("Error cleaning up socket %s: %s", cfg.DataplaneSocket, err)
	} else {
		dataplaneGoals.SetSocketCleanReady()
	}

	cfg.DataplaneSocketType, ok = os.LookupEnv(DataplaneSocketTypeKey)
	if !ok {
		logrus.Infof("%s not set, using default %s", DataplaneSocketTypeKey, DataplaneSocketTypeDefault)
		cfg.DataplaneSocketType = DataplaneSocketTypeDefault
	}
	logrus.Infof("DataplaneSocketType: %s", cfg.DataplaneSocketType)

	cfg.NSMBaseDir, ok = os.LookupEnv(NSMBaseDirKey)
	if !ok {
		logrus.Infof("%s not set, using default %s", NSMBaseDirKey, NSMBaseDirDefault)
		cfg.NSMBaseDir = NSMBaseDirDefault
	}
	logrus.Infof("NSMBaseDir: %s", cfg.NSMBaseDir)

	cfg.RegistrarSocket, ok = os.LookupEnv(DataplaneRegistrarSocketKey)
	if !ok {
		logrus.Infof("%s not set, using default %s", DataplaneRegistrarSocketKey, DataplaneRegistrarSocketDefault)
		cfg.RegistrarSocket = DataplaneRegistrarSocketDefault
	}
	logrus.Infof("RegistrarSocket: %s", cfg.RegistrarSocket)

	cfg.RegistrarSocketType, ok = os.LookupEnv(DataplaneRegistrarSocketTypeKey)
	if !ok {
		logrus.Infof("%s not set, using default %s", DataplaneRegistrarSocketTypeKey, DataplaneRegistrarSocketTypeDefault)
		cfg.RegistrarSocketType = DataplaneRegistrarSocketTypeDefault
	}
	logrus.Infof("RegistrarSocketType: %s", cfg.RegistrarSocketType)

	cfg.GRPCserver = tools.NewServer()

	cfg.Monitor = monitor_crossconnect.NewMonitorServer()
	crossconnect.RegisterMonitorCrossConnectServer(cfg.GRPCserver, cfg.Monitor)

	cfg.MetricsEnabled = DataplaneMetricsEnabledDefault
	val, ok := os.LookupEnv(DataplaneMetricsEnabledKey)
	if ok {
		res, err := strconv.ParseBool(val)
		if err == nil {
			cfg.MetricsEnabled = res
		}
	}
	logrus.Infof("MetricsEnabled: %v", cfg.MetricsEnabled)

	if cfg.MetricsEnabled {
		cfg.MetricsPeriod = DataplaneMetricsRequestPeriodDefault
		if val, ok = os.LookupEnv(DataplaneMetricsRequestPeriodKey); ok {
			parsedPeriod, err := time.ParseDuration(val)
			if err == nil {
				cfg.MetricsPeriod = parsedPeriod
			}
		}
		logrus.Infof("MetricsPeriod: %v ", cfg.MetricsPeriod)
	}

	srcIPStr, ok := os.LookupEnv(DataplaneSrcIPKey)
	if !ok {
		logrus.Fatalf("Env variable %s must be set to valid srcIP for use for tunnels from this Pod.  Consider using downward API to do so.", DataplaneSrcIPKey)
	} else {
		dataplaneGoals.SetSrcIPReady()
	}
	cfg.SrcIP = net.ParseIP(srcIPStr)
	if cfg.SrcIP == nil {
		logrus.Fatalf("Env variable %s must be set to a valid IP address, was set to %s", DataplaneSrcIPKey, srcIPStr)
	} else {
		dataplaneGoals.SetValidIPReady()
	}
	cfg.EgressInterface, err = NewEgressInterface(cfg.SrcIP)
	if err != nil {
		logrus.Fatalf("Unable to find egress Interface: %s", err)
	} else {
		dataplaneGoals.SetNewEgressIFReady()
	}
	logrus.Infof("SrcIP: %s, IfaceName: %s, SrcIPNet: %s", cfg.SrcIP, cfg.EgressInterface.Name(), cfg.EgressInterface.SrcIPNet())

	return cfg
}

// CreateDataplane creates new Dataplane Registrar client
func CreateDataplane(dp NSMDataplane, dataplaneGoals *DataplaneProbeGoals) *DataplaneRegistration {
	start := time.Now()
	// Populate common configuration
	config := createDataplaneConfig(dataplaneGoals)

	// Initialize the dataplane
	err := dp.Init(config)
	if err != nil {
		logrus.Fatalf("Dataplane initialization failed: %s ", err)
	}

	// Verify the configuration is populated
	if !sanityCheckConfig(config) {
		logrus.Fatalf("Dataplane configuration sanity check failed: %s ", err)
	}

	// Prepare the gRPC server
	config.Listener, err = net.Listen(config.DataplaneSocketType, config.DataplaneSocket)
	if err != nil {
		logrus.Fatalf("Error listening on socket %s: %s ", config.DataplaneSocket, err)
	} else {
		dataplaneGoals.SetSocketListenReady()
	}
	dataplane.RegisterDataplaneServer(config.GRPCserver, dp)
	dataplane.RegisterMechanismsMonitorServer(config.GRPCserver, dp)

	// Start the server
	logrus.Infof("Creating %s server...", config.Name)
	go func() {
		_ = config.GRPCserver.Serve(config.Listener)
	}()
	logrus.Infof("%s server serving", config.Name)

	logrus.Debugf("Starting the %s dataplane server took: %s", config.Name, time.Since(start))

	logrus.Info("Creating Dataplane Registrar Client...")
	registrar := NewDataplaneRegistrarClient(config.RegistrarSocketType, config.RegistrarSocket)
	registration := registrar.Register(context.Background(), config.Name, config.DataplaneSocket, nil, nil)
	logrus.Info("Registered Dataplane Registrar Client")

	return registration
}

func sanityCheckConfig(dataplaneConfig *DataplaneConfig) bool {
	return len(dataplaneConfig.Name) > 0 &&
		len(dataplaneConfig.NSMBaseDir) > 0 &&
		len(dataplaneConfig.RegistrarSocket) > 0 &&
		len(dataplaneConfig.RegistrarSocketType) > 0 &&
		len(dataplaneConfig.DataplaneSocket) > 0 &&
		len(dataplaneConfig.DataplaneSocketType) > 0
}

// SanityCheckConnectionType checks whether the forwarding plane supports the connection type in the request
func SanityCheckConnectionType(mechanisms *Mechanisms, crossConnect *crossconnect.CrossConnect) error {
	localFound, remoteFound := false, false
	/* Verify local mechanisms */
	for _, mech := range mechanisms.LocalMechanisms {
		if crossConnect.GetLocalSource().GetMechanism().GetType() == mech.GetType() || crossConnect.GetLocalDestination().GetMechanism().GetType() == mech.GetType() {
			localFound = true
			break
		}
	}
	/* Verify remote mechanisms */
	for _, mech := range mechanisms.RemoteMechanisms {
		if crossConnect.GetRemoteSource().GetMechanism().GetType() == mech.GetType() || crossConnect.GetRemoteDestination().GetMechanism().GetType() == mech.GetType() {
			remoteFound = true
			break
		}
	}
	/* If none of them matched, mechanism is not supported by the forwarding plane */
	if !localFound && !remoteFound {
		return fmt.Errorf("connection mechanism type not supported by the forwarding plane")
	}
	return nil
}
<|endoftext|>