<commit_before>// Package newsbox provides a client for accessing newsbox services.
package newsbox

import (
	"encoding/json"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/machinebox/sdk-go/x/boxutil"
	"github.com/pkg/errors"
)

// Analysis represents an analysis of title, content and domain.
type Analysis struct {
	// Title is the response object for the title analysis
	Title Title `json:"title"`
	// Content is the response object for the content analysis
	Content Content `json:"content"`
	// Domain is the response object for the domain analysis
	Domain Domain `json:"domain"`
}

type Title struct {
	// Decision is the string representing the decision could be bias/unsure/impartial
	Decision string `json:"decision,omitempty"`
	// Score is the numeric score of the decision is between 0.00 (bias) and 1.00 (impartial)
	Score float64 `json:"score,omitempty"`
	// Entities represents entities discovered in the text.
	Entities []Entity `json:"entities,omitempty"`
}

type Content struct {
	// Decision is the string representing the decision could be bias/unsure/impartial
	Decision string `json:"decision,omitempty"`
	// Score is the numeric score of the decision is between 0.00 (bias) and 1.00 (impartial)
	Score float64 `json:"score,omitempty"`
	// Entities represents entities discovered in the text.
	Entities []Entity `json:"entities,omitempty"`
	// Keywords are the most relevant keywords extracted from the text
	Keywords []Keyword `json:"keywords"`
}

type Domain struct {
	// Domain is the domain extracted from the URL
	Domain string `json:"domain,omitempty"`
	// Category is one of the listed on the API docs
	Category string `json:"category,omitempty"`
}

// Entity represents an entity discovered in the text.
type Entity struct {
	// Type is a string describing the kind of entity.
	Type string `json:"type"`
	// Text is the text of the entity.
	Text string `json:"text"`
	// Start is the absolute start position of the entity (in the original text).
	Start int `json:"start"`
	// Start is the absolute end position of the entity (in the original text).
	End int `json:"end"`
}

// Keyword represents a key word.
type Keyword struct {
	Keyword string `json:"keyword"`
}

// Client is an HTTP client that can make requests to the box.
type Client struct {
	addr string

	// HTTPClient is the http.Client that will be used to
	// make requests.
	HTTPClient *http.Client
}

// New makes a new Client.
func New(addr string) *Client {
	return &Client{
		addr: addr,
		HTTPClient: &http.Client{
			Timeout: 10 * time.Second,
		},
	}
}

// Info gets the details about the box.
func (c *Client) Info() (*boxutil.Info, error) {
	var info boxutil.Info
	u, err := url.Parse(c.addr + "/info")
	if err != nil {
		return nil, err
	}
	if !u.IsAbs() {
		return nil, errors.New("box address must be absolute")
	}
	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Accept", "application/json; charset=utf-8")
	resp, err := c.HTTPClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
		return nil, err
	}
	return &info, nil
}

// Check passes the text from the Reader to newsbox for analysis.
func (c *Client) Check(title string, content string, u *url.URL) (*Analysis, error) {
	uu, err := url.Parse(c.addr + "/newsbox/check")
	if err != nil {
		return nil, err
	}
	if !u.IsAbs() {
		return nil, errors.New("box address must be absolute")
	}
	vals := url.Values{}
	vals.Set("title", title)
	vals.Set("content", content)
	vals.Set("url", u.String())

	req, err := http.NewRequest("POST", uu.String(), strings.NewReader(vals.Encode()))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Accept", "application/json; charset=utf-8")
	resp, err := c.HTTPClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var response struct {
		Success bool
		Error   string

		Title   Title   `json:"title"`
		Content Content `json:"content"`
		Domain  Domain  `json:"domain"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		return nil, errors.Wrap(err, "decoding response")
	}
	if !response.Success {
		return nil, ErrNewsbox(response.Error)
	}
	return &Analysis{
		Title:   response.Title,
		Content: response.Content,
		Domain:  response.Domain,
	}, nil
}

// ErrNewsbox represents an error from newsbox.
type ErrNewsbox string

func (e ErrNewsbox) Error() string {
	return "newsbox: " + string(e)
}
<commit_msg>adjust comments<commit_after>// Package newsbox provides a client for accessing newsbox services.
package newsbox

import (
	"encoding/json"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/machinebox/sdk-go/x/boxutil"
	"github.com/pkg/errors"
)

// Analysis represents an analysis of title, content and domain.
type Analysis struct {
	// Title is the response object for the title analysis.
	Title Title `json:"title"`
	// Content is the response object for the content analysis.
	Content Content `json:"content"`
	// Domain is the response object for the domain analysis.
	Domain Domain `json:"domain"`
}

// Title is the response object for the title analysis.
type Title struct {
	// Decision is the string representing the decision could be bias/unsure/impartial
	Decision string `json:"decision,omitempty"`
	// Score is the numeric score of the decision is between 0.00 (bias) and 1.00 (impartial)
	Score float64 `json:"score,omitempty"`
	// Entities represents entities discovered in the text.
	Entities []Entity `json:"entities,omitempty"`
}

// Content is the response object for the content analysis.
type Content struct {
	// Decision is the string representing the decision could be bias/unsure/impartial
	Decision string `json:"decision,omitempty"`
	// Score is the numeric score of the decision is between 0.00 (bias) and 1.00 (impartial)
	Score float64 `json:"score,omitempty"`
	// Entities represents entities discovered in the text.
	Entities []Entity `json:"entities,omitempty"`
	// Keywords are the most relevant keywords extracted from the text
	Keywords []Keyword `json:"keywords"`
}

// Domain is the response object for the domain analysis.
type Domain struct {
	// Domain is the domain extracted from the URL
	Domain string `json:"domain,omitempty"`
	// Category is one of the listed on the API docs
	Category string `json:"category,omitempty"`
}

// Entity represents an entity discovered in the text.
type Entity struct {
	// Type is a string describing the kind of entity.
	Type string `json:"type"`
	// Text is the text of the entity.
	Text string `json:"text"`
	// Start is the absolute start position of the entity (in the original text).
	Start int `json:"start"`
	// Start is the absolute end position of the entity (in the original text).
	End int `json:"end"`
}

// Keyword represents a key word.
type Keyword struct {
	Keyword string `json:"keyword"`
}

// Client is an HTTP client that can make requests to the box.
type Client struct {
	addr string

	// HTTPClient is the http.Client that will be used to
	// make requests.
	HTTPClient *http.Client
}

// New makes a new Client.
func New(addr string) *Client {
	return &Client{
		addr: addr,
		HTTPClient: &http.Client{
			Timeout: 10 * time.Second,
		},
	}
}

// Info gets the details about the box.
func (c *Client) Info() (*boxutil.Info, error) {
	var info boxutil.Info
	u, err := url.Parse(c.addr + "/info")
	if err != nil {
		return nil, err
	}
	if !u.IsAbs() {
		return nil, errors.New("box address must be absolute")
	}
	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Accept", "application/json; charset=utf-8")
	resp, err := c.HTTPClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
		return nil, err
	}
	return &info, nil
}

// Check passes the text from the Reader to newsbox for analysis.
func (c *Client) Check(title string, content string, u *url.URL) (*Analysis, error) {
	uu, err := url.Parse(c.addr + "/newsbox/check")
	if err != nil {
		return nil, err
	}
	if !u.IsAbs() {
		return nil, errors.New("box address must be absolute")
	}
	vals := url.Values{}
	vals.Set("title", title)
	vals.Set("content", content)
	vals.Set("url", u.String())

	req, err := http.NewRequest("POST", uu.String(), strings.NewReader(vals.Encode()))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Accept", "application/json; charset=utf-8")
	resp, err := c.HTTPClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var response struct {
		Success bool
		Error   string

		Title   Title   `json:"title"`
		Content Content `json:"content"`
		Domain  Domain  `json:"domain"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		return nil, errors.Wrap(err, "decoding response")
	}
	if !response.Success {
		return nil, ErrNewsbox(response.Error)
	}
	return &Analysis{
		Title:   response.Title,
		Content: response.Content,
		Domain:  response.Domain,
	}, nil
}

// ErrNewsbox represents an error from newsbox.
type ErrNewsbox string

func (e ErrNewsbox) Error() string {
	return "newsbox: " + string(e)
}
<|endoftext|>


<commit_before>package testflight_test

import (
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"syscall"
	"testing"
	"time"

	"github.com/cloudfoundry-incubator/garden/warden"
	WardenRunner "github.com/cloudfoundry-incubator/warden-linux/integration/runner"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"
	"github.com/tedsuo/ifrit/grouper"
)

var externalAddr string

var processes ifrit.Process
var wardenClient warden.Client

var fixturesDir = "./fixtures"
var atcDir string

var builtComponents map[string]string

var wardenBinPath string

var helperRootfs string

var _ = SynchronizedBeforeSuite(func() []byte {
	wardenBinPath = os.Getenv("WARDEN_BINPATH")
	Ω(wardenBinPath).ShouldNot(BeEmpty(), "must provide $WARDEN_BINPATH")

	Ω(os.Getenv("BASE_GOPATH")).ShouldNot(BeEmpty(), "must provide $BASE_GOPATH")

	turbineBin, err := buildWithGodeps("github.com/concourse/turbine", "-race")
	Ω(err).ShouldNot(HaveOccurred())

	atcBin, err := buildWithGodeps("github.com/concourse/atc", "-race")
	Ω(err).ShouldNot(HaveOccurred())

	gliderBin, err := buildWithGodeps("github.com/concourse/glider", "-race")
	Ω(err).ShouldNot(HaveOccurred())

	flyBin, err := buildWithGodeps("github.com/concourse/fly", "-race")
	Ω(err).ShouldNot(HaveOccurred())

	wardenLinuxBin, err := buildWithGodeps("github.com/cloudfoundry-incubator/warden-linux", "-race")
	Ω(err).ShouldNot(HaveOccurred())

	components, err := json.Marshal(map[string]string{
		"turbine":      turbineBin,
		"atc":          atcBin,
		"glider":       gliderBin,
		"fly":          flyBin,
		"warden-linux": wardenLinuxBin,
	})
	Ω(err).ShouldNot(HaveOccurred())

	return components
}, func(components []byte) {
	err := json.Unmarshal(components, &builtComponents)
	Ω(err).ShouldNot(HaveOccurred())

	atcDir = findSource("github.com/concourse/atc")
})

var _ = BeforeEach(func() {
	externalAddr = os.Getenv("EXTERNAL_ADDRESS")
	Ω(externalAddr).ShouldNot(BeEmpty(), "must specify $EXTERNAL_ADDRESS")

	rawResourceRootfs := os.Getenv("RAW_RESOURCE_ROOTFS")
	Ω(rawResourceRootfs).ShouldNot(BeEmpty(), "must specify $RAW_RESOURCE_ROOTFS")

	gitResourceRootfs := os.Getenv("GIT_RESOURCE_ROOTFS")
	Ω(gitResourceRootfs).ShouldNot(BeEmpty(), "must specify $GIT_RESOURCE_ROOTFS")

	helperRootfs = os.Getenv("HELPER_ROOTFS")
	Ω(helperRootfs).ShouldNot(BeEmpty(), "must specify $HELPER_ROOTFS")

	wardenAddr := fmt.Sprintf("127.0.0.1:%d", 4859+GinkgoParallelNode())

	wardenRunner := WardenRunner.New(
		"tcp",
		wardenAddr,
		builtComponents["warden-linux"],
		wardenBinPath,
		"bogus/rootfs",
	)

	wardenClient = wardenRunner.NewClient()

	turbineRunner := &ginkgomon.Runner{
		Name:          "turbine",
		AnsiColorCode: "33m",
		Command: exec.Command(
			builtComponents["turbine"],
			"-wardenNetwork", "tcp",
			"-wardenAddr", wardenAddr,
			"-resourceTypes", fmt.Sprintf(`{
				"raw": "%s",
				"git": "%s"
			}`, rawResourceRootfs, gitResourceRootfs),
		),
		StartCheck:        "listening",
		StartCheckTimeout: 30 * time.Second,
	}

	gliderRunner := &ginkgomon.Runner{
		Name:          "glider",
		AnsiColorCode: "32m",
		Command: exec.Command(
			builtComponents["glider"],
			"-peerAddr", externalAddr+":5637",
		),
		StartCheck: "listening",
	}

	processes = grouper.EnvokeGroup(grouper.RunGroup{
		"turbine":      turbineRunner,
		"glider":       gliderRunner,
		"warden-linux": wardenRunner,
	})

	Consistently(processes.Wait(), 1*time.Second).ShouldNot(Receive())

	os.Setenv("GLIDER_URL", "http://127.0.0.1:5637")
})

