<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package kvm

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"strings"
	"text/template"

	"github.com/docker/machine/libmachine/log"
	libvirt "github.com/libvirt/libvirt-go"
	"github.com/pkg/errors"
)

// Replace with hardcoded range with CIDR
// https://play.golang.org/p/m8TNTtygK0
const networkTmpl = `
<network>
  <name>{{.PrivateNetwork}}</name>
  <dns enable='no'/>
  <ip address='192.168.39.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='192.168.39.2' end='192.168.39.254'/>
    </dhcp>
  </ip>
</network>
`

func (d *Driver) createNetwork() error {
	if d.MAC == "" {
		mac, err := randomMAC()
		if err != nil {
			return errors.Wrap(err, "generating mac address")
		}
		d.MAC = mac.String()
	}
	conn, err := getConnection()
	if err != nil {
		return errors.Wrap(err, "getting libvirt connection")
	}
	defer conn.Close()

	tmpl := template.Must(template.New("network").Parse(networkTmpl))
	var networkXML bytes.Buffer
	err = tmpl.Execute(&networkXML, d)
	if err != nil {
		return errors.Wrap(err, "executing network template")
	}

	//Check if network already exists
	if _, err := conn.LookupNetworkByName(d.PrivateNetwork); err == nil {
		return nil
	}

	network, err := conn.NetworkDefineXML(networkXML.String())
	if err != nil {
		return errors.Wrapf(err, "defining network from xml: %s", networkXML.String())
	}
	err = network.SetAutostart(true)
	if err != nil {
		return errors.Wrap(err, "setting network to autostart")
	}

	err = network.Create()
	if err != nil {
		return errors.Wrap(err, "creating network")
	}

	return nil
}

func (d *Driver) lookupIP() (string, error) {
	conn, err := getConnection()
	if err != nil {
		return "", errors.Wrap(err, "getting connection and domain")
	}

	defer conn.Close()

	libVersion, err := conn.GetLibVersion()
	if err != nil {
		return "", errors.Wrap(err, "getting libversion")
	}

	// Earlier versions of libvirt use a lease file instead of a status file
	if libVersion < 1002006 {
		return d.lookupIPFromLeasesFile()
	}

	return d.lookupIPFromStatusFile(conn)
}

func (d *Driver) lookupIPFromStatusFile(conn *libvirt.Connect) (string, error) {
	network, err := conn.LookupNetworkByName(d.PrivateNetwork)
	if err != nil {
		return "", errors.Wrap(err, "looking up network by name")
	}

	bridge, err := network.GetBridgeName()
	if err != nil {
		log.Warnf("Failed to get network bridge: %s", err)
		return "", err
	}
	statusFile := fmt.Sprintf("/var/lib/libvirt/dnsmasq/%s.status", bridge)
	statuses, err := ioutil.ReadFile(statusFile)
	if err != nil {
		return "", errors.Wrap(err, "reading status file")
	}
	type StatusEntry struct {
		IPAddress string `json:"ip-address"`
		Hostname  string `json:"hostname"`
	}

	var statusEntries []StatusEntry

	// If the status file is empty, parsing will fail, ignore this error.
	_ = json.Unmarshal(statuses, &statusEntries)

	ipAddress := ""
	for _, status := range statusEntries {
		if status.Hostname == d.MachineName {
			ipAddress = status.IPAddress
		}
	}

	return ipAddress, nil
}

func (d *Driver) lookupIPFromLeasesFile() (string, error) {
	leasesFile := fmt.Sprintf("/var/lib/libvirt/dnsmasq/%s.leases", d.PrivateNetwork)
	leases, err := ioutil.ReadFile(leasesFile)
	if err != nil {
		return "", errors.Wrap(err, "reading leases file")
	}
	ipAddress := ""
	for _, lease := range strings.Split(string(leases), "\n") {
		if len(lease) == 0 {
			continue
		}
		// format for lease entry
		// ExpiryTime MAC IP Hostname ExtendedMAC
		entry := strings.Split(lease, " ")
		if len(entry) != 5 {
			return "", fmt.Errorf("Malformed leases entry: %s", entry)
		}
		if entry[1] == d.MAC {
			ipAddress = entry[2]
		}
	}
	return ipAddress, nil
}
<commit_msg>Start the default network for the kvm2 driver.<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package kvm

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"strings"
	"text/template"

	"github.com/docker/machine/libmachine/log"
	libvirt "github.com/libvirt/libvirt-go"
	"github.com/pkg/errors"
)

// Replace with hardcoded range with CIDR
// https://play.golang.org/p/m8TNTtygK0
const networkTmpl = `
<network>
  <name>{{.PrivateNetwork}}</name>
  <dns enable='no'/>
  <ip address='192.168.39.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='192.168.39.2' end='192.168.39.254'/>
    </dhcp>
  </ip>
</network>
`

func setupNetwork(conn *libvirt.Connect, name string) error {
	n, err := conn.LookupNetworkByName(defaultNetworkName)
	if err != nil {
		return errors.Wrapf(err, "checking network %s", name)
	}
	autostart, err := n.GetAutostart()
	if err != nil {
		return errors.Wrapf(err, "checking network %s autostart", name)
	}
	if !autostart {
		if err := n.SetAutostart(true); err != nil {
			return errors.Wrapf(err, "setting autostart for network %s", name)
		}
	}

	active, err := n.IsActive()
	if err != nil {
		return errors.Wrapf(err, "checking network status for %s", name)
	}
	if !active {
		if err := n.Create(); err != nil {
			return errors.Wrapf(err, "starting network %s", name)
		}
	}
	return nil

}

func (d *Driver) createNetwork() error {
	if d.MAC == "" {
		mac, err := randomMAC()
		if err != nil {
			return errors.Wrap(err, "generating mac address")
		}
		d.MAC = mac.String()
	}
	conn, err := getConnection()
	if err != nil {
		return errors.Wrap(err, "getting libvirt connection")
	}
	defer conn.Close()

	tmpl := template.Must(template.New("network").Parse(networkTmpl))
	var networkXML bytes.Buffer
	if err := tmpl.Execute(&networkXML, d); err != nil {
		return errors.Wrap(err, "executing network template")
	}

	// Start the default network
	log.Infof("Setting up network %s", defaultNetworkName)
	if err := setupNetwork(conn, defaultNetworkName); err != nil {
		return err
	}

	//Check if network already exists
	if _, err := conn.LookupNetworkByName(d.PrivateNetwork); err == nil {
		return nil
	}

	network, err := conn.NetworkDefineXML(networkXML.String())
	if err != nil {
		return errors.Wrapf(err, "defining network from xml: %s", networkXML.String())
	}
	if err := network.Create(); err != nil {
		return errors.Wrapf(err, "creating network %s", d.PrivateNetwork)
	}

	log.Infof("Setting up network %s", d.PrivateNetwork)
	if err := setupNetwork(conn, d.PrivateNetwork); err != nil {
		return err
	}

	return nil
}

func (d *Driver) lookupIP() (string, error) {
	conn, err := getConnection()
	if err != nil {
		return "", errors.Wrap(err, "getting connection and domain")
	}

	defer conn.Close()

	libVersion, err := conn.GetLibVersion()
	if err != nil {
		return "", errors.Wrap(err, "getting libversion")
	}

	// Earlier versions of libvirt use a lease file instead of a status file
	if libVersion < 1002006 {
		return d.lookupIPFromLeasesFile()
	}

	return d.lookupIPFromStatusFile(conn)
}

func (d *Driver) lookupIPFromStatusFile(conn *libvirt.Connect) (string, error) {
	network, err := conn.LookupNetworkByName(d.PrivateNetwork)
	if err != nil {
		return "", errors.Wrap(err, "looking up network by name")
	}

	bridge, err := network.GetBridgeName()
	if err != nil {
		log.Warnf("Failed to get network bridge: %s", err)
		return "", err
	}
	statusFile := fmt.Sprintf("/var/lib/libvirt/dnsmasq/%s.status", bridge)
	statuses, err := ioutil.ReadFile(statusFile)
	if err != nil {
		return "", errors.Wrap(err, "reading status file")
	}
	type StatusEntry struct {
		IPAddress string `json:"ip-address"`
		Hostname  string `json:"hostname"`
	}

	var statusEntries []StatusEntry

	// If the status file is empty, parsing will fail, ignore this error.
	_ = json.Unmarshal(statuses, &statusEntries)

	ipAddress := ""
	for _, status := range statusEntries {
		if status.Hostname == d.MachineName {
			ipAddress = status.IPAddress
		}
	}

	return ipAddress, nil
}

func (d *Driver) lookupIPFromLeasesFile() (string, error) {
	leasesFile := fmt.Sprintf("/var/lib/libvirt/dnsmasq/%s.leases", d.PrivateNetwork)
	leases, err := ioutil.ReadFile(leasesFile)
	if err != nil {
		return "", errors.Wrap(err, "reading leases file")
	}
	ipAddress := ""
	for _, lease := range strings.Split(string(leases), "\n") {
		if len(lease) == 0 {
			continue
		}
		// format for lease entry
		// ExpiryTime MAC IP Hostname ExtendedMAC
		entry := strings.Split(lease, " ")
		if len(entry) != 5 {
			return "", fmt.Errorf("Malformed leases entry: %s", entry)
		}
		if entry[1] == d.MAC {
			ipAddress = entry[2]
		}
	}
	return ipAddress, nil
}
<|endoftext|>


<commit_before>package notifications

import (
	"encoding/base64"
	"fmt"
	"github.com/spf13/cobra"
	"net/smtp"
	"os"
	"time"

	t "github.com/containrrr/watchtower/pkg/types"
	log "github.com/sirupsen/logrus"
	"strconv"
)

const (
	emailType = "email"
)

// Implements Notifier, logrus.Hook
// The default logrus email integration would have several issues:
// - It would send one email per log output
// - It would only send errors
// We work around that by holding on to log entries until the update cycle is done.
type emailTypeNotifier struct {
	From, To               string
	Server, User, Password, SubjectTag string
	Port                   int
	tlsSkipVerify          bool
	entries                []*log.Entry
	logLevels              []log.Level
	delay                  time.Duration
}

func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Notifier {
	flags := c.PersistentFlags()

	from, _ := flags.GetString("notification-email-from")
	to, _ := flags.GetString("notification-email-to")
	server, _ := flags.GetString("notification-email-server")
	user, _ := flags.GetString("notification-email-server-user")
	password, _ := flags.GetString("notification-email-server-password")
	port, _ := flags.GetInt("notification-email-server-port")
	tlsSkipVerify, _ := flags.GetBool("notification-email-server-tls-skip-verify")
	delay, _ := flags.GetInt("notification-email-delay")
	subjecttag, _ := flags.GetString("notification-email-subjecttag")

	n := &emailTypeNotifier{
		From:          from,
		To:            to,
		Server:        server,
		User:          user,
		Password:      password,
		Port:          port,
		tlsSkipVerify: tlsSkipVerify,
		logLevels:     acceptedLogLevels,
		delay:         time.Duration(delay) * time.Second,
		SubjectTag:    subjecttag,
	}

	log.AddHook(n)

	return n
}

func (e *emailTypeNotifier) buildMessage(entries []*log.Entry) []byte {
	// If variable SubjectTag is empty define emailSubject as "Watchtower updates".
	// If variable SubjectTag is set define emailSubject as "$SubjectTag Watchtower updates"
	// For example: SubjectTag=[Server Munich] -> "[Server Munich] Watchtower updates ..."
	if SubjectTag == nil {
		emailSubject := "Watchtower updates"
	} else {
		emailSubject := SubjectTag + " Watchtower updates"
	}
	if hostname, err := os.Hostname(); err == nil {
		emailSubject += " on " + hostname
	}
	body := ""
	for _, entry := range entries {
		body += entry.Time.Format("2006-01-02 15:04:05") + " (" + entry.Level.String() + "): " + entry.Message + "\r\n"
		// We don't use fields in watchtower, so don't bother sending them.
	}

	t := time.Now()

	header := make(map[string]string)
	header["From"] = e.From
	header["To"] = e.To
	header["Subject"] = emailSubject
	header["Date"] = t.Format(time.RFC1123Z)
	header["MIME-Version"] = "1.0"
	header["Content-Type"] = "text/plain; charset=\"utf-8\""
	header["Content-Transfer-Encoding"] = "base64"

	message := ""
	for k, v := range header {
		message += fmt.Sprintf("%s: %s\r\n", k, v)
	}

	encodedBody := base64.StdEncoding.EncodeToString([]byte(body))
	//RFC 2045 base64 encoding demands line no longer than 76 characters.
	for _, line := range SplitSubN(encodedBody, 76) {
		message += "\r\n" + line
	}

	return []byte(message)
}

func (e *emailTypeNotifier) sendEntries(entries []*log.Entry) {
	// Do the sending in a separate goroutine so we don't block the main process.
	msg := e.buildMessage(entries)
	go func() {
		var auth smtp.Auth
		if e.User != "" {
			auth = smtp.PlainAuth("", e.User, e.Password, e.Server)
		}
		err := SendMail(e.Server+":"+strconv.Itoa(e.Port), e.tlsSkipVerify, auth, e.From, []string{e.To}, msg)
		if err != nil {
			// Use fmt so it doesn't trigger another email.
			fmt.Println("Failed to send notification email: ", err)
		}
	}()
}

