<commit_before>/*
Copyright 2019 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 oci

import (
	"fmt"
	"net"
	"os/exec"
	"runtime"
	"strconv"
	"strings"

	"github.com/blang/semver"
	"github.com/golang/glog"
	"github.com/pkg/errors"
)

// RoutableHostIPFromInside returns the ip/dns of the host that container lives on
// is routable from inside the container
func RoutableHostIPFromInside(ociBin string, clusterName string, containerName string) (net.IP, error) {
	if ociBin == Docker {
		if runtime.GOOS == "linux" {
			_, gateway, err := dockerNetworkInspect(clusterName)
			if err != nil {
				return gateway, errors.Wrap(err, "network inspect")
			}
			return gateway, nil
		}
		// for windows and mac, the gateway ip is not routable so we use dns trick.
		return digDNS(ociBin, containerName, "host.docker.internal")
	}

	if runtime.GOOS == "linux" {
		return podmanGatewayIP(containerName)
	}

	return nil, fmt.Errorf("RoutableHostIPFromInside is currently only implemented for linux")
}

// digDNS will get the IP record for a dns
func digDNS(ociBin, containerName, dns string) (net.IP, error) {
	rr, err := runCmd(exec.Command(ociBin, "exec", "-t", containerName, "dig", "+short", dns))
	ip := net.ParseIP(strings.TrimSpace(rr.Stdout.String()))
	if err != nil {
		return ip, errors.Wrapf(err, "resolve dns to ip")
	}

	glog.Infof("got host ip for mount in container by digging dns: %s", ip.String())
	return ip, nil
}

// podmanGatewayIP gets the default gateway ip for the container
func podmanGatewayIP(containerName string) (net.IP, error) {
	rr, err := runCmd(exec.Command(Podman, "container", "inspect", "--format", "{{.NetworkSettings.Gateway}}", containerName))
	if err != nil {
		return nil, errors.Wrapf(err, "inspect gateway")
	}
	ip := net.ParseIP(strings.TrimSpace(rr.Stdout.String()))
	return ip, nil
}

// ForwardedPort will return port mapping for a container using cli.
// example : ForwardedPort("docker", "minikube", "22")
// will return the docker assigned port:
// 32769, nil
// only supports TCP ports
func ForwardedPort(ociBin string, ociID string, contPort int) (int, error) {
	var rr *RunResult
	var err error
	var v semver.Version

	if ociBin == Podman {
		rr, err = runCmd(exec.Command(Podman, "version", "--format", "{{.Version}}"))
		if err != nil {
			return 0, errors.Wrapf(err, "podman version")
		}
		output := strings.TrimSpace(rr.Stdout.String())
		v, err = semver.Make(output)
		if err != nil {
			return 0, errors.Wrapf(err, "podman version")
		}
	}

	// podman 2.0.1 introduced docker syntax for .NetworkSettings.Ports (podman#5380)
	if ociBin == Podman && v.LT(semver.Version{Major: 2, Minor: 0, Patch: 1}) {
		rr, err = runCmd(exec.Command(ociBin, "container", "inspect", "-f", fmt.Sprintf("{{range .NetworkSettings.Ports}}{{if eq .ContainerPort %s}}{{.HostPort}}{{end}}{{end}}", fmt.Sprint(contPort)), ociID))
		if err != nil {
			return 0, errors.Wrapf(err, "get port %d for %q", contPort, ociID)
		}
	} else {
		rr, err = runCmd(exec.Command(ociBin, "container", "inspect", "-f", fmt.Sprintf("'{{(index (index .NetworkSettings.Ports \"%d/tcp\") 0).HostPort}}'", contPort), ociID))
		if err != nil {
			return 0, errors.Wrapf(err, "get port %d for %q", contPort, ociID)
		}
	}

	o := strings.TrimSpace(rr.Stdout.String())
	o = strings.Trim(o, "'")
	p, err := strconv.Atoi(o)

	if err != nil {
		return p, errors.Wrapf(err, "convert host-port %q to number", p)
	}

	return p, nil
}

// ContainerIPs returns ipv4,ipv6, error of a container by their name
func ContainerIPs(ociBin string, name string) (string, string, error) {
	if ociBin == Podman {
		return podmanContainerIP(name)
	}
	return dockerContainerIP(name)
}

// podmanContainerIP returns ipv4, ipv6 of container or error
func podmanContainerIP(name string) (string, string, error) {
	rr, err := runCmd(exec.Command(Podman, "container", "inspect",
		"-f", "{{.NetworkSettings.IPAddress}}",
		name))
	if err != nil {
		return "", "", errors.Wrapf(err, "podman inspect ip %s", name)
	}
	output := strings.TrimSpace(rr.Stdout.String())
	if err == nil && output == "" { // podman returns empty for 127.0.0.1
		return DefaultBindIPV4, "", nil
	}
	return output, "", nil
}

// dockerContainerIP returns ipv4, ipv6 of container or error
func dockerContainerIP(name string) (string, string, error) {
	// retrieve the IP address of the node using docker inspect
	lines, err := inspect(Docker, name, "{{range .NetworkSettings.Networks}}{{.IPAddress}},{{.GlobalIPv6Address}}{{end}}")
	if err != nil {
		return "", "", errors.Wrap(err, "inspecting NetworkSettings.Networks")
	}

	if len(lines) != 1 {
		return "", "", errors.Errorf("IPs output should only be one line, got %d lines", len(lines))
	}

	ips := strings.Split(lines[0], ",")
	if len(ips) != 2 {
		return "", "", errors.Errorf("container addresses should have 2 values, got %d values: %+v", len(ips), ips)
	}

	return ips[0], ips[1], nil
}
<commit_msg>try alternate if no network<commit_after>/*
Copyright 2019 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 oci

import (
	"fmt"
	"net"
	"os/exec"
	"runtime"
	"strconv"
	"strings"

	"github.com/blang/semver"
	"github.com/golang/glog"
	"github.com/pkg/errors"
)

// RoutableHostIPFromInside returns the ip/dns of the host that container lives on
// is routable from inside the container
func RoutableHostIPFromInside(ociBin string, clusterName string, containerName string) (net.IP, error) {
	if ociBin == Docker {
		if runtime.GOOS == "linux" {
			_, gateway, err := dockerNetworkInspect(clusterName)
			if err != nil {
				if errors.Is(err, ErrNetworkNotFound) {
					glog.Infof("The container %s is not attached to a network, this could be because the cluster was created by an older than v.1.14 minikube, will try to get the IP using container gatway", containerName)
					return containerGatewayIP(Docker, containerName)
				}
				return gateway, errors.Wrap(err, "network inspect")
			}
			return gateway, nil
		}
		// for windows and mac, the gateway ip is not routable so we use dns trick.
		return digDNS(ociBin, containerName, "host.docker.internal")
	}
	//	podman
	if runtime.GOOS == "linux" {
		return containerGatewayIP(Podman, containerName)
	}

	return nil, fmt.Errorf("RoutableHostIPFromInside is currently only implemented for linux")
}

// digDNS will get the IP record for a dns
func digDNS(ociBin, containerName, dns string) (net.IP, error) {
	rr, err := runCmd(exec.Command(ociBin, "exec", "-t", containerName, "dig", "+short", dns))
	ip := net.ParseIP(strings.TrimSpace(rr.Stdout.String()))
	if err != nil {
		return ip, errors.Wrapf(err, "resolve dns to ip")
	}

	glog.Infof("got host ip for mount in container by digging dns: %s", ip.String())
	return ip, nil
}

// containerGatewayIP gets the default gateway ip for the container
func containerGatewayIP(ociBin string, containerName string) (net.IP, error) {
	rr, err := runCmd(exec.Command(ociBin, "container", "inspect", "--format", "{{.NetworkSettings.Gateway}}", containerName))
	if err != nil {
		return nil, errors.Wrapf(err, "inspect gateway")
	}
	ip := net.ParseIP(strings.TrimSpace(rr.Stdout.String()))
	return ip, nil
}

// ForwardedPort will return port mapping for a container using cli.
// example : ForwardedPort("docker", "minikube", "22")
// will return the docker assigned port:
// 32769, nil
// only supports TCP ports
func ForwardedPort(ociBin string, ociID string, contPort int) (int, error) {
	var rr *RunResult
	var err error
	var v semver.Version

	if ociBin == Podman {
		rr, err = runCmd(exec.Command(Podman, "version", "--format", "{{.Version}}"))
		if err != nil {
			return 0, errors.Wrapf(err, "podman version")
		}
		output := strings.TrimSpace(rr.Stdout.String())
		v, err = semver.Make(output)
		if err != nil {
			return 0, errors.Wrapf(err, "podman version")
		}
	}

	// podman 2.0.1 introduced docker syntax for .NetworkSettings.Ports (podman#5380)
	if ociBin == Podman && v.LT(semver.Version{Major: 2, Minor: 0, Patch: 1}) {
		rr, err = runCmd(exec.Command(ociBin, "container", "inspect", "-f", fmt.Sprintf("{{range .NetworkSettings.Ports}}{{if eq .ContainerPort %s}}{{.HostPort}}{{end}}{{end}}", fmt.Sprint(contPort)), ociID))
		if err != nil {
			return 0, errors.Wrapf(err, "get port %d for %q", contPort, ociID)
		}
	} else {
		rr, err = runCmd(exec.Command(ociBin, "container", "inspect", "-f", fmt.Sprintf("'{{(index (index .NetworkSettings.Ports \"%d/tcp\") 0).HostPort}}'", contPort), ociID))
		if err != nil {
			return 0, errors.Wrapf(err, "get port %d for %q", contPort, ociID)
		}
	}

	o := strings.TrimSpace(rr.Stdout.String())
	o = strings.Trim(o, "'")
	p, err := strconv.Atoi(o)

	if err != nil {
		return p, errors.Wrapf(err, "convert host-port %q to number", p)
	}

	return p, nil
}

// ContainerIPs returns ipv4,ipv6, error of a container by their name
func ContainerIPs(ociBin string, name string) (string, string, error) {
	if ociBin == Podman {
		return podmanContainerIP(name)
	}
	return dockerContainerIP(name)
}

// podmanContainerIP returns ipv4, ipv6 of container or error
func podmanContainerIP(name string) (string, string, error) {
	rr, err := runCmd(exec.Command(Podman, "container", "inspect",
		"-f", "{{.NetworkSettings.IPAddress}}",
		name))
	if err != nil {
		return "", "", errors.Wrapf(err, "podman inspect ip %s", name)
	}
	output := strings.TrimSpace(rr.Stdout.String())
	if err == nil && output == "" { // podman returns empty for 127.0.0.1
		return DefaultBindIPV4, "", nil
	}
	return output, "", nil
}

// dockerContainerIP returns ipv4, ipv6 of container or error
func dockerContainerIP(name string) (string, string, error) {
	// retrieve the IP address of the node using docker inspect
	lines, err := inspect(Docker, name, "{{range .NetworkSettings.Networks}}{{.IPAddress}},{{.GlobalIPv6Address}}{{end}}")
	if err != nil {
		return "", "", errors.Wrap(err, "inspecting NetworkSettings.Networks")
	}

	if len(lines) != 1 {
		return "", "", errors.Errorf("IPs output should only be one line, got %d lines", len(lines))
	}

	ips := strings.Split(lines[0], ",")
	if len(ips) != 2 {
		return "", "", errors.Errorf("container addresses should have 2 values, got %d values: %+v", len(ips), ips)
	}

	return ips[0], ips[1], nil
}
<|endoftext|>


<commit_before>// Copyright 2016-2017 Authors of Cilium
//
// 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 endpointmanager

import (
	"context"
	"fmt"
	"sync"

	"github.com/cilium/cilium/pkg/endpoint"
	endpointid "github.com/cilium/cilium/pkg/endpoint/id"
	"github.com/cilium/cilium/pkg/lock"
	"github.com/cilium/cilium/pkg/logging"
	"github.com/cilium/cilium/pkg/metrics"
	"github.com/cilium/cilium/pkg/option"
	"github.com/cilium/cilium/pkg/policy"

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

var (
	log = logging.DefaultLogger

	// mutex protects endpoints and endpointsAux
	mutex lock.RWMutex

	// endpoints is the global list of endpoints indexed by ID. mutex must
	// be held to read and write.
	endpoints    = map[uint16]*endpoint.Endpoint{}
	endpointsAux = map[string]*endpoint.Endpoint{}
)

func init() {
	// EndpointCount is a function used to collect this metric. We cannot
	// increment/decrement a gauge since we invoke Remove gratuitiously and that
	// would result in negative counts.
	// It must be thread-safe.
	metrics.EndpointCount = prometheus.NewGaugeFunc(prometheus.GaugeOpts{
		Namespace: metrics.Namespace,
		Name:      "endpoint_count",
		Help:      "Number of endpoints managed by this agent",
	},
		func() float64 { return float64(len(GetEndpoints())) },
	)
	metrics.MustRegister(metrics.EndpointCount)
}

// Insert inserts the endpoint into the global maps.
// Must be called with ep.Mutex.RLock held.
func Insert(ep *endpoint.Endpoint) {
	mutex.Lock()
	defer mutex.Unlock()

	endpoints[ep.ID] = ep
	updateReferences(ep)
	ep.RunK8sCiliumEndpointSync() // start the k8s update controller
}

// Lookup looks up the endpoint by prefix id
func Lookup(id string) (*endpoint.Endpoint, error) {
	mutex.RLock()
	defer mutex.RUnlock()

	prefix, eid, err := endpointid.ParseID(id)
	if err != nil {
		return nil, err
	}

	switch prefix {
	case endpointid.CiliumLocalIdPrefix:
		n, err := endpointid.ParseCiliumID(id)
		if err != nil {
			return nil, err
		}
		return lookupCiliumID(uint16(n)), nil

	case endpointid.CiliumGlobalIdPrefix:
		return nil, fmt.Errorf("Unsupported id format for now")

	case endpointid.ContainerIdPrefix:
		return lookupDockerID(eid), nil

	case endpointid.DockerEndpointPrefix:
		return lookupDockerEndpoint(eid), nil

	case endpointid.ContainerNamePrefix:
		return lookupDockerContainerName(eid), nil

	case endpointid.PodNamePrefix:
		return lookupPodNameLocked(eid), nil

	case endpointid.IPv4Prefix:
		return lookupIPv4(eid), nil

	default:
		return nil, fmt.Errorf("Unknown endpoint prefix %s", prefix)
	}
}

// LookupCiliumID looks up endpoint by endpoint ID
func LookupCiliumID(id uint16) *endpoint.Endpoint {
	mutex.RLock()
	ep := lookupCiliumID(id)
	mutex.RUnlock()
	return ep
}

// LookupDockerID looks up endpoint by Docker ID
func LookupDockerID(id string) *endpoint.Endpoint {
	mutex.RLock()
	ep := lookupDockerID(id)
	mutex.RUnlock()
	return ep
}

// LookupIPv4 looks up endpoint by IPv4 address
func LookupIPv4(ipv4 string) *endpoint.Endpoint {
	mutex.RLock()
	ep := lookupIPv4(ipv4)
	mutex.RUnlock()
	return ep
}

// LookupPodName looks up endpoint by namespace + pod name
func LookupPodName(name string) *endpoint.Endpoint {
	mutex.RLock()
	ep := lookupPodNameLocked(name)
	mutex.RUnlock()
	return ep
}

// UpdateReferences makes an endpoint available by all possible reference
// fields as available for this endpoint (containerID, IPv4 address, ...)
// Must be called with ep.Mutex.RLock held.
func UpdateReferences(ep *endpoint.Endpoint) {
	mutex.Lock()
	defer mutex.Unlock()
	updateReferences(ep)
}

// Remove removes the endpoint from the global maps.
// Must be called with ep.Mutex.RLock held.
func Remove(ep *endpoint.Endpoint) {
	mutex.Lock()
	defer mutex.Unlock()
	delete(endpoints, ep.ID)

	if ep.DockerID != "" {
		delete(endpointsAux, endpointid.NewID(endpointid.ContainerIdPrefix, ep.DockerID))
	}

	if ep.DockerEndpointID != "" {
		delete(endpointsAux, endpointid.NewID(endpointid.DockerEndpointPrefix, ep.DockerEndpointID))
	}

	if ep.IPv4.String() != "" {
		delete(endpointsAux, endpointid.NewID(endpointid.IPv4Prefix, ep.IPv4.String()))
	}

	if ep.ContainerName != "" {
		delete(endpointsAux, endpointid.NewID(endpointid.ContainerNamePrefix, ep.ContainerName))
	}

	if podName := ep.GetK8sNamespaceAndPodNameLocked(); podName != "" {
		delete(endpointsAux, endpointid.NewID(endpointid.PodNamePrefix, podName))
	}
}

// RemoveAll removes all endpoints from the global maps.
func RemoveAll() {
	mutex.Lock()
	defer mutex.Unlock()
	endpoints = map[uint16]*endpoint.Endpoint{}
	endpointsAux = map[string]*endpoint.Endpoint{}
}

// lookupCiliumID looks up endpoint by endpoint ID
func lookupCiliumID(id uint16) *endpoint.Endpoint {
	if ep, ok := endpoints[id]; ok {
		return ep
	}
	return nil
}

func lookupDockerEndpoint(id string) *endpoint.Endpoint {
	if ep, ok := endpointsAux[endpointid.NewID(endpointid.DockerEndpointPrefix, id)]; ok {
		return ep
	}
	return nil
}

func lookupPodNameLocked(name string) *endpoint.Endpoint {
	if ep, ok := endpointsAux[endpointid.NewID(endpointid.PodNamePrefix, name)]; ok {
		return ep
	}
	return nil
}

func lookupDockerContainerName(name string) *endpoint.Endpoint {
	if ep, ok := endpointsAux[endpointid.NewID(endpointid.ContainerNamePrefix, name)]; ok {
		return ep
	}
	return nil
}

func lookupIPv4(ipv4 string) *endpoint.Endpoint {
	if ep, ok := endpointsAux[endpointid.NewID(endpointid.IPv4Prefix, ipv4)]; ok {
		return ep
	}
	return nil
}

func lookupDockerID(id string) *endpoint.Endpoint {
	if ep, ok := endpointsAux[endpointid.NewID(endpointid.ContainerIdPrefix, id)]; ok {
		return ep
	}
	return nil
}

func linkContainerID(ep *endpoint.Endpoint) {
	endpointsAux[endpointid.NewID(endpointid.ContainerIdPrefix, ep.DockerID)] = ep
}

// UpdateReferences updates the mappings of various values to their corresponding
// endpoints, such as DockerID, Docker Container Name, Pod Name, etc.
func updateReferences(ep *endpoint.Endpoint) {
	if ep.DockerID != "" {
		linkContainerID(ep)
	}

	if ep.DockerEndpointID != "" {
		endpointsAux[endpointid.NewID(endpointid.DockerEndpointPrefix, ep.DockerEndpointID)] = ep
	}

	if ep.IPv4.String() != "" {
		endpointsAux[endpointid.NewID(endpointid.IPv4Prefix, ep.IPv4.String())] = ep
	}

	if ep.ContainerName != "" {
		endpointsAux[endpointid.NewID(endpointid.ContainerNamePrefix, ep.ContainerName)] = ep
	}

	if podName := ep.GetK8sNamespaceAndPodNameLocked(); podName != "" {
		endpointsAux[endpointid.NewID(endpointid.PodNamePrefix, podName)] = ep
	}
}

// TriggerPolicyUpdates calls TriggerPolicyUpdatesLocked for each endpoint and
// regenerates as required. During this process, the endpoint list is locked
// and cannot be modified.
// Returns a waiting group that can be used to know when all the endpoints are
// regenerated.
func TriggerPolicyUpdates(owner endpoint.Owner, force bool) *sync.WaitGroup {
	var wg sync.WaitGroup

	eps := GetEndpoints()
	wg.Add(len(eps))

	for _, ep := range eps {
		go func(ep *endpoint.Endpoint, wg *sync.WaitGroup) {
			ep.Mutex.Lock()
			policyChanges, err := ep.TriggerPolicyUpdatesLocked(owner, nil)
			regen := false
			if err == nil && (policyChanges || force) {
				// Regenerate only if state transition succeeds
				regen = ep.SetStateLocked(endpoint.StateWaitingToRegenerate, "Triggering endpoint regeneration due to policy updates")
			}
			ep.Mutex.Unlock()

			if err != nil {
				log.WithError(err).Warn("Error while handling policy updates for endpoint")
				ep.LogStatus(endpoint.Policy, endpoint.Failure, "Error while handling policy updates for endpoint: "+err.Error())
			} else {
				if !policyChanges && !force {
					ep.LogStatusOK(endpoint.Policy, "Endpoint policy update skipped because no changes were needed")
				} else if regen {
					// Regenerate logs status according to the build success/failure
					<-ep.Regenerate(owner, "endpoint policy updated & changes were needed")
				} // else policy changed, but can't regenerate => do not change status
			}
			wg.Done()
		}(ep, &wg)
	}

	return &wg
}

// HasGlobalCT returns true if the endpoints have a global CT, false otherwise.
func HasGlobalCT() bool {
	eps := GetEndpoints()
	for _, e := range eps {
		e.RLock()
		globalCT := !e.Options.IsEnabled(option.ConntrackLocal)
		e.RUnlock()
		if globalCT {
			return true
		}
	}
	return false
}

// GetEndpoints returns a slice of all endpoints present in endpoint manager.
func GetEndpoints() []*endpoint.Endpoint {
	mutex.RLock()
	eps := make([]*endpoint.Endpoint, 0, len(endpoints))
	for _, ep := range endpoints {
		eps = append(eps, ep)
	}
	mutex.RUnlock()
	return eps
}

// AddEndpoint takes the prepared endpoint object and starts managing it.
func AddEndpoint(owner endpoint.Owner, ep *endpoint.Endpoint, reason string) error {
	alwaysEnforce := policy.GetPolicyEnabled() == option.AlwaysEnforce
	ep.Options.SetBool(option.IngressPolicy, alwaysEnforce)
	ep.Options.SetBool(option.EgressPolicy, alwaysEnforce)

	if err := ep.CreateDirectory(); err != nil {
		return err
	}

	// Regenerate immediately if ready or waiting for identity
	ep.Mutex.Lock()
	build := false
	state := ep.GetStateLocked()

	// We can only trigger regeneration of endpoints if the endpoint is in a
	// state where it can regenerate. See endpoint.SetStateLocked().
	if state == endpoint.StateReady {
		ep.SetStateLocked(endpoint.StateWaitingToRegenerate, reason)
		build = true
	}
	ep.Mutex.Unlock()
	if build {
		if err := ep.RegenerateWait(owner, reason); err != nil {
			ep.RemoveDirectory()
			return err
		}
	}

	ep.Mutex.RLock()
	Insert(ep)
	ep.InsertEvent()
	ep.Mutex.RUnlock()

	return nil
}

// WaitForEndpointsAtPolicyRev waits for all endpoints which existed at the time
// this function is called to be at a given policy revision.
// New endpoints appearing while waiting are ignored.
func WaitForEndpointsAtPolicyRev(ctx context.Context, rev uint64) error {
	eps := GetEndpoints()
	for i := range eps {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-eps[i].WaitForPolicyRevision(ctx, rev):
		}
	}
	return nil
}
<commit_msg>pkg/endpointmanager: remove useless endpoint RLock<commit_after>// Copyright 2016-2017 Authors of Cilium
//
// 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 endpointmanager

import (
	"context"
	"fmt"
	"sync"

	"github.com/cilium/cilium/pkg/endpoint"
	endpointid "github.com/cilium/cilium/pkg/endpoint/id"
	"github.com/cilium/cilium/pkg/lock"
	"github.com/cilium/cilium/pkg/logging"
	"github.com/cilium/cilium/pkg/metrics"
	"github.com/cilium/cilium/pkg/option"
	"github.com/cilium/cilium/pkg/policy"

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

var (
	log = logging.DefaultLogger

	// mutex protects endpoints and endpointsAux
	mutex lock.RWMutex

	// endpoints is the global list of endpoints indexed by ID. mutex must
	// be held to read and write.
	endpoints    = map[uint16]*endpoint.Endpoint{}
	endpointsAux = map[string]*endpoint.Endpoint{}
)

func init() {
	// EndpointCount is a function used to collect this metric. We cannot
	// increment/decrement a gauge since we invoke Remove gratuitiously and that
	// would result in negative counts.
	// It must be thread-safe.
	metrics.EndpointCount = prometheus.NewGaugeFunc(prometheus.GaugeOpts{
		Namespace: metrics.Namespace,
		Name:      "endpoint_count",
		Help:      "Number of endpoints managed by this agent",
	},
		func() float64 { return float64(len(GetEndpoints())) },
	)
	metrics.MustRegister(metrics.EndpointCount)
}

// Insert inserts the endpoint into the global maps.
// Must be called with ep.Mutex.RLock held.
func Insert(ep *endpoint.Endpoint) {
	mutex.Lock()
	defer mutex.Unlock()

	endpoints[ep.ID] = ep
	updateReferences(ep)
	ep.RunK8sCiliumEndpointSync() // start the k8s update controller
}

// Lookup looks up the endpoint by prefix id
func Lookup(id string) (*endpoint.Endpoint, error) {
	mutex.RLock()
	defer mutex.RUnlock()

	prefix, eid, err := endpointid.ParseID(id)
	if err != nil {
		return nil, err
	}

	switch prefix {
	case endpointid.CiliumLocalIdPrefix:
		n, err := endpointid.ParseCiliumID(id)
		if err != nil {
			return nil, err
		}
		return lookupCiliumID(uint16(n)), nil

	case endpointid.CiliumGlobalIdPrefix:
		return nil, fmt.Errorf("Unsupported id format for now")

	case endpointid.ContainerIdPrefix:
		return lookupDockerID(eid), nil

	case endpointid.DockerEndpointPrefix:
		return lookupDockerEndpoint(eid), nil

	case endpointid.ContainerNamePrefix:
		return lookupDockerContainerName(eid), nil

	case endpointid.PodNamePrefix:
		return lookupPodNameLocked(eid), nil

	case endpointid.IPv4Prefix:
		return lookupIPv4(eid), nil

	default:
		return nil, fmt.Errorf("Unknown endpoint prefix %s", prefix)
	}
}

// LookupCiliumID looks up endpoint by endpoint ID
func LookupCiliumID(id uint16) *endpoint.Endpoint {
	mutex.RLock()
	ep := lookupCiliumID(id)
	mutex.RUnlock()
	return ep
}

// LookupDockerID looks up endpoint by Docker ID
func LookupDockerID(id string) *endpoint.Endpoint {
	mutex.RLock()
	ep := lookupDockerID(id)
	mutex.RUnlock()
	return ep
}

// LookupIPv4 looks up endpoint by IPv4 address
func LookupIPv4(ipv4 string) *endpoint.Endpoint {
	mutex.RLock()
	ep := lookupIPv4(ipv4)
	mutex.RUnlock()
	return ep
}

// LookupPodName looks up endpoint by namespace + pod name
func LookupPodName(name string) *endpoint.Endpoint {
	mutex.RLock()
	ep := lookupPodNameLocked(name)
	mutex.RUnlock()
	return ep
}

// UpdateReferences makes an endpoint available by all possible reference
// fields as available for this endpoint (containerID, IPv4 address, ...)
// Must be called with ep.Mutex.RLock held.
func UpdateReferences(ep *endpoint.Endpoint) {
	mutex.Lock()
	defer mutex.Unlock()
	updateReferences(ep)
}

// Remove removes the endpoint from the global maps.
// Must be called with ep.Mutex.RLock held.
func Remove(ep *endpoint.Endpoint) {
	mutex.Lock()
	defer mutex.Unlock()
	delete(endpoints, ep.ID)

	if ep.DockerID != "" {
		delete(endpointsAux, endpointid.NewID(endpointid.ContainerIdPrefix, ep.DockerID))
	}

	if ep.DockerEndpointID != "" {
		delete(endpointsAux, endpointid.NewID(endpointid.DockerEndpointPrefix, ep.DockerEndpointID))
	}

	if ep.IPv4.String() != "" {
		delete(endpointsAux, endpointid.NewID(endpointid.IPv4Prefix, ep.IPv4.String()))
	}

	if ep.ContainerName != "" {
		delete(endpointsAux, endpointid.NewID(endpointid.ContainerNamePrefix, ep.ContainerName))
	}

	if podName := ep.GetK8sNamespaceAndPodNameLocked(); podName != "" {
		delete(endpointsAux, endpointid.NewID(endpointid.PodNamePrefix, podName))
	}
}

// RemoveAll removes all endpoints from the global maps.
func RemoveAll() {
	mutex.Lock()
	defer mutex.Unlock()
	endpoints = map[uint16]*endpoint.Endpoint{}
	endpointsAux = map[string]*endpoint.Endpoint{}
}

// lookupCiliumID looks up endpoint by endpoint ID
func lookupCiliumID(id uint16) *endpoint.Endpoint {
	if ep, ok := endpoints[id]; ok {
		return ep
	}
	return nil
}

func lookupDockerEndpoint(id string) *endpoint.Endpoint {
	if ep, ok := endpointsAux[endpointid.NewID(endpointid.DockerEndpointPrefix, id)]; ok {
		return ep
	}
	return nil
}

func lookupPodNameLocked(name string) *endpoint.Endpoint {
	if ep, ok := endpointsAux[endpointid.NewID(endpointid.PodNamePrefix, name)]; ok {
		return ep
	}
	return nil
}

func lookupDockerContainerName(name string) *endpoint.Endpoint {
	if ep, ok := endpointsAux[endpointid.NewID(endpointid.ContainerNamePrefix, name)]; ok {
		return ep
	}
	return nil
}

func lookupIPv4(ipv4 string) *endpoint.Endpoint {
	if ep, ok := endpointsAux[endpointid.NewID(endpointid.IPv4Prefix, ipv4)]; ok {
		return ep
	}
	return nil
}

func lookupDockerID(id string) *endpoint.Endpoint {
	if ep, ok := endpointsAux[endpointid.NewID(endpointid.ContainerIdPrefix, id)]; ok {
		return ep
	}
	return nil
}

func linkContainerID(ep *endpoint.Endpoint) {
	endpointsAux[endpointid.NewID(endpointid.ContainerIdPrefix, ep.DockerID)] = ep
}

// UpdateReferences updates the mappings of various values to their corresponding
// endpoints, such as DockerID, Docker Container Name, Pod Name, etc.
func updateReferences(ep *endpoint.Endpoint) {
	if ep.DockerID != "" {
		linkContainerID(ep)
	}

	if ep.DockerEndpointID != "" {
		endpointsAux[endpointid.NewID(endpointid.DockerEndpointPrefix, ep.DockerEndpointID)] = ep
	}

	if ep.IPv4.String() != "" {
		endpointsAux[endpointid.NewID(endpointid.IPv4Prefix, ep.IPv4.String())] = ep
	}

	if ep.ContainerName != "" {
		endpointsAux[endpointid.NewID(endpointid.ContainerNamePrefix, ep.ContainerName)] = ep
	}

	if podName := ep.GetK8sNamespaceAndPodNameLocked(); podName != "" {
		endpointsAux[endpointid.NewID(endpointid.PodNamePrefix, podName)] = ep
	}
}

// TriggerPolicyUpdates calls TriggerPolicyUpdatesLocked for each endpoint and
// regenerates as required. During this process, the endpoint list is locked
// and cannot be modified.
// Returns a waiting group that can be used to know when all the endpoints are
// regenerated.
func TriggerPolicyUpdates(owner endpoint.Owner, force bool) *sync.WaitGroup {
	var wg sync.WaitGroup

	eps := GetEndpoints()
	wg.Add(len(eps))

	for _, ep := range eps {
		go func(ep *endpoint.Endpoint, wg *sync.WaitGroup) {
			ep.Mutex.Lock()
			policyChanges, err := ep.TriggerPolicyUpdatesLocked(owner, nil)
			regen := false
			if err == nil && (policyChanges || force) {
				// Regenerate only if state transition succeeds
				regen = ep.SetStateLocked(endpoint.StateWaitingToRegenerate, "Triggering endpoint regeneration due to policy updates")
			}
			ep.Mutex.Unlock()

			if err != nil {
				log.WithError(err).Warn("Error while handling policy updates for endpoint")
				ep.LogStatus(endpoint.Policy, endpoint.Failure, "Error while handling policy updates for endpoint: "+err.Error())
			} else {
				if !policyChanges && !force {
					ep.LogStatusOK(endpoint.Policy, "Endpoint policy update skipped because no changes were needed")
				} else if regen {
					// Regenerate logs status according to the build success/failure
					<-ep.Regenerate(owner, "endpoint policy updated & changes were needed")
				} // else policy changed, but can't regenerate => do not change status
			}
			wg.Done()
		}(ep, &wg)
	}

	return &wg
}

// HasGlobalCT returns true if the endpoints have a global CT, false otherwise.
func HasGlobalCT() bool {
	eps := GetEndpoints()
	for _, e := range eps {
		if !e.Options.IsEnabled(option.ConntrackLocal) {
			return true
		}
	}
	return false
}

// GetEndpoints returns a slice of all endpoints present in endpoint manager.
func GetEndpoints() []*endpoint.Endpoint {
	mutex.RLock()
	eps := make([]*endpoint.Endpoint, 0, len(endpoints))
	for _, ep := range endpoints {
		eps = append(eps, ep)
	}
	mutex.RUnlock()
	return eps
}

// AddEndpoint takes the prepared endpoint object and starts managing it.
func AddEndpoint(owner endpoint.Owner, ep *endpoint.Endpoint, reason string) error {
	alwaysEnforce := policy.GetPolicyEnabled() == option.AlwaysEnforce
	ep.Options.SetBool(option.IngressPolicy, alwaysEnforce)
	ep.Options.SetBool(option.EgressPolicy, alwaysEnforce)

	if err := ep.CreateDirectory(); err != nil {
		return err
	}

	// Regenerate immediately if ready or waiting for identity
	ep.Mutex.Lock()
	build := false
	state := ep.GetStateLocked()

	// We can only trigger regeneration of endpoints if the endpoint is in a
	// state where it can regenerate. See endpoint.SetStateLocked().
	if state == endpoint.StateReady {
		ep.SetStateLocked(endpoint.StateWaitingToRegenerate, reason)
		build = true
	}
	ep.Mutex.Unlock()
	if build {
		if err := ep.RegenerateWait(owner, reason); err != nil {
			ep.RemoveDirectory()
			return err
		}
	}

	ep.Mutex.RLock()
	Insert(ep)
	ep.InsertEvent()
	ep.Mutex.RUnlock()

	return nil
}

// WaitForEndpointsAtPolicyRev waits for all endpoints which existed at the time
// this function is called to be at a given policy revision.
// New endpoints appearing while waiting are ignored.
func WaitForEndpointsAtPolicyRev(ctx context.Context, rev uint64) error {
	eps := GetEndpoints()
	for i := range eps {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-eps[i].WaitForPolicyRevision(ctx, rev):
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package install

import (
	"fmt"
	"reflect"
	"testing"

	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/assert"
	corev1 "k8s.io/api/core/v1"
	v1beta1extensions "k8s.io/api/extensions/v1beta1"
	v1beta1rbac "k8s.io/api/rbac/v1beta1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/coreos-inc/alm/pkg/apis/clusterserviceversion/v1alpha1"
	"github.com/stretchr/testify/require"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

func testDeployment(name, namespace string, mockOwnerMeta metav1.ObjectMeta) v1beta1extensions.Deployment {
	testDeploymentLabels := map[string]string{"alm-owner-name": mockOwnerMeta.Name, "alm-owner-namespace": mockOwnerMeta.Namespace}

	deployment := v1beta1extensions.Deployment{
		TypeMeta: metav1.TypeMeta{},
		ObjectMeta: metav1.ObjectMeta{
			Namespace: namespace,
			Name:      name,
			OwnerReferences: []metav1.OwnerReference{
				{
					APIVersion:         v1alpha1.SchemeGroupVersion.String(),
					Kind:               v1alpha1.ClusterServiceVersionKind,
					Name:               mockOwnerMeta.GetName(),
					UID:                mockOwnerMeta.UID,
					Controller:         &Controller,
					BlockOwnerDeletion: &BlockOwnerDeletion,
				},
			},
			Labels: testDeploymentLabels,
		},
	}
	return deployment
}

func testServiceAccount(name string, mockOwnerMeta metav1.ObjectMeta) *corev1.ServiceAccount {
	serviceAccount := &corev1.ServiceAccount{}
	serviceAccount.SetName(name)
	serviceAccount.SetOwnerReferences([]metav1.OwnerReference{
		{
			APIVersion:         v1alpha1.SchemeGroupVersion.String(),
			Kind:               v1alpha1.ClusterServiceVersionKind,
			Name:               mockOwnerMeta.GetName(),
			UID:                mockOwnerMeta.UID,
			Controller:         &Controller,
			BlockOwnerDeletion: &BlockOwnerDeletion,
		},
	})
	return serviceAccount
}

type RoleMatcher struct{ rules []v1beta1rbac.PolicyRule }

func MatchesRoleRules(rules []v1beta1rbac.PolicyRule) gomock.Matcher {
	return &RoleMatcher{rules}
}

func (e *RoleMatcher) Matches(x interface{}) bool {
	role, ok := x.(*v1beta1rbac.Role)
	if !ok {
		return false
	}
	return reflect.DeepEqual(role.Rules, e.rules)
}

func (e *RoleMatcher) String() string {
	return "matches expected rules"
}

func strategy(n int, namespace string, mockOwnerMeta metav1.ObjectMeta) *StrategyDetailsDeployment {
	var deploymentSpecs = []StrategyDeploymentSpec{}
	var permissions = []StrategyDeploymentPermissions{}
	for i := 1; i <= n; i++ {
		dep := testDeployment(fmt.Sprintf("alm-dep-%d", i), namespace, mockOwnerMeta)
		spec := StrategyDeploymentSpec{Name: dep.GetName(), Spec: dep.Spec}
		deploymentSpecs = append(deploymentSpecs, spec)
		serviceAccount := testServiceAccount(fmt.Sprintf("alm-sa-%d", i), mockOwnerMeta)
		permissions = append(permissions, StrategyDeploymentPermissions{
			ServiceAccountName: serviceAccount.Name,
			Rules: []v1beta1rbac.PolicyRule{
				{
					Verbs:     []string{"list", "delete"},
					APIGroups: []string{""},
					Resources: []string{"pods"},
				},
			},
		})
	}
	return &StrategyDetailsDeployment{
		DeploymentSpecs: deploymentSpecs,
		Permissions:     permissions,
	}
}

func TestInstallStrategyDeployment(t *testing.T) {
	namespace := "alm-test-deployment"
	mockOwnerMeta := metav1.ObjectMeta{
		Name:      "clusterserviceversion-owner",
		Namespace: namespace,
	}

	tests := []struct {
		numMockServiceAccounts int
		numMockDeployments     int
		numExpected            int
		description            string
	}{
		{
			numMockServiceAccounts: 0,
			numMockDeployments:     0,
			numExpected:            1,
			description:            "NoServiceAccount/NoDeployment/Require1,1",
		},
		{
			numMockServiceAccounts: 1,
			numMockDeployments:     1,
			numExpected:            1,
			description:            "1ServiceAccount/1Deployment/Require1,1",
		},
		{
			numMockServiceAccounts: 0,
			numMockDeployments:     1,
			numExpected:            1,
			description:            "0ServiceAccount/1Deployment/Require1,1",
		},
		{
			numMockServiceAccounts: 1,
			numMockDeployments:     0,
			numExpected:            1,
			description:            "1ServiceAccount/0Deployment/Require1,1",
		},
		{
			numMockServiceAccounts: 3,
			numMockDeployments:     3,
			numExpected:            3,
			description:            "3ServiceAccount/3Deployment/Require3,3",
		},
	}

	for _, tt := range tests {
		t.Run(tt.description, func(t *testing.T) {
			ctrl := gomock.NewController(t)
			mockClient := NewMockInstallStrategyDeploymentInterface(ctrl)
			strategy := strategy(tt.numExpected, namespace, mockOwnerMeta)
			for i, p := range strategy.Permissions {
				if i < tt.numMockServiceAccounts {
					t.Logf("mocking %s true", p.ServiceAccountName)
					mockClient.EXPECT().
						GetServiceAccountByName(p.ServiceAccountName).
						Return(testServiceAccount(p.ServiceAccountName, mockOwnerMeta), nil)
				}
				if i == tt.numMockServiceAccounts {
					t.Logf("mocking %s false", p.ServiceAccountName)
					mockClient.EXPECT().
						GetServiceAccountByName(p.ServiceAccountName).
						Return(nil, apierrors.NewNotFound(schema.GroupResource{}, p.ServiceAccountName))
				}

				serviceAccount := testServiceAccount(p.ServiceAccountName, mockOwnerMeta)
				mockClient.EXPECT().EnsureServiceAccount(serviceAccount).Return(serviceAccount, nil)
				mockClient.EXPECT().
					CreateRole(MatchesRoleRules(p.Rules)).
					Return(&v1beta1rbac.Role{Rules: p.Rules}, nil)
				mockClient.EXPECT().CreateRoleBinding(gomock.Any()).Return(&v1beta1rbac.RoleBinding{}, nil)
			}
			mockedDeps := []v1beta1extensions.Deployment{}
			for i := 1; i <= tt.numMockDeployments; i++ {
				dep := testDeployment(fmt.Sprintf("alm-dep-%d", i), namespace, mockOwnerMeta)
				dep.Spec = v1beta1extensions.DeploymentSpec{Paused: true} // arbitrary

				mockedDeps = append(mockedDeps, dep)
			}
			if tt.numMockServiceAccounts == tt.numExpected {
				t.Log("mocking dep check")
				// if all serviceaccounts exist then we check if deployments exist
				mockClient.EXPECT().
					GetOwnedDeployments(mockOwnerMeta).
					Return(&v1beta1extensions.DeploymentList{Items: mockedDeps}, nil)
			}

			if len(strategy.DeploymentSpecs) > 0 {
				mockClient.EXPECT().
					GetOwnedDeployments(mockOwnerMeta).
					Return(&v1beta1extensions.DeploymentList{Items: mockedDeps}, nil)
			}
			for i := range make([]int, len(strategy.DeploymentSpecs)) {
				deployment := testDeployment(fmt.Sprintf("alm-dep-%d", i+1), namespace, mockOwnerMeta)
				mockClient.EXPECT().
					CreateDeployment(&deployment).
					Return(&deployment, nil)
			}

			installer := &StrategyDeploymentInstaller{
				strategyClient: mockClient,
				ownerMeta:      mockOwnerMeta,
			}
			installed, err := installer.CheckInstalled(strategy)
			require.False(t, installed)
			assert.NoError(t, err)
			assert.NoError(t, installer.Install(strategy))

			ctrl.Finish()
		})
	}
}

type BadStrategy struct{}

func (b *BadStrategy) GetStrategyName() string {
	return "bad"
}

func TestNewStrategyDeploymentInstaller(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	mockOwnerMeta := metav1.ObjectMeta{
		Name:      "clusterserviceversion-owner",
		Namespace: "ns",
	}
	mockClient := NewMockInstallStrategyDeploymentInterface(ctrl)
	strategy := NewStrategyDeploymentInstaller(mockClient, mockOwnerMeta)
	require.Implements(t, (*StrategyInstaller)(nil), strategy)
	require.Error(t, strategy.Install(&BadStrategy{}))
	_, err := strategy.CheckInstalled(&BadStrategy{})
	require.Error(t, err)
}

func TestInstallStrategyDeploymentCheckInstallErrors(t *testing.T) {
	namespace := "alm-test-deployment"
	mockOwnerMeta := metav1.ObjectMeta{
		Name:      "clusterserviceversion-owner",
		Namespace: namespace,
	}

	tests := []struct {
		createRoleErr           error
		createRoleBindingErr    error
		createServiceAccountErr error
		createDeploymentErr     error
		checkServiceAccountErr  error
		checkDeploymentErr      error
		description             string
	}{
		{
			checkServiceAccountErr: fmt.Errorf("couldn't query serviceaccount"),
			description:            "ErrorCheckingForServiceAccount",
		},
		{
			checkDeploymentErr: fmt.Errorf("couldn't query deployments"),
			description:        "ErrorCheckingForDeployments",
		},
		{
			createRoleErr: fmt.Errorf("error creating role"),
			description:   "ErrorCreatingRole",
		},
		{
			createServiceAccountErr: fmt.Errorf("error creating serviceaccount"),
			description:             "ErrorCreatingServiceAccount",
		},
		{
			createRoleBindingErr: fmt.Errorf("error creating rolebinding"),
			description:          "ErrorCreatingRoleBinding",
		},
		{
			createDeploymentErr: fmt.Errorf("error creating deployment"),
			description:         "ErrorCreatingDeployment",
		},
	}

	for _, tt := range tests {
		t.Run(tt.description, func(t *testing.T) {
			ctrl := gomock.NewController(t)
			defer ctrl.Finish()

			mockClient := NewMockInstallStrategyDeploymentInterface(ctrl)
			strategy := strategy(1, namespace, mockOwnerMeta)
			installer := &StrategyDeploymentInstaller{
				strategyClient: mockClient,
				ownerMeta:      mockOwnerMeta,
			}

			skipInstall := tt.checkDeploymentErr != nil || tt.checkServiceAccountErr != nil

			mockClient.EXPECT().
				GetServiceAccountByName(strategy.Permissions[0].ServiceAccountName).
				Return(testServiceAccount(strategy.Permissions[0].ServiceAccountName, mockOwnerMeta), tt.checkServiceAccountErr)
			if tt.checkServiceAccountErr == nil {
				dep := testDeployment("alm-dep", namespace, mockOwnerMeta)
				dep.Spec = v1beta1extensions.DeploymentSpec{Paused: true} // arbitrary
				mockClient.EXPECT().
					GetOwnedDeployments(mockOwnerMeta).
					Return(
						&v1beta1extensions.DeploymentList{
							Items: []v1beta1extensions.Deployment{
								dep,
							},
						}, tt.checkDeploymentErr)
			}

			installed, err := installer.CheckInstalled(strategy)

			if skipInstall {
				require.False(t, installed)
				require.Error(t, err)
				return
			} else {
				require.False(t, installed)
				require.NoError(t, err)
			}

			mockClient.EXPECT().
				CreateRole(MatchesRoleRules(strategy.Permissions[0].Rules)).
				Return(&v1beta1rbac.Role{Rules: strategy.Permissions[0].Rules}, tt.createRoleErr)

			if tt.createRoleErr != nil {
				err := installer.Install(strategy)
				require.Error(t, err)
				return
			}

			serviceAccount := testServiceAccount(strategy.Permissions[0].ServiceAccountName, mockOwnerMeta)
			mockClient.EXPECT().EnsureServiceAccount(serviceAccount).Return(serviceAccount, tt.createServiceAccountErr)

			if tt.createServiceAccountErr != nil {
				err := installer.Install(strategy)
				require.Error(t, err)
				return
			}

			mockClient.EXPECT().CreateRoleBinding(gomock.Any()).Return(&v1beta1rbac.RoleBinding{}, tt.createRoleBindingErr)

			if tt.createRoleBindingErr != nil {
				err := installer.Install(strategy)
				require.Error(t, err)
				return
			}

			deployment := testDeployment("alm-dep-1", namespace, mockOwnerMeta)

			dep := testDeployment("alm-dep-1", namespace, mockOwnerMeta)
			dep.Spec = v1beta1extensions.DeploymentSpec{Paused: true} // arbitrary
			mockClient.EXPECT().
				GetOwnedDeployments(mockOwnerMeta).
				Return(
					&v1beta1extensions.DeploymentList{
						Items: []v1beta1extensions.Deployment{
							dep,
						},
					}, nil)

			mockClient.EXPECT().
				CreateDeployment(&deployment).
				Return(&deployment, tt.createDeploymentErr)

			if tt.createDeploymentErr != nil {
				err := installer.Install(strategy)
				require.Error(t, err)
				return
			}
		})
	}
}
<commit_msg>style(pkg/install/deployment_test): remove unnecessary struct init<commit_after>package install

import (
	"fmt"
	"reflect"
	"testing"

	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/assert"
	corev1 "k8s.io/api/core/v1"
	v1beta1extensions "k8s.io/api/extensions/v1beta1"
	v1beta1rbac "k8s.io/api/rbac/v1beta1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/coreos-inc/alm/pkg/apis/clusterserviceversion/v1alpha1"
	"github.com/stretchr/testify/require"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

func testDeployment(name, namespace string, mockOwnerMeta metav1.ObjectMeta) v1beta1extensions.Deployment {
	testDeploymentLabels := map[string]string{"alm-owner-name": mockOwnerMeta.Name, "alm-owner-namespace": mockOwnerMeta.Namespace}

	deployment := v1beta1extensions.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: namespace,
			Name:      name,
			OwnerReferences: []metav1.OwnerReference{
				{
					APIVersion:         v1alpha1.SchemeGroupVersion.String(),
					Kind:               v1alpha1.ClusterServiceVersionKind,
					Name:               mockOwnerMeta.GetName(),
					UID:                mockOwnerMeta.UID,
					Controller:         &Controller,
					BlockOwnerDeletion: &BlockOwnerDeletion,
				},
			},
			Labels: testDeploymentLabels,
		},
	}
	return deployment
}

func testServiceAccount(name string, mockOwnerMeta metav1.ObjectMeta) *corev1.ServiceAccount {
	serviceAccount := &corev1.ServiceAccount{}
	serviceAccount.SetName(name)
	serviceAccount.SetOwnerReferences([]metav1.OwnerReference{
		{
			APIVersion:         v1alpha1.SchemeGroupVersion.String(),
			Kind:               v1alpha1.ClusterServiceVersionKind,
			Name:               mockOwnerMeta.GetName(),
			UID:                mockOwnerMeta.UID,
			Controller:         &Controller,
			BlockOwnerDeletion: &BlockOwnerDeletion,
		},
	})
	return serviceAccount
}

type RoleMatcher struct{ rules []v1beta1rbac.PolicyRule }

func MatchesRoleRules(rules []v1beta1rbac.PolicyRule) gomock.Matcher {
	return &RoleMatcher{rules}
}

func (e *RoleMatcher) Matches(x interface{}) bool {
	role, ok := x.(*v1beta1rbac.Role)
	if !ok {
		return false
	}
	return reflect.DeepEqual(role.Rules, e.rules)
}

func (e *RoleMatcher) String() string {
	return "matches expected rules"
}

func strategy(n int, namespace string, mockOwnerMeta metav1.ObjectMeta) *StrategyDetailsDeployment {
	var deploymentSpecs = []StrategyDeploymentSpec{}
	var permissions = []StrategyDeploymentPermissions{}
	for i := 1; i <= n; i++ {
		dep := testDeployment(fmt.Sprintf("alm-dep-%d", i), namespace, mockOwnerMeta)
		spec := StrategyDeploymentSpec{Name: dep.GetName(), Spec: dep.Spec}
		deploymentSpecs = append(deploymentSpecs, spec)
		serviceAccount := testServiceAccount(fmt.Sprintf("alm-sa-%d", i), mockOwnerMeta)
		permissions = append(permissions, StrategyDeploymentPermissions{
			ServiceAccountName: serviceAccount.Name,
			Rules: []v1beta1rbac.PolicyRule{
				{
					Verbs:     []string{"list", "delete"},
					APIGroups: []string{""},
					Resources: []string{"pods"},
				},
			},
		})
	}
	return &StrategyDetailsDeployment{
		DeploymentSpecs: deploymentSpecs,
		Permissions:     permissions,
	}
}

func TestInstallStrategyDeployment(t *testing.T) {
	namespace := "alm-test-deployment"
	mockOwnerMeta := metav1.ObjectMeta{
		Name:      "clusterserviceversion-owner",
		Namespace: namespace,
	}

	tests := []struct {
		numMockServiceAccounts int
		numMockDeployments     int
		numExpected            int
		description            string
	}{
		{
			numMockServiceAccounts: 0,
			numMockDeployments:     0,
			numExpected:            1,
			description:            "NoServiceAccount/NoDeployment/Require1,1",
		},
		{
			numMockServiceAccounts: 1,
			numMockDeployments:     1,
			numExpected:            1,
			description:            "1ServiceAccount/1Deployment/Require1,1",
		},
		{
			numMockServiceAccounts: 0,
			numMockDeployments:     1,
			numExpected:            1,
			description:            "0ServiceAccount/1Deployment/Require1,1",
		},
		{
			numMockServiceAccounts: 1,
			numMockDeployments:     0,
			numExpected:            1,
			description:            "1ServiceAccount/0Deployment/Require1,1",
		},
		{
			numMockServiceAccounts: 3,
			numMockDeployments:     3,
			numExpected:            3,
			description:            "3ServiceAccount/3Deployment/Require3,3",
		},
	}

	for _, tt := range tests {
		t.Run(tt.description, func(t *testing.T) {
			ctrl := gomock.NewController(t)
			mockClient := NewMockInstallStrategyDeploymentInterface(ctrl)
			strategy := strategy(tt.numExpected, namespace, mockOwnerMeta)
			for i, p := range strategy.Permissions {
				if i < tt.numMockServiceAccounts {
					t.Logf("mocking %s true", p.ServiceAccountName)
					mockClient.EXPECT().
						GetServiceAccountByName(p.ServiceAccountName).
						Return(testServiceAccount(p.ServiceAccountName, mockOwnerMeta), nil)
				}
				if i == tt.numMockServiceAccounts {
					t.Logf("mocking %s false", p.ServiceAccountName)
					mockClient.EXPECT().
						GetServiceAccountByName(p.ServiceAccountName).
						Return(nil, apierrors.NewNotFound(schema.GroupResource{}, p.ServiceAccountName))
				}

				serviceAccount := testServiceAccount(p.ServiceAccountName, mockOwnerMeta)
				mockClient.EXPECT().EnsureServiceAccount(serviceAccount).Return(serviceAccount, nil)
				mockClient.EXPECT().
					CreateRole(MatchesRoleRules(p.Rules)).
					Return(&v1beta1rbac.Role{Rules: p.Rules}, nil)
				mockClient.EXPECT().CreateRoleBinding(gomock.Any()).Return(&v1beta1rbac.RoleBinding{}, nil)
			}
			mockedDeps := []v1beta1extensions.Deployment{}
			for i := 1; i <= tt.numMockDeployments; i++ {
				dep := testDeployment(fmt.Sprintf("alm-dep-%d", i), namespace, mockOwnerMeta)
				dep.Spec = v1beta1extensions.DeploymentSpec{Paused: true} // arbitrary

				mockedDeps = append(mockedDeps, dep)
			}
			if tt.numMockServiceAccounts == tt.numExpected {
				t.Log("mocking dep check")
				// if all serviceaccounts exist then we check if deployments exist
				mockClient.EXPECT().
					GetOwnedDeployments(mockOwnerMeta).
					Return(&v1beta1extensions.DeploymentList{Items: mockedDeps}, nil)
			}

			if len(strategy.DeploymentSpecs) > 0 {
				mockClient.EXPECT().
					GetOwnedDeployments(mockOwnerMeta).
					Return(&v1beta1extensions.DeploymentList{Items: mockedDeps}, nil)
			}
			for i := range make([]int, len(strategy.DeploymentSpecs)) {
				deployment := testDeployment(fmt.Sprintf("alm-dep-%d", i+1), namespace, mockOwnerMeta)
				mockClient.EXPECT().
					CreateDeployment(&deployment).
					Return(&deployment, nil)
			}

			installer := &StrategyDeploymentInstaller{
				strategyClient: mockClient,
				ownerMeta:      mockOwnerMeta,
			}
			installed, err := installer.CheckInstalled(strategy)
			require.False(t, installed)
			assert.NoError(t, err)
			assert.NoError(t, installer.Install(strategy))

			ctrl.Finish()
		})
	}
}

type BadStrategy struct{}

func (b *BadStrategy) GetStrategyName() string {
	return "bad"
}

func TestNewStrategyDeploymentInstaller(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	mockOwnerMeta := metav1.ObjectMeta{
		Name:      "clusterserviceversion-owner",
		Namespace: "ns",
	}
	mockClient := NewMockInstallStrategyDeploymentInterface(ctrl)
	strategy := NewStrategyDeploymentInstaller(mockClient, mockOwnerMeta)
	require.Implements(t, (*StrategyInstaller)(nil), strategy)
	require.Error(t, strategy.Install(&BadStrategy{}))
	_, err := strategy.CheckInstalled(&BadStrategy{})
	require.Error(t, err)
}

func TestInstallStrategyDeploymentCheckInstallErrors(t *testing.T) {
	namespace := "alm-test-deployment"
	mockOwnerMeta := metav1.ObjectMeta{
		Name:      "clusterserviceversion-owner",
		Namespace: namespace,
	}

	tests := []struct {
		createRoleErr           error
		createRoleBindingErr    error
		createServiceAccountErr error
		createDeploymentErr     error
		checkServiceAccountErr  error
		checkDeploymentErr      error
		description             string
	}{
		{
			checkServiceAccountErr: fmt.Errorf("couldn't query serviceaccount"),
			description:            "ErrorCheckingForServiceAccount",
		},
		{
			checkDeploymentErr: fmt.Errorf("couldn't query deployments"),
			description:        "ErrorCheckingForDeployments",
		},
		{
			createRoleErr: fmt.Errorf("error creating role"),
			description:   "ErrorCreatingRole",
		},
		{
			createServiceAccountErr: fmt.Errorf("error creating serviceaccount"),
			description:             "ErrorCreatingServiceAccount",
		},
		{
			createRoleBindingErr: fmt.Errorf("error creating rolebinding"),
			description:          "ErrorCreatingRoleBinding",
		},
		{
			createDeploymentErr: fmt.Errorf("error creating deployment"),
			description:         "ErrorCreatingDeployment",
		},
	}

	for _, tt := range tests {
		t.Run(tt.description, func(t *testing.T) {
			ctrl := gomock.NewController(t)
			defer ctrl.Finish()

			mockClient := NewMockInstallStrategyDeploymentInterface(ctrl)
			strategy := strategy(1, namespace, mockOwnerMeta)
			installer := &StrategyDeploymentInstaller{
				strategyClient: mockClient,
				ownerMeta:      mockOwnerMeta,
			}

			skipInstall := tt.checkDeploymentErr != nil || tt.checkServiceAccountErr != nil

			mockClient.EXPECT().
				GetServiceAccountByName(strategy.Permissions[0].ServiceAccountName).
				Return(testServiceAccount(strategy.Permissions[0].ServiceAccountName, mockOwnerMeta), tt.checkServiceAccountErr)
			if tt.checkServiceAccountErr == nil {
				dep := testDeployment("alm-dep", namespace, mockOwnerMeta)
				dep.Spec = v1beta1extensions.DeploymentSpec{Paused: true} // arbitrary
				mockClient.EXPECT().
					GetOwnedDeployments(mockOwnerMeta).
					Return(
						&v1beta1extensions.DeploymentList{
							Items: []v1beta1extensions.Deployment{
								dep,
							},
						}, tt.checkDeploymentErr)
			}

			installed, err := installer.CheckInstalled(strategy)

			if skipInstall {
				require.False(t, installed)
				require.Error(t, err)
				return
			} else {
				require.False(t, installed)
				require.NoError(t, err)
			}

			mockClient.EXPECT().
				CreateRole(MatchesRoleRules(strategy.Permissions[0].Rules)).
				Return(&v1beta1rbac.Role{Rules: strategy.Permissions[0].Rules}, tt.createRoleErr)

			if tt.createRoleErr != nil {
				err := installer.Install(strategy)
				require.Error(t, err)
				return
			}

			serviceAccount := testServiceAccount(strategy.Permissions[0].ServiceAccountName, mockOwnerMeta)
			mockClient.EXPECT().EnsureServiceAccount(serviceAccount).Return(serviceAccount, tt.createServiceAccountErr)

			if tt.createServiceAccountErr != nil {
				err := installer.Install(strategy)
				require.Error(t, err)
				return
			}

			mockClient.EXPECT().CreateRoleBinding(gomock.Any()).Return(&v1beta1rbac.RoleBinding{}, tt.createRoleBindingErr)

			if tt.createRoleBindingErr != nil {
				err := installer.Install(strategy)
				require.Error(t, err)
				return
			}

			deployment := testDeployment("alm-dep-1", namespace, mockOwnerMeta)

			dep := testDeployment("alm-dep-1", namespace, mockOwnerMeta)
			dep.Spec = v1beta1extensions.DeploymentSpec{Paused: true} // arbitrary
			mockClient.EXPECT().
				GetOwnedDeployments(mockOwnerMeta).
				Return(
					&v1beta1extensions.DeploymentList{
						Items: []v1beta1extensions.Deployment{
							dep,
						},
					}, nil)

			mockClient.EXPECT().
				CreateDeployment(&deployment).
				Return(&deployment, tt.createDeploymentErr)

			if tt.createDeploymentErr != nil {
				err := installer.Install(strategy)
				require.Error(t, err)
				return
			}
		})
	}
}
<|endoftext|>


<commit_before>package public

// Received message types
const (
	MessageText  = "text"
	MessageImage = "image"
	MessageVoice = "voice"
	MessageVideo = "video"
	MessageMusic = "music"
	MessageNews  = "news"
	MessageEvent = "event"
)

// Event types for MessageEvent
const (
	EventSubscribe                   = "subscribe"
	EventUnsubscribe                 = "unsubscribe"
	EventScan                        = "SCAN"
	EventLocation                    = "LOCATION"
	EventMenuClick                   = "CLICK"
	EventMenuView                    = "VIEW"
	EventQualificationVerifySuccess  = "qualification_verify_success"
	EventQualificationVerifyFail     = "qualification_verify_fail"
	EventNamingVerifySuccess         = "naming_verify_success"
	EventNamingVerifyFail            = "naming_verify_fail"
	EventAnnualRenew                 = "annual_renew"
	EventVerifyExpired               = "verify_expired"
	EventCardPassCheck               = "card_pass_check"
	EventNotCardPassCheck            = "card_not_pass_check"
	EventUserGetCard                 = "user_get_card"
	EventUserDelCard                 = "user_del_card"
	EventUserConsumeCard             = "user_consume_card"
	EventUserPayFromPayCell          = "user_pay_from_pay_cell"
	EventUserViewCard                = "user_view_card"
	EventUserEnterSessionFromCard    = "user_enter_session_from_card"
	EventUpdateMemberCard            = "update_member_card"
	EventCardSkuRemind               = "card_sku_remind"
	EventCardPayOrder                = "card_pay_order"
	EventUserScanProduct             = "user_scan_product"
	EventUserScanProductEnterSession = "user_scan_product_enter_session"
	EventUserScanProductAsync        = "user_scan_product_async"
	EventUserScanProductVerifyAction = "user_scan_product_verify_action"
	EventShakeAroundUserShake        = "ShakearoundUserShake"
)

type EventHeader struct {
	ToUser      string `xml:"ToUserName"   json:"ToUserName"`
	FromUser    string `xml:"FromUserName" json:"FromUserName"`
	CreatedTime int64  `xml:"CreateTime"   json:"CreateTime"`
	Type        string `xml:"MsgType"      json:"MsgType"`
}

type Event struct {
	EventHeader

	Event string `xml:"Event" json:"Event"`

	MsgId        int     `xml:"MsgId"        json:"MsgId"`
	Content      string  `xml:"Content"      json:"Content"`
	MediaId      string  `xml:"MediaId"      json:"MediaId"`
	PicURL       string  `xml:"PicUrl"       json:"PicUrl"`
	Format       string  `xml:"Format"       json:"Format"`
	Recognition  string  `xml:"Recognition"  json:"Recognition"`
	ThumbMediaId string  `xml:"ThumbMediaId" json:"ThumbMediaId"`
	LocationX    float64 `xml:"Location_X"   json:"Location_X"`
	LocationY    float64 `xml:"Location_Y"   json:"Location_Y"`
	Scale        int     `xml:"Scale"        json:"Scale"`
	Label        string  `xml:"Label"        json:"Label"`
	Title        string  `xml:"Title"        json:"Title"`
	Description  string  `xml:"Description"  json:"Description"`
	URL          string  `xml:"Url"          json:"Url"`
	EventKey     string  `xml:"EventKey"     json:"EventKey"`
	Ticket       string  `xml:"Ticket"       json:"Ticket"`
	Latitude     float64 `xml:"Latitude"     json:"Latitude"`
	Longitude    float64 `xml:"Longitude"    json:"Longitude"`
	Precision    float64 `xml:"Precision"    json:"Precision"`

	MenuId       int `xml:"MenuId" json:"MenuId"`
	ScanCodeInfo *struct {
		ScanType   string `xml:"ScanType"   json:"ScanType"`
		ScanResult string `xml:"ScanResult" json:"ScanResult"`
	} `xml:"ScanCodeInfo,omitempty" json:"ScanCodeInfo,omitempty"`
	SendPicsInfo *struct {
		Count   int `xml:"Count" json:"Count"`
		PicList []struct {
			PicMd5Sum string `xml:"PicMd5Sum" json:"PicMd5Sum"`
		} `xml:"PicList>item,omitempty" json:"PicList,omitempty"`
	} `xml:"SendPicsInfo,omitempty" json:"SendPicsInfo,omitempty"`
	SendLocationInfo *struct {
		LocationX float64 `xml:"Location_X" json:"Location_X"`
		LocationY float64 `xml:"Location_Y" json:"Location_Y"`
		Scale     int     `xml:"Scale"      json:"Scale"`
		Label     string  `xml:"Label"      json:"Label"`
		PoiName   string  `xml:"Poiname"    json:"Poiname"`
	} `xml:"SendLocationInfo,omitempty" json:"SendLocationInfo,omitempty"`

	Status string `xml:"Status" json:"Status"`

	ChosenBeacon  *Beacon `xml:"ChosenBeacon,omitempty" json:"ChosenBeacon,omitempty"`
	AroundBeacons *Beacon `xml:"AroundBeacons>AroundBeacon,omitempty" json:"AroundBeacons,omitempty"`
}

type Beacon struct {
	UUID     string  `xml:"Uuid"     json:"Uuid"`
	Major    int     `xml:"Major"    json:"Major"`
	Minor    int     `xml:"Minor"    json:"Minor"`
	Distance float64 `xml:"Distance" json:"Distance"`
}
<commit_msg>Update event.go<commit_after>package public

// Received message types
const (
	MessageText  = "text"
	MessageImage = "image"
	MessageVoice = "voice"
	MessageVideo = "video"
	MessageMusic = "music"
	MessageNews  = "news"
	MessageEvent = "event"
)

// Event types for MessageEvent
const (
	EventSubscribe                   = "subscribe"
	EventUnsubscribe                 = "unsubscribe"
	EventScan                        = "SCAN"
	EventLocation                    = "LOCATION"
	EventMenuClick                   = "CLICK"
	EventMenuView                    = "VIEW"
	EventQualificationVerifySuccess  = "qualification_verify_success"
	EventQualificationVerifyFail     = "qualification_verify_fail"
	EventNamingVerifySuccess         = "naming_verify_success"
	EventNamingVerifyFail            = "naming_verify_fail"
	EventAnnualRenew                 = "annual_renew"
	EventVerifyExpired               = "verify_expired"
	EventCardPassCheck               = "card_pass_check"
	EventNotCardPassCheck            = "card_not_pass_check"
	EventUserGetCard                 = "user_get_card"
	EventUserDelCard                 = "user_del_card"
	EventUserConsumeCard             = "user_consume_card"
	EventUserPayFromPayCell          = "user_pay_from_pay_cell"
	EventUserViewCard                = "user_view_card"
	EventUserEnterSessionFromCard    = "user_enter_session_from_card"
	EventUpdateMemberCard            = "update_member_card"
	EventCardSkuRemind               = "card_sku_remind"
	EventCardPayOrder                = "card_pay_order"
	EventUserScanProduct             = "user_scan_product"
	EventUserScanProductEnterSession = "user_scan_product_enter_session"
	EventUserScanProductAsync        = "user_scan_product_async"
	EventUserScanProductVerifyAction = "user_scan_product_verify_action"
	EventShakeAroundUserShake        = "ShakearoundUserShake"
)

type EventHeader struct {
	ToUser      string `xml:"ToUserName"   json:"ToUserName"`
	FromUser    string `xml:"FromUserName" json:"FromUserName"`
	CreatedTime int64  `xml:"CreateTime"   json:"CreateTime"`
	Type        string `xml:"MsgType"      json:"MsgType"`
}

type Event struct {
	EventHeader

	Event string `xml:"Event" json:"Event"`

	MsgId        int     `xml:"MsgId"        json:"MsgId"`
	Content      string  `xml:"Content"      json:"Content"`
	MediaId      string  `xml:"MediaId"      json:"MediaId"`
	PicURL       string  `xml:"PicUrl"       json:"PicUrl"`
	Format       string  `xml:"Format"       json:"Format"`
	Recognition  string  `xml:"Recognition"  json:"Recognition"`
	ThumbMediaId string  `xml:"ThumbMediaId" json:"ThumbMediaId"`
	LocationX    float64 `xml:"Location_X"   json:"Location_X"`
	LocationY    float64 `xml:"Location_Y"   json:"Location_Y"`
	Scale        int     `xml:"Scale"        json:"Scale"`
	Label        string  `xml:"Label"        json:"Label"`
	Title        string  `xml:"Title"        json:"Title"`
	Description  string  `xml:"Description"  json:"Description"`
	URL          string  `xml:"Url"          json:"Url"`
	EventKey     string  `xml:"EventKey"     json:"EventKey"`
	Ticket       string  `xml:"Ticket"       json:"Ticket"`
	Latitude     float64 `xml:"Latitude"     json:"Latitude"`
	Longitude    float64 `xml:"Longitude"    json:"Longitude"`
	Precision    float64 `xml:"Precision"    json:"Precision"`

	MenuId       int `xml:"MenuId" json:"MenuId"`
	ScanCodeInfo *struct {
		ScanType   string `xml:"ScanType"   json:"ScanType"`
		ScanResult string `xml:"ScanResult" json:"ScanResult"`
	} `xml:"ScanCodeInfo,omitempty" json:"ScanCodeInfo,omitempty"`
	SendPicsInfo *struct {
		Count   int `xml:"Count" json:"Count"`
		PicList []struct {
			PicMd5Sum string `xml:"PicMd5Sum" json:"PicMd5Sum"`
		} `xml:"PicList>item,omitempty" json:"PicList,omitempty"`
	} `xml:"SendPicsInfo,omitempty" json:"SendPicsInfo,omitempty"`
	SendLocationInfo *struct {
		LocationX float64 `xml:"Location_X" json:"Location_X"`
		LocationY float64 `xml:"Location_Y" json:"Location_Y"`
		Scale     int     `xml:"Scale"      json:"Scale"`
		Label     string  `xml:"Label"      json:"Label"`
		PoiName   string  `xml:"Poiname"    json:"Poiname"`
	} `xml:"SendLocationInfo,omitempty" json:"SendLocationInfo,omitempty"`

	Status string `xml:"Status" json:"Status"`

	ChosenBeacon  *Beacon `xml:"ChosenBeacon,omitempty" json:"ChosenBeacon,omitempty"`
	AroundBeacons *Beacon `xml:"AroundBeacons>AroundBeacon,omitempty" json:"AroundBeacons,omitempty"`
}

type Beacon struct {
	UUID     string  `xml:"Uuid"     json:"Uuid"`
	Major    int     `xml:"Major"    json:"Major"`
	Minor    int     `xml:"Minor"    json:"Minor"`
	Distance float64 `xml:"Distance" json:"Distance"`
}

func responseEventHeader(msgType string, event *Event) *EventHeader {
	return &EventHeader{
		ToUser:      event.FromUser,
		FromUser:    event.ToUser,
		CreatedTime: event.CreatedTime,
		Type:        msgType,
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2013 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

func (gen *MoveGen) GenerateEvasions() *MoveGen {
        color := gen.p.color
        enemy := gen.p.color^1
        square := gen.p.outposts[King(color)].first()
        pawn, knight, bishop, rook, queen := Pawn(enemy), Knight(enemy), Bishop(enemy), Rook(enemy), Queen(enemy)
        //
        // Find out what pieces are checking the king. Usually it's a single
        // piece but double check is also a possibility.
        //
        checkers := maskPawn[enemy][square] & gen.p.outposts[pawn]
        checkers |= gen.p.Targets(square, Knight(color)) & gen.p.outposts[knight]
        checkers |= gen.p.Targets(square, Bishop(color)) & (gen.p.outposts[bishop] | gen.p.outposts[queen])
        checkers |= gen.p.Targets(square, Rook(color)) & (gen.p.outposts[rook] | gen.p.outposts[queen])
        //
        // Generate possible king retreats first, i.e. moves to squares not
        // occupied by friendly pieces and not attacked by the opponent.
        //
        retreats := gen.p.targets[square] & ^gen.p.attacks[enemy]
        //
        // If the attacking piece is bishop, rook, or queen then exclude the
        // square behind the king using evasion mask. Note that knight's
        // evasion mask is full board so we only check if the attacking piece
        // is not a pawn.
        //
        attackSquare := checkers.pop()
        if gen.p.pieces[attackSquare] != pawn {
                retreats &= maskEvade[square][attackSquare]
        }
        //
        // If checkers mask is not empty then we've got double check and
        // retreat is the only option.
        //
        if checkers != 0 {
                attackSquare = checkers.first()
                if gen.p.pieces[attackSquare] != pawn {
                        retreats &= maskEvade[square][attackSquare]
                }
                for retreats != 0 {
                        gen.add(gen.p.NewMove(square, retreats.pop()))
                }
                return gen
        }
        //
        // Generate king retreats.
        //
        for retreats != 0 {
                gen.add(gen.p.NewMove(square, retreats.pop()))
        }
        //
        // Pawn captures: do we have any pawns available that could capture
        // the attacking piece?
        //
        pawns := maskPawn[color][attackSquare] & gen.p.outposts[Pawn(color)]
        for pawns != 0 {
                gen.add(gen.p.NewMove(pawns.pop(), attackSquare))
        }
        //
        // Rare case when the check could be avoided by en-passant capture.
        // For example: Ke4, c5, e5 vs. Ke8, d7. Black's d7-d5+ could be
        // evaded by c5xd6 or e5xd6 en-passant captures.
        //
        if enpassant := attackSquare + eight[color]; gen.p.flags.enpassant == enpassant {
                pawns := maskPawn[color][enpassant] & gen.p.outposts[Pawn(color)]
                for pawns != 0 {
                        gen.add(gen.p.NewEnpassant(pawns.pop(), attackSquare + eight[color]))
                }
        }
        //
        // See if the check could be blocked.
        //
        block := maskBlock[square][attackSquare]
        //
        // Handle one square pawn pushes: promote to Queen when reaching last rank.
        //
        pawns = gen.p.pawnMovesMask(color) & block
        for pawns != 0 {
                to := pawns.pop(); from := to - eight[color]
                move := gen.p.NewMove(from, to)
                if to >= A8 || to <= H1 {
                        move.promote(QUEEN)
                }
                gen.add(move)
        }
        //
        // Handle two square pawn pushes.
        //
        pawns = gen.p.pawnJumpsMask(color) & block
        for pawns != 0 {
                to := pawns.pop(); from := to - 2 * eight[color]
                gen.add(gen.p.NewMove(from, to))
        }
        //
        // What's left is to generate all possible knight, bishop, rook, and
        // queen moves that evade the check.
        //
        for _, kind := range [4]int{ KNIGHT, BISHOP, ROOK, QUEEN } {
                gen.addEvasion(Piece(kind|color), block)
        }

        return gen
}

func (gen *MoveGen) addEvasion(piece Piece, block Bitmask) {
        outposts := gen.p.outposts[piece]
        for outposts != 0 {
                from := outposts.pop()
                targets := gen.p.targets[from] & block
                for targets != 0 {
                        gen.add(gen.p.NewMove(from, targets.pop()))
                }
        }
}
<commit_msg>Small refactoring<commit_after>// Copyright (c) 2013 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

func (gen *MoveGen) GenerateEvasions() *MoveGen {
        color := gen.p.color
        enemy := gen.p.color^1
        square := gen.p.outposts[King(color)].first()
        pawn, knight, bishop, rook, queen := Pawn(enemy), Knight(enemy), Bishop(enemy), Rook(enemy), Queen(enemy)
        //
        // Find out what pieces are checking the king. Usually it's a single
        // piece but double check is also a possibility.
        //
        checkers := maskPawn[enemy][square] & gen.p.outposts[pawn]
        checkers |= gen.p.Targets(square, Knight(color)) & gen.p.outposts[knight]
        checkers |= gen.p.Targets(square, Bishop(color)) & (gen.p.outposts[bishop] | gen.p.outposts[queen])
        checkers |= gen.p.Targets(square, Rook(color)) & (gen.p.outposts[rook] | gen.p.outposts[queen])
        //
        // Generate possible king retreats first, i.e. moves to squares not
        // occupied by friendly pieces and not attacked by the opponent.
        //
        retreats := gen.p.targets[square] & ^gen.p.attacks[enemy]
        //
        // If the attacking piece is bishop, rook, or queen then exclude the
        // square behind the king using evasion mask. Note that knight's
        // evasion mask is full board so we only check if the attacking piece
        // is not a pawn.
        //
        attackSquare := checkers.pop()
        if gen.p.pieces[attackSquare] != pawn {
                retreats &= maskEvade[square][attackSquare]
        }
        //
        // If checkers mask is not empty then we've got double check and
        // retreat is the only option.
        //
        if checkers != 0 {
                attackSquare = checkers.first()
                if gen.p.pieces[attackSquare] != pawn {
                        retreats &= maskEvade[square][attackSquare]
                }
                return gen.movePiece(square, retreats)
        }
        //
        // Generate king retreats. Since castle is not an option there is
        // no reason to use moveKing().
        //
        gen.movePiece(square, retreats)
        //
        // Pawn captures: do we have any pawns available that could capture
        // the attacking piece?
        //
        pawns := maskPawn[color][attackSquare] & gen.p.outposts[Pawn(color)]
        for pawns != 0 {
                gen.add(gen.p.NewMove(pawns.pop(), attackSquare))
        }
        //
        // Rare case when the check could be avoided by en-passant capture.
        // For example: Ke4, c5, e5 vs. Ke8, d7. Black's d7-d5+ could be
        // evaded by c5xd6 or e5xd6 en-passant captures.
        //
        if enpassant := attackSquare + eight[color]; gen.p.flags.enpassant == enpassant {
                pawns := maskPawn[color][enpassant] & gen.p.outposts[Pawn(color)]
                for pawns != 0 {
                        gen.add(gen.p.NewEnpassant(pawns.pop(), attackSquare + eight[color]))
                }
        }
        //
        // See if the check could be blocked.
        //
        block := maskBlock[square][attackSquare]
        //
        // Handle one square pawn pushes: promote to Queen when reaching last rank.
        //
        pawns = gen.p.pawnMovesMask(color) & block
        for pawns != 0 {
                to := pawns.pop(); from := to - eight[color]
                move := gen.p.NewMove(from, to)
                if to >= A8 || to <= H1 {
                        move.promote(QUEEN)
                }
                gen.add(move)
        }
        //
        // Handle two square pawn pushes.
        //
        pawns = gen.p.pawnJumpsMask(color) & block
        for pawns != 0 {
                to := pawns.pop(); from := to - 2 * eight[color]
                gen.add(gen.p.NewMove(from, to))
        }
        //
        // What's left is to generate all possible knight, bishop, rook, and
        // queen moves that evade the check.
        //
        for _, kind := range [4]int{ KNIGHT, BISHOP, ROOK, QUEEN } {
                gen.addEvasion(Piece(kind|color), block)
        }

        return gen
}

func (gen *MoveGen) addEvasion(piece Piece, block Bitmask) {
        outposts := gen.p.outposts[piece]
        for outposts != 0 {
                from := outposts.pop()
                targets := gen.p.targets[from] & block
                gen.movePiece(from, targets)
        }
}
<|endoftext|>


<commit_before>package backend

import (
	"bufio"
	"bytes"
	"compress/gzip"
	"encoding/json"
	"github.com/boivie/lovebeat/config"
	"github.com/boivie/lovebeat/metrics"
	"github.com/boivie/lovebeat/model"
	"github.com/op/go-logging"
	"os"
	"time"
)

const (
	MAX_PENDING_WRITES = 1000
)

var (
	RECORD_SERVICE = []byte("SERV\t")
	RECORD_VIEW    = []byte("VIEW\t")
	NEWLINE        = []byte("\n")
)

var (
	log      = logging.MustGetLogger("lovebeat")
	counters = metrics.NopMetrics()
)

type FileBackend struct {
	cfg      *config.ConfigDatabase
	q        chan update
	sync     chan chan bool
	services map[string]*model.Service
	views    map[string]*model.View
}

func (f FileBackend) Sync() {
	reply := make(chan bool)
	f.sync <- reply
	<-reply
}

func (f FileBackend) SaveService(service *model.Service) {
	f.q <- update{setService: service}
}

func (f FileBackend) SaveView(view *model.View) {
	f.q <- update{setView: view}
}

func (r FileBackend) LoadServices() []*model.Service {
	v := make([]*model.Service, len(r.services))
	idx := 0
	for _, value := range r.services {
		v[idx] = value
		idx++
	}
	return v
}

func (r FileBackend) LoadViews() []*model.View {
	v := make([]*model.View, len(r.views))
	idx := 0
	for _, value := range r.views {
		v[idx] = value
		idx++
	}
	return v
}

func (f FileBackend) DeleteService(name string) {
	f.q <- update{deleteService: name}
}

func (f FileBackend) DeleteView(name string) {
	f.q <- update{deleteView: name}
}

func (f FileBackend) loadService(data []byte) {
	service := &model.Service{}
	json.Unmarshal(data, &service)
	f.services[service.Name] = service
}

func (f FileBackend) loadView(data []byte) {
	view := &model.View{}
	json.Unmarshal(data, &view)
	f.views[view.Name] = view
}

func (f FileBackend) readAll() {
	s := f.cfg.Filename
	fi, err := os.Open(s)
	if err != nil {
		log.Error("Couldn't open '%s'\n", s)
		return
	}
	gz, err := gzip.NewReader(fi)
	if err != nil {
		log.Error("Couldn't read from '%s'\n", s)
		return
	}

	buf := bufio.NewReader(gz)
	for {
		line, err := buf.ReadBytes('\n')
		if err != nil {
			break
		}
		if bytes.HasPrefix(line, RECORD_SERVICE) {
			f.loadService(line[5:])
		} else if bytes.HasPrefix(line, RECORD_VIEW) {
			f.loadView(line[5:])
		} else {
			log.Info("Found unexpected line in database - skipping")
		}
	}
	log.Info("Loaded %d services and %d views from '%s'",
		len(f.services), len(f.views), s)
}

func (f FileBackend) saveAll() {
	start := time.Now()
	s := f.cfg.Filename + ".new"
	fi, err := os.OpenFile(s, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0640)
	if err != nil {
		log.Error("Error creating file\n")
		return
	}
	gz := gzip.NewWriter(fi)

	for _, service := range f.services {
		b, _ := json.Marshal(service)
		gz.Write(RECORD_SERVICE)
		gz.Write(b)
		gz.Write(NEWLINE)
	}
	for _, view := range f.views {
		b, _ := json.Marshal(view)
		gz.Write(RECORD_VIEW)
		gz.Write(b)
		gz.Write(NEWLINE)
	}
	gz.Close()
	fi.Close()
	if err = os.Rename(s, f.cfg.Filename); err != nil {
		log.Error("Failed to overwrite database")
		return
	}
	duration := time.Since(start)
	log.Debug("Saved %d items in %d ms", len(f.services)+len(f.views),
		duration.Nanoseconds()/1000000)

	counters.IncCounter("db.save.count")
	counters.SetGauge("db.save.duration", int(duration.Nanoseconds()/1000000))
	counters.SetGauge("service.count", len(f.services))
	counters.SetGauge("view.count", len(f.views))
}

type update struct {
	setService    *model.Service
	setView       *model.View
	deleteService string
	deleteView    string
}

func (f FileBackend) fileSaver() {
	period := time.Duration(f.cfg.Interval) * time.Second
	ticker := time.NewTicker(period)
	for {
		select {
		case <-ticker.C:
			f.saveAll()
		case reply := <-f.sync:
			f.saveAll()
			reply <- true
		case upd := <-f.q:
			if upd.setService != nil {
				f.services[upd.setService.Name] = upd.setService
			}
			if upd.deleteService != "" {
				delete(f.services, upd.deleteService)
			}
			if upd.setView != nil {
				f.views[upd.setView.Name] = upd.setView
			}
			if upd.deleteView != "" {
				delete(f.views, upd.deleteView)
			}
		}
	}
}

func NewFileBackend(cfg *config.ConfigDatabase, m metrics.Metrics) Backend {
	counters = m
	var q = make(chan update, MAX_PENDING_WRITES)
	be := FileBackend{
		cfg:      cfg,
		q:        q,
		sync:     make(chan chan bool),
		services: make(map[string]*model.Service),
		views:    make(map[string]*model.View),
	}
	be.readAll()
	go be.fileSaver()
	return be
}
<commit_msg>Minor refactoring of file backend<commit_after>package backend

import (
	"bufio"
	"bytes"
	"compress/gzip"
	"encoding/json"
	"github.com/boivie/lovebeat/config"
	"github.com/boivie/lovebeat/metrics"
	"github.com/boivie/lovebeat/model"
	"github.com/op/go-logging"
	"os"
	"time"
)

const MAX_PENDING_WRITES = 1000

var (
	RECORD_SERVICE = []byte("SERV\t")
	RECORD_VIEW    = []byte("VIEW\t")
	NEWLINE        = []byte("\n")
)

var log = logging.MustGetLogger("lovebeat")

type FileBackend struct {
	cfg      *config.ConfigDatabase
	q        chan update
	sync     chan chan bool
	services map[string]*model.Service
	views    map[string]*model.View
}

func (f FileBackend) Sync() {
	reply := make(chan bool)
	f.sync <- reply
	<-reply
}

func (f FileBackend) SaveService(service *model.Service) {
	f.q <- update{setService: service}
}

func (f FileBackend) SaveView(view *model.View) {
	f.q <- update{setView: view}
}

func (r FileBackend) LoadServices() []*model.Service {
	v := make([]*model.Service, len(r.services))
	idx := 0
	for _, value := range r.services {
		v[idx] = value
		idx++
	}
	return v
}

func (r FileBackend) LoadViews() []*model.View {
	v := make([]*model.View, len(r.views))
	idx := 0
	for _, value := range r.views {
		v[idx] = value
		idx++
	}
	return v
}

func (f FileBackend) DeleteService(name string) {
	f.q <- update{deleteService: name}
}

func (f FileBackend) DeleteView(name string) {
	f.q <- update{deleteView: name}
}

func (f FileBackend) readAll() {
	s := f.cfg.Filename
	fi, err := os.Open(s)
	if err != nil {
		log.Error("Couldn't open '%s'\n", s)
		return
	}
	gz, err := gzip.NewReader(fi)
	if err != nil {
		log.Error("Couldn't read from '%s'\n", s)
		return
	}

	buf := bufio.NewReader(gz)
	for {
		line, err := buf.ReadBytes('\n')
		if err != nil {
			break
		}
		if bytes.HasPrefix(line, RECORD_SERVICE) {
			service := &model.Service{}
			json.Unmarshal(line[5:], &service)
			f.services[service.Name] = service
		} else if bytes.HasPrefix(line, RECORD_VIEW) {
			view := &model.View{}
			json.Unmarshal(line[5:], &view)
			f.views[view.Name] = view
		} else {
			log.Info("Found unexpected line in database - skipping")
		}
	}
	log.Info("Loaded %d services and %d views from '%s'",
		len(f.services), len(f.views), s)
}

func (f FileBackend) saveAll(counters metrics.Metrics) {
	start := time.Now()
	s := f.cfg.Filename + ".new"
	fi, err := os.OpenFile(s, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0640)
	if err != nil {
		log.Error("Error creating file\n")
		return
	}
	gz := gzip.NewWriter(fi)

	for _, service := range f.services {
		b, _ := json.Marshal(service)
		gz.Write(RECORD_SERVICE)
		gz.Write(b)
		gz.Write(NEWLINE)
	}
	for _, view := range f.views {
		b, _ := json.Marshal(view)
		gz.Write(RECORD_VIEW)
		gz.Write(b)
		gz.Write(NEWLINE)
	}
	gz.Close()
	fi.Close()
	if err = os.Rename(s, f.cfg.Filename); err != nil {
		log.Error("Failed to overwrite database")
		return
	}
	duration := time.Since(start)
	log.Debug("Saved %d items in %d ms", len(f.services)+len(f.views),
		duration.Nanoseconds()/1000000)

	counters.IncCounter("db.save.count")
	counters.SetGauge("db.save.duration", int(duration.Nanoseconds()/1000000))
	counters.SetGauge("service.count", len(f.services))
	counters.SetGauge("view.count", len(f.views))
}

type update struct {
	setService    *model.Service
	setView       *model.View
	deleteService string
	deleteView    string
}

func (f FileBackend) fileSaver(counters metrics.Metrics) {
	period := time.Duration(f.cfg.Interval) * time.Second
	ticker := time.NewTicker(period)
	for {
		select {
		case <-ticker.C:
			f.saveAll(counters)
		case reply := <-f.sync:
			f.saveAll(counters)
			reply <- true
		case upd := <-f.q:
			if upd.setService != nil {
				f.services[upd.setService.Name] = upd.setService
			}
			if upd.deleteService != "" {
				delete(f.services, upd.deleteService)
			}
			if upd.setView != nil {
				f.views[upd.setView.Name] = upd.setView
			}
			if upd.deleteView != "" {
				delete(f.views, upd.deleteView)
			}
		}
	}
}

func NewFileBackend(cfg *config.ConfigDatabase, m metrics.Metrics) Backend {
	var q = make(chan update, MAX_PENDING_WRITES)
	be := FileBackend{
		cfg:      cfg,
		q:        q,
		sync:     make(chan chan bool),
		services: make(map[string]*model.Service),
		views:    make(map[string]*model.View),
	}
	be.readAll()
	go be.fileSaver(m)
	return be
}
<|endoftext|>


<commit_before>package bouncermain_test

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/pjwerneck/bouncer/bouncermain"
)

var (
	server *httptest.Server
	reader io.Reader
)

func init() {
	server = httptest.NewServer(bouncermain.Router())
}

func GetRequest(url string) (status int, body string, err error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return
	}

	rep, err := http.DefaultClient.Do(req)
	if err != nil {
		return
	}

	bs, err := ioutil.ReadAll(rep.Body)
	if err != nil {
		return
	}

	body = string(bs)
	status = rep.StatusCode

	return
}

func TestGetToken(t *testing.T) {
	url := fmt.Sprintf("%s/v1/tokenbucket/testingbucket/acquire?size=10", server.URL)

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		t.Error(err)
	}

	rep, err := http.DefaultClient.Do(req)
	if err != nil {
		t.Error(err)
	}

	if rep.StatusCode != 204 {
		t.Errorf("204 No Content expected: %d", rep.StatusCode)
	}

}

func TestGetTokens(t *testing.T) {
	url := fmt.Sprintf("%s/v1/tokenbucket/testingbucket/acquire?size=10&maxwait=1", server.URL)

	for i := 0; i < 9; i++ {
		status, body, err := GetRequest(url)
		if err != nil {
			t.Error(err)
		}
		if status != 204 {
			t.Errorf("204 No Content expected: %v %v", status, body)
		}
	}

	status, body, err := GetRequest(url)
	if err != nil {
		t.Error(err)
	}
	if status != 408 {
		t.Errorf("408 Request Timeout expected: %v %v", status, body)
	}

}
<commit_msg>Add more tests<commit_after>package bouncermain_test

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/pjwerneck/bouncer/bouncermain"
	"github.com/stretchr/testify/require"
)

var (
	server *httptest.Server
	reader io.Reader
)

func init() {
	server = httptest.NewServer(bouncermain.Router())
}

func GetRequest(url string) (status int, body string, err error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return
	}

	rep, err := http.DefaultClient.Do(req)
	if err != nil {
		return
	}

	bs, err := ioutil.ReadAll(rep.Body)
	if err != nil {
		return
	}

	body = string(bs)
	status = rep.StatusCode

	return
}

func TestGetTokensUntilEmpty(t *testing.T) {
	url := fmt.Sprintf("%s/v1/tokenbucket/test1/acquire?size=100&maxwait=1", server.URL)
	n := 0
	for {
		status, _, err := GetRequest(url)
		require.Nil(t, err)
		if status != 204 {
			break
		}
		n++
	}
	require.Equal(t, 100, n)
}

func TestGetTokensUntilEmptyAndWaitForRefill(t *testing.T) {
	url := fmt.Sprintf("%s/v1/tokenbucket/test2/acquire?size=10&maxwait=1&interval=10", server.URL)

	n := 0
	for {
		status, _, err := GetRequest(url)
		require.Nil(t, err)
		if status != 204 {
			break
		}
		n++
	}
	require.Equal(t, 10, n)

	time.Sleep(time.Duration(10 * 1e6))
	for {
		status, _, err := GetRequest(url)
		require.Nil(t, err)
		if status != 204 {
			break
		}
		n++
	}
	require.Equal(t, 20, n)

}

func TestSemaphoreAcquireAndRelease(t *testing.T) {
	url := fmt.Sprintf("%s/v1/semaphore/test1/acquire?maxwait=1", server.URL)

	status, key, err := GetRequest(url)
	require.Nil(t, err)
	require.Equal(t, 200, status)

	status, _, err = GetRequest(url)
	require.Nil(t, err)
	require.Equal(t, 408, status)

	url = fmt.Sprintf("%s/v1/semaphore/test1/release?maxwait=1&key=%s", server.URL, key)
	status, key, err = GetRequest(url)
	require.Nil(t, err)
	require.Equal(t, 204, status)

}

func TestEventWaitAndSend(t *testing.T) {
	time.AfterFunc(time.Duration(1e7),
		func() {

			status, _, err := GetRequest(fmt.Sprintf("%s/v1/event/test1/send", server.URL))
			require.Nil(t, err)
			require.Equal(t, 204, status)
		})

	status, _, err := GetRequest(fmt.Sprintf("%s/v1/event/test1/wait?maxwait=100", server.URL))
	require.Nil(t, err)
	require.Equal(t, 204, status)
}
<|endoftext|>


<commit_before>package kafkareporter

import (
	"github.com/Shopify/sarama"
)

type Reporter interface {
	Report([]byte)
	Stop() error
}

type KafkaReporterConfig struct {
	BrokerAddr string
	Topic      string
}

type kafkaReporter struct {
	KafkaReporterConfig
	producer sarama.AsyncProducer
}

func NewReporter(c KafkaReporterConfig) (Reporter, error) {
	logger.WithField("config", c).Info("NewReporter")
	saramaConfig := sarama.NewConfig()
	saramaConfig.Version = sarama.V0_10_1_0
	producer, err := sarama.NewAsyncProducer([]string{c.BrokerAddr}, saramaConfig)
	if err != nil {
		//TODO Cosmin
		return nil, err
	}
	return kafkaReporter{c, producer}, nil
}

func (kr *kafkaReporter) Report(b []byte) {
	kr.producer.Input() <- &sarama.ProducerMessage{
		Topic: kr.Topic,
		Value: sarama.ByteEncoder(b),
	}
}

func (kr *kafkaReporter) Stop() error {
	logger.Info("Stop")
	if err := kr.producer.Close(); err != nil {
		//TODO Cosmin
		return err
	}
	return nil
}
<commit_msg>simplified kafkareporter<commit_after>package kafkareporter

import (
	"github.com/Shopify/sarama"
	"time"
	"io"
)

type Reporter interface {
	io.Closer
	Report([]byte)
}

type Config struct {
	Brokers []string
	Topic   string
}

type reporter struct {
	Config

	producer sarama.AsyncProducer
}

func NewReporter(c Config) (Reporter, error) {
	logger.WithField("config", c).Info("NewReporter")
	saramaConfig := sarama.NewConfig()
	saramaConfig.Version = sarama.V0_10_1_0
	saramaConfig.Producer.Return.Errors = false
	saramaConfig.Producer.Retry.Max = 10
	saramaConfig.Producer.Retry.Backoff = time.Second
	p, err := sarama.NewAsyncProducer(c.Brokers, saramaConfig)
	if err != nil {
		logger.WithError(err).Error("Could not create AsyncProducer")
		return nil, err
	}
	return &reporter{
		Config:   c,
		producer: p,
	}, nil
}

func (r *reporter) Report(b []byte) {
	r.producer.Input() <- &sarama.ProducerMessage{
		Topic: r.Topic,
		Value: sarama.ByteEncoder(b),
	}
}

func (r *reporter) Close() error {
	logger.Info("Close")
	if err := r.producer.Close(); err != nil {
		logger.WithError(err).Error("Could not close Kafka Producer")
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"net/http"
	"os"

	"github.com/taironas/route"
)

var root = flag.String("root", "app", "file system path")

func init() {
	log.SetFlags(log.Ltime | log.Ldate | log.Lshortfile)
}

func main() {
	r := new(route.Router)

	r.AddStaticResource(root)

	log.Println("Listening on " + os.Getenv("PORT"))
	err := http.ListenAndServe(":"+os.Getenv("PORT"), r)
	if err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}
<commit_msg>simple api/helloworld route<commit_after>package main

import (
	"encoding/json"
	"flag"
	"log"
	"net/http"
	"os"

	"github.com/taironas/route"
)

var root = flag.String("root", "app", "file system path")

func init() {
	log.SetFlags(log.Ltime | log.Ldate | log.Lshortfile)
}

func main() {
	r := new(route.Router)

	r.HandleFunc("/api/hello", helloWorld)

	r.AddStaticResource(root)

	log.Println("Listening on " + os.Getenv("PORT"))
	err := http.ListenAndServe(":"+os.Getenv("PORT"), r)
	if err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}

// helloWorld handler returns a json file with a helloworld message.
//
func helloWorld(w http.ResponseWriter, r *http.Request) {
	data := struct {
		Message string
	}{
		"hello world",
	}

	if err := renderJson(w, data); err != nil {
		log.Println(err)
	}
}

// renderJson renders data to json and writes it to response writer
func renderJson(w http.ResponseWriter, data interface{}) error {
	return json.NewEncoder(w).Encode(data)
}
<|endoftext|>