var _ = AfterEach(func() {
	processes.Signal(syscall.SIGINT)
	Eventually(processes.Wait(), 10*time.Second).Should(Receive())
})

func TestFlightTest(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "FlightTest Suite")
}

func findSource(pkg string) string {
	for _, path := range filepath.SplitList(os.Getenv("BASE_GOPATH")) {
		srcPath := filepath.Join(path, "src", pkg)

		_, err := os.Stat(srcPath)
		if err != nil {
			continue
		}

		return srcPath
	}

	return ""
}

func buildWithGodeps(pkg string, args ...string) (string, error) {
	srcPath := findSource(pkg)
	Ω(srcPath).ShouldNot(BeEmpty(), "could not find source for "+pkg)

	gopath := fmt.Sprintf(
		"%s%c%s",
		filepath.Join(srcPath, "Godeps", "_workspace"),
		os.PathListSeparator,
		os.Getenv("BASE_GOPATH"),
	)

	return gexec.BuildIn(gopath, pkg, args...)
}
<commit_msg>specify graph path<commit_after>package testflight_test

import (
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"syscall"
	"testing"
	"time"

	"github.com/cloudfoundry-incubator/garden/warden"
	WardenRunner "github.com/cloudfoundry-incubator/warden-linux/integration/runner"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"
	"github.com/tedsuo/ifrit/grouper"
)

var externalAddr string

var processes ifrit.Process
var wardenClient warden.Client

var fixturesDir = "./fixtures"
var atcDir string

var builtComponents map[string]string

var wardenBinPath string

var helperRootfs string

var _ = SynchronizedBeforeSuite(func() []byte {
	wardenBinPath = os.Getenv("WARDEN_BINPATH")
	Ω(wardenBinPath).ShouldNot(BeEmpty(), "must provide $WARDEN_BINPATH")

	Ω(os.Getenv("BASE_GOPATH")).ShouldNot(BeEmpty(), "must provide $BASE_GOPATH")

	turbineBin, err := buildWithGodeps("github.com/concourse/turbine", "-race")
	Ω(err).ShouldNot(HaveOccurred())

	atcBin, err := buildWithGodeps("github.com/concourse/atc", "-race")
	Ω(err).ShouldNot(HaveOccurred())

	gliderBin, err := buildWithGodeps("github.com/concourse/glider", "-race")
	Ω(err).ShouldNot(HaveOccurred())

	flyBin, err := buildWithGodeps("github.com/concourse/fly", "-race")
	Ω(err).ShouldNot(HaveOccurred())

	wardenLinuxBin, err := buildWithGodeps("github.com/cloudfoundry-incubator/warden-linux", "-race")
	Ω(err).ShouldNot(HaveOccurred())

	components, err := json.Marshal(map[string]string{
		"turbine":      turbineBin,
		"atc":          atcBin,
		"glider":       gliderBin,
		"fly":          flyBin,
		"warden-linux": wardenLinuxBin,
	})
	Ω(err).ShouldNot(HaveOccurred())

	return components
}, func(components []byte) {
	err := json.Unmarshal(components, &builtComponents)
	Ω(err).ShouldNot(HaveOccurred())

	atcDir = findSource("github.com/concourse/atc")
})

var _ = BeforeEach(func() {
	externalAddr = os.Getenv("EXTERNAL_ADDRESS")
	Ω(externalAddr).ShouldNot(BeEmpty(), "must specify $EXTERNAL_ADDRESS")

	rawResourceRootfs := os.Getenv("RAW_RESOURCE_ROOTFS")
	Ω(rawResourceRootfs).ShouldNot(BeEmpty(), "must specify $RAW_RESOURCE_ROOTFS")

	gitResourceRootfs := os.Getenv("GIT_RESOURCE_ROOTFS")
	Ω(gitResourceRootfs).ShouldNot(BeEmpty(), "must specify $GIT_RESOURCE_ROOTFS")

	helperRootfs = os.Getenv("HELPER_ROOTFS")
	Ω(helperRootfs).ShouldNot(BeEmpty(), "must specify $HELPER_ROOTFS")

	wardenAddr := fmt.Sprintf("127.0.0.1:%d", 4859+GinkgoParallelNode())

	wardenRunner := WardenRunner.New(
		"tcp",
		wardenAddr,
		builtComponents["warden-linux"],
		wardenBinPath,
		"bogus/rootfs",
		"/tmp",
	)

	wardenClient = wardenRunner.NewClient()

	turbineRunner := &ginkgomon.Runner{
		Name:          "turbine",
		AnsiColorCode: "33m",
		Command: exec.Command(
			builtComponents["turbine"],
			"-wardenNetwork", "tcp",
			"-wardenAddr", wardenAddr,
			"-resourceTypes", fmt.Sprintf(`{
				"raw": "%s",
				"git": "%s"
			}`, rawResourceRootfs, gitResourceRootfs),
		),
		StartCheck:        "listening",
		StartCheckTimeout: 30 * time.Second,
	}

	gliderRunner := &ginkgomon.Runner{
		Name:          "glider",
		AnsiColorCode: "32m",
		Command: exec.Command(
			builtComponents["glider"],
			"-peerAddr", externalAddr+":5637",
		),
		StartCheck: "listening",
	}

	processes = grouper.EnvokeGroup(grouper.RunGroup{
		"turbine":      turbineRunner,
		"glider":       gliderRunner,
		"warden-linux": wardenRunner,
	})

	Consistently(processes.Wait(), 1*time.Second).ShouldNot(Receive())

	os.Setenv("GLIDER_URL", "http://127.0.0.1:5637")
})

var _ = AfterEach(func() {
	processes.Signal(syscall.SIGINT)
	Eventually(processes.Wait(), 10*time.Second).Should(Receive())
})

func TestFlightTest(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "FlightTest Suite")
}

func findSource(pkg string) string {
	for _, path := range filepath.SplitList(os.Getenv("BASE_GOPATH")) {
		srcPath := filepath.Join(path, "src", pkg)

		_, err := os.Stat(srcPath)
		if err != nil {
			continue
		}

		return srcPath
	}

	return ""
}

func buildWithGodeps(pkg string, args ...string) (string, error) {
	srcPath := findSource(pkg)
	Ω(srcPath).ShouldNot(BeEmpty(), "could not find source for "+pkg)

	gopath := fmt.Sprintf(
		"%s%c%s",
		filepath.Join(srcPath, "Godeps", "_workspace"),
		os.PathListSeparator,
		os.Getenv("BASE_GOPATH"),
	)

	return gexec.BuildIn(gopath, pkg, args...)
}
<|endoftext|>


<commit_before>package plugin

import (
	"fmt"
	"sync"

	"github.com/docker/libnetwork/drivers/remote/api"
	"github.com/docker/libnetwork/types"

	. "github.com/weaveworks/weave/common"
	"github.com/weaveworks/weave/common/docker"
	"github.com/weaveworks/weave/common/odp"
	"github.com/weaveworks/weave/plugin/skel"

	"github.com/vishvananda/netlink"
)

const (
	WeaveContainer = "weave"
	WeaveBridge    = "weave"
)

type driver struct {
	version          string
	nameserver       string
	scope            string
	noMulticastRoute bool
	sync.RWMutex
	endpoints map[string]struct{}
}

func New(client *docker.Client, version string, nameserver string, scope string, noMulticastRoute bool) (skel.Driver, error) {
	driver := &driver{
		nameserver:       nameserver,
		noMulticastRoute: noMulticastRoute,
		version:          version,
		scope:            scope,
		endpoints:        make(map[string]struct{}),
	}

	_, err := NewWatcher(client, driver)
	if err != nil {
		return nil, err
	}
	return driver, nil
}

func errorf(format string, a ...interface{}) error {
	Log.Errorf(format, a...)
	return fmt.Errorf(format, a...)
}

// === protocol handlers

func (driver *driver) GetCapabilities() (*api.GetCapabilityResponse, error) {
	var caps = &api.GetCapabilityResponse{
		Scope: driver.scope,
	}
	Log.Debugf("Get capabilities: responded with %+v", caps)
	return caps, nil
}

func (driver *driver) CreateNetwork(create *api.CreateNetworkRequest) error {
	Log.Debugf("Create network request %+v", create)
	Log.Infof("Create network %s", create.NetworkID)
	return nil
}

func (driver *driver) DeleteNetwork(delete *api.DeleteNetworkRequest) error {
	Log.Debugf("Delete network request: %+v", delete)
	Log.Infof("Destroy network %s", delete.NetworkID)
	return nil
}

func (driver *driver) CreateEndpoint(create *api.CreateEndpointRequest) (*api.CreateEndpointResponse, error) {
	Log.Debugf("Create endpoint request %+v interface %+v", create, create.Interface)
	endID := create.EndpointID

	if create.Interface == nil {
		return nil, fmt.Errorf("Not supported: creating an interface from within CreateEndpoint")
	}
	// create veths. note we assume endpoint IDs are unique in the first 5 chars
	local := vethPair(endID)
	if err := netlink.LinkAdd(local); err != nil {
		return nil, errorf("could not create veth pair: %s", err)
	}
	driver.Lock()
	driver.endpoints[endID] = struct{}{}
	driver.Unlock()

	// Send back the MAC address
	link, _ := netlink.LinkByName(local.PeerName)
	resp := &api.CreateEndpointResponse{Interface: &api.EndpointInterface{MacAddress: link.Attrs().HardwareAddr.String()}}

	Log.Infof("Create endpoint %s %+v", endID, resp)
	Log.Infof("Veth info %+v", local)
	return resp, nil
}

func (driver *driver) DeleteEndpoint(deleteReq *api.DeleteEndpointRequest) error {
	Log.Debugf("Delete endpoint request: %+v", deleteReq)
	Log.Infof("Delete endpoint %s", deleteReq.EndpointID)
	driver.Lock()
	delete(driver.endpoints, deleteReq.EndpointID)
	driver.Unlock()
	local := vethPair(deleteReq.EndpointID)
	if err := netlink.LinkDel(local); err != nil {
		Log.Warningf("unable to delete veth: %s", err)
	}
	return nil
}

func (driver *driver) HasEndpoint(endpointID string) bool {
	driver.Lock()
	_, found := driver.endpoints[endpointID]
	driver.Unlock()
	return found
}

func (driver *driver) EndpointInfo(req *api.EndpointInfoRequest) (*api.EndpointInfoResponse, error) {
	Log.Debugf("Endpoint info request: %+v", req)
	Log.Infof("Endpoint info %s", req.EndpointID)
	return &api.EndpointInfoResponse{Value: map[string]interface{}{}}, nil
}

func (driver *driver) JoinEndpoint(j *api.JoinRequest) (*api.JoinResponse, error) {
	endID := j.EndpointID

	maybeBridge, err := netlink.LinkByName(WeaveBridge)
	if err != nil {
		return nil, errorf(`bridge "%s" not present; did you launch weave?`, WeaveBridge)
	}

	local := vethPair(endID)
	if err = netlink.LinkSetMTU(local, maybeBridge.Attrs().MTU); err != nil {
		return nil, errorf(`unable to set mtu: %s`, err)
	}

	switch maybeBridge.(type) {
	case *netlink.Bridge:
		if err := netlink.LinkSetMasterByIndex(local, maybeBridge.Attrs().Index); err != nil {
			return nil, errorf(`unable to set master: %s`, err)
		}
	case *netlink.GenericLink:
		if maybeBridge.Type() != "openvswitch" {
			Log.Errorf("device %s is %+v", WeaveBridge, maybeBridge)
			return nil, errorf(`device "%s" is of type "%s"`, WeaveBridge, maybeBridge.Type())
		}
		odp.AddDatapathInterface(WeaveBridge, local.Name)
	case *netlink.Device:
		Log.Warnf("kernel does not report what kind of device %s is, just %+v", WeaveBridge, maybeBridge)
		// Assume it's our openvswitch device, and the kernel has not been updated to report the kind.
		odp.AddDatapathInterface(WeaveBridge, local.Name)
	default:
		Log.Errorf("device %s is %+v", WeaveBridge, maybeBridge)
		return nil, errorf(`device "%s" not a bridge`, WeaveBridge)
	}
	if err := netlink.LinkSetUp(local); err != nil {
		return nil, errorf(`unable to bring veth up: %s`, err)
	}

	ifname := &api.InterfaceName{
		SrcName:   local.PeerName,
		DstPrefix: "ethwe",
	}

	response := &api.JoinResponse{
		InterfaceName: ifname,
	}
	if driver.nameserver != "" {
		routeToDNS := api.StaticRoute{
			Destination: driver.nameserver + "/32",
			RouteType:   types.CONNECTED,
			NextHop:     "",
		}
		response.StaticRoutes = []api.StaticRoute{routeToDNS}
	}
	if !driver.noMulticastRoute {
		multicastRoute := api.StaticRoute{
			Destination: "224.0.0.0/4",
			RouteType:   types.CONNECTED,
		}
		response.StaticRoutes = append(response.StaticRoutes, multicastRoute)
	}
	Log.Infof("Join endpoint %s:%s to %s", j.NetworkID, j.EndpointID, j.SandboxKey)
	return response, nil
}