func (e *emailTypeNotifier) StartNotification() {
	if e.entries == nil {
		e.entries = make([]*log.Entry, 0, 10)
	}
}

func (e *emailTypeNotifier) SendNotification() {
	if e.entries == nil || len(e.entries) <= 0 {
		return
	}

	if e.delay > 0 {
		time.Sleep(e.delay)
	}

	e.sendEntries(e.entries)
	e.entries = nil
}

func (e *emailTypeNotifier) Levels() []log.Level {
	return e.logLevels
}

func (e *emailTypeNotifier) Fire(entry *log.Entry) error {
	if e.entries != nil {
		e.entries = append(e.entries, entry)
	} else {
		// Log output generated outside a cycle is sent immediately.
		e.sendEntries([]*log.Entry{entry})
	}
	return nil
}
<commit_msg>Update email.go<commit_after>package notifications

import (
	"encoding/base64"
	"fmt"
	"github.com/spf13/cobra"
	"net/smtp"
	"os"
	"time"

	t "github.com/containrrr/watchtower/pkg/types"
	log "github.com/sirupsen/logrus"
	"strconv"
)

const (
	emailType = "email"
)

// Implements Notifier, logrus.Hook
// The default logrus email integration would have several issues:
// - It would send one email per log output
// - It would only send errors
// We work around that by holding on to log entries until the update cycle is done.
type emailTypeNotifier struct {
	From, To               string
	Server, User, Password, SubjectTag string
	Port                   int
	tlsSkipVerify          bool
	entries                []*log.Entry
	logLevels              []log.Level
	delay                  time.Duration
}

func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Notifier {
	flags := c.PersistentFlags()

	from, _ := flags.GetString("notification-email-from")
	to, _ := flags.GetString("notification-email-to")
	server, _ := flags.GetString("notification-email-server")
	user, _ := flags.GetString("notification-email-server-user")
	password, _ := flags.GetString("notification-email-server-password")
	port, _ := flags.GetInt("notification-email-server-port")
	tlsSkipVerify, _ := flags.GetBool("notification-email-server-tls-skip-verify")
	delay, _ := flags.GetInt("notification-email-delay")
	subjecttag, _ := flags.GetString("notification-email-subjecttag")

	n := &emailTypeNotifier{
		From:          from,
		To:            to,
		Server:        server,
		User:          user,
		Password:      password,
		Port:          port,
		tlsSkipVerify: tlsSkipVerify,
		logLevels:     acceptedLogLevels,
		delay:         time.Duration(delay) * time.Second,
		SubjectTag:    subjecttag,
	}

	log.AddHook(n)

	return n
}

func (e *emailTypeNotifier) buildMessage(entries []*log.Entry) []byte {
	if SubjectTag == "" {
		emailSubject := "Watchtower updates"
	} else {
		emailSubject := SubjectTag + " Watchtower updates"
	}
	if hostname, err := os.Hostname(); err == nil {
		emailSubject += " on " + hostname
	}
	body := ""
	for _, entry := range entries {
		body += entry.Time.Format("2006-01-02 15:04:05") + " (" + entry.Level.String() + "): " + entry.Message + "\r\n"
		// We don't use fields in watchtower, so don't bother sending them.
	}

	t := time.Now()

	header := make(map[string]string)
	header["From"] = e.From
	header["To"] = e.To
	header["Subject"] = emailSubject
	header["Date"] = t.Format(time.RFC1123Z)
	header["MIME-Version"] = "1.0"
	header["Content-Type"] = "text/plain; charset=\"utf-8\""
	header["Content-Transfer-Encoding"] = "base64"

	message := ""
	for k, v := range header {
		message += fmt.Sprintf("%s: %s\r\n", k, v)
	}

	encodedBody := base64.StdEncoding.EncodeToString([]byte(body))
	//RFC 2045 base64 encoding demands line no longer than 76 characters.
	for _, line := range SplitSubN(encodedBody, 76) {
		message += "\r\n" + line
	}

	return []byte(message)
}

func (e *emailTypeNotifier) sendEntries(entries []*log.Entry) {
	// Do the sending in a separate goroutine so we don't block the main process.
	msg := e.buildMessage(entries)
	go func() {
		var auth smtp.Auth
		if e.User != "" {
			auth = smtp.PlainAuth("", e.User, e.Password, e.Server)
		}
		err := SendMail(e.Server+":"+strconv.Itoa(e.Port), e.tlsSkipVerify, auth, e.From, []string{e.To}, msg)
		if err != nil {
			// Use fmt so it doesn't trigger another email.
			fmt.Println("Failed to send notification email: ", err)
		}
	}()
}

func (e *emailTypeNotifier) StartNotification() {
	if e.entries == nil {
		e.entries = make([]*log.Entry, 0, 10)
	}
}

func (e *emailTypeNotifier) SendNotification() {
	if e.entries == nil || len(e.entries) <= 0 {
		return
	}

	if e.delay > 0 {
		time.Sleep(e.delay)
	}

	e.sendEntries(e.entries)
	e.entries = nil
}

func (e *emailTypeNotifier) Levels() []log.Level {
	return e.logLevels
}

func (e *emailTypeNotifier) Fire(entry *log.Entry) error {
	if e.entries != nil {
		e.entries = append(e.entries, entry)
	} else {
		// Log output generated outside a cycle is sent immediately.
		e.sendEntries([]*log.Entry{entry})
	}
	return nil
}
<|endoftext|>


<commit_before>package tlsconfig

import (
	"crypto/tls"
)

// SecureCiphers updates a *tls.Config with a secure ciphersuite configuration.
// If c is nil, a new config will be provided.
func SecureCiphers(c *tls.Config) *tls.Config {
	if c == nil {
		c = &tls.Config{}
	}

	c.MinVersion = tls.VersionTLS10 // disable SSLv3
	c.MaxVersion = tls.VersionTLS12 // enable TLS_FALLBACK_SCSV: https://go-review.googlesource.com/#/c/1776/

	// Use all available ciphersuites minus RC4 and 3DES in a sane order.
	// This configuration has forward secrecy for supported browsers, and gets
	// an ‘A’ grade on SSL Labs.
	c.CipherSuites = []uint16{
		tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
		tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
		tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
		tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
		tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
		tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
		tls.TLS_RSA_WITH_AES_128_CBC_SHA,
		tls.TLS_RSA_WITH_AES_256_CBC_SHA,
	}
	c.PreferServerCipherSuites = true

	return c
}
<commit_msg>pkg/tlsconfig: Add new TLS ciphersuites available in Go 1.6<commit_after>package tlsconfig

import (
	"crypto/tls"
)