func (driver *driver) LeaveEndpoint(leave *api.LeaveRequest) error {
	Log.Debugf("Leave request: %+v", leave)
	Log.Infof("Leave %s:%s", leave.NetworkID, leave.EndpointID)
	return nil
}

func (driver *driver) DiscoverNew(disco *api.DiscoveryNotification) error {
	Log.Debugf("Dicovery new notification: %+v", disco)
	return nil
}

func (driver *driver) DiscoverDelete(disco *api.DiscoveryNotification) error {
	Log.Debugf("Dicovery delete notification: %+v", disco)
	return nil
}

// ===

func vethPair(endpointID string) *netlink.Veth {
	suffix := endpointID[:5]
	return &netlink.Veth{
		LinkAttrs: netlink.LinkAttrs{Name: "vethwl" + suffix},
		PeerName:  "vethwg" + suffix,
	}
}
<commit_msg>Use more chars from endpointID to reduce clashes<commit_after>package plugin

import (
	"fmt"
	"sync"

	"github.com/docker/libnetwork/drivers/remote/api"
	"github.com/docker/libnetwork/types"

	. "github.com/weaveworks/weave/common"
	"github.com/weaveworks/weave/common/docker"
	"github.com/weaveworks/weave/common/odp"
	"github.com/weaveworks/weave/plugin/skel"

	"github.com/vishvananda/netlink"
)

const (
	WeaveContainer = "weave"
	WeaveBridge    = "weave"
)

type driver struct {
	version          string
	nameserver       string
	scope            string
	noMulticastRoute bool
	sync.RWMutex
	endpoints map[string]struct{}
}

func New(client *docker.Client, version string, nameserver string, scope string, noMulticastRoute bool) (skel.Driver, error) {
	driver := &driver{
		nameserver:       nameserver,
		noMulticastRoute: noMulticastRoute,
		version:          version,
		scope:            scope,
		endpoints:        make(map[string]struct{}),
	}

	_, err := NewWatcher(client, driver)
	if err != nil {
		return nil, err
	}
	return driver, nil
}

func errorf(format string, a ...interface{}) error {
	Log.Errorf(format, a...)
	return fmt.Errorf(format, a...)
}

// === protocol handlers

func (driver *driver) GetCapabilities() (*api.GetCapabilityResponse, error) {
	var caps = &api.GetCapabilityResponse{
		Scope: driver.scope,
	}
	Log.Debugf("Get capabilities: responded with %+v", caps)
	return caps, nil
}

func (driver *driver) CreateNetwork(create *api.CreateNetworkRequest) error {
	Log.Debugf("Create network request %+v", create)
	Log.Infof("Create network %s", create.NetworkID)
	return nil
}

func (driver *driver) DeleteNetwork(delete *api.DeleteNetworkRequest) error {
	Log.Debugf("Delete network request: %+v", delete)
	Log.Infof("Destroy network %s", delete.NetworkID)
	return nil
}

func (driver *driver) CreateEndpoint(create *api.CreateEndpointRequest) (*api.CreateEndpointResponse, error) {
	Log.Debugf("Create endpoint request %+v interface %+v", create, create.Interface)
	endID := create.EndpointID

	if create.Interface == nil {
		return nil, fmt.Errorf("Not supported: creating an interface from within CreateEndpoint")
	}
	// create veths. note we assume endpoint IDs are unique in the first 8 chars
	local := vethPair(endID)
	if err := netlink.LinkAdd(local); err != nil {
		return nil, errorf("could not create veth pair: %s", err)
	}
	driver.Lock()
	driver.endpoints[endID] = struct{}{}
	driver.Unlock()

	// Send back the MAC address
	link, _ := netlink.LinkByName(local.PeerName)
	resp := &api.CreateEndpointResponse{Interface: &api.EndpointInterface{MacAddress: link.Attrs().HardwareAddr.String()}}

	Log.Infof("Create endpoint %s %+v", endID, resp)
	Log.Infof("Veth info %+v", local)
	return resp, nil
}

func (driver *driver) DeleteEndpoint(deleteReq *api.DeleteEndpointRequest) error {
	Log.Debugf("Delete endpoint request: %+v", deleteReq)
	Log.Infof("Delete endpoint %s", deleteReq.EndpointID)
	driver.Lock()
	delete(driver.endpoints, deleteReq.EndpointID)
	driver.Unlock()
	local := vethPair(deleteReq.EndpointID)
	if err := netlink.LinkDel(local); err != nil {
		Log.Warningf("unable to delete veth: %s", err)
	}
	return nil
}

func (driver *driver) HasEndpoint(endpointID string) bool {
	driver.Lock()
	_, found := driver.endpoints[endpointID]
	driver.Unlock()
	return found
}

func (driver *driver) EndpointInfo(req *api.EndpointInfoRequest) (*api.EndpointInfoResponse, error) {
	Log.Debugf("Endpoint info request: %+v", req)
	Log.Infof("Endpoint info %s", req.EndpointID)
	return &api.EndpointInfoResponse{Value: map[string]interface{}{}}, nil
}

func (driver *driver) JoinEndpoint(j *api.JoinRequest) (*api.JoinResponse, error) {
	endID := j.EndpointID

	maybeBridge, err := netlink.LinkByName(WeaveBridge)
	if err != nil {
		return nil, errorf(`bridge "%s" not present; did you launch weave?`, WeaveBridge)
	}

	local := vethPair(endID)
	if err = netlink.LinkSetMTU(local, maybeBridge.Attrs().MTU); err != nil {
		return nil, errorf(`unable to set mtu: %s`, err)
	}

	switch maybeBridge.(type) {
	case *netlink.Bridge:
		if err := netlink.LinkSetMasterByIndex(local, maybeBridge.Attrs().Index); err != nil {
			return nil, errorf(`unable to set master: %s`, err)
		}
	case *netlink.GenericLink:
		if maybeBridge.Type() != "openvswitch" {
			Log.Errorf("device %s is %+v", WeaveBridge, maybeBridge)
			return nil, errorf(`device "%s" is of type "%s"`, WeaveBridge, maybeBridge.Type())
		}
		odp.AddDatapathInterface(WeaveBridge, local.Name)
	case *netlink.Device:
		Log.Warnf("kernel does not report what kind of device %s is, just %+v", WeaveBridge, maybeBridge)
		// Assume it's our openvswitch device, and the kernel has not been updated to report the kind.
		odp.AddDatapathInterface(WeaveBridge, local.Name)
	default:
		Log.Errorf("device %s is %+v", WeaveBridge, maybeBridge)
		return nil, errorf(`device "%s" not a bridge`, WeaveBridge)
	}
	if err := netlink.LinkSetUp(local); err != nil {
		return nil, errorf(`unable to bring veth up: %s`, err)
	}

	ifname := &api.InterfaceName{
		SrcName:   local.PeerName,
		DstPrefix: "ethwe",
	}

	response := &api.JoinResponse{
		InterfaceName: ifname,
	}
	if driver.nameserver != "" {
		routeToDNS := api.StaticRoute{
			Destination: driver.nameserver + "/32",
			RouteType:   types.CONNECTED,
			NextHop:     "",
		}
		response.StaticRoutes = []api.StaticRoute{routeToDNS}
	}
	if !driver.noMulticastRoute {
		multicastRoute := api.StaticRoute{
			Destination: "224.0.0.0/4",
			RouteType:   types.CONNECTED,
		}
		response.StaticRoutes = append(response.StaticRoutes, multicastRoute)
	}
	Log.Infof("Join endpoint %s:%s to %s", j.NetworkID, j.EndpointID, j.SandboxKey)
	return response, nil
}

func (driver *driver) LeaveEndpoint(leave *api.LeaveRequest) error {
	Log.Debugf("Leave request: %+v", leave)
	Log.Infof("Leave %s:%s", leave.NetworkID, leave.EndpointID)
	return nil
}

func (driver *driver) DiscoverNew(disco *api.DiscoveryNotification) error {
	Log.Debugf("Dicovery new notification: %+v", disco)
	return nil
}

func (driver *driver) DiscoverDelete(disco *api.DiscoveryNotification) error {
	Log.Debugf("Dicovery delete notification: %+v", disco)
	return nil
}

// ===

func vethPair(endpointID string) *netlink.Veth {
	suffix := endpointID[:8]
	return &netlink.Veth{
		LinkAttrs: netlink.LinkAttrs{Name: "vethwl" + suffix},
		PeerName:  "vethwg" + suffix,
	}
}
<|endoftext|>


<commit_before>package bugsnag

import (
	"fmt"
	"reflect"
	"strings"
)

// MetaData is added to the Bugsnag dashboard in tabs. Each tab is
// a map of strings -> values. You can pass MetaData to Notify, Recover
// and AutoNotify as rawData.
type MetaData map[string]map[string]interface{}

// Update the meta-data with more information. Tabs are merged together such
// that unique keys from both sides are preserved, and duplicate keys end up
// with the provided values.
func (meta MetaData) Update(other MetaData) {
	for name, tab := range other {

		if meta[name] == nil {
			meta[name] = make(map[string]interface{})
		}

		for key, value := range tab {
			meta[name][key] = value
		}
	}
}

// Add creates a tab of Bugsnag meta-data.
// If the tab doesn't yet exist it will be created.
// If the key already exists, it will be overwritten.
func (meta MetaData) Add(tab string, key string, value interface{}) {
	if meta[tab] == nil {
		meta[tab] = make(map[string]interface{})
	}

	meta[tab][key] = value
}

// AddStruct creates a tab of Bugsnag meta-data.
// The struct will be converted to an Object using the
// reflect library so any private fields will not be exported.
// As a safety measure, if you pass a non-struct the value will be
// sent to Bugsnag under the "Extra data" tab.
func (meta MetaData) AddStruct(tab string, obj interface{}) {
	val := sanitizer{}.Sanitize(obj)
	content, ok := val.(map[string]interface{})
	if ok {
		meta[tab] = content
	} else {
		// Wasn't a struct
		meta.Add("Extra data", tab, obj)
	}

}

// Remove any values from meta-data that have keys matching the filters,
// and any that are recursive data-structures
func (meta MetaData) sanitize(filters []string) interface{} {
	return sanitizer{
		Filters: filters,
		Seen:    make([]interface{}, 0),
	}.Sanitize(meta)

}

// The sanitizer is used to remove filtered params and recursion from meta-data.
type sanitizer struct {
	Filters []string
	Seen    []interface{}
}

func (s sanitizer) Sanitize(data interface{}) interface{} {
	for _, s := range s.Seen {
		// TODO: we don't need deep equal here, just type-ignoring equality
		if reflect.DeepEqual(data, s) {
			return "[RECURSION]"
		}
	}

	// Sanitizers are passed by value, so we can modify s and it only affects
	// s.Seen for nested calls.
	s.Seen = append(s.Seen, data)

	t := reflect.TypeOf(data)
	v := reflect.ValueOf(data)

	switch t.Kind() {
	case reflect.Bool,
		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
		reflect.Float32, reflect.Float64:
		return data

	case reflect.String:
		return data

	case reflect.Interface, reflect.Ptr:
		return s.Sanitize(v.Elem().Interface())

	case reflect.Array, reflect.Slice:
		ret := make([]interface{}, v.Len())
		for i := 0; i < v.Len(); i++ {
			ret[i] = s.Sanitize(v.Index(i).Interface())
		}
		return ret

	case reflect.Map:
		return s.sanitizeMap(v)

	case reflect.Struct:
		return s.sanitizeStruct(v, t)

		// Things JSON can't serialize:
		// case t.Chan, t.Func, reflect.Complex64, reflect.Complex128, reflect.UnsafePointer:
	default:
		return "[" + t.String() + "]"

	}

}

func (s sanitizer) sanitizeMap(v reflect.Value) interface{} {
	ret := make(map[string]interface{})

	for _, key := range v.MapKeys() {
		val := s.Sanitize(v.MapIndex(key).Interface())
		newKey := fmt.Sprintf("%v", key.Interface())

		if s.shouldRedact(newKey) {
			val = "[REDACTED]"
		}

		ret[newKey] = val
	}

	return ret
}

func (s sanitizer) sanitizeStruct(v reflect.Value, t reflect.Type) interface{} {
	ret := make(map[string]interface{})

	for i := 0; i < v.NumField(); i++ {

		val := v.Field(i)
		// Don't export private fields
		if !val.CanInterface() {
			continue
		}

		name := t.Field(i).Name
		var opts tagOptions

		// Parse JSON tags. Supports name and "omitempty"
		if jsonTag := t.Field(i).Tag.Get("json"); len(jsonTag) != 0 {
			name, opts = parseTag(jsonTag)
		}

		if s.shouldRedact(name) {
			ret[name] = "[REDACTED]"
		} else {
			sanitized := s.Sanitize(val.Interface())
			if str, ok := sanitized.(string); ok {
				if !(opts.Contains("omitempty") && len(str) == 0) {
					ret[name] = str
				}
			} else {
				ret[name] = sanitized
			}

		}
	}

	return ret
}

func (s sanitizer) shouldRedact(key string) bool {
	for _, filter := range s.Filters {
		if strings.Contains(strings.ToLower(filter), strings.ToLower(key)) {
			return true
		}
	}
	return false
}
<commit_msg>Fixed Sanitize when data is nil<commit_after>package bugsnag

import (
	"fmt"
	"reflect"
	"strings"
)

// MetaData is added to the Bugsnag dashboard in tabs. Each tab is
// a map of strings -> values. You can pass MetaData to Notify, Recover
// and AutoNotify as rawData.
type MetaData map[string]map[string]interface{}

// Update the meta-data with more information. Tabs are merged together such
// that unique keys from both sides are preserved, and duplicate keys end up
// with the provided values.
func (meta MetaData) Update(other MetaData) {
	for name, tab := range other {

		if meta[name] == nil {
			meta[name] = make(map[string]interface{})
		}

		for key, value := range tab {
			meta[name][key] = value
		}
	}
}

// Add creates a tab of Bugsnag meta-data.
// If the tab doesn't yet exist it will be created.
// If the key already exists, it will be overwritten.
func (meta MetaData) Add(tab string, key string, value interface{}) {
	if meta[tab] == nil {
		meta[tab] = make(map[string]interface{})
	}

	meta[tab][key] = value
}

// AddStruct creates a tab of Bugsnag meta-data.
// The struct will be converted to an Object using the
// reflect library so any private fields will not be exported.
// As a safety measure, if you pass a non-struct the value will be
// sent to Bugsnag under the "Extra data" tab.
func (meta MetaData) AddStruct(tab string, obj interface{}) {
	val := sanitizer{}.Sanitize(obj)
	content, ok := val.(map[string]interface{})
	if ok {
		meta[tab] = content
	} else {
		// Wasn't a struct
		meta.Add("Extra data", tab, obj)
	}

}

// Remove any values from meta-data that have keys matching the filters,
// and any that are recursive data-structures
func (meta MetaData) sanitize(filters []string) interface{} {
	return sanitizer{
		Filters: filters,
		Seen:    make([]interface{}, 0),
	}.Sanitize(meta)

}

// The sanitizer is used to remove filtered params and recursion from meta-data.
type sanitizer struct {
	Filters []string
	Seen    []interface{}
}

func (s sanitizer) Sanitize(data interface{}) interface{} {
	for _, s := range s.Seen {
		// TODO: we don't need deep equal here, just type-ignoring equality
		if reflect.DeepEqual(data, s) {
			return "[RECURSION]"
		}
	}

	// Sanitizers are passed by value, so we can modify s and it only affects
	// s.Seen for nested calls.
	s.Seen = append(s.Seen, data)

	t := reflect.TypeOf(data)
	v := reflect.ValueOf(data)

	if t == nil {
		return "<nil>"
	}

	switch t.Kind() {
	case reflect.Bool,
		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
		reflect.Float32, reflect.Float64:
		return data

	case reflect.String:
		return data

	case reflect.Interface, reflect.Ptr:
		return s.Sanitize(v.Elem().Interface())

	case reflect.Array, reflect.Slice:
		ret := make([]interface{}, v.Len())
		for i := 0; i < v.Len(); i++ {
			ret[i] = s.Sanitize(v.Index(i).Interface())
		}
		return ret

	case reflect.Map:
		return s.sanitizeMap(v)

	case reflect.Struct:
		return s.sanitizeStruct(v, t)

		// Things JSON can't serialize:
		// case t.Chan, t.Func, reflect.Complex64, reflect.Complex128, reflect.UnsafePointer:
	default:
		return "[" + t.String() + "]"

	}

}

func (s sanitizer) sanitizeMap(v reflect.Value) interface{} {
	ret := make(map[string]interface{})

	for _, key := range v.MapKeys() {
		val := s.Sanitize(v.MapIndex(key).Interface())
		newKey := fmt.Sprintf("%v", key.Interface())

		if s.shouldRedact(newKey) {
			val = "[REDACTED]"
		}

		ret[newKey] = val
	}

	return ret
}

func (s sanitizer) sanitizeStruct(v reflect.Value, t reflect.Type) interface{} {
	ret := make(map[string]interface{})

	for i := 0; i < v.NumField(); i++ {

		val := v.Field(i)
		// Don't export private fields
		if !val.CanInterface() {
			continue
		}

		name := t.Field(i).Name
		var opts tagOptions

		// Parse JSON tags. Supports name and "omitempty"
		if jsonTag := t.Field(i).Tag.Get("json"); len(jsonTag) != 0 {
			name, opts = parseTag(jsonTag)
		}

		if s.shouldRedact(name) {
			ret[name] = "[REDACTED]"
		} else {
			sanitized := s.Sanitize(val.Interface())
			if str, ok := sanitized.(string); ok {
				if !(opts.Contains("omitempty") && len(str) == 0) {
					ret[name] = str
				}
			} else {
				ret[name] = sanitized
			}

		}
	}

	return ret
}

func (s sanitizer) shouldRedact(key string) bool {
	for _, filter := range s.Filters {
		if strings.Contains(strings.ToLower(filter), strings.ToLower(key)) {
			return true
		}
	}
	return false
}
<|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 openstack

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"github.com/golang/glog"
	"k8s.io/kubernetes/pkg/util/exec"
	"k8s.io/kubernetes/pkg/util/mount"
)

// metadataUrl is URL to OpenStack metadata server. It's hardcoded IPv4
// link-local address as documented in "OpenStack Cloud Administrator Guide",
// chapter Compute - Networking with nova-network.
// http://docs.openstack.org/admin-guide-cloud/compute-networking-nova.html#metadata-service
const metadataUrl = "http://169.254.169.254/openstack/2012-08-10/meta_data.json"

// Config drive is defined as an iso9660 or vfat (deprecated) drive
// with the "config-2" label.
// http://docs.openstack.org/user-guide/cli-config-drive.html
const configDriveLabel = "config-2"
const configDrivePath = "openstack/2012-08-10/meta_data.json"

var ErrBadMetadata = errors.New("Invalid OpenStack metadata, got empty uuid")

// Assumes the "2012-08-10" meta_data.json format.
// See http://docs.openstack.org/user-guide/cli_config_drive.html
type Metadata struct {
	Uuid             string `json:"uuid"`
	Name             string `json:"name"`
	AvailabilityZone string `json:"availability_zone"`
	// .. and other fields we don't care about.  Expand as necessary.
}

// parseMetadataUUID reads JSON from OpenStack metadata server and parses
// instance ID out of it.
func parseMetadata(r io.Reader) (*Metadata, error) {
	var metadata Metadata
	json := json.NewDecoder(r)
	if err := json.Decode(&metadata); err != nil {
		return nil, err
	}

	if metadata.Uuid == "" {
		return nil, ErrBadMetadata
	}

	return &metadata, nil
}

func getMetadataFromConfigDrive() (*Metadata, error) {
	// Try to read instance UUID from config drive.
	dev := "/dev/disk/by-label/" + configDriveLabel
	if _, err := os.Stat(dev); os.IsNotExist(err) {
		out, err := exec.New().Command(
			"blkid", "-l",
			"-t", "LABEL="+configDriveLabel,
			"-o", "device",
		).CombinedOutput()
		if err != nil {
			glog.V(2).Infof("Unable to run blkid: %v", err)
			return nil, err
		}
		dev = strings.TrimSpace(string(out))
	}

	mntdir, err := ioutil.TempDir("", "configdrive")
	if err != nil {
		return nil, err
	}
	defer os.Remove(mntdir)

	glog.V(4).Infof("Attempting to mount configdrive %s on %s", dev, mntdir)

	mounter := mount.New("" /* default mount path */)
	err = mounter.Mount(dev, mntdir, "iso9660", []string{"ro"})
	if err != nil {
		err = mounter.Mount(dev, mntdir, "vfat", []string{"ro"})
	}
	if err != nil {
		glog.Errorf("Error mounting configdrive %s: %v", dev, err)
		return nil, err
	}
	defer mounter.Unmount(mntdir)

	glog.V(4).Infof("Configdrive mounted on %s", mntdir)

	f, err := os.Open(
		filepath.Join(mntdir, configDrivePath))
	if err != nil {
		glog.Errorf("Error reading %s on config drive: %v", configDrivePath, err)
		return nil, err
	}
	defer f.Close()

	return parseMetadata(f)
}

func getMetadataFromMetadataService() (*Metadata, error) {
	// Try to get JSON from metdata server.
	glog.V(4).Infof("Attempting to fetch metadata from %s", metadataUrl)
	resp, err := http.Get(metadataUrl)
	if err != nil {
		glog.V(3).Infof("Cannot read %s: %v", metadataUrl, err)
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		err = fmt.Errorf("Unexpected status code when reading metadata from %s: %s", metadataUrl, resp.Status)
		glog.V(3).Infof("%v", err)
		return nil, err
	}

	return parseMetadata(resp.Body)
}

// Metadata is fixed for the current host, so cache the value process-wide
var metadataCache *Metadata

func getMetadata() (*Metadata, error) {
	if metadataCache == nil {
		md, err := getMetadataFromConfigDrive()
		if err != nil {
			md, err = getMetadataFromMetadataService()
		}
		if err != nil {
			return nil, err
		}
		metadataCache = md
	}
	return metadataCache, nil
}
<commit_msg>update openstack metadata-service url<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 openstack

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"github.com/golang/glog"
	"k8s.io/kubernetes/pkg/util/exec"
	"k8s.io/kubernetes/pkg/util/mount"
)

// metadataUrl is URL to OpenStack metadata server. It's hardcoded IPv4
// link-local address as documented in "OpenStack Cloud Administrator Guide",
// chapter Compute - Networking with nova-network.
// https://docs.openstack.org/admin-guide/compute-networking-nova.html#metadata-service
const metadataUrl = "http://169.254.169.254/openstack/2012-08-10/meta_data.json"

// Config drive is defined as an iso9660 or vfat (deprecated) drive
// with the "config-2" label.
// http://docs.openstack.org/user-guide/cli-config-drive.html
const configDriveLabel = "config-2"
const configDrivePath = "openstack/2012-08-10/meta_data.json"

var ErrBadMetadata = errors.New("Invalid OpenStack metadata, got empty uuid")

// Assumes the "2012-08-10" meta_data.json format.
// See http://docs.openstack.org/user-guide/cli_config_drive.html
type Metadata struct {
	Uuid             string `json:"uuid"`
	Name             string `json:"name"`
	AvailabilityZone string `json:"availability_zone"`
	// .. and other fields we don't care about.  Expand as necessary.
}

// parseMetadataUUID reads JSON from OpenStack metadata server and parses
// instance ID out of it.
func parseMetadata(r io.Reader) (*Metadata, error) {
	var metadata Metadata
	json := json.NewDecoder(r)
	if err := json.Decode(&metadata); err != nil {
		return nil, err
	}

	if metadata.Uuid == "" {
		return nil, ErrBadMetadata
	}

	return &metadata, nil
}