// SecureCiphers updates a *tls.Config with a secure ciphersuite configuration.
// If c is nil, a new config will be provided.
func SecureCiphers(c *tls.Config) *tls.Config {
	if c == nil {
		c = &tls.Config{}
	}

	c.MinVersion = tls.VersionTLS10 // disable SSLv3
	c.MaxVersion = tls.VersionTLS12 // enable TLS_FALLBACK_SCSV: https://go-review.googlesource.com/#/c/1776/

	// Use all available ciphersuites minus RC4 and 3DES in a sane order.
	// This configuration has forward secrecy for supported browsers, and gets
	// an ‘A’ grade on SSL Labs.
	c.CipherSuites = []uint16{
		tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
		tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
		tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
		tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
		tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
		tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
		tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
		tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
		tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
		tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
		tls.TLS_RSA_WITH_AES_128_CBC_SHA,
		tls.TLS_RSA_WITH_AES_256_CBC_SHA,
	}
	c.PreferServerCipherSuites = true

	return c
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes 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 node

import (
	"net"
	"reflect"
	"testing"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	netutils "k8s.io/utils/net"
)

func TestGetPreferredAddress(t *testing.T) {
	testcases := map[string]struct {
		Labels      map[string]string
		Addresses   []v1.NodeAddress
		Preferences []v1.NodeAddressType

		ExpectErr     string
		ExpectAddress string
	}{
		"no addresses": {
			ExpectErr: "no preferred addresses found; known addresses: []",
		},
		"missing address": {
			Addresses: []v1.NodeAddress{
				{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
			},
			Preferences: []v1.NodeAddressType{v1.NodeHostName},
			ExpectErr:   "no preferred addresses found; known addresses: [{InternalIP 1.2.3.4}]",
		},
		"found address": {
			Addresses: []v1.NodeAddress{
				{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
				{Type: v1.NodeExternalIP, Address: "1.2.3.5"},
				{Type: v1.NodeExternalIP, Address: "1.2.3.7"},
			},
			Preferences:   []v1.NodeAddressType{v1.NodeHostName, v1.NodeExternalIP},
			ExpectAddress: "1.2.3.5",
		},
		"found hostname address": {
			Labels: map[string]string{v1.LabelHostname: "label-hostname"},
			Addresses: []v1.NodeAddress{
				{Type: v1.NodeExternalIP, Address: "1.2.3.5"},
				{Type: v1.NodeHostName, Address: "status-hostname"},
			},
			Preferences:   []v1.NodeAddressType{v1.NodeHostName, v1.NodeExternalIP},
			ExpectAddress: "status-hostname",
		},
		"label address ignored": {
			Labels: map[string]string{v1.LabelHostname: "label-hostname"},
			Addresses: []v1.NodeAddress{
				{Type: v1.NodeExternalIP, Address: "1.2.3.5"},
			},
			Preferences:   []v1.NodeAddressType{v1.NodeHostName, v1.NodeExternalIP},
			ExpectAddress: "1.2.3.5",
		},
	}

	for k, tc := range testcases {
		node := &v1.Node{
			ObjectMeta: metav1.ObjectMeta{Labels: tc.Labels},
			Status:     v1.NodeStatus{Addresses: tc.Addresses},
		}
		address, err := GetPreferredNodeAddress(node, tc.Preferences)
		errString := ""
		if err != nil {
			errString = err.Error()
		}
		if errString != tc.ExpectErr {
			t.Errorf("%s: expected err=%q, got %q", k, tc.ExpectErr, errString)
		}
		if address != tc.ExpectAddress {
			t.Errorf("%s: expected address=%q, got %q", k, tc.ExpectAddress, address)
		}
	}
}

func TestGetNodeHostIPs(t *testing.T) {
	testcases := []struct {
		name      string
		addresses []v1.NodeAddress

		expectIPs []net.IP
	}{
		{
			name:      "no addresses",
			expectIPs: nil,
		},
		{
			name: "no InternalIP/ExternalIP",
			addresses: []v1.NodeAddress{
				{Type: v1.NodeHostName, Address: "example.com"},
			},
			expectIPs: nil,
		},
		{
			name: "IPv4-only, simple",
			addresses: []v1.NodeAddress{
				{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
			},
			expectIPs: []net.IP{netutils.ParseIPSloppy("1.2.3.4")},
		},
		{
			name: "IPv4-only, external-first",
			addresses: []v1.NodeAddress{
				{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
				{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
			},
			expectIPs: []net.IP{netutils.ParseIPSloppy("1.2.3.4")},
		},
		{
			name: "IPv4-only, no internal",
			addresses: []v1.NodeAddress{
				{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
			},
			expectIPs: []net.IP{netutils.ParseIPSloppy("4.3.2.1")},
		},
		{
			name: "dual-stack node",
			addresses: []v1.NodeAddress{
				{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
				{Type: v1.NodeInternalIP, Address: "a:b::c:d"},
				{Type: v1.NodeExternalIP, Address: "d:c::b:a"},
			},
			expectIPs: []net.IP{netutils.ParseIPSloppy("1.2.3.4"), netutils.ParseIPSloppy("a:b::c:d")},
		},
		{
			name: "dual-stack node",
			addresses: []v1.NodeAddress{
				{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
				{Type: v1.NodeInternalIP, Address: "a:b::c:d"},
				{Type: v1.NodeExternalIP, Address: "d:c::b:a"},
			},
			expectIPs: []net.IP{netutils.ParseIPSloppy("1.2.3.4"), netutils.ParseIPSloppy("a:b::c:d")},
		},
		{
			name: "dual-stack node, different order",
			addresses: []v1.NodeAddress{
				{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
				{Type: v1.NodeInternalIP, Address: "a:b::c:d"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
				{Type: v1.NodeExternalIP, Address: "d:c::b:a"},
			},
			expectIPs: []net.IP{netutils.ParseIPSloppy("1.2.3.4"), netutils.ParseIPSloppy("a:b::c:d")},
		},
		{
			name: "dual-stack node, different order",
			addresses: []v1.NodeAddress{
				{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
				{Type: v1.NodeInternalIP, Address: "a:b::c:d"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
				{Type: v1.NodeExternalIP, Address: "d:c::b:a"},
			},
			expectIPs: []net.IP{netutils.ParseIPSloppy("1.2.3.4"), netutils.ParseIPSloppy("a:b::c:d")},
		},
		{
			name: "dual-stack node, IPv6-first, no internal IPv4",
			addresses: []v1.NodeAddress{
				{Type: v1.NodeInternalIP, Address: "a:b::c:d"},
				{Type: v1.NodeExternalIP, Address: "d:c::b:a"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
			},
			expectIPs: []net.IP{netutils.ParseIPSloppy("a:b::c:d"), netutils.ParseIPSloppy("4.3.2.1")},
		},
		{
			name: "dual-stack node, IPv6-first, no internal IPv4, dual-stack cluster",
			addresses: []v1.NodeAddress{
				{Type: v1.NodeInternalIP, Address: "a:b::c:d"},
				{Type: v1.NodeExternalIP, Address: "d:c::b:a"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
			},
			expectIPs: []net.IP{netutils.ParseIPSloppy("a:b::c:d"), netutils.ParseIPSloppy("4.3.2.1")},
		},
	}

	for _, tc := range testcases {
		t.Run(tc.name, func(t *testing.T) {
			node := &v1.Node{
				Status: v1.NodeStatus{Addresses: tc.addresses},
			}
			nodeIPs, err := GetNodeHostIPs(node)
			nodeIP, err2 := GetNodeHostIP(node)

			if (err == nil && err2 != nil) || (err != nil && err2 == nil) {
				t.Errorf("GetNodeHostIPs() returned error=%q but GetNodeHostIP() returned error=%q", err, err2)
			}
			if err != nil {
				if tc.expectIPs != nil {
					t.Errorf("expected %v, got error (%v)", tc.expectIPs, err)
				}
			} else if tc.expectIPs == nil {
				t.Errorf("expected error, got %v", nodeIPs)
			} else if !reflect.DeepEqual(nodeIPs, tc.expectIPs) {
				t.Errorf("expected %v, got %v", tc.expectIPs, nodeIPs)
			} else if !nodeIP.Equal(nodeIPs[0]) {
				t.Errorf("GetNodeHostIP did not return same primary (%s) as GetNodeHostIPs (%s)", nodeIP.String(), nodeIPs[0].String())
			}
		})
	}
}

func TestGetHostname(t *testing.T) {
	testCases := []struct {
		hostName         string
		expectedHostName string
		expectError      bool
	}{
		{
			hostName:    "   ",
			expectError: true,
		},
		{
			hostName:         " abc  ",
			expectedHostName: "abc",
			expectError:      false,
		},
	}

	for idx, test := range testCases {
		hostName, err := GetHostname(test.hostName)
		if err != nil && !test.expectError {
			t.Errorf("[%d]: unexpected error: %s", idx, err)
		}
		if err == nil && test.expectError {
			t.Errorf("[%d]: expected error, got none", idx)
		}
		if test.expectedHostName != hostName {
			t.Errorf("[%d]: expected output %q, got %q", idx, test.expectedHostName, hostName)
		}

	}
}
<commit_msg>add unit test coverage for pkg/util/node<commit_after>/*
Copyright 2016 The Kubernetes 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 node

import (
	"context"
	"net"
	"reflect"
	"testing"

	"github.com/stretchr/testify/assert"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	kubeclientfake "k8s.io/client-go/kubernetes/fake"
	netutils "k8s.io/utils/net"
)

func TestGetPreferredAddress(t *testing.T) {
	testcases := map[string]struct {
		Labels      map[string]string
		Addresses   []v1.NodeAddress
		Preferences []v1.NodeAddressType

		ExpectErr     string
		ExpectAddress string
	}{
		"no addresses": {
			ExpectErr: "no preferred addresses found; known addresses: []",
		},
		"missing address": {
			Addresses: []v1.NodeAddress{
				{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
			},
			Preferences: []v1.NodeAddressType{v1.NodeHostName},
			ExpectErr:   "no preferred addresses found; known addresses: [{InternalIP 1.2.3.4}]",
		},
		"found address": {
			Addresses: []v1.NodeAddress{
				{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
				{Type: v1.NodeExternalIP, Address: "1.2.3.5"},
				{Type: v1.NodeExternalIP, Address: "1.2.3.7"},
			},
			Preferences:   []v1.NodeAddressType{v1.NodeHostName, v1.NodeExternalIP},
			ExpectAddress: "1.2.3.5",
		},
		"found hostname address": {
			Labels: map[string]string{v1.LabelHostname: "label-hostname"},
			Addresses: []v1.NodeAddress{
				{Type: v1.NodeExternalIP, Address: "1.2.3.5"},
				{Type: v1.NodeHostName, Address: "status-hostname"},
			},
			Preferences:   []v1.NodeAddressType{v1.NodeHostName, v1.NodeExternalIP},
			ExpectAddress: "status-hostname",
		},
		"label address ignored": {
			Labels: map[string]string{v1.LabelHostname: "label-hostname"},
			Addresses: []v1.NodeAddress{
				{Type: v1.NodeExternalIP, Address: "1.2.3.5"},
			},
			Preferences:   []v1.NodeAddressType{v1.NodeHostName, v1.NodeExternalIP},
			ExpectAddress: "1.2.3.5",
		},
	}

	for k, tc := range testcases {
		node := &v1.Node{
			ObjectMeta: metav1.ObjectMeta{Labels: tc.Labels},
			Status:     v1.NodeStatus{Addresses: tc.Addresses},
		}
		address, err := GetPreferredNodeAddress(node, tc.Preferences)
		errString := ""
		if err != nil {
			errString = err.Error()
		}
		if errString != tc.ExpectErr {
			t.Errorf("%s: expected err=%q, got %q", k, tc.ExpectErr, errString)
		}
		if address != tc.ExpectAddress {
			t.Errorf("%s: expected address=%q, got %q", k, tc.ExpectAddress, address)
		}
	}
}

func TestGetNodeHostIPs(t *testing.T) {
	testcases := []struct {
		name      string
		addresses []v1.NodeAddress

		expectIPs []net.IP
	}{
		{
			name:      "no addresses",
			expectIPs: nil,
		},
		{
			name: "no InternalIP/ExternalIP",
			addresses: []v1.NodeAddress{
				{Type: v1.NodeHostName, Address: "example.com"},
			},
			expectIPs: nil,
		},
		{
			name: "IPv4-only, simple",
			addresses: []v1.NodeAddress{
				{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
			},
			expectIPs: []net.IP{netutils.ParseIPSloppy("1.2.3.4")},
		},
		{
			name: "IPv4-only, external-first",
			addresses: []v1.NodeAddress{
				{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
				{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
			},
			expectIPs: []net.IP{netutils.ParseIPSloppy("1.2.3.4")},
		},
		{
			name: "IPv4-only, no internal",
			addresses: []v1.NodeAddress{
				{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
			},
			expectIPs: []net.IP{netutils.ParseIPSloppy("4.3.2.1")},
		},
		{
			name: "dual-stack node",
			addresses: []v1.NodeAddress{
				{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
				{Type: v1.NodeInternalIP, Address: "a:b::c:d"},
				{Type: v1.NodeExternalIP, Address: "d:c::b:a"},
			},
			expectIPs: []net.IP{netutils.ParseIPSloppy("1.2.3.4"), netutils.ParseIPSloppy("a:b::c:d")},
		},
		{
			name: "dual-stack node",
			addresses: []v1.NodeAddress{
				{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
				{Type: v1.NodeInternalIP, Address: "a:b::c:d"},
				{Type: v1.NodeExternalIP, Address: "d:c::b:a"},
			},
			expectIPs: []net.IP{netutils.ParseIPSloppy("1.2.3.4"), netutils.ParseIPSloppy("a:b::c:d")},
		},
		{
			name: "dual-stack node, different order",
			addresses: []v1.NodeAddress{
				{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
				{Type: v1.NodeInternalIP, Address: "a:b::c:d"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
				{Type: v1.NodeExternalIP, Address: "d:c::b:a"},
			},
			expectIPs: []net.IP{netutils.ParseIPSloppy("1.2.3.4"), netutils.ParseIPSloppy("a:b::c:d")},
		},
		{
			name: "dual-stack node, different order",
			addresses: []v1.NodeAddress{
				{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
				{Type: v1.NodeInternalIP, Address: "a:b::c:d"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
				{Type: v1.NodeExternalIP, Address: "d:c::b:a"},
			},
			expectIPs: []net.IP{netutils.ParseIPSloppy("1.2.3.4"), netutils.ParseIPSloppy("a:b::c:d")},
		},
		{
			name: "dual-stack node, IPv6-first, no internal IPv4",
			addresses: []v1.NodeAddress{
				{Type: v1.NodeInternalIP, Address: "a:b::c:d"},
				{Type: v1.NodeExternalIP, Address: "d:c::b:a"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
			},
			expectIPs: []net.IP{netutils.ParseIPSloppy("a:b::c:d"), netutils.ParseIPSloppy("4.3.2.1")},
		},
		{
			name: "dual-stack node, IPv6-first, no internal IPv4, dual-stack cluster",
			addresses: []v1.NodeAddress{
				{Type: v1.NodeInternalIP, Address: "a:b::c:d"},
				{Type: v1.NodeExternalIP, Address: "d:c::b:a"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
				{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
			},
			expectIPs: []net.IP{netutils.ParseIPSloppy("a:b::c:d"), netutils.ParseIPSloppy("4.3.2.1")},
		},
	}

	for _, tc := range testcases {
		t.Run(tc.name, func(t *testing.T) {
			node := &v1.Node{
				Status: v1.NodeStatus{Addresses: tc.addresses},
			}
			nodeIPs, err := GetNodeHostIPs(node)
			nodeIP, err2 := GetNodeHostIP(node)

			if (err == nil && err2 != nil) || (err != nil && err2 == nil) {
				t.Errorf("GetNodeHostIPs() returned error=%q but GetNodeHostIP() returned error=%q", err, err2)
			}
			if err != nil {
				if tc.expectIPs != nil {
					t.Errorf("expected %v, got error (%v)", tc.expectIPs, err)
				}
			} else if tc.expectIPs == nil {
				t.Errorf("expected error, got %v", nodeIPs)
			} else if !reflect.DeepEqual(nodeIPs, tc.expectIPs) {
				t.Errorf("expected %v, got %v", tc.expectIPs, nodeIPs)
			} else if !nodeIP.Equal(nodeIPs[0]) {
				t.Errorf("GetNodeHostIP did not return same primary (%s) as GetNodeHostIPs (%s)", nodeIP.String(), nodeIPs[0].String())
			}
		})
	}
}

func TestGetHostname(t *testing.T) {
	testCases := []struct {
		hostName         string
		expectedHostName string
		expectError      bool
	}{
		{
			hostName:    "   ",
			expectError: true,
		},
		{
			hostName:         " abc  ",
			expectedHostName: "abc",
			expectError:      false,
		},
	}

	for idx, test := range testCases {
		hostName, err := GetHostname(test.hostName)
		if err != nil && !test.expectError {
			t.Errorf("[%d]: unexpected error: %s", idx, err)
		}
		if err == nil && test.expectError {
			t.Errorf("[%d]: expected error, got none", idx)
		}
		if test.expectedHostName != hostName {
			t.Errorf("[%d]: expected output %q, got %q", idx, test.expectedHostName, hostName)
		}

	}
}

func TestGetNodeIP(t *testing.T) {

	testCases := []struct {
		name   string
		Node   *v1.Node
		expect net.IP
	}{
		{
			name:   "",
			expect: nil,
		},
		{
			name: "node1",
			Node: &v1.Node{
				ObjectMeta: metav1.ObjectMeta{
					Name: "node1",
				},
				Spec:   v1.NodeSpec{},
				Status: v1.NodeStatus{},
			},
			expect: nil,
		},
		{
			name: "node1",
			Node: &v1.Node{
				ObjectMeta: metav1.ObjectMeta{
					Name: "node1",
				},
				Spec: v1.NodeSpec{},
				Status: v1.NodeStatus{Addresses: []v1.NodeAddress{
					{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
					{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
					{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
					{Type: v1.NodeInternalIP, Address: "a:b::c:d"},
					{Type: v1.NodeExternalIP, Address: "d:c::b:a"},
				}},
			},
			expect: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 1, 2, 3, 4},
		},
	}
	for _, test := range testCases {
		kubeClientSet := kubeclientfake.NewSimpleClientset()
		if test.Node != nil {
			if _, err := kubeClientSet.CoreV1().Nodes().Create(context.Background(), test.Node, metav1.CreateOptions{}); err != nil {
				t.Error("create node error")
			}
		}
		result := GetNodeIP(kubeClientSet, test.name)
		assert.Equal(t, test.expect, result)
	}
}

func TestIsNodeReady(t *testing.T) {
	testCases := []struct {
		Node   *v1.Node
		expect bool
	}{
		{
			Node: &v1.Node{
				Status: v1.NodeStatus{
					Conditions: []v1.NodeCondition{
						{
							Type:   v1.NodeReady,
							Status: v1.ConditionTrue,
						},
					},
				},
			},
			expect: true,
		},
		{
			Node: &v1.Node{
				Status: v1.NodeStatus{
					Conditions: []v1.NodeCondition{
						{
							Type:   v1.NodeReady,
							Status: v1.ConditionFalse,
						},
					},
				},
			},
			expect: false,
		},
		{
			Node: &v1.Node{
				Status: v1.NodeStatus{
					Conditions: []v1.NodeCondition{
						{
							Type:   v1.NodeMemoryPressure,
							Status: v1.ConditionFalse,
						},
					},
				},
			},
			expect: false,
		},
	}
	for _, test := range testCases {
		result := IsNodeReady(test.Node)
		assert.Equal(t, test.expect, result)
	}
}
<|endoftext|>


<commit_before>package workloads

import "github.com/intelsdi-x/swan/pkg/executor"

const (
	memcachedBinaryName = "memcached"
)

type Memcached struct {
	exec         executor.Executor
	pathToBinary string
	user         string
	threadsNum   int
	connNum      int
}

func NewMemcached(exec executor.Executor, pathToBinary string,
	user string, threadsNum int, connNum int) Memcached {
	return Memcached{
		exec:         exec,
		pathToBinary: pathToBinary,
		user:         user,
		threadsNum:   threadsNum,
		connNum:      connNum,
	}
}

func (m Memcached) buildCommand() string {
	return m.pathToBinary + memcachedBinaryName +
		" -u " + m.user +
		" -t " + m.threadsNum +
		" -c " + m.connNum +
		" -m mem"
}

func (m Memcached) Launch() (executor.Task, error) {
	return m.exec.Execute(m.buildCommand())
}
<commit_msg>Added memcached description in comments.<commit_after>package workloads

import "github.com/intelsdi-x/swan/pkg/executor"

const (
	memcachedBinaryName = "memcached"
)

// Memcached is a launcher for memcached data caching application.
type Memcached struct {
	exec         executor.Executor
	pathToBinary string
	user         string
	threadsNum   int
	connNum      int
}

// NewMemcached is a constructor for Memcached.
func NewMemcached(exec executor.Executor, pathToBinary string,
	user string, threadsNum int, connNum int) Memcached {
	return Memcached{
		exec:         exec,
		pathToBinary: pathToBinary,
		user:         user,
		threadsNum:   threadsNum,
		connNum:      connNum,
	}
}

func (m Memcached) buildCommand() string {
	return m.pathToBinary + memcachedBinaryName +
		" -u " + m.user +
		" -t " + string(m.threadsNum) +
		" -c " + string(m.connNum) +
		" -m mem"
}

// Launch starts the workload (process or group of processes). It returns a workload
// represented as a Task instance.
// Error is returned when Launcher is unable to start a job.
func (m Memcached) Launch() (executor.Task, error) {
	return m.exec.Execute(m.buildCommand())
}
<|endoftext|>


<commit_before>// Copyright 2014 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package render

import (
	"github.com/quarnster/util/text"
	"reflect"
	"testing"
)

func TestViewRegionsCull(t *testing.T) {
	tests := []struct {
		regions []text.Region
		cull    text.Region
		exp     []text.Region
	}{
		{
			[]text.Region{{100, 200}},
			text.Region{0, 50},
			[]text.Region{},
		},
		{
			[]text.Region{{100, 100}},
			text.Region{100, 100},
			[]text.Region{},
		},
		{
			[]text.Region{{100, 100}},
			text.Region{95, 105},
			[]text.Region{},
		},
		{
			[]text.Region{{100, 100}},
			text.Region{95, 100},
			[]text.Region{},
		},
		{
			[]text.Region{{100, 200}},
			text.Region{150, 150},
			[]text.Region{},
		},
		{
			[]text.Region{{100, 200}},
			text.Region{90, 100},
			[]text.Region{},
		},
		{
			[]text.Region{{100, 200}},
			text.Region{100, 150},
			[]text.Region{{100, 150}},
		},
		{
			[]text.Region{{100, 200}},
			text.Region{150, 175},
			[]text.Region{{150, 175}},
		},
		{
			[]text.Region{{100, 200}},
			text.Region{0, 150},
			[]text.Region{{100, 150}},
		},
		{
			[]text.Region{{100, 200}},
			text.Region{150, 250},
			[]text.Region{{150, 200}},
		},
		{
			[]text.Region{{100, 200}},
			text.Region{0, 250},
			[]text.Region{{100, 200}},
		},
		{
			[]text.Region{{100, 200}, {300, 400}},
			text.Region{0, 500},
			[]text.Region{{100, 200}, {300, 400}},
		},
		{
			[]text.Region{{100, 200}, {300, 400}},
			text.Region{150, 350},
			[]text.Region{{150, 200}, {300, 350}},
		},
		{
			[]text.Region{{100, 200}, {300, 400}},
			text.Region{150, 250},
			[]text.Region{{150, 200}},
		},
		{
			[]text.Region{{100, 200}, {300, 400}},
			text.Region{250, 350},
			[]text.Region{{300, 350}},
		},
	}

	for i, test := range tests {
		vr := ViewRegions{}
		vr.Regions.AddAll(test.regions)

		vr.Cull(test.cull)

		r := vr.Regions.Regions()

		if !reflect.DeepEqual(r, test.exp) {
			t.Errorf("Test %d: Expected %s, but got %s", i, test.exp, r)
		}
	}
}

func TestViewRegionsClone(t *testing.T) {
	vr := ViewRegions{
		Scope: "testScope",
		Icon:  "testIcon",
		Flags: 100,
	}
	vr.Regions.AddAll([]text.Region{{0, 0}, {120, 300}, {24, 34}, {45, 40}})

	c := vr.Clone()
	if !reflect.DeepEqual(c, vr) {
		t.Errorf("Expected %+v, but got %+v", vr, c)
	}
}
<commit_msg>Add ViewRegionMap tests.<commit_after>// Copyright 2014 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package render

import (
	"github.com/quarnster/util/text"
	"reflect"
	"testing"
)

func TestViewRegionMapCull(t *testing.T) {
	tests := []struct {
		regionsA []text.Region
		regionsB []text.Region
		cull     text.Region
		expA     []text.Region
		expB     []text.Region
	}{
		{
			[]text.Region{},
			[]text.Region{},
			text.Region{},
			nil,
			nil,
		},
		{
			[]text.Region{{100, 200}},
			[]text.Region{},
			text.Region{},
			nil,
			nil,
		},
		{
			[]text.Region{},
			[]text.Region{},
			text.Region{0, 50},
			nil,
			nil,
		},
		{
			[]text.Region{{100, 200}},
			[]text.Region{},
			text.Region{0, 50},
			nil,
			nil,
		},
		{
			[]text.Region{{100, 200}},
			[]text.Region{{300, 400}},
			text.Region{0, 50},
			nil,
			nil,
		},
		{
			[]text.Region{{100, 200}},
			[]text.Region{{300, 400}},
			text.Region{0, 150},
			[]text.Region{{100, 150}},
			nil,
		},
		{
			[]text.Region{{100, 200}},
			[]text.Region{{300, 400}},
			text.Region{150, 350},
			[]text.Region{{150, 200}},
			[]text.Region{{300, 350}},
		},
	}

	for i, test := range tests {
		vrm := ViewRegionMap{}

		vrA := ViewRegions{}
		vrA.Regions.AddAll(test.regionsA)
		vrm["A"] = vrA

		vrB := ViewRegions{}
		vrB.Regions.AddAll(test.regionsB)
		vrm["B"] = vrB

		vrm.Cull(test.cull)

		if test.expA != nil {
			rA := vrA.Regions.Regions()

			if !reflect.DeepEqual(rA, test.expA) {
				t.Errorf("Test %d: Expected %s, but got %s", i, test.expA, rA)
			}
		} else if _, ok := vrm["A"]; ok {
			t.Errorf("Test %d: Expected %s, but got %s", i, test.expA, vrm["A"])
		}

		if test.expB != nil {
			rB := vrB.Regions.Regions()

			if !reflect.DeepEqual(rB, test.expB) {
				t.Errorf("Test %d: Expected %s, but got %s", i, test.expB, rB)
			}
		} else if _, ok := vrm["B"]; ok {
			t.Errorf("Test %d: Expected %s, but got %s", i, test.expB, vrm["B"])
		}
	}
}

func TestViewRegionsCull(t *testing.T) {
	tests := []struct {
		regions []text.Region
		cull    text.Region
		exp     []text.Region
	}{
		{
			[]text.Region{{100, 200}},
			text.Region{0, 50},
			[]text.Region{},
		},
		{
			[]text.Region{{100, 100}},
			text.Region{100, 100},
			[]text.Region{},
		},
		{
			[]text.Region{{100, 100}},
			text.Region{95, 105},
			[]text.Region{},
		},
		{
			[]text.Region{{100, 100}},
			text.Region{95, 100},
			[]text.Region{},
		},
		{
			[]text.Region{{100, 200}},
			text.Region{150, 150},
			[]text.Region{},
		},
		{
			[]text.Region{{100, 200}},
			text.Region{90, 100},
			[]text.Region{},
		},
		{
			[]text.Region{{100, 200}},
			text.Region{100, 150},
			[]text.Region{{100, 150}},
		},
		{
			[]text.Region{{100, 200}},
			text.Region{150, 175},
			[]text.Region{{150, 175}},
		},
		{
			[]text.Region{{100, 200}},
			text.Region{0, 150},
			[]text.Region{{100, 150}},
		},
		{
			[]text.Region{{100, 200}},
			text.Region{150, 250},
			[]text.Region{{150, 200}},
		},
		{
			[]text.Region{{100, 200}},
			text.Region{0, 250},
			[]text.Region{{100, 200}},
		},
		{
			[]text.Region{{100, 200}, {300, 400}},
			text.Region{0, 500},
			[]text.Region{{100, 200}, {300, 400}},
		},
		{
			[]text.Region{{100, 200}, {300, 400}},
			text.Region{150, 350},
			[]text.Region{{150, 200}, {300, 350}},
		},
		{
			[]text.Region{{100, 200}, {300, 400}},
			text.Region{150, 250},
			[]text.Region{{150, 200}},
		},
		{
			[]text.Region{{100, 200}, {300, 400}},
			text.Region{250, 350},
			[]text.Region{{300, 350}},
		},
	}

	for i, test := range tests {
		vr := ViewRegions{}
		vr.Regions.AddAll(test.regions)

		vr.Cull(test.cull)

		r := vr.Regions.Regions()

		if !reflect.DeepEqual(r, test.exp) {
			t.Errorf("Test %d: Expected %s, but got %s", i, test.exp, r)
		}
	}
}

func TestViewRegionsClone(t *testing.T) {
	vr := ViewRegions{
		Scope: "testScope",
		Icon:  "testIcon",
		Flags: 100,
	}
	vr.Regions.AddAll([]text.Region{{0, 0}, {120, 300}, {24, 34}, {45, 40}})

	c := vr.Clone()
	if !reflect.DeepEqual(c, vr) {
		t.Errorf("Expected %+v, but got %+v", vr, c)
	}
}
<|endoftext|>


<commit_before>package watch

import (
	"io/ioutil"
	"os"
	"reflect"
	"testing"
	"time"
)

func dum() {
	return
}

func TestWatch(t *testing.T) {
	tests := []struct {
		paths       map[string]func()
		expWatched  []string
		expWatchers []string
	}{
		{
			map[string]func(){
				"testdata/dummy.txt": dum,
				"testdata/test.txt":  dum,
			},
			[]string{"testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata/dummy.txt", "testdata/test.txt"},
		},
		{
			map[string]func(){
				"testdata":           dum,
				"testdata/dummy.txt": dum,
				"testdata/test.txt":  dum,
			},
			[]string{"testdata", "testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata"},
		},
		{
			map[string]func(){
				"testdata/dummy.txt": dum,
				"testdata/test.txt":  dum,
				"testdata":           dum,
			},
			[]string{"testdata", "testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata"},
		},
		// 2 path refer same file or dir but different(e.g abs path and relative path)
	}
	for i, test := range tests {
		for path, action := range test.paths {
			Watch(path, action)
		}
		if len(watched) != len(test.expWatched) {
			t.Errorf("Test %d: Expected len of watched %d, but got %d", i, len(test.expWatched), len(watched))
		}
		for _, p := range test.expWatched {
			if _, exist := watched[p]; !exist {
				t.Errorf("Test %d: Expected %s exist in watched", i, p)
			}
		}
		if !reflect.DeepEqual(test.expWatchers, watchers) {
			t.Errorf("Test %d: Expected watchers %v, but got %v", i, test.expWatchers, watchers)
		}
		for _, path := range test.expWatchers {
			UnWatch(path)
		}
		reinit()
	}
}

func TestUnWatch(t *testing.T) {
	tests := []struct {
		watchs      []string
		unWatchs    []string
		expWatched  []string
		expWatchers []string
	}{
		{
			[]string{"testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata/dummy.txt"},
			[]string{"testdata/test.txt"},
			[]string{"testdata/test.txt"},
		},
		{
			[]string{"testdata", "testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata"},
			[]string{"testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata/test.txt", "testdata/dummy.txt"},
		},
	}
	for i, test := range tests {
		for _, path := range test.watchs {
			Watch(path, dum)
		}
		for _, path := range test.unWatchs {
			UnWatch(path)
		}
		if len(watched) != len(test.expWatched) {
			t.Errorf("Test %d: Expected len of watched %d, but got %d", i, len(test.expWatched), len(watched))
		}
		for _, p := range test.expWatched {
			if _, exist := watched[p]; !exist {
				t.Errorf("Test %d: Expected %s exist in watched", i, p)
			}
		}
		if !reflect.DeepEqual(test.expWatchers, watchers) {
			t.Errorf("Test %d: Expected watchers %v, but got %v", i, test.expWatchers, watchers)
		}
		for _, path := range test.expWatchers {
			UnWatch(path)
		}
		reinit()
	}
}

type dumView struct {
	Text string
	Name string
}

func (d *dumView) Reload() {
	d.Text = "Reloaded"
}

func (d *dumView) Rename() {
	d.Name = "Renamed"
}

func TestObserve(t *testing.T) {
	path := "testdata/test.txt"
	v := new(dumView)
	Watch(path, v.Reload)
	go Observe()

	if err := ioutil.WriteFile(path, []byte("test"), 0644); err != nil {
		t.Fatalf("WriteFile error: %s", err)
	}
	time.Sleep(time.Millisecond * 50)
	if v.Text != "Reloaded" {
		t.Errorf("Expected dumView Text %s, but got %s", "Reloaded", v.Text)
	}
	ioutil.WriteFile(path, []byte(""), 0644)
	UnWatch(path)
	reinit()
}

func TestObserveDirectory(t *testing.T) {
	dir := "testdata"
	path := "testdata/test.txt"
	v := new(dumView)
	Watch(path, v.Reload)
	Watch(dir, nil)
	go Observe()

	if !reflect.DeepEqual(watchers, []string{"testdata"}) {
		t.Errorf("Expected watchers be equal to %v, but got %v", []string{"testdata"}, watchers)
	}
	if err := ioutil.WriteFile(path, []byte("test"), 0644); err != nil {
		t.Fatalf("WriteFile error: %s", err)
	}
	time.Sleep(time.Millisecond * 50)
	if v.Text != "Reloaded" {
		t.Errorf("Expected dumView Text %s, but got %s", "Reloaded", v.Text)
	}
	ioutil.WriteFile(path, []byte(""), 0644)
	UnWatch(dir)
	reinit()
}

func TestObserveCreateEvent(t *testing.T) {
	path := "testdata/new.txt"
	v := new(dumView)
	Watch(path, v.Reload)
	go Observe()

	if !reflect.DeepEqual(watchers, []string{"testdata"}) {
		t.Errorf("Expected watchers be equal to %v, but got %v", []string{path}, watchers)
	}
	if err := ioutil.WriteFile(path, []byte("test"), 0644); err != nil {
		t.Fatalf("WriteFile error: %s", err)
	}
	time.Sleep(time.Millisecond * 50)
	if v.Text != "Reloaded" {
		t.Errorf("Expected dumView Text %s, but got %s", "Reloaded", v.Text)
	}
	os.Remove(path)
	UnWatch(path)
	reinit()
}

func TestObserveDeleteEvent(t *testing.T) {
	path := "testdata/dummy.txt"
	v := new(dumView)
	Watch(path, v.Reload)
	go Observe()

	os.Remove(path)
	time.Sleep(time.Millisecond * 50)
	if v.Text != "Reloaded" {
		t.Errorf("Expected dumView Text %s, but got %s", "Reloaded", v.Text)
	}
	if !reflect.DeepEqual(watchers, []string{"testdata"}) {
		t.Errorf("Expected watchers be equal to %v, but got %v", []string{"testdata"}, watchers)
	}
	UnWatch("testdata")
	ioutil.WriteFile(path, []byte(""), 0644)
	reinit()
}

func TestObserveRenameEvent(t *testing.T) {
	path := "testdata/dummy.txt"
	v := new(dumView)
	Watch(path, v.Reload)
	go Observe()

	os.Rename(path, "testdata/rename.txt")
	time.Sleep(time.Millisecond * 50)
	if v.Text != "Reloaded" {
		t.Errorf("Expected dumView Text %s, but got %s", "Reloaded", v.Text)
	}
	os.Rename("testdata/rename.txt", path)
	UnWatch(path)
	reinit()
}

func TestExist(t *testing.T) {
	test := struct {
		array []string
		elms  []string
		exps  []bool
	}{
		[]string{"a", "b", "c", "d"},
		[]string{"a", "t", "A"},
		[]bool{true, false, false},
	}
	for i, exp := range test.exps {
		if exist(test.array, test.elms[i]) != exp {
			t.Errorf("Expected in %v exist result of element %s be %v, but got %v", test.array, test.elms[i], exp, exist(test.array, test.elms[i]))
		}
	}
}

func TestRemove(t *testing.T) {
	tests := []struct {
		slice  []string
		remove string
		exp    []string
	}{
		{
			[]string{"a", "b", "c"},
			"a",
			[]string{"c", "b"},
		},
		{
			[]string{"a", "b", "c"},
			"k",
			[]string{"a", "b", "c"},
		},
	}
	for i, test := range tests {
		if exp := remove(test.slice, test.remove); !reflect.DeepEqual(exp, test.exp) {
			t.Errorf("Test %d: Expected %v be equal to %v", i, exp, test.exp)
		}
	}
}
<commit_msg>Changed tests to use watcher type<commit_after>package watch

import (
	"io/ioutil"
	"os"
	"reflect"
	"testing"
	"time"
)

func dum() {
	return
}

func TestWatch(t *testing.T) {
	tests := []struct {
		paths       map[string]func()
		expWatched  []string
		expWatchers []string
	}{
		{
			map[string]func(){
				"testdata/dummy.txt": dum,
				"testdata/test.txt":  dum,
			},
			[]string{"testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata/dummy.txt", "testdata/test.txt"},
		},
		{
			map[string]func(){
				"testdata":           dum,
				"testdata/dummy.txt": dum,
				"testdata/test.txt":  dum,
			},
			[]string{"testdata", "testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata"},
		},
		{
			map[string]func(){
				"testdata/dummy.txt": dum,
				"testdata/test.txt":  dum,
				"testdata":           dum,
			},
			[]string{"testdata", "testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata"},
		},
		// 2 path refer same file or dir but different(e.g abs path and relative path)
	}
	for i, test := range tests {
		watcher, err := NewWatcher()
		if err != nil {
			t.Fatalf("Couldn't create watcher: %s", err)
		}
		for path, action := range test.paths {
			watcher.Watch(path, action)
		}
		if len(watcher.watched) != len(test.expWatched) {
			t.Errorf("Test %d: Expected len of watched %d, but got %d", i, len(test.expWatched), len(watcher.watched))
		}
		for _, p := range test.expWatched {
			if _, exist := watcher.watched[p]; !exist {
				t.Errorf("Test %d: Expected %s exist in watched", i, p)
			}
		}
		if !reflect.DeepEqual(test.expWatchers, watcher.watchers) {
			t.Errorf("Test %d: Expected watchers %v, but got %v", i, test.expWatchers, watcher.watchers)
		}
	}
}

func TestUnWatch(t *testing.T) {
	tests := []struct {
		watchs      []string
		unWatchs    []string
		expWatched  []string
		expWatchers []string
	}{
		{
			[]string{"testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata/dummy.txt"},
			[]string{"testdata/test.txt"},
			[]string{"testdata/test.txt"},
		},
		{
			[]string{"testdata", "testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata"},
			[]string{"testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata/test.txt", "testdata/dummy.txt"},
		},
	}
	for i, test := range tests {
		watcher, err := NewWatcher()
		if err != nil {
			t.Fatalf("Couldn't create watcher: %s", err)
		}
		for _, path := range test.watchs {
			watcher.Watch(path, dum)
		}
		for _, path := range test.unWatchs {
			watcher.UnWatch(path)
		}
		if len(watcher.watched) != len(test.expWatched) {
			t.Errorf("Test %d: Expected len of watched %d, but got %d", i, len(test.expWatched), len(watcher.watched))
		}
		for _, p := range test.expWatched {
			if _, exist := watcher.watched[p]; !exist {
				t.Errorf("Test %d: Expected %s exist in watched", i, p)
			}
		}
		if !reflect.DeepEqual(test.expWatchers, watcher.watchers) {
			t.Errorf("Test %d: Expected watchers %v, but got %v", i, test.expWatchers, watcher.watchers)
		}
	}
}

type dumView struct {
	Text string
	Name string
}

func (d *dumView) Reload() {
	d.Text = "Reloaded"
}

func (d *dumView) Rename() {
	d.Name = "Renamed"
}

func TestObserve(t *testing.T) {
	path := "testdata/test.txt"
	watcher, err := NewWatcher()
	if err != nil {
		t.Fatalf("Couldn't create watcher: %s", err)
	}
	v := new(dumView)
	watcher.Watch(path, v.Reload)
	go watcher.Observe()

	if err := ioutil.WriteFile(path, []byte("test"), 0644); err != nil {
		t.Fatalf("WriteFile error: %s", err)
	}
	time.Sleep(time.Millisecond * 50)
	if v.Text != "Reloaded" {
		t.Errorf("Expected dumView Text %s, but got %s", "Reloaded", v.Text)
	}
	ioutil.WriteFile(path, []byte(""), 0644)
}

func TestObserveDirectory(t *testing.T) {
	dir := "testdata"
	path := "testdata/test.txt"
	watcher, err := NewWatcher()
	if err != nil {
		t.Fatalf("Couldn't create watcher: %s", err)
	}
	v := new(dumView)
	watcher.Watch(path, v.Reload)
	watcher.Watch(dir, nil)
	go watcher.Observe()

	if !reflect.DeepEqual(watcher.watchers, []string{"testdata"}) {
		t.Errorf("Expected watchers be equal to %v, but got %v", []string{"testdata"}, watcher.watchers)
	}
	if err := ioutil.WriteFile(path, []byte("test"), 0644); err != nil {
		t.Fatalf("WriteFile error: %s", err)
	}
	time.Sleep(time.Millisecond * 50)
	if v.Text != "Reloaded" {
		t.Errorf("Expected dumView Text %s, but got %s", "Reloaded", v.Text)
	}
	ioutil.WriteFile(path, []byte(""), 0644)
}

func TestObserveCreateEvent(t *testing.T) {
	path := "testdata/new.txt"
	watcher, err := NewWatcher()
	if err != nil {
		t.Fatalf("Couldn't create watcher: %s", err)
	}
	v := new(dumView)
	watcher.Watch(path, v.Reload)
	go watcher.Observe()

	if !reflect.DeepEqual(watcher.watchers, []string{"testdata"}) {
		t.Errorf("Expected watchers be equal to %v, but got %v", []string{path}, watcher.watchers)
	}
	if err := ioutil.WriteFile(path, []byte("test"), 0644); err != nil {
		t.Fatalf("WriteFile error: %s", err)
	}
	time.Sleep(time.Millisecond * 50)
	if v.Text != "Reloaded" {
		t.Errorf("Expected dumView Text %s, but got %s", "Reloaded", v.Text)
	}
	os.Remove(path)
}

func TestObserveDeleteEvent(t *testing.T) {
	path := "testdata/dummy.txt"
	watcher, err := NewWatcher()
	if err != nil {
		t.Fatalf("Couldn't create watcher: %s", err)
	}
	v := new(dumView)
	watcher.Watch(path, v.Reload)
	go watcher.Observe()

	os.Remove(path)
	time.Sleep(time.Millisecond * 50)
	if !reflect.DeepEqual(watcher.watchers, []string{"testdata"}) {
		t.Errorf("Expected watchers be equal to %v, but got %v", []string{"testdata"}, watcher.watchers)
	}
	if v.Text != "Reloaded" {
		t.Errorf("Expected dumView Text %s, but got %s", "Reloaded", v.Text)
	}
	ioutil.WriteFile(path, []byte(""), 0644)
}

func TestObserveRenameEvent(t *testing.T) {
	path := "testdata/dummy.txt"
	watcher, err := NewWatcher()
	if err != nil {
		t.Fatalf("Couldn't create watcher: %s", err)
	}
	v := new(dumView)
	watcher.Watch(path, v.Reload)
	go watcher.Observe()

	os.Rename(path, "testdata/rename.txt")
	time.Sleep(time.Millisecond * 50)
	if v.Text != "Reloaded" {
		t.Errorf("Expected dumView Text %s, but got %s", "Reloaded", v.Text)
	}
	os.Rename("testdata/rename.txt", path)
}

func TestExist(t *testing.T) {
	test := struct {
		array []string
		elms  []string
		exps  []bool
	}{
		[]string{"a", "b", "c", "d"},
		[]string{"a", "t", "A"},
		[]bool{true, false, false},
	}
	for i, exp := range test.exps {
		if exist(test.array, test.elms[i]) != exp {
			t.Errorf("Expected in %v exist result of element %s be %v, but got %v", test.array, test.elms[i], exp, exist(test.array, test.elms[i]))
		}
	}
}

func TestRemove(t *testing.T) {
	tests := []struct {
		slice  []string
		remove string
		exp    []string
	}{
		{
			[]string{"a", "b", "c"},
			"a",
			[]string{"c", "b"},
		},
		{
			[]string{"a", "b", "c"},
			"k",
			[]string{"a", "b", "c"},
		},
	}
	for i, test := range tests {
		if exp := remove(test.slice, test.remove); !reflect.DeepEqual(exp, test.exp) {
			t.Errorf("Test %d: Expected %v be equal to %v", i, exp, test.exp)
		}
	}
}
<|endoftext|>


<commit_before>package mqplan

import (
	"encoding/base64"
	"encoding/hex"
	"errors"
	"fmt"
	"io/ioutil"
	"math"
	"math/rand"
	"strings"
	"time"

	"gopkg.in/resty.v0"
	"gopkg.in/yaml.v2"

	"meqa/mqswag"
	"meqa/mqutil"

	"github.com/go-openapi/spec"
	"github.com/lucasjones/reggen"
	"github.com/xeipuuv/gojsonschema"
)

// Test represents a test object in the DSL
type Test struct {
	Name       string
	Path       string
	Method     string
	Ref        string
	Parameters map[string]interface{}
}

// Run runs the test. It only returns error when there is an internal error.
// Test case failures are not counted.
func (t *Test) Run(swagger *mqswag.Swagger, db mqswag.DB, plan *TestPlan) error {
	if len(t.Ref) != 0 {
		return plan.Run(t.Ref, swagger, db)
	}
	err := t.ResolveParameters(swagger, db, plan)
	if err != nil {
		return err
	}

	// TODO add check for http/https (operation schemes) and pointers
	switch t.Method {
	case resty.MethodGet:
		resp, err := resty.R().SetQueryParams(t.Parameters).Get(swagger.BasePath + "/" + t.Path)
		// TODO properly process resp. Check against the current DB to see if they match
		mqutil.Logger.Print(resp)

		return err
	default:
		str := fmt.Sprintf("Unknow method in test %s: %v", t.Name, t.Method)
		return errors.New(str)
	}
}

func getOperationByMethod(item *spec.PathItem, method string) *spec.Operation {
	switch method {
	case resty.MethodGet:
		return item.Get
	case resty.MethodPost:
		return item.Post
	case resty.MethodPut:
		return item.Put
	case resty.MethodDelete:
		return item.Delete
	case resty.MethodPatch:
		return item.Patch
	case resty.MethodHead:
		return item.Head
	case resty.MethodOptions:
		return item.Options
	}
	return nil
}

// Generate paramter value based on the spec.
func generateParameter(paramSpec *spec.Parameter, db mqswag.DB) (interface{}, error) {
	if paramSpec.Schema != nil {
		return generateBySchema(paramSpec.Schema, db)
	}
	if len(paramSpec.Enum) != 0 {
		return generateByEnum(paramSpec)
	}
	if len(paramSpec.Type) == 0 {
		return "", mqutil.NewError(mqutil.ErrInvalid, "Parameter doesn't have type")
	}
	if paramSpec.Type == gojsonschema.TYPE_OBJECT {
		return generateObject(paramSpec)
	}

	return generateByType(&paramSpec.SimpleSchema, &paramSpec.CommonValidations, paramSpec.Name+"-")
}

func generateByType(s *spec.SimpleSchema, v *spec.CommonValidations, prefix string) (interface{}, error) {
	switch s.Type {
	case gojsonschema.TYPE_ARRAY:
		return generateArray(s, v, prefix)
	case gojsonschema.TYPE_BOOLEAN:
		return generateBool(v)
	case gojsonschema.TYPE_INTEGER:
		return generateInt(v)
	case gojsonschema.TYPE_NUMBER:
		return generateFloat(v)
	case gojsonschema.TYPE_STRING:
		return generateString(s, v, prefix)
	}

	panic("not implemented")
}

// RandomTime generate a random time in the range of [t - r, t).
func RandomTime(t time.Time, r time.Duration) time.Time {
	return t.Add(-time.Duration(float64(r) * rand.Float64()))
}

// TODO we need to make it context aware. Based on different contexts we should generate different
// date ranges. Prefix is a prefix to use when generating strings. It's only used when there is
// no specified pattern in the swagger.json
func generateString(s *spec.SimpleSchema, v *spec.CommonValidations, prefix string) (string, error) {
	if s.Format == "date-time" {
		t := RandomTime(time.Now(), time.Hour*24*30)
		return t.Format(time.RFC3339), nil
	}
	if s.Format == "date" {
		t := RandomTime(time.Now(), time.Hour*24*30)
		return t.Format("2006-01-02"), nil
	}

	// If no pattern is specified, we use the field name + some numbers as pattern
	var pattern string
	length := 0
	if len(v.Pattern) != 0 {
		pattern = v.Pattern
		length = len(v.Pattern) * 2
	} else {
		pattern = prefix + "\\d+"
		length = len(prefix) + 5
	}
	g, err := reggen.NewGenerator(pattern)
	if err != nil {
		return "", mqutil.NewError(mqutil.ErrInvalid, err.Error())
	}
	str := g.Generate(length)

	if len(s.Format) == 0 || s.Format == "password" {
		return str, nil
	}
	if s.Format == "byte" {
		return base64.StdEncoding.EncodeToString([]byte(str)), nil
	}
	if s.Format == "binary" {
		return hex.EncodeToString([]byte(str)), nil
	}
	return "", mqutil.NewError(mqutil.ErrInvalid, fmt.Sprintf("Invalid format string: %s", s.Format))
}

func generateBool(v *spec.CommonValidations) (interface{}, error) {
	return rand.Intn(2) == 0, nil
}

func generateFloat(v *spec.CommonValidations) (float64, error) {
	var realmin float64
	if v.Minimum != nil {
		realmin = *v.Minimum
		if v.ExclusiveMinimum {
			realmin += 0.01
		}
	}
	var realmax float64
	if v.Maximum != nil {
		realmax = *v.Maximum
		if v.ExclusiveMaximum {
			realmax -= 0.01
		}
	}
	if realmin >= realmax {
		if v.Minimum == nil && v.Maximum == nil {
			realmin = -1.0
			realmax = 1.0
		} else if v.Minimum != nil {
			realmax = realmin + math.Abs(realmin)
		} else if v.Maximum != nil {
			realmin = realmax - math.Abs(realmax)
		} else {
			// both are present but conflicting
			return 0, mqutil.NewError(mqutil.ErrInvalid, fmt.Sprintf("specified min value %v is bigger than max %v",
				*v.Minimum, *v.Maximum))
		}
	}
	return rand.Float64()*(realmax-realmin) + realmin, nil
}

func generateInt(v *spec.CommonValidations) (int64, error) {
	f, err := generateFloat(v)
	if err != nil {
		return 0, err
	}
	i := int64(f)
	if v.Minimum != nil && i <= int64(*v.Minimum) {
		i++
	}
	return i, nil
}

func generateArray(s *spec.SimpleSchema, v *spec.CommonValidations, prefix string) (interface{}, error) {
	var maxItems int
	if v.MaxItems != nil {
		maxItems = int(*v.MaxItems)
		if maxItems < 0 {
			maxItems = 0
		}
	}
	var minItems int
	if v.MinItems != nil {
		minItems = int(*v.MinItems)
		if minItems < 0 {
			minItems = 0
		}
	}
	count := 0
	if minItems == maxItems && minItems != 0 {
		count = minItems
	}
	maxDiff := maxItems - minItems
	if maxDiff < 0 {
		maxDiff = 1
	}
	numItems := rand.Intn(int(maxDiff)) + minItems

	var ar []interface{}
	for i := 0; i < numItems; i++ {
		entry, err := generateByType(&s.Items.SimpleSchema, &s.Items.CommonValidations, prefix+"-")
		if err != nil {
			return nil, err
		}
		ar = append(ar, entry)
	}
	return ar, nil
}

func generateBySchema(schema *spec.Schema, db mqswag.DB) (string, error) {
	panic("not implemented")
}

func generateByEnum(paramSpec *spec.Parameter) (string, error) {
	return fmt.Sprintf("%v", paramSpec.Enum[rand.Intn(len(paramSpec.Enum))]), nil
}

// ResolveParameters fullfills the parameters for the specified request using the in-mem DB.
// The resolved parameters will be added to test.Parameters map.
func (t *Test) ResolveParameters(swagger *mqswag.Swagger, db mqswag.DB, plan *TestPlan) error {
	pathItem := swagger.Paths.Paths[t.Path]
	op := getOperationByMethod(&pathItem, t.Method)
	if op == nil {
		return mqutil.NewError(mqutil.ErrNotFound, fmt.Sprintf("Path %s not found in swagger file", t.Path))
	}

	for _, params := range op.Parameters {
		// We don't override the existing parameters
		if _, ok := t.Parameters[params.Name]; ok {
			continue
		}
		p, err := generateParameter(&params, db)
		if err != nil {
			return err
		}
		t.Parameters[params.Name] = p
		return nil
	}
	return nil
}

type TestCase []*Test

// Represents all the test cases in the DSL.
type TestPlan struct {
	CaseMap  map[string](TestCase)
	CaseList [](TestCase)
}

// Add a new TestCase, returns whether the Case is successfully added.
func (plan *TestPlan) Add(name string, testCase TestCase) error {
	if _, exist := plan.CaseMap[name]; exist {
		str := fmt.Sprintf("Duplicate name %s found in test plan", name)
		mqutil.Logger.Println(str)
		return errors.New(str)
	}
	plan.CaseMap[name] = testCase
	plan.CaseList = append(plan.CaseList, testCase)
	return nil
}

func (plan *TestPlan) AddFromString(data string) error {
	var caseMap map[string]TestCase
	err := yaml.Unmarshal([]byte(data), &caseMap)
	if err != nil {
		mqutil.Logger.Printf("The following is not a valud TestCase:\n%s", data)
		return err
	}
	for testName, testCase := range caseMap {
		for _, t := range testCase {
			if len(t.Method) != 0 {
				t.Method = strings.ToUpper(t.Method)
			}
		}
		err = plan.Add(testName, testCase)
		if err != nil {
			return err
		}
	}
	return nil
}

func (plan *TestPlan) InitFromFile(path string) error {
	plan.CaseMap = make(map[string]TestCase)
	plan.CaseList = nil

	data, err := ioutil.ReadFile(path)
	if err != nil {
		mqutil.Logger.Printf("Can't open the following file: %s", path)
		mqutil.Logger.Println(err.Error())
		return err
	}
	chunks := strings.Split(string(data), "---")
	for _, chunk := range chunks {
		plan.AddFromString(chunk)
	}
	return nil
}

// Run a named TestCase in the test plan.
func (plan *TestPlan) Run(name string, swagger *mqswag.Swagger, db mqswag.DB) (err error) {
	tc, ok := plan.CaseMap[name]
	if !ok || len(tc) == 0 {
		str := fmt.Sprintf("The following test case is not found: %s", name)
		mqutil.Logger.Println(str)
		return errors.New(str)
	}

	for _, test := range tc {
		err = test.Run(swagger, db, plan)
		if err != nil {
			return err
		}

	}
	return nil
}

// The current global TestPlan
var Current TestPlan
<commit_msg>Make it build.<commit_after>package mqplan

import (
	"encoding/base64"
	"encoding/hex"
	"errors"
	"fmt"
	"io/ioutil"
	"math"
	"math/rand"
	"strings"
	"time"

	"gopkg.in/resty.v0"
	"gopkg.in/yaml.v2"

	"meqa/mqswag"
	"meqa/mqutil"

	"github.com/go-openapi/spec"
	"github.com/lucasjones/reggen"
	"github.com/xeipuuv/gojsonschema"
)

func MapInterfaceToMapString(src map[string]interface{}) map[string]string {
	dst := make(map[string]string)
	for k, v := range src {
		dst[k] = fmt.Sprint(v)
	}
	return dst
}

// Test represents a test object in the DSL
type Test struct {
	Name       string
	Path       string
	Method     string
	Ref        string
	Parameters map[string]interface{}
}

// Run runs the test. It only returns error when there is an internal error.
// Test case failures are not counted.
func (t *Test) Run(swagger *mqswag.Swagger, db mqswag.DB, plan *TestPlan) error {
	if len(t.Ref) != 0 {
		return plan.Run(t.Ref, swagger, db)
	}
	err := t.ResolveParameters(swagger, db, plan)
	if err != nil {
		return err
	}

	// TODO add check for http/https (operation schemes) and pointers
	switch t.Method {
	case resty.MethodGet:
		resp, err := resty.R().SetQueryParams(MapInterfaceToMapString(t.Parameters)).
			Get(swagger.BasePath + "/" + t.Path)
		// TODO properly process resp. Check against the current DB to see if they match
		mqutil.Logger.Print(resp)

		return err
	default:
		str := fmt.Sprintf("Unknow method in test %s: %v", t.Name, t.Method)
		return errors.New(str)
	}
}

func getOperationByMethod(item *spec.PathItem, method string) *spec.Operation {
	switch method {
	case resty.MethodGet:
		return item.Get
	case resty.MethodPost:
		return item.Post
	case resty.MethodPut:
		return item.Put
	case resty.MethodDelete:
		return item.Delete
	case resty.MethodPatch:
		return item.Patch
	case resty.MethodHead:
		return item.Head
	case resty.MethodOptions:
		return item.Options
	}
	return nil
}

// Generate paramter value based on the spec.
func generateParameter(paramSpec *spec.Parameter, db mqswag.DB) (interface{}, error) {
	if paramSpec.Schema != nil {
		return generateBySchema(paramSpec.Schema, db)
	}
	if len(paramSpec.Enum) != 0 {
		return generateByEnum(paramSpec)
	}
	if len(paramSpec.Type) == 0 {
		return nil, mqutil.NewError(mqutil.ErrInvalid, "Parameter doesn't have type")
	}
	if paramSpec.Type == gojsonschema.TYPE_OBJECT {
		return nil, mqutil.NewError(mqutil.ErrInvalid, fmt.Sprintf("parameter %s: type can't be object",
			paramSpec.Name))
	}

	return generateByType(&paramSpec.SimpleSchema, &paramSpec.CommonValidations, paramSpec.Name+"-")
}

func generateByType(s *spec.SimpleSchema, v *spec.CommonValidations, prefix string) (interface{}, error) {
	switch s.Type {
	case gojsonschema.TYPE_ARRAY:
		return generateArray(s, v, prefix)
	case gojsonschema.TYPE_BOOLEAN:
		return generateBool(v)
	case gojsonschema.TYPE_INTEGER:
		return generateInt(v)
	case gojsonschema.TYPE_NUMBER:
		return generateFloat(v)
	case gojsonschema.TYPE_STRING:
		return generateString(s, v, prefix)
	}

	return nil, mqutil.NewError(mqutil.ErrInvalid, fmt.Sprintf("unrecognized type: %s", s.Type))
}

// RandomTime generate a random time in the range of [t - r, t).
func RandomTime(t time.Time, r time.Duration) time.Time {
	return t.Add(-time.Duration(float64(r) * rand.Float64()))
}

// TODO we need to make it context aware. Based on different contexts we should generate different
// date ranges. Prefix is a prefix to use when generating strings. It's only used when there is
// no specified pattern in the swagger.json
func generateString(s *spec.SimpleSchema, v *spec.CommonValidations, prefix string) (string, error) {
	if s.Format == "date-time" {
		t := RandomTime(time.Now(), time.Hour*24*30)
		return t.Format(time.RFC3339), nil
	}
	if s.Format == "date" {
		t := RandomTime(time.Now(), time.Hour*24*30)
		return t.Format("2006-01-02"), nil
	}

	// If no pattern is specified, we use the field name + some numbers as pattern
	var pattern string
	length := 0
	if len(v.Pattern) != 0 {
		pattern = v.Pattern
		length = len(v.Pattern) * 2
	} else {
		pattern = prefix + "\\d+"
		length = len(prefix) + 5
	}
	g, err := reggen.NewGenerator(pattern)
	if err != nil {
		return "", mqutil.NewError(mqutil.ErrInvalid, err.Error())
	}
	str := g.Generate(length)

	if len(s.Format) == 0 || s.Format == "password" {
		return str, nil
	}
	if s.Format == "byte" {
		return base64.StdEncoding.EncodeToString([]byte(str)), nil
	}
	if s.Format == "binary" {
		return hex.EncodeToString([]byte(str)), nil
	}
	return "", mqutil.NewError(mqutil.ErrInvalid, fmt.Sprintf("Invalid format string: %s", s.Format))
}

func generateBool(v *spec.CommonValidations) (interface{}, error) {
	return rand.Intn(2) == 0, nil
}

func generateFloat(v *spec.CommonValidations) (float64, error) {
	var realmin float64
	if v.Minimum != nil {
		realmin = *v.Minimum
		if v.ExclusiveMinimum {
			realmin += 0.01
		}
	}
	var realmax float64
	if v.Maximum != nil {
		realmax = *v.Maximum
		if v.ExclusiveMaximum {
			realmax -= 0.01
		}
	}
	if realmin >= realmax {
		if v.Minimum == nil && v.Maximum == nil {
			realmin = -1.0
			realmax = 1.0
		} else if v.Minimum != nil {
			realmax = realmin + math.Abs(realmin)
		} else if v.Maximum != nil {
			realmin = realmax - math.Abs(realmax)
		} else {
			// both are present but conflicting
			return 0, mqutil.NewError(mqutil.ErrInvalid, fmt.Sprintf("specified min value %v is bigger than max %v",
				*v.Minimum, *v.Maximum))
		}
	}
	return rand.Float64()*(realmax-realmin) + realmin, nil
}

func generateInt(v *spec.CommonValidations) (int64, error) {
	f, err := generateFloat(v)
	if err != nil {
		return 0, err
	}
	i := int64(f)
	if v.Minimum != nil && i <= int64(*v.Minimum) {
		i++
	}
	return i, nil
}

func generateArray(s *spec.SimpleSchema, v *spec.CommonValidations, prefix string) (interface{}, error) {
	var maxItems int
	if v.MaxItems != nil {
		maxItems = int(*v.MaxItems)
		if maxItems < 0 {
			maxItems = 0
		}
	}
	var minItems int
	if v.MinItems != nil {
		minItems = int(*v.MinItems)
		if minItems < 0 {
			minItems = 0
		}
	}
	maxDiff := maxItems - minItems
	if maxDiff < 0 {
		maxDiff = 1
	}
	numItems := rand.Intn(int(maxDiff)) + minItems

	var ar []interface{}
	for i := 0; i < numItems; i++ {
		entry, err := generateByType(&s.Items.SimpleSchema, &s.Items.CommonValidations, prefix+"-")
		if err != nil {
			return nil, err
		}
		ar = append(ar, entry)
	}
	return ar, nil
}

func generateBySchema(schema *spec.Schema, db mqswag.DB) (string, error) {
	panic("not implemented")
}

func generateByEnum(paramSpec *spec.Parameter) (string, error) {
	return fmt.Sprintf("%v", paramSpec.Enum[rand.Intn(len(paramSpec.Enum))]), nil
}

// ResolveParameters fullfills the parameters for the specified request using the in-mem DB.
// The resolved parameters will be added to test.Parameters map.
func (t *Test) ResolveParameters(swagger *mqswag.Swagger, db mqswag.DB, plan *TestPlan) error {
	pathItem := swagger.Paths.Paths[t.Path]
	op := getOperationByMethod(&pathItem, t.Method)
	if op == nil {
		return mqutil.NewError(mqutil.ErrNotFound, fmt.Sprintf("Path %s not found in swagger file", t.Path))
	}

	for _, params := range op.Parameters {
		// We don't override the existing parameters
		if _, ok := t.Parameters[params.Name]; ok {
			continue
		}
		p, err := generateParameter(&params, db)
		if err != nil {
			return err
		}
		t.Parameters[params.Name] = p
		return nil
	}
	return nil
}

type TestCase []*Test

// Represents all the test cases in the DSL.
type TestPlan struct {
	CaseMap  map[string](TestCase)
	CaseList [](TestCase)
}

// Add a new TestCase, returns whether the Case is successfully added.
func (plan *TestPlan) Add(name string, testCase TestCase) error {
	if _, exist := plan.CaseMap[name]; exist {
		str := fmt.Sprintf("Duplicate name %s found in test plan", name)
		mqutil.Logger.Println(str)
		return errors.New(str)
	}
	plan.CaseMap[name] = testCase
	plan.CaseList = append(plan.CaseList, testCase)
	return nil
}

func (plan *TestPlan) AddFromString(data string) error {
	var caseMap map[string]TestCase
	err := yaml.Unmarshal([]byte(data), &caseMap)
	if err != nil {
		mqutil.Logger.Printf("The following is not a valud TestCase:\n%s", data)
		return err
	}
	for testName, testCase := range caseMap {
		for _, t := range testCase {
			if len(t.Method) != 0 {
				t.Method = strings.ToUpper(t.Method)
			}
		}
		err = plan.Add(testName, testCase)
		if err != nil {
			return err
		}
	}
	return nil
}

func (plan *TestPlan) InitFromFile(path string) error {
	plan.CaseMap = make(map[string]TestCase)
	plan.CaseList = nil

	data, err := ioutil.ReadFile(path)
	if err != nil {
		mqutil.Logger.Printf("Can't open the following file: %s", path)
		mqutil.Logger.Println(err.Error())
		return err
	}
	chunks := strings.Split(string(data), "---")
	for _, chunk := range chunks {
		plan.AddFromString(chunk)
	}
	return nil
}

// Run a named TestCase in the test plan.
func (plan *TestPlan) Run(name string, swagger *mqswag.Swagger, db mqswag.DB) (err error) {
	tc, ok := plan.CaseMap[name]
	if !ok || len(tc) == 0 {
		str := fmt.Sprintf("The following test case is not found: %s", name)
		mqutil.Logger.Println(str)
		return errors.New(str)
	}

	for _, test := range tc {
		err = test.Run(swagger, db, plan)
		if err != nil {
			return err
		}

	}
	return nil
}

// The current global TestPlan
var Current TestPlan
<|endoftext|>


<commit_before>package proxy

import (
	"net/url"

	"github.com/webx-top/echo"
	mw "github.com/webx-top/echo/middleware"
)

func NewTarget(t *mw.ProxyTarget) mw.ProxyTargeter {
	return &Target{ProxyTarget: t}
}

type Target struct {
	*mw.ProxyTarget
}

func (t *Target) GetURL(c echo.Context) *url.URL {
	address := c.Internal().String(`frp.admin.address`)
	u := *t.ProxyTarget.URL
	u.Host = address
	echo.Dump(u)
	return &u
}
<commit_msg>update<commit_after>package proxy

import (
	"net/url"

	"github.com/webx-top/echo"
	mw "github.com/webx-top/echo/middleware"
)

func NewTarget(t *mw.ProxyTarget) mw.ProxyTargeter {
	return &Target{ProxyTarget: t}
}

type Target struct {
	*mw.ProxyTarget
}

func (t *Target) GetURL(c echo.Context) *url.URL {
	address := c.Internal().String(`frp.admin.address`)
	u := *t.ProxyTarget.URL
	u.Host = address
	//echo.Dump(u)
	return &u
}
<|endoftext|>


<commit_before>// Copyright 2015 Square Inc.
//
// 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.

// program which takes
// - a rule file
// - a sample list of metrics
// and sees how well the rule performs against the metrics.
package main

import (
	"bytes"
	"compress/zlib"
	"encoding/gob"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"sync"

	"github.com/square/metrics/api"
	"github.com/square/metrics/main/common"
	"github.com/square/metrics/metric_metadata/cassandra"
	"github.com/square/metrics/util"
)

var (
	metricsFile   = flag.String("metrics-file", "", "Location of zlib compressed gob string file.")
	unmatchedFile = flag.String("unmatched-file", "", "location of metrics list to output unmatched transformations.")
	reverse       = flag.Bool("reverse", false, "If true, then attempt the reverse-rule lookup also.")
)

// Statistics represents the aggregated result of rules
// after running through the test file.
type Statistics struct {
	perMetric map[api.MetricKey]PerMetricStatistics
	matched   int // number of matched rows
	unmatched int // number of unmatched rows
}

// PerMetricStatistics represents per-metric result of rules
// after running through the test file.
type PerMetricStatistics struct {
	matched          int // number of matched rows
	reverseSuccess   int // number of reversed entries
	reverseError     int // number of incorrectly reversed entries.
	reverseIncorrect int // number of incorrectly reversed entries.
}

func ReadMetricsFile(file string) ([]string, error) {
	data, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}

	result := make(map[string]int)
	r, err := zlib.NewReader(bytes.NewBuffer(data))
	if err != nil {
		panic("Problem with zlib compressed data")
	}
	b := new(bytes.Buffer)
	io.Copy(b, r)
	r.Close()
	d := gob.NewDecoder(b)

	err = d.Decode(&result)
	if err != nil {
		return nil, err
	}

	strings := []string{}
	for k := range result {
		strings = append(strings, k)
	}
	return strings, nil
}

func main() {
	flag.Parse()
	common.SetupLogger()

	if *metricsFile == "" {
		common.ExitWithMessage("No metric file.")
		fmt.Printf("You must specify a metrics file.\n")
		os.Exit(1)
	}

	config := common.LoadConfig()

	// graphiteConfig := util.GraphiteConverterConfig{ConversionRulesPath: config.MetricMetadataAPI.ConversionRulesPath}
	//TODO(cchandler): Make a constructor for a graphite converter so we don't
	//have to stich everything together outside of the package.

	ruleset, err := util.LoadRules(config.MetricMetadataConfig.ConversionRulesPath)

	if err != nil {
		common.ExitWithMessage(fmt.Sprintf("Error while reading rules: %s", err.Error()))
	}
	graphiteConverter := util.RuleBasedGraphiteConverter{Ruleset: ruleset}

	// metricFile, err := os.Open(*metricsFile)
	metrics, err := ReadMetricsFile(*metricsFile)
	if err != nil {
		fmt.Printf("Fatal error reading metrics file %s\n", err)
		os.Exit(1)
	}

	DoAnalysis(metrics, graphiteConverter)

	fmt.Printf("Total metric count %d\n", len(metrics))

	// matched := 0
	// unmatched := 0
	// reverse_convert_failed := 0

	// fmt.Printf("Matched: %d\n", matched)
	// fmt.Printf("Unmatched: %d\n", unmatched)
	// fmt.Printf("Reverse convert failed: %d\n", reverse_convert_failed)

	// if err != nil {
	// 	common.ExitWithMessage("No metric file.")
	// }
	// scanner := bufio.NewScanner(metricFile)
	cassandraConfig := cassandra.CassandraMetricMetadataConfig{
		Hosts:    config.MetricMetadataConfig.Hosts,
		Keyspace: config.MetricMetadataConfig.Keyspace,
	}
	_ = common.NewMetricMetadataAPI(cassandraConfig)

	// var output *os.File
	// if *unmatchedFile != "" {
	// 	output, err = os.Create(*unmatchedFile)
	// 	if err != nil {
	// 		common.ExitWithMessage(fmt.Sprintf("Error creating the output file: %s", err.Error()))
	// 	}
	// }
	// stat := run(ruleset, scanner, apiInstance, output)
	// report(stat)
}

type ChunkResult struct {
	matched                int
	unmatched              int
	reverse_convert_failed int
}

func DoAnalysis(metrics []string, graphiteConverter util.RuleBasedGraphiteConverter) {
	graphiteConverter.EnableStats()

	workQueue := make(chan []string, len(metrics)/25+1)
	resultQueue := make(chan ChunkResult, len(metrics))
	unmatchedQueue := make(chan string, len(metrics))
	var wg sync.WaitGroup

	i := 0
	for i = 0; i+25 < len(metrics); i = i + 25 {
		workQueue <- metrics[i : i+25]
	}
	if i < len(metrics) {
		workQueue <- metrics[i:]
	}

	close(workQueue)

	fmt.Printf("Starting work...\n")
	for j := 0; j < 10; j++ {
		wg.Add(1)
		go func() {
			counter := 0
			defer wg.Done()
			for metrics := range workQueue {
				counter++
				if counter%100 == 0 && counter != 0 {
					fmt.Printf(".")
				}
				chunk_result := ChunkResult{}
				for _, metric := range metrics {
					graphiteMetric := util.GraphiteMetric(metric)
					taggedMetric, err := graphiteConverter.ToTaggedName(graphiteMetric)
					if err != nil {
						_, err := graphiteConverter.ToGraphiteName(taggedMetric)
						if err != nil {
							chunk_result.reverse_convert_failed++
						} else {

						}
						chunk_result.matched++
					} else {
						unmatchedQueue <- metric
						chunk_result.unmatched++
					}
				}
				resultQueue <- chunk_result
			}

		}()
	}
	wg.Wait()
	close(resultQueue)
	close(unmatchedQueue)

	fmt.Printf("\n")
	fmt.Printf("Processing results!")
	totalResults := ChunkResult{}
	//Merge chunks
	wg.Add(1)
	go func() {
		defer wg.Done()
		for result := range resultQueue {
			totalResults.matched += result.matched
			totalResults.unmatched += result.unmatched
			totalResults.reverse_convert_failed += result.reverse_convert_failed
		}
	}()
	wg.Add(1)
	unmatchedResults := []string{}
	go func() {
		defer wg.Done()
		for unmatched := range unmatchedQueue {
			unmatchedResults = append(unmatchedResults, unmatched)
		}
	}()

	wg.Wait()
	fmt.Printf("\n")
	fmt.Printf("Matched: %d\n", totalResults.matched)
	fmt.Printf("Unmatched: %d\n", totalResults.unmatched)
	fmt.Printf("Reverse convert failed: %d\n", totalResults.reverse_convert_failed)

	GenerateReport(unmatchedResults, graphiteConverter)
}

func GenerateReport(unmatched []string, graphiteConverter util.RuleBasedGraphiteConverter) {
	err := os.RemoveAll("report")
	if err != nil {
		panic("Can't delete the report directory")
	}
	if err := os.Mkdir("report", 0744); err != nil {
		panic("Can't create report directory")
	}

	f, err := os.Create("report/unmatched.txt")
	defer f.Close()

	for _, metric := range unmatched {
		f.WriteString(fmt.Sprintf("%s\n", metric))
	}

	for i, rule := range graphiteConverter.Ruleset.Rules {
		f, err := os.Create(fmt.Sprintf("report/%d.txt", i))
		defer f.Close()
		if err != nil {
			panic("Unable to create report file!")
		}
		f.WriteString(fmt.Sprintf("Rule: %s\n", rule.MetricKeyRegex))

		for _, match := range rule.Statistics.SuccessfulMatches {
			f.WriteString(fmt.Sprintf("%s\n", match))
		}

	}
}
<commit_msg>use channels for communicating<commit_after>// Copyright 2015 Square Inc.
//
// 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.

// program which takes
// - a rule file
// - a sample list of metrics
// and sees how well the rule performs against the metrics.
package main

import (
	"bytes"
	"compress/zlib"
	"encoding/gob"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"sync"

	"github.com/square/metrics/api"
	"github.com/square/metrics/main/common"
	"github.com/square/metrics/metric_metadata/cassandra"
	"github.com/square/metrics/util"
)

var (
	metricsFile   = flag.String("metrics-file", "", "Location of zlib compressed gob string file.")
	unmatchedFile = flag.String("unmatched-file", "", "location of metrics list to output unmatched transformations.")
	reverse       = flag.Bool("reverse", false, "If true, then attempt the reverse-rule lookup also.")
)

// Statistics represents the aggregated result of rules
// after running through the test file.
type Statistics struct {
	perMetric map[api.MetricKey]PerMetricStatistics
	matched   int // number of matched rows
	unmatched int // number of unmatched rows
}

// PerMetricStatistics represents per-metric result of rules
// after running through the test file.
type PerMetricStatistics struct {
	matched          int // number of matched rows
	reverseSuccess   int // number of reversed entries
	reverseError     int // number of incorrectly reversed entries.
	reverseIncorrect int // number of incorrectly reversed entries.
}

func ReadMetricsFile(file string) ([]string, error) {
	data, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}

	// Read the data with a zlib reader
	r, err := zlib.NewReader(bytes.NewBuffer(data))
	defer r.Close()
	if err != nil {
		return nil, fmt.Errorf("Problem with zlib compressed data: %s", err.Error())
	}

	// Store the result of the decode in this map:
	result := map[string]int{}
	err = gob.NewDecoder(r).Decode(&result)
	if err != nil {
		return nil, err
	}

	strings := make([]string, 0, len(result))
	for k := range result {
		strings = append(strings, k)
	}
	return strings, nil
}

func main() {
	flag.Parse()
	common.SetupLogger()

	if *metricsFile == "" {
		common.ExitWithMessage("No metric file.")
		fmt.Printf("You must specify a metrics file.\n")
		os.Exit(1)
	}

	config := common.LoadConfig()

	// graphiteConfig := util.GraphiteConverterConfig{ConversionRulesPath: config.MetricMetadataAPI.ConversionRulesPath}
	//TODO(cchandler): Make a constructor for a graphite converter so we don't
	//have to stich everything together outside of the package.

	ruleset, err := util.LoadRules(config.MetricMetadataConfig.ConversionRulesPath)
	if err != nil {
		common.ExitWithMessage(fmt.Sprintf("Error while reading rules: %s", err.Error()))
	}

	graphiteConverter := util.RuleBasedGraphiteConverter{Ruleset: ruleset}

	metrics, err := ReadMetricsFile(*metricsFile)
	if err != nil {
		fmt.Printf("Fatal error reading metrics file %s\n", err)
		os.Exit(1)
	}

	DoAnalysis(metrics, graphiteConverter)

	fmt.Printf("Total metric count %d\n", len(metrics))

	// matched := 0
	// unmatched := 0
	// reverse_convert_failed := 0

	// fmt.Printf("Matched: %d\n", matched)
	// fmt.Printf("Unmatched: %d\n", unmatched)
	// fmt.Printf("Reverse convert failed: %d\n", reverse_convert_failed)

	// if err != nil {
	// 	common.ExitWithMessage("No metric file.")
	// }
	// scanner := bufio.NewScanner(metricFile)
	cassandraConfig := cassandra.CassandraMetricMetadataConfig{
		Hosts:    config.MetricMetadataConfig.Hosts,
		Keyspace: config.MetricMetadataConfig.Keyspace,
	}
	_ = common.NewMetricMetadataAPI(cassandraConfig)

	// var output *os.File
	// if *unmatchedFile != "" {
	// 	output, err = os.Create(*unmatchedFile)
	// 	if err != nil {
	// 		common.ExitWithMessage(fmt.Sprintf("Error creating the output file: %s", err.Error()))
	// 	}
	// }
	// stat := run(ruleset, scanner, apiInstance, output)
	// report(stat)
}

type ConversionStatus int

const (
	Matched ConversionStatus = iota
	Unmatched
	ReverseFailed
	ReverseChanged
)

func ClassifyMetric(metric string, graphiteConverter util.RuleBasedGraphiteConverter) ConversionStatus {
	graphiteMetric := util.GraphiteMetric(metric)
	taggedMetric, err := graphiteConverter.ToTaggedName(graphiteMetric)
	if err != nil {
		return Unmatched
	}
	reversedMetric, err := graphiteConverter.ToGraphiteName(taggedMetric)
	if err != nil {
		return ReverseFailed
	}
	if reversedMetric != graphiteMetric {
		return ReverseChanged
	}
	return Matched
}

func DoAnalysis(metrics []string, graphiteConverter util.RuleBasedGraphiteConverter) {
	graphiteConverter.EnableStats()

	goroutineCount := 10

	workQueue := make(chan string, goroutineCount)

	go func() {
		// Add the metrics to the work queue
		for _, metric := range metrics {
			workQueue <- metric
		}
		close(workQueue)
	}()

	classifiedMetricResults := map[ConversionStatus]chan string{
		Matched:        make(chan string),
		Unmatched:      make(chan string),
		ReverseFailed:  make(chan string),
		ReverseChanged: make(chan string),
	}

	classifiedMetrics := map[ConversionStatus][]string{}

	var wgClassifyAppend sync.WaitGroup

	for status := range classifiedMetricResults {
		wgClassifyAppend.Add(1)
		go func() {
			for metric := range classifiedMetricResults[status] {
				classifiedMetrics[status] = append(classifiedMetrics[status], metric)
			}
			wgClassifyAppend.Done()
		}()
	}

	var wgWorkQueue sync.WaitGroup

	fmt.Printf("Starting work...\n")
	for i := 0; i < goroutineCount; i++ {
		// Launch 10 goroutines to process the work queue
		wgWorkQueue.Add(1)
		go func() {
			counter := 0
			defer wgWorkQueue.Done()
			for metric := range workQueue {
				counter++
				if counter%1000 == 0 && counter != 0 {
					fmt.Printf(".")
				}
				// Classify the metric, then send it to the corresponding channel.
				classifiedMetricResults[ClassifyMetric(metric, graphiteConverter)] <- metric
			}
		}()
	}
	wgWorkQueue.Wait()

	for _, channel := range classifiedMetricResults {
		close(channel)
	}
	// Wait for the results to be moved from the channels into the slices.
	wgClassifyAppend.Wait()

	fmt.Printf("\n")
	fmt.Printf("Matched: %d\n", len(classifiedMetrics[Matched]))
	fmt.Printf("Unmatched: %d\n", len(classifiedMetrics[Unmatched]))
	fmt.Printf("Reverse convert failed: %d\n", len(classifiedMetrics[ReverseFailed]))
	// Since these indicate broken rules, printing out the particular metrics is very helpful.
	for _, metric := range classifiedMetrics[ReverseFailed] {
		fmt.Printf("\t%s\n", metric)
	}
	fmt.Printf("Reverse convert changed metric: %d\n", len(classifiedMetrics[ReverseChanged]))
	// Since these indicate broken rules, printing out the particular metrics is very helpful.
	for _, metric := range classifiedMetrics[ReverseChanged] {
		fmt.Printf("\t%s\n", metric)
	}

	GenerateReport(classifiedMetrics[Unmatched], graphiteConverter)
}

func GenerateReport(unmatched []string, graphiteConverter util.RuleBasedGraphiteConverter) {
	err := os.RemoveAll("report")
	if err != nil {
		panic("Can't delete the report directory")
	}
	if err := os.Mkdir("report", 0744); err != nil {
		panic("Can't create report directory")
	}

	f, err := os.Create("report/unmatched.txt")
	defer f.Close()

	for _, metric := range unmatched {
		f.WriteString(fmt.Sprintf("%s\n", metric))
	}

	for i, rule := range graphiteConverter.Ruleset.Rules {
		f, err := os.Create(fmt.Sprintf("report/%d.txt", i))
		defer f.Close()
		if err != nil {
			panic("Unable to create report file!")
		}
		f.WriteString(fmt.Sprintf("Rule: %s\n", rule.MetricKeyRegex))

		for _, match := range rule.Statistics.SuccessfulMatches {
			f.WriteString(fmt.Sprintf("%s\n", match))
		}

	}
}
<|endoftext|>