func getMetadataFromConfigDrive() (*Metadata, error) {
	// Try to read instance UUID from config drive.
	dev := "/dev/disk/by-label/" + configDriveLabel
	if _, err := os.Stat(dev); os.IsNotExist(err) {
		out, err := exec.New().Command(
			"blkid", "-l",
			"-t", "LABEL="+configDriveLabel,
			"-o", "device",
		).CombinedOutput()
		if err != nil {
			glog.V(2).Infof("Unable to run blkid: %v", err)
			return nil, err
		}
		dev = strings.TrimSpace(string(out))
	}

	mntdir, err := ioutil.TempDir("", "configdrive")
	if err != nil {
		return nil, err
	}
	defer os.Remove(mntdir)

	glog.V(4).Infof("Attempting to mount configdrive %s on %s", dev, mntdir)

	mounter := mount.New("" /* default mount path */)
	err = mounter.Mount(dev, mntdir, "iso9660", []string{"ro"})
	if err != nil {
		err = mounter.Mount(dev, mntdir, "vfat", []string{"ro"})
	}
	if err != nil {
		glog.Errorf("Error mounting configdrive %s: %v", dev, err)
		return nil, err
	}
	defer mounter.Unmount(mntdir)

	glog.V(4).Infof("Configdrive mounted on %s", mntdir)

	f, err := os.Open(
		filepath.Join(mntdir, configDrivePath))
	if err != nil {
		glog.Errorf("Error reading %s on config drive: %v", configDrivePath, err)
		return nil, err
	}
	defer f.Close()

	return parseMetadata(f)
}

func getMetadataFromMetadataService() (*Metadata, error) {
	// Try to get JSON from metdata server.
	glog.V(4).Infof("Attempting to fetch metadata from %s", metadataUrl)
	resp, err := http.Get(metadataUrl)
	if err != nil {
		glog.V(3).Infof("Cannot read %s: %v", metadataUrl, err)
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		err = fmt.Errorf("Unexpected status code when reading metadata from %s: %s", metadataUrl, resp.Status)
		glog.V(3).Infof("%v", err)
		return nil, err
	}

	return parseMetadata(resp.Body)
}

// Metadata is fixed for the current host, so cache the value process-wide
var metadataCache *Metadata

func getMetadata() (*Metadata, error) {
	if metadataCache == nil {
		md, err := getMetadataFromConfigDrive()
		if err != nil {
			md, err = getMetadataFromMetadataService()
		}
		if err != nil {
			return nil, err
		}
		metadataCache = md
	}
	return metadataCache, nil
}
<|endoftext|>


<commit_before>// this file contains a pre-processor to pull some stuff out of the markdown file before parsing it

package gnosis

import (
	"bufio"
	"bytes"
	"errors"
	"html/template"
	"io"
	//"log"
	"os"
)

type PageMetadata struct {
	Keywords map[string]bool
	Topics   map[string]bool
	Page     []byte
}

// takes a single line of input and determines if it's a top level markdown header
func (pdata *PageMetadata) lineIsTitle(line []byte) bool {
	// trim any whitespace from the start and the end of the line
	line = bytes.TrimSpace(line)

	// run through all of the ='s - make sure they're all correct
	for i := 0; i < len(line); i++ {
		if line[i] != '=' {
			return false
		}
	}

	// if you got here, it should all be legit
	return true
}

// take a given line, and check it against every possible type of tag
func (pdata *PageMetadata) processMetadata(line []byte) {
	pdata.checkMatch(line, []byte("tag"), &pdata.Topics)
	pdata.checkMatch(line, []byte("topic"), &pdata.Topics)
	pdata.checkMatch(line, []byte("category"), &pdata.Topics)

	pdata.checkMatch(line, []byte("keyword"), &pdata.Keywords)
	pdata.checkMatch(line, []byte("keywords"), &pdata.Keywords)
	pdata.checkMatch(line, []byte("meta"), &pdata.Keywords)
}

func (pdata *PageMetadata) checkMatch(input []byte, looking []byte, tracker *map[string]bool) {
	// trim off any blank spaces at the start of the line
	value := bytes.TrimSpace(input)

	// should be a substring match based on the start of the array
	if bytes.Equal(input[:len(looking)], looking) {

		// trim off the target from the []byte
		value = input[len(looking):]

		// trim spaces at the start and at the end
		value = bytes.TrimSpace(value)

		if value[0] == ':' || value[0] == '=' {
			value = bytes.Trim(value, " \t\n=:")
		}

		// replace any spaces in the middle with -'s
		value = bytes.Replace(value, []byte(" "), []byte("-"), -1)

		// suppress any double dashes
		for i := 0; i < len(value)-1; i++ {
			if value[i] == '-' && value[i+1] == '-' {
				value = append(value[:i], value[i+1:]...)
			}
		}

		// now just add the value to the array that you're tracking
		if *tracker != nil {
			(*tracker)[string(value)] = true
		} else {
			*tracker = map[string]bool{string(value): true}
		}
	}
}

func (pdata *PageMetadata) readRestOfPage(topLine []byte, bottomLine []byte, r *bufio.Reader) error {
	// read the rest of the page
	var restOfPage []byte
	var err error

	// put the start of stuff into the final destination
	pdata.Page = bytes.Join([][]byte{topLine, bottomLine, []byte("")}, []byte(""))

	for err == nil {
		// read a line, and then add it to pdata
		restOfPage, err = r.ReadBytes('\n')
		pdata.Page = append(pdata.Page, restOfPage...)
	}

	if err == io.EOF {
		return nil
	} else if err != nil {
		return err
	} else {
		return errors.New("I should have an error - why don't i have an error?")
	}
}

func (pdata *PageMetadata) LoadPage(pageName string) error {
	// open the file
	f, err := os.Open(pageName)
	reader := bufio.NewReader(f)
	if err != nil {
		return err
	}

	// read a line
	upperLine, err := reader.ReadBytes(byte('\n'))

	// check the first line you read
	if err == io.EOF {
		return errors.New("I only read in... one line?")
	} else if err != nil {
		return errors.New("first line error - " + err.Error())
	} else if pdata.lineIsTitle(upperLine) {
		return errors.New("first line looks an awful lot like the underside of the title o.O")
	}

	// read a second line - this might actually be a real line
	lowerLine, err := reader.ReadBytes('\n')
	// inspect the lower line
	if err == io.EOF {
		return errors.New("Second line is the underline of the title... is this page just a title?")
	} else if err != nil {
		return errors.New("secont line error - " + err.Error())
	} else if pdata.lineIsTitle(lowerLine) {
		return pdata.readRestOfPage(upperLine, lowerLine, reader)
	}

	// if you're at this point, the first line is metadata
	// you gotta process it and work with the next line
	for !pdata.lineIsTitle(lowerLine) && err != io.EOF {
		// process the line
		pdata.processMetadata(upperLine)
		// shift the lower line up
		upperLine = lowerLine
		// read in a new lower line
		lowerLine, err = reader.ReadBytes('\n')
		if err == io.EOF {
			return errors.New("never hit a title")
		} else if err != nil {
			return err
		}
	}

	// by this point, I should have read everything in - let's read the rest and just return it
	return pdata.readRestOfPage(upperLine, lowerLine, reader)
}

// returns all the tags within a list as an array of strings
func (pdata *PageMetadata) ListMeta() ([]string, []string) {
	topics := []string{}
	for oneTag, _ := range pdata.Topics {
		topics = append(topics[:], oneTag)
	}

	keywords := []string{}
	for oneKeyword, _ := range pdata.Keywords {
		keywords = append(keywords[:], oneKeyword)
	}

	return topics, keywords
}

// return the bytes to display the tags on the page
// takes the prefix for the tags
func (pdata *PageMetadata) PrintTopics(tagPrefix string) template.HTML {
	response := []byte{}
	openingTag := []byte("
")
	closingTag := []byte("
")
	for oneTag, _ := range pdata.Topics {
		response = bytes.Join([][]byte{openingTag, []byte(tagPrefix), []byte(oneTag), closingTag}, []byte(""))
	}
	return template.HTML(response)
}

// returns the bytes to add the keywrods to the html output
func (pdata *PageMetadata) PrintKeywords() template.HTML {
	response := []byte("<meta name='keywords' content='")
	for oneKeyword, _ := range pdata.Keywords {
		response = bytes.Join([][]byte{response, []byte(oneKeyword)}, []byte(","))
	}
	// clean up the end of the string and add the ending tag
	response = bytes.TrimSuffix(response, []byte{','})
	response = append(response, []byte("'>")...)

	return template.HTML(response)
}

// runs through all restricted tags, and looks for a match
// if matched, returns true, otherwise false
func (pdata *PageMetadata) MatchedTag(checkTags []string) bool {
	for _, tag := range checkTags {
		if pdata.Topics[tag] == true {
			return true
		}
	}
	return false
}
<commit_msg>changed an error message - shortening it<commit_after>// this file contains a pre-processor to pull some stuff out of the markdown file before parsing it

package gnosis

import (
	"bufio"
	"bytes"
	"errors"
	"html/template"
	"io"
	//"log"
	"os"
)

type PageMetadata struct {
	Keywords map[string]bool
	Topics   map[string]bool
	Page     []byte
}

// takes a single line of input and determines if it's a top level markdown header
func (pdata *PageMetadata) lineIsTitle(line []byte) bool {
	// trim any whitespace from the start and the end of the line
	line = bytes.TrimSpace(line)

	// run through all of the ='s - make sure they're all correct
	for i := 0; i < len(line); i++ {
		if line[i] != '=' {
			return false
		}
	}

	// if you got here, it should all be legit
	return true
}

// take a given line, and check it against every possible type of tag
func (pdata *PageMetadata) processMetadata(line []byte) {
	pdata.checkMatch(line, []byte("tag"), &pdata.Topics)
	pdata.checkMatch(line, []byte("topic"), &pdata.Topics)
	pdata.checkMatch(line, []byte("category"), &pdata.Topics)

	pdata.checkMatch(line, []byte("keyword"), &pdata.Keywords)
	pdata.checkMatch(line, []byte("keywords"), &pdata.Keywords)
	pdata.checkMatch(line, []byte("meta"), &pdata.Keywords)
}

func (pdata *PageMetadata) checkMatch(input []byte, looking []byte, tracker *map[string]bool) {
	// trim off any blank spaces at the start of the line
	value := bytes.TrimSpace(input)

	// should be a substring match based on the start of the array
	if len(input) > len(looking) && bytes.Equal(input[:len(looking)], looking) {

		// trim off the target from the []byte
		value = input[len(looking):]

		// trim spaces at the start and at the end
		value = bytes.TrimSpace(value)

		if value[0] == ':' || value[0] == '=' {
			value = bytes.Trim(value, " \t\n=:")
		}

		// replace any spaces in the middle with -'s
		value = bytes.Replace(value, []byte(" "), []byte("-"), -1)

		// suppress any double dashes
		for i := 0; i < len(value)-1; i++ {
			if value[i] == '-' && value[i+1] == '-' {
				value = append(value[:i], value[i+1:]...)
			}
		}

		// now just add the value to the array that you're tracking
		if *tracker != nil {
			(*tracker)[string(value)] = true
		} else {
			*tracker = map[string]bool{string(value): true}
		}
	}
}

func (pdata *PageMetadata) readRestOfPage(topLine []byte, bottomLine []byte, r *bufio.Reader) error {
	// read the rest of the page
	var restOfPage []byte
	var err error

	// put the start of stuff into the final destination
	pdata.Page = bytes.Join([][]byte{topLine, bottomLine, []byte("")}, []byte(""))

	for err == nil {
		// read a line, and then add it to pdata
		restOfPage, err = r.ReadBytes('\n')
		pdata.Page = append(pdata.Page, restOfPage...)
	}

	if err == io.EOF {
		return nil
	} else if err != nil {
		return err
	} else {
		return errors.New("I should have an error - why don't i have an error?")
	}
}

func (pdata *PageMetadata) LoadPage(pageName string) error {
	// open the file
	f, err := os.Open(pageName)
	reader := bufio.NewReader(f)
	if err != nil {
		return err
	}

	// read a line
	upperLine, err := reader.ReadBytes(byte('\n'))

	// check the first line you read
	if err == io.EOF {
		return errors.New("I only read in... one line?")
	} else if err != nil {
		return errors.New("first line error - " + err.Error())
	}

	// read a second line - this might actually be a real line
	lowerLine, err := reader.ReadBytes('\n')
	// inspect the lower line
	if err == io.EOF {
		return errors.New("Is this page just a title?")
	} else if err != nil {
		return errors.New("secont line error - " + err.Error())
	} else if pdata.lineIsTitle(lowerLine) {
		return pdata.readRestOfPage(upperLine, lowerLine, reader)
	}

	// if you're at this point, the first line is metadata
	// you gotta process it and work with the next line
	for !pdata.lineIsTitle(lowerLine) && err != io.EOF {
		// process the line
		pdata.processMetadata(upperLine)
		// shift the lower line up
		upperLine = lowerLine
		// read in a new lower line
		lowerLine, err = reader.ReadBytes('\n')
		if err == io.EOF {
			return errors.New("never hit a title")
		} else if err != nil {
			return err
		}
	}

	// by this point, I should have read everything in - let's read the rest and just return it
	return pdata.readRestOfPage(upperLine, lowerLine, reader)
}

// returns all the tags within a list as an array of strings
func (pdata *PageMetadata) ListMeta() ([]string, []string) {
	topics := []string{}
	for oneTag, _ := range pdata.Topics {
		topics = append(topics[:], oneTag)
	}

	keywords := []string{}
	for oneKeyword, _ := range pdata.Keywords {
		keywords = append(keywords[:], oneKeyword)
	}

	return topics, keywords
}

// return the bytes to display the tags on the page
// takes the prefix for the tags
func (pdata *PageMetadata) PrintTopics(tagPrefix string) template.HTML {
	response := []byte{}
	openingTag := []byte("
")
	closingTag := []byte("
")
	for oneTag, _ := range pdata.Topics {
		response = bytes.Join([][]byte{openingTag, []byte(tagPrefix), []byte(oneTag), closingTag}, []byte(""))
	}
	return template.HTML(response)
}

// returns the bytes to add the keywrods to the html output
func (pdata *PageMetadata) PrintKeywords() template.HTML {
	response := []byte("<meta name='keywords' content='")
	for oneKeyword, _ := range pdata.Keywords {
		response = bytes.Join([][]byte{response, []byte(oneKeyword)}, []byte(","))
	}
	// clean up the end of the string and add the ending tag
	response = bytes.TrimSuffix(response, []byte{','})
	response = append(response, []byte("'>")...)

	return template.HTML(response)
}

// runs through all restricted tags, and looks for a match
// if matched, returns true, otherwise false
func (pdata *PageMetadata) MatchedTag(checkTags []string) bool {
	for _, tag := range checkTags {
		if pdata.Topics[tag] == true {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package model_test

import (
	"encoding/json"
	"github.com/crezam/actions-on-google-golang/internal/test"
	"github.com/crezam/actions-on-google-golang/model"
	"os"
	"testing"
	"time"
)

func TestRequest1Parsing(t *testing.T) {

	var req model.ApiAiRequest

	file, _ := os.Open("./data/sample_request1.json")
	dec := json.NewDecoder(file)

	err := dec.Decode(&req)

	// test if any issues decoding file
	test.Ok(t, err)

	// assert correct parsing
	test.Equals(t, "209eefa7-adb5-4d03-a8b9-9f7ae68a0c11", req.Id)

	expectedTimestamp, _ := time.Parse(time.RFC3339Nano, "2016-10-10T07:41:40.098Z")
	test.Equals(t, expectedTimestamp, req.Timestamp)

	test.Equals(t, "Hi, my name is Sam!", req.Result.ResolvedQuery)
	test.Equals(t, "agent", req.Result.Source)
	test.Equals(t, "greetings", req.Result.Action)
	test.Equals(t, false, req.Result.ActionIncomplete)
	test.Equals(t, "Sam", req.Result.Parameters["user_name"])
	test.Equals(t, "", req.Result.Parameters["school"])

	test.Equals(t, "greetings", req.Result.Contexts[0].Name)
	test.Equals(t, "Sam", req.Result.Contexts[0].Parameters["user_name"])
	test.Equals(t, "Sam!", req.Result.Contexts[0].Parameters["user_name.original"])

	test.Equals(t, "373a354b-c15a-4a60-ac9d-a9f2aee76cb4", req.Result.Metadata.IntentID)
	test.Equals(t, "true", req.Result.Metadata.WebhookUsed)
	test.Equals(t, "greetings", req.Result.Metadata.IntentName)

	test.Equals(t, "Nice to meet you, Sam!", req.Result.Fulfillment.Speech)

	test.Equals(t, float64(1), req.Result.Score)

	test.Equals(t, "...", req.OriginalRequest.Data.User.UserID)
	test.Equals(t, "Sam", req.OriginalRequest.Data.User.Profile.DisplayName)
	test.Equals(t, "Sam", req.OriginalRequest.Data.User.Profile.GivenName)
	test.Equals(t, "Johnson", req.OriginalRequest.Data.User.Profile.FamilyName)

	test.Equals(t, "...", req.OriginalRequest.Data.User.AccessToken)

	test.Equals(t, 123.456, req.OriginalRequest.Data.Device.Location.Coordinates.Latitude)
	test.Equals(t, -123.456, req.OriginalRequest.Data.Device.Location.Coordinates.Longitude)

	test.Equals(t, "1234 Random Road, Anytown, CA 12345, United States", req.OriginalRequest.Data.Device.Location.FormattedAddress)
	test.Equals(t, "12345", req.OriginalRequest.Data.Device.Location.ZipCode)
	test.Equals(t, "Anytown", req.OriginalRequest.Data.Device.Location.City)

	test.Equals(t, 200, req.Status.Code)
	test.Equals(t, "success", req.Status.ErrorType)

	test.Equals(t, "37151f7c-a409-48b8-9890-cd980cd2548e", req.SessionID)
}

func TestRequest2Parsing(t *testing.T) {

	var req model.ApiAiRequest

	file, _ := os.Open("./data/sample_request2.json")
	dec := json.NewDecoder(file)

	err := dec.Decode(&req)

	// test if any issues decoding file
	test.Ok(t, err)

	// assert correct parsing
	test.Equals(t, "f4b72ee9-cabb-4acd-af9b-2d2cb6ff53d2", req.Id)

	expectedTimestamp, _ := time.Parse(time.RFC3339Nano, "2016-12-24T07:23:46.64Z")
	test.Equals(t, expectedTimestamp, req.Timestamp)

	test.Equals(t, "i wonder if you are ready for me?", req.Result.ResolvedQuery)
	test.Equals(t, "agent", req.Result.Source)
	test.Equals(t, "get_status", req.Result.Action)
	test.Equals(t, false, req.Result.ActionIncomplete)
	test.Equals(t, map[string]string{}, req.Result.Parameters)

	//TODO test Context
	//test.Equals(t, []interface{}, req.Result.Contexts)

	test.Equals(t, "dd7f8d83-6f10-474f-b16c-d3dd3d071730", req.Result.Metadata.IntentID)
	test.Equals(t, "true", req.Result.Metadata.WebhookUsed)
	test.Equals(t, "false", req.Result.Metadata.WebhookForSlotFillingUsed)
	test.Equals(t, "get_status", req.Result.Metadata.IntentName)

	test.Equals(t, "", req.Result.Fulfillment.Speech)
	test.Equals(t, 0, req.Result.Fulfillment.Messages[0].Type)
	test.Equals(t, "", req.Result.Fulfillment.Messages[0].Speech)
	test.Equals(t, 0.31, req.Result.Score)

	test.Equals(t, 200, req.Status.Code)
	test.Equals(t, "success", req.Status.ErrorType)

	test.Equals(t, "1482564192341", req.SessionID)

	test.Equals(t, "google", req.OriginalRequest.Source)
	test.Equals(t, "i wonder if you are ready for me?", req.OriginalRequest.Data.Inputs[0].Arguments[0].RawText)
	test.Equals(t, "i wonder if you are ready for me?", req.OriginalRequest.Data.Inputs[0].Arguments[0].TextValue)
	test.Equals(t, "text", req.OriginalRequest.Data.Inputs[0].Arguments[0].Name)
	test.Equals(t, "assistant.intent.action.TEXT", req.OriginalRequest.Data.Inputs[0].Intent)
	test.Equals(t, "i wonder if you are ready for me?", req.OriginalRequest.Data.Inputs[0].RawInputs[0].Query)
	test.Equals(t, 2, req.OriginalRequest.Data.Inputs[0].RawInputs[0].InputType)

	test.Equals(t, "qGWZsN9WDIL6x8VffNu3YNacqN4FWnyT+8uEA+GcWXc=", req.OriginalRequest.Data.User.UserID)
	test.Equals(t, "1482564192341", req.OriginalRequest.Data.Conversation.ConversationID)
	test.Equals(t, 2, req.OriginalRequest.Data.Conversation.Type)

}

<commit_msg>Todo conversation token<commit_after>package model_test

import (
	"encoding/json"
	"github.com/crezam/actions-on-google-golang/internal/test"
	"github.com/crezam/actions-on-google-golang/model"
	"os"
	"testing"
	"time"
)

func TestRequest1Parsing(t *testing.T) {

	var req model.ApiAiRequest

	file, _ := os.Open("./data/sample_request1.json")
	dec := json.NewDecoder(file)

	err := dec.Decode(&req)

	// test if any issues decoding file
	test.Ok(t, err)

	// assert correct parsing
	test.Equals(t, "209eefa7-adb5-4d03-a8b9-9f7ae68a0c11", req.Id)

	expectedTimestamp, _ := time.Parse(time.RFC3339Nano, "2016-10-10T07:41:40.098Z")
	test.Equals(t, expectedTimestamp, req.Timestamp)

	test.Equals(t, "Hi, my name is Sam!", req.Result.ResolvedQuery)
	test.Equals(t, "agent", req.Result.Source)
	test.Equals(t, "greetings", req.Result.Action)
	test.Equals(t, false, req.Result.ActionIncomplete)
	test.Equals(t, "Sam", req.Result.Parameters["user_name"])
	test.Equals(t, "", req.Result.Parameters["school"])

	test.Equals(t, "greetings", req.Result.Contexts[0].Name)
	test.Equals(t, "Sam", req.Result.Contexts[0].Parameters["user_name"])
	test.Equals(t, "Sam!", req.Result.Contexts[0].Parameters["user_name.original"])

	test.Equals(t, "373a354b-c15a-4a60-ac9d-a9f2aee76cb4", req.Result.Metadata.IntentID)
	test.Equals(t, "true", req.Result.Metadata.WebhookUsed)
	test.Equals(t, "greetings", req.Result.Metadata.IntentName)

	test.Equals(t, "Nice to meet you, Sam!", req.Result.Fulfillment.Speech)

	test.Equals(t, float64(1), req.Result.Score)

	test.Equals(t, "...", req.OriginalRequest.Data.User.UserID)
	test.Equals(t, "Sam", req.OriginalRequest.Data.User.Profile.DisplayName)
	test.Equals(t, "Sam", req.OriginalRequest.Data.User.Profile.GivenName)
	test.Equals(t, "Johnson", req.OriginalRequest.Data.User.Profile.FamilyName)

	test.Equals(t, "...", req.OriginalRequest.Data.User.AccessToken)

	test.Equals(t, 123.456, req.OriginalRequest.Data.Device.Location.Coordinates.Latitude)
	test.Equals(t, -123.456, req.OriginalRequest.Data.Device.Location.Coordinates.Longitude)

	test.Equals(t, "1234 Random Road, Anytown, CA 12345, United States", req.OriginalRequest.Data.Device.Location.FormattedAddress)
	test.Equals(t, "12345", req.OriginalRequest.Data.Device.Location.ZipCode)
	test.Equals(t, "Anytown", req.OriginalRequest.Data.Device.Location.City)

	test.Equals(t, 200, req.Status.Code)
	test.Equals(t, "success", req.Status.ErrorType)

	test.Equals(t, "37151f7c-a409-48b8-9890-cd980cd2548e", req.SessionID)
}

func TestRequest2Parsing(t *testing.T) {

	var req model.ApiAiRequest

	file, _ := os.Open("./data/sample_request2.json")
	dec := json.NewDecoder(file)

	err := dec.Decode(&req)

	// test if any issues decoding file
	test.Ok(t, err)

	// assert correct parsing
	test.Equals(t, "f4b72ee9-cabb-4acd-af9b-2d2cb6ff53d2", req.Id)

	expectedTimestamp, _ := time.Parse(time.RFC3339Nano, "2016-12-24T07:23:46.64Z")
	test.Equals(t, expectedTimestamp, req.Timestamp)

	test.Equals(t, "i wonder if you are ready for me?", req.Result.ResolvedQuery)
	test.Equals(t, "agent", req.Result.Source)
	test.Equals(t, "get_status", req.Result.Action)
	test.Equals(t, false, req.Result.ActionIncomplete)
	test.Equals(t, map[string]string{}, req.Result.Parameters)

	//TODO test Context
	//test.Equals(t, []interface{}, req.Result.Contexts)

	test.Equals(t, "dd7f8d83-6f10-474f-b16c-d3dd3d071730", req.Result.Metadata.IntentID)
	test.Equals(t, "true", req.Result.Metadata.WebhookUsed)
	test.Equals(t, "false", req.Result.Metadata.WebhookForSlotFillingUsed)
	test.Equals(t, "get_status", req.Result.Metadata.IntentName)

	test.Equals(t, "", req.Result.Fulfillment.Speech)
	test.Equals(t, 0, req.Result.Fulfillment.Messages[0].Type)
	test.Equals(t, "", req.Result.Fulfillment.Messages[0].Speech)
	test.Equals(t, 0.31, req.Result.Score)

	test.Equals(t, 200, req.Status.Code)
	test.Equals(t, "success", req.Status.ErrorType)

	test.Equals(t, "1482564192341", req.SessionID)

	test.Equals(t, "google", req.OriginalRequest.Source)
	test.Equals(t, "i wonder if you are ready for me?", req.OriginalRequest.Data.Inputs[0].Arguments[0].RawText)
	test.Equals(t, "i wonder if you are ready for me?", req.OriginalRequest.Data.Inputs[0].Arguments[0].TextValue)
	test.Equals(t, "text", req.OriginalRequest.Data.Inputs[0].Arguments[0].Name)
	test.Equals(t, "assistant.intent.action.TEXT", req.OriginalRequest.Data.Inputs[0].Intent)
	test.Equals(t, "i wonder if you are ready for me?", req.OriginalRequest.Data.Inputs[0].RawInputs[0].Query)
	test.Equals(t, 2, req.OriginalRequest.Data.Inputs[0].RawInputs[0].InputType)

	test.Equals(t, "qGWZsN9WDIL6x8VffNu3YNacqN4FWnyT+8uEA+GcWXc=", req.OriginalRequest.Data.User.UserID)

	//TODO test Conversation Token
	//test.Equals(t, []interface{}, req.OriginalRequest.Data.Conversation.ConversationToken)

	test.Equals(t, "1482564192341", req.OriginalRequest.Data.Conversation.ConversationID)
	test.Equals(t, 2, req.OriginalRequest.Data.Conversation.Type)

}

<|endoftext|>


<commit_before>package paperless

import (
	"fmt"
	"net/http"
	"path"
	"time"

	"github.com/pressly/chi"
	"github.com/pressly/chi/docgen"
	"github.com/pressly/chi/middleware"

	"github.com/kopoli/go-util"
)

func todoHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("{ item: \"todo\" }"))
}

func loadImageCtx(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	})
}

func loadTagCtx(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	})
}

func StartWeb(o util.Options) (err error) {

	r := chi.NewRouter()

	r.Use(middleware.RequestID)
	r.Use(middleware.RealIP)
	r.Use(middleware.Logger)
	r.Use(middleware.Recoverer)
	r.Use(middleware.Timeout(60 * time.Second))

	// REST API
	r.Route("/api/v1", func(r chi.Router) {
		r.Route("/image", func(r chi.Router) {
			r.Get("/", todoHandler)
			r.Post("/", todoHandler)
			r.Route("/:imageID", func(r chi.Router) {
				r.Use(loadImageCtx)
				r.Get("/", todoHandler)
				r.Put("/", todoHandler)
				r.Delete("/", todoHandler)
			})
		})

		r.Route("/tag", func(r chi.Router) {
			r.Get("/", todoHandler)
			r.Post("/", todoHandler)
			r.Route("/:tagID", func(r chi.Router) {
				r.Use(loadTagCtx)
				r.Get("/", todoHandler)
				r.Put("/", todoHandler)
				r.Delete("/", todoHandler)
			})
		})
	})

	// Web interface
	webdir := o.Get("webdir", "web")
	uploaddir := o.Get("uploaddir", "static")
	r.FileServer("/html", http.Dir(webdir))
	r.FileServer("/static", http.Dir(uploaddir))
	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, path.Join(webdir, "paperless.html"))
	})

	if o.IsSet("print-routes") {
		fmt.Println(docgen.JSONRoutesDoc(r))
		return
	}

	http.ListenAndServe(o.Get("address-port", ":8078"), r)

	return
}
<commit_msg>rest: add version endpoint<commit_after>package paperless

import (
	"fmt"
	"net/http"
	"path"
	"time"

	"github.com/pressly/chi"
	"github.com/pressly/chi/docgen"
	"github.com/pressly/chi/middleware"

	"github.com/kopoli/go-util"
)

type backend struct {
	options util.Options
}

func todoHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("{ item: \"todo\" }"))
}

func loadImageCtx(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	})
}

func loadTagCtx(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	})
}

func (b *backend) versionHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("{ \"version\": \"" + b.options.Get("version", "unversioned") + "\" }"))
}

func StartWeb(o util.Options) (err error) {

	back := &backend{o}

	r := chi.NewRouter()

	r.Use(middleware.RequestID)
	r.Use(middleware.RealIP)
	r.Use(middleware.Logger)
	r.Use(middleware.Recoverer)
	r.Use(middleware.Timeout(60 * time.Second))

	// REST API
	r.Route("/api/v1", func(r chi.Router) {
		r.Get("/version", back.versionHandler)
		r.Route("/image", func(r chi.Router) {
			r.Get("/", todoHandler)
			r.Post("/", todoHandler)
			r.Route("/:imageID", func(r chi.Router) {
				r.Use(loadImageCtx)
				r.Get("/", todoHandler)
				r.Put("/", todoHandler)
				r.Delete("/", todoHandler)
			})
		})

		r.Route("/tag", func(r chi.Router) {
			r.Get("/", todoHandler)
			r.Post("/", todoHandler)
			r.Route("/:tagID", func(r chi.Router) {
				r.Use(loadTagCtx)
				r.Get("/", todoHandler)
				r.Put("/", todoHandler)
				r.Delete("/", todoHandler)
			})
		})
	})

	// Web interface
	webdir := o.Get("webdir", "web")
	uploaddir := o.Get("uploaddir", "static")
	r.FileServer("/html", http.Dir(webdir))
	r.FileServer("/static", http.Dir(uploaddir))
	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, path.Join(webdir, "paperless.html"))
	})

	if o.IsSet("print-routes") {
		fmt.Println(docgen.JSONRoutesDoc(r))
		return
	}

	http.ListenAndServe(o.Get("address-port", ":8078"), r)

	return
}
<|endoftext|>


<commit_before>package proto

import (
	"errors"
	log "github.com/Sirupsen/logrus"
	"math/rand"
	"sync"
	"time"

	"github.com/libopenstorage/gossip/types"
)

const (
	DEFAULT_GOSSIP_INTERVAL     = 2 * time.Second
	DEFAULT_NODE_DEATH_INTERVAL = 5 * DEFAULT_GOSSIP_INTERVAL
)

// Implements the UnreliableBroadcast interface
type GossiperImpl struct {
	// GossipstoreImpl implements the GossipStoreInterface
	GossipStoreImpl

	// node list, maintained separately
	nodes     []string
	name      string
	nodesLock sync.Mutex
	// to signal exit gossip loop
	done              chan bool
	gossipInterval    time.Duration
	nodeDeathInterval time.Duration
}

// Utility methods
func logAndGetError(msg string) error {
	log.Error(msg)
	return errors.New(msg)
}

func (g *GossiperImpl) Init(ip string, selfNodeId types.NodeId) {
	g.InitStore(selfNodeId)
	g.name = ip
	g.nodes = make([]string, 0)
	g.done = make(chan bool, 1)
	g.gossipInterval = DEFAULT_GOSSIP_INTERVAL
	g.nodeDeathInterval = DEFAULT_NODE_DEATH_INTERVAL
	rand.Seed(time.Now().UnixNano())

	// start gossiping
	go g.sendLoop()
	go g.receiveLoop()
	go g.updateStatusLoop()
}

func (g *GossiperImpl) Stop() {
	// one for send loop, one for receive loop, one for update loop
	if g.done != nil {
		g.done <- true
		g.done <- true
		g.done <- true
		g.done = nil
	}
}

func (g *GossiperImpl) SetGossipInterval(t time.Duration) {
	g.gossipInterval = t
}

func (g *GossiperImpl) GossipInterval() time.Duration {
	return g.gossipInterval
}

func (g *GossiperImpl) SetNodeDeathInterval(t time.Duration) {
	g.nodeDeathInterval = t
}

func (g *GossiperImpl) NodeDeathInterval() time.Duration {
	return g.nodeDeathInterval
}

func (g *GossiperImpl) AddNode(ip string) error {
	g.nodesLock.Lock()
	defer g.nodesLock.Unlock()

	for _, node := range g.nodes {
		if node == ip {
			return logAndGetError("Node being added already exists:" + ip)
		}
	}
	g.nodes = append(g.nodes, ip)

	return nil
}

func (g *GossiperImpl) RemoveNode(ip string) error {
	g.nodesLock.Lock()
	defer g.nodesLock.Unlock()

	for i, node := range g.nodes {
		if node == ip {
			// not sure if this is the most efficient way
			g.nodes = append(g.nodes[:i], g.nodes[i+1:]...)
			return nil
		}
	}
	return logAndGetError("Node being added already exists:" + ip)
}

func (g *GossiperImpl) GetNodes() []string {
	g.nodesLock.Lock()
	defer g.nodesLock.Unlock()

	nodeList := make([]string, len(g.nodes))
	copy(nodeList, g.nodes)
	return nodeList
}

// getUpdatesFromPeer receives node data from the peer
// for which the peer has more latest information available
func (g *GossiperImpl) getUpdatesFromPeer(conn types.MessageChannel) error {

	var newPeerData types.StoreDiff
	err := conn.RcvData(&newPeerData)
	if err != nil {
		log.Error("Error fetching the latest peer data", err)
		return err
	}

	g.Update(newPeerData)

	return nil
}

// sendNodeMetaInfo sends a list of meta info for all
// the nodes in the nodes's store to the peer
func (g *GossiperImpl) sendNodeMetaInfo(conn types.MessageChannel) error {
	msg := g.MetaInfo()
	err := conn.SendData(&msg)
	return err
}

// sendUpdatesToPeer sends the information about the given
// nodes to the peer
func (g *GossiperImpl) sendUpdatesToPeer(diff *types.StoreNodes,
	conn types.MessageChannel) error {
	dataToSend := g.Subset(*diff)
	return conn.SendData(&dataToSend)
}

func (g *GossiperImpl) handleGossip(conn types.MessageChannel) {
	log.Debug(g.id, " Servicing gossip request")
	var peerMetaInfo types.StoreMetaInfo
	err := error(nil)

	// Get the info about the node data that the sender has
	err = conn.RcvData(&peerMetaInfo)
	log.Debug(g.id, " Got meta data: \n", peerMetaInfo)
	if err != nil {
		return
	}

	// 2. Compare with current data that this node has and get
	//    the names of the nodes for which this node has stale info
	//    as compared to the sender
	diffNew, selfNew := g.Diff(peerMetaInfo)
	log.Debug(g.id, " The diff is: diffNew: \n", diffNew, " \nselfNew:\n", selfNew)

	// Send this list to the peer, and get the latest data
	// for them
	err = conn.SendData(diffNew)
	if err != nil {
		log.Error("Error sending list of nodes to fetch: ", err)
		return
	}

	// get the data for nodes sent above from the peer
	err = g.getUpdatesFromPeer(conn)
	if err != nil {
		log.Error("Failed to get data for nodes from the peer: ", err)
		return
	}

	// Since you know which data is stale on the sender side,
	// send him the data for the updated nodes
	err = g.sendUpdatesToPeer(&selfNew, conn)
	if err != nil {
		return
	}
	log.Debug(g.id, " Finished Servicing gossip request")
}

func (g *GossiperImpl) receiveLoop() {
	var handler types.OnMessageRcv = func(c types.MessageChannel) { g.handleGossip(c) }
	c := NewRunnableMessageChannel(g.name, handler)
	go c.RunOnRcvData()
	// block waiting for the done signal
	<-g.done
	c.Close()
}

// sendLoop periodically connects to a random peer
// and gossips about the state of the cluster
func (g *GossiperImpl) sendLoop() {
	tick := time.Tick(g.gossipInterval)
	for {
		select {
		case <-tick:
			g.gossip()
		case <-g.done:
			return
		}
	}
}

// updateStatusLoop updates the status of each node
// depending on when it was last updated
func (g *GossiperImpl) updateStatusLoop() {
	tick := time.Tick(g.gossipInterval)
	for {
		select {
		case <-tick:
			g.UpdateNodeStatuses(g.nodeDeathInterval)
		case <-g.done:
			return
		}
	}
}

// selectGossipPeer randomly selects a peer
// to gossip with from the list of nodes added
func (g *GossiperImpl) selectGossipPeer() string {
	g.nodesLock.Lock()
	defer g.nodesLock.Unlock()

	nodesLen := len(g.nodes)
	if nodesLen == 0 {
		return ""
	}

	return g.nodes[rand.Intn(nodesLen)]
}

func (g *GossiperImpl) gossip() {

	// select a node to gossip with
	peerNode := g.selectGossipPeer()
	if len(peerNode) == 0 {
		return
	}
	log.Debug("Starting gossip with ", peerNode)

	conn := NewMessageChannel(peerNode)
	if conn == nil {
		//XXX: FIXME : note that the peer is down
		return
	}

	// send meta data info about the node to the peer
	err := g.sendNodeMetaInfo(conn)
	if err != nil {
		log.Error("Failed to send meta info to the peer: ", err)
		//XXX: FIXME : note that the peer is down
		return
	}

	// get a list of requested nodes from the peer and
	var diff types.StoreNodes
	err = conn.RcvData(&diff)
	if err != nil {
		log.Error("Failed to get request info to the peer: ", err)
		//XXX: FIXME : note that the peer is down
		return
	}

	// send back the data
	err = g.sendUpdatesToPeer(&diff, conn)
	if err != nil {
		log.Error("Failed to send newer data to the peer: ", err)
		//XXX: FIXME : note that the peer is down
		return
	}

	// receive any updates the send has for us
	err = g.getUpdatesFromPeer(conn)
	if err != nil {
		log.Error("Failed to get newer data from the peer: ", err)
		//XXX: FIXME : note that the peer is down
		return
	}
	log.Debug("Ending gossip with ", peerNode)

}
<commit_msg>Pull in latest updates from gossip fixing a bug.<commit_after>package proto

import (
	"errors"
	log "github.com/Sirupsen/logrus"
	"math"
	"math/rand"
	"sort"
	"sync"
	"sync/atomic"
	"time"

	"github.com/libopenstorage/gossip/types"
)

const (
	DEFAULT_GOSSIP_INTERVAL     = 2 * time.Second
	DEFAULT_NODE_DEATH_INTERVAL = 5 * DEFAULT_GOSSIP_INTERVAL
)

// Implements the UnreliableBroadcast interface
type GossiperImpl struct {
	// GossipstoreImpl implements the GossipStoreInterface
	GossipStoreImpl

	// node list, maintained separately
	nodes     []string
	name      string
	nodesLock sync.Mutex
	// to signal exit gossip loop
	done              chan bool
	gossipInterval    time.Duration
	nodeDeathInterval time.Duration
	peerSelector      PeerSelector
}

// Utility methods
func logAndGetError(msg string) error {
	log.Error(msg)
	return errors.New(msg)
}

type PeerSelector interface {
	SetMaxLen(uint32)
	NextPeer() int32
	SetStartHint(m uint32)
}

type RoundRobinPeerSelector struct {
	maxLen       uint32
	lastSelected uint32
}

func (r *RoundRobinPeerSelector) Init() {
	r.maxLen = 0
	r.lastSelected = 0
}

func (r *RoundRobinPeerSelector) SetStartHint(m uint32) {
	maxLen := atomic.LoadUint32(&r.maxLen)
	var lastSelected uint32
	lastSelected = 0
	if m != maxLen {
		lastSelected = uint32((m + 1) % maxLen)
	}
	atomic.StoreUint32(&r.lastSelected, lastSelected)
}

func (r *RoundRobinPeerSelector) SetMaxLen(m uint32) {
	if m > math.MaxUint16 {
		log.Panicf("Number of peers %v greater than those suported %v",
			m, math.MaxUint16)
	}
	atomic.StoreUint32(&r.maxLen, m)
}

func (r *RoundRobinPeerSelector) NextPeer() int32 {
	maxLen := atomic.LoadUint32(&r.maxLen)
	lastSelected := atomic.LoadUint32(&r.lastSelected)
	if maxLen < 2 {
		return -1
	}

	atomic.StoreUint32(&r.lastSelected, (lastSelected+1)%maxLen)
	return int32(r.lastSelected)
}

func NewPeerSelector() PeerSelector {
	s := new(RoundRobinPeerSelector)
	s.Init()
	return s
}

func (g *GossiperImpl) Init(ip string, selfNodeId types.NodeId) {
	g.InitStore(selfNodeId)
	g.name = ip
	g.nodes = make([]string, 0)
	g.done = make(chan bool, 1)
	g.gossipInterval = DEFAULT_GOSSIP_INTERVAL
	g.nodeDeathInterval = DEFAULT_NODE_DEATH_INTERVAL
	g.peerSelector = NewPeerSelector()
	rand.Seed(time.Now().UnixNano())

	// start gossiping
	go g.sendLoop()
	go g.receiveLoop()
	go g.updateStatusLoop()
}

func (g *GossiperImpl) Stop() {
	// one for send loop, one for receive loop, one for update loop
	if g.done != nil {
		g.done <- true
		g.done <- true
		g.done <- true
		g.done = nil
	}
}

func (g *GossiperImpl) SetGossipInterval(t time.Duration) {
	g.gossipInterval = t
}

func (g *GossiperImpl) GossipInterval() time.Duration {
	return g.gossipInterval
}

func (g *GossiperImpl) SetNodeDeathInterval(t time.Duration) {
	g.nodeDeathInterval = t
}

func (g *GossiperImpl) NodeDeathInterval() time.Duration {
	return g.nodeDeathInterval
}

func (g *GossiperImpl) AddNode(ip string) error {
	g.nodesLock.Lock()
	defer g.nodesLock.Unlock()

	for _, node := range g.nodes {
		if node == ip {
			return logAndGetError("Node being added already exists:" + ip)
		}
	}
	g.nodes = append(g.nodes, ip)
	sort.Strings(g.nodes)
	g.peerSelector.SetMaxLen(uint32(len(g.nodes)))
	if len(g.nodes) >= 2 {
		// In order to make sure that not all of the
		// nodes go in the same order, try to reset the order
		// by sorting the nodes by name and starting at the position
		// next to this node
		temp := make([]string, len(g.nodes))
		copy(temp, g.nodes)
		temp = append(temp, g.name)
		sort.Strings(temp)
		for i, n := range temp {
			if n == g.name {
				g.peerSelector.SetStartHint(uint32(i % len(g.nodes)))
			}
		}
	}

	return nil
}

func (g *GossiperImpl) RemoveNode(ip string) error {
	g.nodesLock.Lock()
	defer g.nodesLock.Unlock()

	for i, node := range g.nodes {
		if node == ip {
			// not sure if this is the most efficient way
			g.nodes = append(g.nodes[:i], g.nodes[i+1:]...)
			g.peerSelector.SetMaxLen(uint32(len(g.nodes)))
			return nil
		}
	}
	return logAndGetError("Node being added already exists:" + ip)
}

func (g *GossiperImpl) GetNodes() []string {
	g.nodesLock.Lock()
	defer g.nodesLock.Unlock()

	nodeList := make([]string, len(g.nodes))
	copy(nodeList, g.nodes)
	return nodeList
}

// getUpdatesFromPeer receives node data from the peer
// for which the peer has more latest information available
func (g *GossiperImpl) getUpdatesFromPeer(conn types.MessageChannel) error {

	var newPeerData types.StoreDiff
	err := conn.RcvData(&newPeerData)
	if err != nil {
		log.Error("Error fetching the latest peer data", err)
		return err
	}

	g.Update(newPeerData)

	return nil
}

// sendNodeMetaInfo sends a list of meta info for all
// the nodes in the nodes's store to the peer
func (g *GossiperImpl) sendNodeMetaInfo(conn types.MessageChannel) error {
	msg := g.MetaInfo()
	err := conn.SendData(&msg)
	return err
}

// sendUpdatesToPeer sends the information about the given
// nodes to the peer
func (g *GossiperImpl) sendUpdatesToPeer(diff *types.StoreNodes,
	conn types.MessageChannel) error {
	dataToSend := g.Subset(*diff)
	return conn.SendData(&dataToSend)
}

func (g *GossiperImpl) handleGossip(conn types.MessageChannel) {
	log.Debug(g.id, " Servicing gossip request")
	var peerMetaInfo types.StoreMetaInfo
	err := error(nil)

	// Get the info about the node data that the sender has
	err = conn.RcvData(&peerMetaInfo)
	log.Debug(g.id, " Got meta data: \n", peerMetaInfo)
	if err != nil {
		return
	}

	// 2. Compare with current data that this node has and get
	//    the names of the nodes for which this node has stale info
	//    as compared to the sender
	diffNew, selfNew := g.Diff(peerMetaInfo)
	log.Debug(g.id, " The diff is: diffNew: \n", diffNew, " \nselfNew:\n", selfNew)

	// Send this list to the peer, and get the latest data
	// for them
	err = conn.SendData(diffNew)
	if err != nil {
		log.Error("Error sending list of nodes to fetch: ", err)
		return
	}

	// get the data for nodes sent above from the peer
	err = g.getUpdatesFromPeer(conn)
	if err != nil {
		log.Error("Failed to get data for nodes from the peer: ", err)
		return
	}

	// Since you know which data is stale on the sender side,
	// send him the data for the updated nodes
	err = g.sendUpdatesToPeer(&selfNew, conn)
	if err != nil {
		return
	}
	log.Debug(g.id, " Finished Servicing gossip request")
}

func (g *GossiperImpl) receiveLoop() {
	var handler types.OnMessageRcv = func(c types.MessageChannel) { g.handleGossip(c) }
	c := NewRunnableMessageChannel(g.name, handler)
	go c.RunOnRcvData()
	// block waiting for the done signal
	<-g.done
	c.Close()
}

// sendLoop periodically connects to a random peer
// and gossips about the state of the cluster
func (g *GossiperImpl) sendLoop() {
	tick := time.Tick(g.gossipInterval)
	for {
		select {
		case <-tick:
			g.gossip()
		case <-g.done:
			return
		}
	}
}

// updateStatusLoop updates the status of each node
// depending on when it was last updated
func (g *GossiperImpl) updateStatusLoop() {
	tick := time.Tick(g.gossipInterval)
	for {
		select {
		case <-tick:
			g.UpdateNodeStatuses(g.nodeDeathInterval)
		case <-g.done:
			return
		}
	}
}

// selectGossipPeer randomly selects a peer
// to gossip with from the list of nodes added
func (g *GossiperImpl) selectGossipPeer() string {
	g.nodesLock.Lock()
	defer g.nodesLock.Unlock()

	nodesLen := len(g.nodes)
	if nodesLen == 0 {
		return ""
	}

	return g.nodes[g.peerSelector.NextPeer()]
}

func (g *GossiperImpl) gossip() {

	// select a node to gossip with
	peerNode := g.selectGossipPeer()
	if len(peerNode) == 0 {
		return
	}
	log.Debug("Starting gossip with ", peerNode)

	conn := NewMessageChannel(peerNode)
	if conn == nil {
		//XXX: FIXME : note that the peer is down
		return
	}

	// send meta data info about the node to the peer
	err := g.sendNodeMetaInfo(conn)
	if err != nil {
		log.Error("Failed to send meta info to the peer: ", err)
		//XXX: FIXME : note that the peer is down
		return
	}

	// get a list of requested nodes from the peer and
	var diff types.StoreNodes
	err = conn.RcvData(&diff)
	if err != nil {
		log.Error("Failed to get request info to the peer: ", err)
		//XXX: FIXME : note that the peer is down
		return
	}

	// send back the data
	err = g.sendUpdatesToPeer(&diff, conn)
	if err != nil {
		log.Error("Failed to send newer data to the peer: ", err)
		//XXX: FIXME : note that the peer is down
		return
	}

	// receive any updates the send has for us
	err = g.getUpdatesFromPeer(conn)
	if err != nil {
		log.Error("Failed to get newer data from the peer: ", err)
		//XXX: FIXME : note that the peer is down
		return
	}
	log.Debug("Ending gossip with ", peerNode)

}
<|endoftext|>