<commit_before>package api

import (
	"fmt"
	"github.com/nais/naisd/api/app"
	v1 "k8s.io/api/core/v1"
	k8sapps "k8s.io/api/apps/v1"
	k8smeta "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/client-go/kubernetes"
)

const (
	defaultRedisPort          = 6379
	defaultRedisExporterPort  = 9121
	defaultRedisExporterImage = "oliver006/redis_exporter:v1.0.4-alpine"
	defaultRedisImage         = "redis:5-alpine"
)

type Redis struct {
	Enabled  bool
	Image    string
	Limits   ResourceList
	Requests ResourceList
}

func updateDefaultRedisValues(redis Redis) Redis {
	if redis.Image == "" {
		redis.Image = defaultRedisImage
	}
	if len(redis.Limits.Cpu) == 0 {
		redis.Limits.Cpu = "100m"
	}
	if len(redis.Limits.Memory) == 0 {
		redis.Limits.Memory = "128Mi"
	}
	if len(redis.Requests.Cpu) == 0 {
		redis.Requests.Cpu = "100m"
	}
	if len(redis.Requests.Memory) == 0 {
		redis.Requests.Memory = "128Mi"
	}
	return redis
}

func createRedisPodSpec(redis Redis) v1.PodSpec {
	return v1.PodSpec{
		Containers: []v1.Container{
			{
				Name:  "redis",
				Image: redis.Image,
				Resources: createResourceLimits(redis.Requests.Cpu, redis.Requests.Memory,
					redis.Limits.Cpu, redis.Limits.Memory),
				ImagePullPolicy: v1.PullIfNotPresent,
				Ports: []v1.ContainerPort{
					{
						ContainerPort: int32(defaultRedisPort),
						Name:          DefaultPortName,
						Protocol:      v1.ProtocolTCP,
					},
				},
			},
			{
				Name:  "exporter",
				Image: defaultRedisExporterImage,
				Resources: createResourceLimits("100m", "100Mi",
					"100m", "100Mi"),
				ImagePullPolicy: v1.PullIfNotPresent,
				Ports: []v1.ContainerPort{
					{
						ContainerPort: int32(defaultRedisExporterPort),
						Name:          DefaultPortName,
						Protocol:      v1.ProtocolTCP,
					},
				},
			},
		},
	}
}

func createRedisDeploymentSpec(resourceName string, spec app.Spec, redis Redis) k8sapps.DeploymentSpec {
	objectMeta := generateObjectMeta(spec)
	objectMeta.Name = resourceName
	objectMeta.Annotations = map[string]string{
		"prometheus.io/scrape": "true",
		"prometheus.io/port":   string(defaultRedisExporterPort),
		"prometheus.io/path":   "/metrics",
	}

	return k8sapps.DeploymentSpec{
		Replicas: int32p(1),
		Selector: &k8smeta.LabelSelector{
			MatchLabels: createPodSelector(spec),
		},
		Strategy: k8sapps.DeploymentStrategy{
			Type: k8sapps.RecreateDeploymentStrategyType,
		},
		ProgressDeadlineSeconds: int32p(300),
		RevisionHistoryLimit:    int32p(10),
		Template: v1.PodTemplateSpec{
			ObjectMeta: objectMeta,
			Spec:       createRedisPodSpec(redis),
		},
	}
}

func createRedisDeploymentDef(resourceName string, spec app.Spec, redis Redis, existingDeployment *k8sapps.Deployment) *k8sapps.Deployment {
	deploymentSpec := createRedisDeploymentSpec(resourceName, spec, redis)
	if existingDeployment != nil {
		existingDeployment.ObjectMeta = addLabelsToObjectMeta(existingDeployment.ObjectMeta, spec)
		existingDeployment.Spec = deploymentSpec
		return existingDeployment
	} else {
		return &k8sapps.Deployment{
			TypeMeta: k8smeta.TypeMeta{
				Kind:       "Deployment",
				APIVersion: "apps/v1beta1",
			},
			ObjectMeta: generateObjectMeta(spec),
			Spec:       deploymentSpec,
		}
	}
}

func createOrUpdateRedisInstance(spec app.Spec, redis Redis, k8sClient kubernetes.Interface) (*k8sapps.Deployment, error) {
	redisName := fmt.Sprintf("%s-redis", spec.ResourceName())
	existingDeployment, err := getExistingDeployment(redisName, spec.Namespace, k8sClient)

	if err != nil {
		return nil, fmt.Errorf("unable to get existing deployment: %s", err)
	}

	deploymentDef := createRedisDeploymentDef(redisName, spec, redis, existingDeployment)
	deploymentDef.Name = fmt.Sprintf("%s-redis", spec.ResourceName())

	return createOrUpdateDeploymentResource(deploymentDef, spec.Namespace, k8sClient)
}

func createRedisServiceDef(spec app.Spec) *v1.Service {
	return &v1.Service{
		TypeMeta: k8smeta.TypeMeta{
			Kind:       "Service",
			APIVersion: "v1",
		},
		ObjectMeta: generateObjectMeta(spec),
		Spec: v1.ServiceSpec{
			Type:     v1.ServiceTypeClusterIP,
			Selector: createPodSelector(spec),
			Ports: []v1.ServicePort{
				{
					Name:     DefaultPortName,
					Protocol: v1.ProtocolTCP,
					Port:     6379,
					TargetPort: intstr.IntOrString{
						Type:   intstr.String,
						StrVal: DefaultPortName,
					},
				},
			},
		},
	}
}

func createOrUpdateRedisService(spec app.Spec, k8sClient kubernetes.Interface) (*v1.Service, error) {
	redisName := fmt.Sprintf("%s-redis", spec.ResourceName())
	service, err := getExistingService(redisName, spec.Namespace, k8sClient)

	if err != nil {
		return nil, fmt.Errorf("unable to get existing service: %s", err)
	} else if service == nil {
		service = createRedisServiceDef(spec)
		service.Name = redisName
	}

	service.ObjectMeta = addLabelsToObjectMeta(service.ObjectMeta, spec)
	return createOrUpdateServiceResource(service, spec.Namespace, k8sClient)
}
<commit_msg>Feil type konvertering<commit_after>package api

import (
	"fmt"
	"github.com/nais/naisd/api/app"
	v1 "k8s.io/api/core/v1"
	k8sapps "k8s.io/api/apps/v1"
	k8smeta "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/client-go/kubernetes"
	"strconv"
)

const (
	defaultRedisPort          = 6379
	defaultRedisExporterPort  = 9121
	defaultRedisExporterImage = "oliver006/redis_exporter:v1.0.4-alpine"
	defaultRedisImage         = "redis:5-alpine"
)

type Redis struct {
	Enabled  bool
	Image    string
	Limits   ResourceList
	Requests ResourceList
}

func updateDefaultRedisValues(redis Redis) Redis {
	if redis.Image == "" {
		redis.Image = defaultRedisImage
	}
	if len(redis.Limits.Cpu) == 0 {
		redis.Limits.Cpu = "100m"
	}
	if len(redis.Limits.Memory) == 0 {
		redis.Limits.Memory = "128Mi"
	}
	if len(redis.Requests.Cpu) == 0 {
		redis.Requests.Cpu = "100m"
	}
	if len(redis.Requests.Memory) == 0 {
		redis.Requests.Memory = "128Mi"
	}
	return redis
}

func createRedisPodSpec(redis Redis) v1.PodSpec {
	return v1.PodSpec{
		Containers: []v1.Container{
			{
				Name:  "redis",
				Image: redis.Image,
				Resources: createResourceLimits(redis.Requests.Cpu, redis.Requests.Memory,
					redis.Limits.Cpu, redis.Limits.Memory),
				ImagePullPolicy: v1.PullIfNotPresent,
				Ports: []v1.ContainerPort{
					{
						ContainerPort: int32(defaultRedisPort),
						Name:          DefaultPortName,
						Protocol:      v1.ProtocolTCP,
					},
				},
			},
			{
				Name:  "exporter",
				Image: defaultRedisExporterImage,
				Resources: createResourceLimits("100m", "100Mi",
					"100m", "100Mi"),
				ImagePullPolicy: v1.PullIfNotPresent,
				Ports: []v1.ContainerPort{
					{
						ContainerPort: int32(defaultRedisExporterPort),
						Name:          DefaultPortName,
						Protocol:      v1.ProtocolTCP,
					},
				},
			},
		},
	}
}

func createRedisDeploymentSpec(resourceName string, spec app.Spec, redis Redis) k8sapps.DeploymentSpec {
	objectMeta := generateObjectMeta(spec)
	objectMeta.Name = resourceName
	objectMeta.Annotations = map[string]string{
		"prometheus.io/scrape": "true",
		"prometheus.io/port":   strconv.Itoa(defaultRedisExporterPort),
		"prometheus.io/path":   "/metrics",
	}

	return k8sapps.DeploymentSpec{
		Replicas: int32p(1),
		Selector: &k8smeta.LabelSelector{
			MatchLabels: createPodSelector(spec),
		},
		Strategy: k8sapps.DeploymentStrategy{
			Type: k8sapps.RecreateDeploymentStrategyType,
		},
		ProgressDeadlineSeconds: int32p(300),
		RevisionHistoryLimit:    int32p(10),
		Template: v1.PodTemplateSpec{
			ObjectMeta: objectMeta,
			Spec:       createRedisPodSpec(redis),
		},
	}
}

func createRedisDeploymentDef(resourceName string, spec app.Spec, redis Redis, existingDeployment *k8sapps.Deployment) *k8sapps.Deployment {
	deploymentSpec := createRedisDeploymentSpec(resourceName, spec, redis)
	if existingDeployment != nil {
		existingDeployment.ObjectMeta = addLabelsToObjectMeta(existingDeployment.ObjectMeta, spec)
		existingDeployment.Spec = deploymentSpec
		return existingDeployment
	} else {
		return &k8sapps.Deployment{
			TypeMeta: k8smeta.TypeMeta{
				Kind:       "Deployment",
				APIVersion: "apps/v1beta1",
			},
			ObjectMeta: generateObjectMeta(spec),
			Spec:       deploymentSpec,
		}
	}
}

func createOrUpdateRedisInstance(spec app.Spec, redis Redis, k8sClient kubernetes.Interface) (*k8sapps.Deployment, error) {
	redisName := fmt.Sprintf("%s-redis", spec.ResourceName())
	existingDeployment, err := getExistingDeployment(redisName, spec.Namespace, k8sClient)

	if err != nil {
		return nil, fmt.Errorf("unable to get existing deployment: %s", err)
	}

	deploymentDef := createRedisDeploymentDef(redisName, spec, redis, existingDeployment)
	deploymentDef.Name = fmt.Sprintf("%s-redis", spec.ResourceName())

	return createOrUpdateDeploymentResource(deploymentDef, spec.Namespace, k8sClient)
}

func createRedisServiceDef(spec app.Spec) *v1.Service {
	return &v1.Service{
		TypeMeta: k8smeta.TypeMeta{
			Kind:       "Service",
			APIVersion: "v1",
		},
		ObjectMeta: generateObjectMeta(spec),
		Spec: v1.ServiceSpec{
			Type:     v1.ServiceTypeClusterIP,
			Selector: createPodSelector(spec),
			Ports: []v1.ServicePort{
				{
					Name:     DefaultPortName,
					Protocol: v1.ProtocolTCP,
					Port:     6379,
					TargetPort: intstr.IntOrString{
						Type:   intstr.String,
						StrVal: DefaultPortName,
					},
				},
			},
		},
	}
}

func createOrUpdateRedisService(spec app.Spec, k8sClient kubernetes.Interface) (*v1.Service, error) {
	redisName := fmt.Sprintf("%s-redis", spec.ResourceName())
	service, err := getExistingService(redisName, spec.Namespace, k8sClient)

	if err != nil {
		return nil, fmt.Errorf("unable to get existing service: %s", err)
	} else if service == nil {
		service = createRedisServiceDef(spec)
		service.Name = redisName
	}

	service.ObjectMeta = addLabelsToObjectMeta(service.ObjectMeta, spec)
	return createOrUpdateServiceResource(service, spec.Namespace, k8sClient)
}
<|endoftext|>


<commit_before>// Copyright 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 cmd

import (
	"github.com/cilium/cilium/common"
	"github.com/cilium/cilium/pkg/command"
	"github.com/cilium/cilium/pkg/maps/lbmap"

	"github.com/spf13/cobra"
)

const (
	idTitle             = "ID"
	serviceAddressTitle = "SERVICE ADDRESS"
	backendAddressTitle = "BACKEND ADDRESS"
)

var listRevNAT bool

// bpfCtListCmd represents the bpf_ct_list command
var bpfLBListCmd = &cobra.Command{
	Use:     "list",
	Aliases: []string{"ls"},
	Short:   "List load-balancing configuration",
	Run: func(cmd *cobra.Command, args []string) {
		common.RequireRootPrivilege("cilium bpf lb list")

		var firstTitle string
		serviceList := make(map[string][]string)
		if listRevNAT {
			firstTitle = idTitle
			if err := lbmap.RevNat4Map.DumpIfExists(serviceList); err != nil {
				Fatalf("Unable to dump IPv4 reverse NAT table: %s", err)
			}
			if err := lbmap.RevNat6Map.DumpIfExists(serviceList); err != nil {
				Fatalf("Unable to dump IPv6 reverse NAT table: %s", err)
			}
		} else {
			firstTitle = serviceAddressTitle
			if err := lbmap.Service4Map.DumpIfExists(serviceList); err != nil {
				Fatalf("Unable to dump IPv4 services table: %s", err)
			}
			if err := lbmap.Service6Map.DumpIfExists(serviceList); err != nil {
				Fatalf("Unable to dump IPv6 services table: %s", err)
			}
		}

		if command.OutputJSON() {
			if err := command.PrintOutput(serviceList); err != nil {
				Fatalf("Unable to generate JSON output: %s", err)
			}
			return
		}

		TablePrinter(firstTitle, backendAddressTitle, serviceList)
	},
}

func init() {
	bpfLBCmd.AddCommand(bpfLBListCmd)
	bpfLBListCmd.Flags().BoolVarP(&listRevNAT, "revnat", "", false, "List reverse NAT entries")
	command.AddJSONOutput(bpfLBListCmd)
}
<commit_msg>cli: Use svc v2 maps when listing<commit_after>// Copyright 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 cmd

import (
	"fmt"

	"github.com/cilium/cilium/common"
	"github.com/cilium/cilium/pkg/bpf"
	"github.com/cilium/cilium/pkg/command"
	"github.com/cilium/cilium/pkg/loadbalancer"
	"github.com/cilium/cilium/pkg/maps/lbmap"

	"github.com/spf13/cobra"
)

const (
	idTitle             = "ID"
	serviceAddressTitle = "SERVICE ADDRESS"
	backendAddressTitle = "BACKEND ADDRESS"
)

var listRevNAT bool

// bpfCtListCmd represents the bpf_ct_list command
var bpfLBListCmd = &cobra.Command{
	Use:     "list",
	Aliases: []string{"ls"},
	Short:   "List load-balancing configuration",
	Run: func(cmd *cobra.Command, args []string) {
		common.RequireRootPrivilege("cilium bpf lb list")

		var firstTitle string
		serviceList := make(map[string][]string)
		if listRevNAT {
			firstTitle = idTitle
			if err := lbmap.RevNat4Map.DumpIfExists(serviceList); err != nil {
				Fatalf("Unable to dump IPv4 reverse NAT table: %s", err)
			}
			if err := lbmap.RevNat6Map.DumpIfExists(serviceList); err != nil {
				Fatalf("Unable to dump IPv6 reverse NAT table: %s", err)
			}
		} else {
			// It's safe to use the same map for both IPv4 and IPv6, as backend
			// IDs are allocated from the same pool regardless the protocol
			backendMap := make(map[loadbalancer.BackendID]lbmap.BackendValue)

			parseBackendEntry := func(key bpf.MapKey, value bpf.MapValue) {
				id := key.(lbmap.BackendKey).GetID()
				backendMap[id] = value.(lbmap.BackendValue)
			}
			if err := lbmap.Backend4Map.DumpWithCallbackIfExists(parseBackendEntry); err != nil {
				Fatalf("Unable to dump IPv4 backends table: %s", err)
			}
			if err := lbmap.Backend6Map.DumpWithCallbackIfExists(parseBackendEntry); err != nil {
				Fatalf("Unable to dump IPv6 backends table: %s", err)
			}

			parseSVCEntry := func(key bpf.MapKey, value bpf.MapValue) {
				var entry string

				svcKey := key.(lbmap.ServiceKeyV2)
				svcVal := value.(lbmap.ServiceValueV2)
				svc := svcKey.String()
				revNATID := svcVal.GetRevNat()
				backendID := svcVal.GetBackendID()

				if backendID == 0 {
					ip := "0.0.0.0"
					if svcKey.IsIPv6() {
						ip = "[::]"
					}
					entry = fmt.Sprintf("%s:%d (%d)", ip, 0, revNATID)
				} else if backend, found := backendMap[backendID]; !found {
					entry = fmt.Sprintf("backend %d not found", backendID)
				} else {
					fmtStr := "%s:%d (%d)"
					if svcKey.IsIPv6() {
						fmtStr = "[%s]:%d (%d)"
					}
					entry = fmt.Sprintf(fmtStr, backend.GetAddress(),
						backend.GetPort(), revNATID)
				}

				serviceList[svc] = append(serviceList[svc], entry)
			}

			firstTitle = serviceAddressTitle
			if err := lbmap.Service4MapV2.DumpWithCallback(parseSVCEntry); err != nil {
				Fatalf("Unable to dump IPv4 services table: %s", err)
			}
			if err := lbmap.Service6MapV2.DumpWithCallback(parseSVCEntry); err != nil {
				Fatalf("Unable to dump IPv6 services table: %s", err)
			}
		}

		if command.OutputJSON() {
			if err := command.PrintOutput(serviceList); err != nil {
				Fatalf("Unable to generate JSON output: %s", err)
			}
			return
		}

		TablePrinter(firstTitle, backendAddressTitle, serviceList)
	},
}

func init() {
	bpfLBCmd.AddCommand(bpfLBListCmd)
	bpfLBListCmd.Flags().BoolVarP(&listRevNAT, "revnat", "", false, "List reverse NAT entries")
	command.AddJSONOutput(bpfLBListCmd)
}
<|endoftext|>


<commit_before>package controllers

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"

	"github.com/eirka/eirka-libs/audit"
	"github.com/eirka/eirka-libs/config"
	e "github.com/eirka/eirka-libs/errors"
	"github.com/eirka/eirka-libs/redis"
	"github.com/eirka/eirka-libs/user"

	"github.com/eirka/eirka-post/models"
	u "github.com/eirka/eirka-post/utils"
)

// Input from new reply form
type replyForm struct {
	Key     string `form:"askey" binding:"required"`
	Comment string `form:"comment"`
	Thread  uint   `form:"thread" binding:"required"`
}

// ReplyController handles the creation of new threads
func ReplyController(c *gin.Context) {
	var err error
	var rf replyForm
	req := c.Request

	// get userdata from session middleware
	userdata := c.MustGet("userdata").(user.User)

	// check size of content
	if req.ContentLength > int64(config.Settings.Limits.ImageMaxSize) {
		c.JSON(http.StatusExpectationFailed, gin.H{"error_message": e.ErrImageSize.Error()})
		c.Error(e.ErrImageSize)
		return
	}

	// set max bytes reader
	req.Body = http.MaxBytesReader(c.Writer, req.Body, int64(config.Settings.Limits.ImageMaxSize))

	err = c.Bind(&rf)
	if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInvalidParam))
		c.Error(err)
		return
	}

	// Test for antispam key from Prim
	antispam := rf.Key
	if antispam != config.Settings.Antispam.AntispamKey {
		c.JSON(http.StatusBadRequest, gin.H{"error_message": e.ErrInvalidKey.Error()})
		c.Error(e.ErrInvalidKey)
		return
	}

	// Set parameters to ReplyModel
	m := models.ReplyModel{
		Uid:     userdata.Id,
		Ip:      c.ClientIP(),
		Comment: rf.Comment,
		Thread:  rf.Thread,
		Image:   true,
	}

	image := u.ImageType{}

	// Check if theres a file
	image.File, image.Header, err = req.FormFile("file")
	if err == http.ErrMissingFile {
		m.Image = false
	}

	// Validate input parameters
	err = m.ValidateInput()
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error_message": err.Error()})
		c.Error(err)
		return
	}

	// Check thread status
	err = m.Status()
	if err == e.ErrThreadClosed {
		c.JSON(http.StatusBadRequest, gin.H{"error_message": err.Error()})
		c.Error(err)
		return
	} else if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInternalError))
		c.Error(err)
		return
	}

	if m.Comment != "" {

		// Check comment in SFS and Akismet
		check := u.CheckComment{
			Ip:      m.Ip,
			Ua:      req.UserAgent(),
			Referer: req.Referer(),
			Comment: m.Comment,
		}

		err = check.Get()
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error_message": err.Error()})
			c.Error(err)
			return
		}

	}

	if m.Image {

		// process the uploaded file, this creates an md5
		err = image.ProcessFile()
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error_message": err.Error()})
			c.Error(err)
			return
		}

		// Set MD5 from results
		m.MD5 = image.MD5

		// Initialize check duplicate
		duplicate := u.CheckDuplicate{
			Ib:  m.Ib,
			MD5: m.MD5,
		}

		// Check database for duplicate image hashes
		err = duplicate.Get()
		if err == e.ErrDuplicateImage {
			c.JSON(http.StatusBadRequest, gin.H{"error_message": err.Error(), "thread": duplicate.Thread, "post": duplicate.Post})
			c.Error(err)
			return
		} else if err != nil {
			c.JSON(e.ErrorMessage(e.ErrInternalError))
			c.Error(err)
			return
		}

		if image.Ext == ".webm" {

			// Save the webm to a file
			err = image.SaveWebM()
			if err != nil {
				c.JSON(http.StatusBadRequest, gin.H{"error_message": err.Error()})
				c.Error(err)
				return
			}

		} else {

			// Save the image to a file
			err = image.SaveImage()
			if err != nil {
				c.JSON(http.StatusBadRequest, gin.H{"error_message": err.Error()})
				c.Error(err)
				return
			}

		}

		m.OrigWidth = image.OrigWidth
		m.OrigHeight = image.OrigHeight
		m.ThumbWidth = image.ThumbWidth
		m.ThumbHeight = image.ThumbHeight
		m.Filename = image.Filename
		m.Thumbnail = image.Thumbnail

	}

	// Post data
	err = m.Post()
	if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInternalError))
		c.Error(err)
		return
	}

	// Initialize cache handle
	cache := redis.RedisCache

	// Delete redis stuff
	index_key := fmt.Sprintf("%s:%d", "index", m.Ib)
	directory_key := fmt.Sprintf("%s:%d", "directory", m.Ib)
	thread_key := fmt.Sprintf("%s:%d:%d", "thread", m.Ib, m.Thread)
	image_key := fmt.Sprintf("%s:%d", "image", m.Ib)

	err = cache.Delete(index_key, directory_key, thread_key, image_key)
	if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInternalError))
		c.Error(err)
		return
	}

	// get board domain and redirect to it
	redirect, err := u.Link(m.Ib, req.Referer())
	if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInternalError))
		c.Error(err)
		return
	}

	c.Redirect(303, redirect)

	audit := audit.Audit{
		User:   userdata.Id,
		Ib:     m.Ib,
		Ip:     m.Ip,
		Action: audit.AuditReply,
		Info:   fmt.Sprintf("%d/%d", m.Thread),
	}

	err = audit.Submit()
	if err != nil {
		c.Error(err)
	}

	return

}
<commit_msg>fix reply audit<commit_after>package controllers

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"

	"github.com/eirka/eirka-libs/audit"
	"github.com/eirka/eirka-libs/config"
	e "github.com/eirka/eirka-libs/errors"
	"github.com/eirka/eirka-libs/redis"
	"github.com/eirka/eirka-libs/user"

	"github.com/eirka/eirka-post/models"
	u "github.com/eirka/eirka-post/utils"
)

// Input from new reply form
type replyForm struct {
	Key     string `form:"askey" binding:"required"`
	Comment string `form:"comment"`
	Thread  uint   `form:"thread" binding:"required"`
}

// ReplyController handles the creation of new threads
func ReplyController(c *gin.Context) {
	var err error
	var rf replyForm
	req := c.Request

	// get userdata from session middleware
	userdata := c.MustGet("userdata").(user.User)

	// check size of content
	if req.ContentLength > int64(config.Settings.Limits.ImageMaxSize) {
		c.JSON(http.StatusExpectationFailed, gin.H{"error_message": e.ErrImageSize.Error()})
		c.Error(e.ErrImageSize)
		return
	}

	// set max bytes reader
	req.Body = http.MaxBytesReader(c.Writer, req.Body, int64(config.Settings.Limits.ImageMaxSize))

	err = c.Bind(&rf)
	if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInvalidParam))
		c.Error(err)
		return
	}

	// Test for antispam key from Prim
	antispam := rf.Key
	if antispam != config.Settings.Antispam.AntispamKey {
		c.JSON(http.StatusBadRequest, gin.H{"error_message": e.ErrInvalidKey.Error()})
		c.Error(e.ErrInvalidKey)
		return
	}

	// Set parameters to ReplyModel
	m := models.ReplyModel{
		Uid:     userdata.Id,
		Ip:      c.ClientIP(),
		Comment: rf.Comment,
		Thread:  rf.Thread,
		Image:   true,
	}

	image := u.ImageType{}

	// Check if theres a file
	image.File, image.Header, err = req.FormFile("file")
	if err == http.ErrMissingFile {
		m.Image = false
	}

	// Validate input parameters
	err = m.ValidateInput()
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error_message": err.Error()})
		c.Error(err)
		return
	}

	// Check thread status
	err = m.Status()
	if err == e.ErrThreadClosed {
		c.JSON(http.StatusBadRequest, gin.H{"error_message": err.Error()})
		c.Error(err)
		return
	} else if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInternalError))
		c.Error(err)
		return
	}

	if m.Comment != "" {

		// Check comment in SFS and Akismet
		check := u.CheckComment{
			Ip:      m.Ip,
			Ua:      req.UserAgent(),
			Referer: req.Referer(),
			Comment: m.Comment,
		}

		err = check.Get()
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error_message": err.Error()})
			c.Error(err)
			return
		}

	}

	if m.Image {

		// process the uploaded file, this creates an md5
		err = image.ProcessFile()
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error_message": err.Error()})
			c.Error(err)
			return
		}

		// Set MD5 from results
		m.MD5 = image.MD5

		// Initialize check duplicate
		duplicate := u.CheckDuplicate{
			Ib:  m.Ib,
			MD5: m.MD5,
		}

		// Check database for duplicate image hashes
		err = duplicate.Get()
		if err == e.ErrDuplicateImage {
			c.JSON(http.StatusBadRequest, gin.H{"error_message": err.Error(), "thread": duplicate.Thread, "post": duplicate.Post})
			c.Error(err)
			return
		} else if err != nil {
			c.JSON(e.ErrorMessage(e.ErrInternalError))
			c.Error(err)
			return
		}

		if image.Ext == ".webm" {

			// Save the webm to a file
			err = image.SaveWebM()
			if err != nil {
				c.JSON(http.StatusBadRequest, gin.H{"error_message": err.Error()})
				c.Error(err)
				return
			}

		} else {

			// Save the image to a file
			err = image.SaveImage()
			if err != nil {
				c.JSON(http.StatusBadRequest, gin.H{"error_message": err.Error()})
				c.Error(err)
				return
			}

		}

		m.OrigWidth = image.OrigWidth
		m.OrigHeight = image.OrigHeight
		m.ThumbWidth = image.ThumbWidth
		m.ThumbHeight = image.ThumbHeight
		m.Filename = image.Filename
		m.Thumbnail = image.Thumbnail

	}

	// Post data
	err = m.Post()
	if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInternalError))
		c.Error(err)
		return
	}

	// Initialize cache handle
	cache := redis.RedisCache

	// Delete redis stuff
	index_key := fmt.Sprintf("%s:%d", "index", m.Ib)
	directory_key := fmt.Sprintf("%s:%d", "directory", m.Ib)
	thread_key := fmt.Sprintf("%s:%d:%d", "thread", m.Ib, m.Thread)
	image_key := fmt.Sprintf("%s:%d", "image", m.Ib)

	err = cache.Delete(index_key, directory_key, thread_key, image_key)
	if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInternalError))
		c.Error(err)
		return
	}

	// get board domain and redirect to it
	redirect, err := u.Link(m.Ib, req.Referer())
	if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInternalError))
		c.Error(err)
		return
	}

	c.Redirect(303, redirect)

	audit := audit.Audit{
		User:   userdata.Id,
		Ib:     m.Ib,
		Ip:     m.Ip,
		Action: audit.AuditReply,
		Info:   fmt.Sprintf("%d", m.Thread),
	}

	err = audit.Submit()
	if err != nil {
		c.Error(err)
	}

	return

}
<|endoftext|>


<commit_before>package pelicantun

import (
	"testing"

	cv "github.com/glycerine/goconvey/convey"
)

//		psp := NewPelicanSocksProxy(PelicanSocksProxyConfig{})
//		psp.Start()
//		defer psp.Stop()

func TestPelicanSocksProxyHomePolicyMakerForLongPolling014(t *testing.T) {
	cv.Convey("When we start the PelicanSocksProxy, the long-polling implementation over two sockets should prefer to have one connection long-polling to the server (for the server to reply with data on) when the other connection is 'at home' waiting for the client to send data on.\n", t, func() {

		cv.Convey("Initiatially both alpha and beta txn are at home, and the home controller should indicate that beta should stay and alpha should go\n", func() {

			s := NewChaser()
			cv.So(s.home.alphaHome, cv.ShouldEqual, true)
			cv.So(s.home.betaHome, cv.ShouldEqual, true)

			// don't do s.Start(), since that will begin the automatic
			// alpha and beta making transitions on their own; instead
			// we test the s.home logic in isolation here.

			s.home.Start()
			defer s.home.Stop()
			cv.So(<-s.home.shouldBetaGoNow, cv.ShouldEqual, false)
			cv.So(<-s.home.shouldAlphaGoNow, cv.ShouldEqual, true)
		})
	})
}
<commit_msg>more home logic testing<commit_after>package pelicantun

import (
	"testing"

	cv "github.com/glycerine/goconvey/convey"
)

//		psp := NewPelicanSocksProxy(PelicanSocksProxyConfig{})
//		psp.Start()
//		defer psp.Stop()

func TestPelicanSocksProxyHomePolicyMakerForLongPolling014(t *testing.T) {
	cv.Convey("When we start the PelicanSocksProxy, the long-polling implementation over two sockets should prefer to have one connection long-polling to the server (for the server to reply with data on) when the other connection is 'at home' waiting for the client to send data on.\n", t, func() {

		cv.Convey("Initiatially both alpha and beta txn are at home, and the home controller should indicate that beta should stay and alpha should go\n", func() {

			s := NewChaser()
			cv.So(s.home.alphaHome, cv.ShouldEqual, true)
			cv.So(s.home.betaHome, cv.ShouldEqual, true)

			// don't do s.Start(), since that will begin the automatic
			// alpha and beta making transitions on their own; instead
			// we test the s.home logic in isolation here.

			s.home.Start()
			defer s.home.Stop()
			cv.So(<-s.home.shouldAlphaGoNow, cv.ShouldEqual, true)
			cv.So(<-s.home.shouldBetaGoNow, cv.ShouldEqual, false)

			s.home.alphaDepartsHome <- true
			cv.So(<-s.home.shouldAlphaGoNow, cv.ShouldEqual, false)
			cv.So(<-s.home.shouldBetaGoNow, cv.ShouldEqual, false)

			s.home.alphaArrivesHome <- true
			cv.So(<-s.home.shouldAlphaGoNow, cv.ShouldEqual, true)
			cv.So(<-s.home.shouldBetaGoNow, cv.ShouldEqual, false)

			s.home.betaDepartsHome <- true
			cv.So(<-s.home.shouldAlphaGoNow, cv.ShouldEqual, false)
			cv.So(<-s.home.shouldBetaGoNow, cv.ShouldEqual, false)

			s.home.alphaDepartsHome <- true
			cv.So(<-s.home.shouldAlphaGoNow, cv.ShouldEqual, false)
			cv.So(<-s.home.shouldBetaGoNow, cv.ShouldEqual, false)

			s.home.betaArrivesHome <- true
			cv.So(<-s.home.shouldAlphaGoNow, cv.ShouldEqual, false)
			cv.So(<-s.home.shouldBetaGoNow, cv.ShouldEqual, false)

		})
	})
}
<|endoftext|>


<commit_before>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
// +build leak

package testleak

import (
	"runtime"
	"sort"
	"strings"
	"testing"
	"time"

	"github.com/pingcap/check"
)

func interestingGoroutines() (gs []string) {
	buf := make([]byte, 2<<20)
	buf = buf[:runtime.Stack(buf, true)]
	for _, g := range strings.Split(string(buf), "\n\n") {
		sl := strings.SplitN(g, "\n", 2)
		if len(sl) != 2 {
			continue
		}
		stack := strings.TrimSpace(sl[1])
		if stack == "" ||
			strings.Contains(stack, "created by github.com/pingcap/tidb.init") ||
			strings.Contains(stack, "testing.RunTests") ||
			strings.Contains(stack, "check.(*resultTracker).start") ||
			strings.Contains(stack, "check.(*suiteRunner).runFunc") ||
			strings.Contains(stack, "check.(*suiteRunner).parallelRun") ||
			strings.Contains(stack, "localstore.(*dbStore).scheduler") ||
			strings.Contains(stack, "tikv.(*noGCHandler).Start") ||
			strings.Contains(stack, "ddl.(*ddl).start") ||
			strings.Contains(stack, "ddl.(*delRange).startEmulator") ||
			strings.Contains(stack, "domain.NewDomain") ||
			strings.Contains(stack, "testing.(*T).Run") ||
			strings.Contains(stack, "domain.(*Domain).LoadPrivilegeLoop") ||
			strings.Contains(stack, "domain.(*Domain).UpdateTableStatsLoop") ||
			strings.Contains(stack, "testing.Main(") ||
			strings.Contains(stack, "runtime.goexit") ||
			strings.Contains(stack, "created by runtime.gc") ||
			strings.Contains(stack, "interestingGoroutines") ||
			strings.Contains(stack, "runtime.MHeap_Scavenger") {
			continue
		}
		gs = append(gs, stack)
	}
	sort.Strings(gs)
	return
}

var beforeTestGorountines = map[string]bool{}

// BeforeTest gets the current goroutines.
// It's used for check.Suite.SetUpSuite() function.
// Now it's only used in the tidb_test.go.
func BeforeTest() {
	for _, g := range interestingGoroutines() {
		beforeTestGorountines[g] = true
	}
}

const defaultCheckCnt = 50

func checkLeakAfterTest(errorFunc func(cnt int, g string)) func() {
	if len(beforeTestGorountines) == 0 {
		for _, g := range interestingGoroutines() {
			beforeTestGorountines[g] = true
		}
	}

	cnt := defaultCheckCnt
	return func() {
		defer func() {
			beforeTestGorountines = map[string]bool{}
		}()

		var leaked []string
		for i := 0; i < cnt; i++ {
			leaked = leaked[:0]
			for _, g := range interestingGoroutines() {
				if !beforeTestGorountines[g] {
					leaked = append(leaked, g)
				}
			}
			// Bad stuff found, but goroutines might just still be
			// shutting down, so give it some time.
			if len(leaked) != 0 {
				time.Sleep(50 * time.Millisecond)
				continue
			}

			return
		}
		for _, g := range leaked {
			errorFunc(cnt, g)
		}
	}
}

// AfterTest gets the current goroutines and runs the returned function to
// get the goroutines at that time to contrast whether any goroutines leaked.
// Usage: defer testleak.AfterTest(c)()
// It can call with BeforeTest() at the beginning of check.Suite.TearDownSuite() or
// call alone at the beginning of each test.
func AfterTest(c *check.C) func() {
	errorFunc := func(cnt int, g string) {
		c.Errorf("Test %s check-count %d appears to have leaked: %v", c.TestName(), cnt, g)
	}
	return checkLeakAfterTest(errorFunc)
}

// AfterTestT is used after all the test cases is finished.
func AfterTestT(t *testing.T) func() {
	errorFunc := func(cnt int, g string) {
		t.Errorf("Test %s check-count %d appears to have leaked: %v", t.Name(), cnt, g)
	}
	return checkLeakAfterTest(errorFunc)
}
<commit_msg>test: add some goroutines to leak check white list (#14538)<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
// +build leak

package testleak

import (
	"runtime"
	"sort"
	"strings"
	"testing"
	"time"

	"github.com/pingcap/check"
)

func interestingGoroutines() (gs []string) {
	buf := make([]byte, 2<<20)
	buf = buf[:runtime.Stack(buf, true)]
	for _, g := range strings.Split(string(buf), "\n\n") {
		sl := strings.SplitN(g, "\n", 2)
		if len(sl) != 2 {
			continue
		}
		stack := strings.TrimSpace(sl[1])
		if stack == "" ||
			strings.Contains(stack, "created by github.com/pingcap/tidb.init") ||
			strings.Contains(stack, "testing.RunTests") ||
			strings.Contains(stack, "check.(*resultTracker).start") ||
			strings.Contains(stack, "check.(*suiteRunner).runFunc") ||
			strings.Contains(stack, "check.(*suiteRunner).parallelRun") ||
			strings.Contains(stack, "localstore.(*dbStore).scheduler") ||
			strings.Contains(stack, "tikv.(*noGCHandler).Start") ||
			strings.Contains(stack, "ddl.(*ddl).start") ||
			strings.Contains(stack, "ddl.(*delRange).startEmulator") ||
			strings.Contains(stack, "domain.NewDomain") ||
			strings.Contains(stack, "testing.(*T).Run") ||
			strings.Contains(stack, "domain.(*Domain).LoadPrivilegeLoop") ||
			strings.Contains(stack, "domain.(*Domain).UpdateTableStatsLoop") ||
			strings.Contains(stack, "testing.Main(") ||
			strings.Contains(stack, "runtime.goexit") ||
			strings.Contains(stack, "created by runtime.gc") ||
			strings.Contains(stack, "interestingGoroutines") ||
			strings.Contains(stack, "runtime.MHeap_Scavenger") ||
			// these go routines are async terminated, so they may still alive after test end, thus cause
			// false positive leak failures
			strings.Contains(stack, "google.golang.org/grpc.(*addrConn).resetTransport") ||
			strings.Contains(stack, "google.golang.org/grpc.(*ccBalancerWrapper).watcher") ||
			strings.Contains(stack, "github.com/pingcap/goleveldb/leveldb/util.(*BufferPool).drain") ||
			strings.Contains(stack, "github.com/pingcap/goleveldb/leveldb.(*DB).compactionError") ||
			strings.Contains(stack, "github.com/pingcap/goleveldb/leveldb.(*DB).mpoolDrain") {
			continue
		}
		gs = append(gs, stack)
	}
	sort.Strings(gs)
	return
}

var beforeTestGorountines = map[string]bool{}

// BeforeTest gets the current goroutines.
// It's used for check.Suite.SetUpSuite() function.
// Now it's only used in the tidb_test.go.
func BeforeTest() {
	for _, g := range interestingGoroutines() {
		beforeTestGorountines[g] = true
	}
}

const defaultCheckCnt = 50

func checkLeakAfterTest(errorFunc func(cnt int, g string)) func() {
	if len(beforeTestGorountines) == 0 {
		for _, g := range interestingGoroutines() {
			beforeTestGorountines[g] = true
		}
	}

	cnt := defaultCheckCnt
	return func() {
		defer func() {
			beforeTestGorountines = map[string]bool{}
		}()

		var leaked []string
		for i := 0; i < cnt; i++ {
			leaked = leaked[:0]
			for _, g := range interestingGoroutines() {
				if !beforeTestGorountines[g] {
					leaked = append(leaked, g)
				}
			}
			// Bad stuff found, but goroutines might just still be
			// shutting down, so give it some time.
			if len(leaked) != 0 {
				time.Sleep(50 * time.Millisecond)
				continue
			}

			return
		}
		for _, g := range leaked {
			errorFunc(cnt, g)
		}
	}
}

// AfterTest gets the current goroutines and runs the returned function to
// get the goroutines at that time to contrast whether any goroutines leaked.
// Usage: defer testleak.AfterTest(c)()
// It can call with BeforeTest() at the beginning of check.Suite.TearDownSuite() or
// call alone at the beginning of each test.
func AfterTest(c *check.C) func() {
	errorFunc := func(cnt int, g string) {
		c.Errorf("Test %s check-count %d appears to have leaked: %v", c.TestName(), cnt, g)
	}
	return checkLeakAfterTest(errorFunc)
}

// AfterTestT is used after all the test cases is finished.
func AfterTestT(t *testing.T) func() {
	errorFunc := func(cnt int, g string) {
		t.Errorf("Test %s check-count %d appears to have leaked: %v", t.Name(), cnt, g)
	}
	return checkLeakAfterTest(errorFunc)
}
<|endoftext|>


<commit_before>// Copyright 2017 The go-hep Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package rootio provides a pure-go read-access to ROOT files.
// rootio might, with time, provide write-access too.
//
// A typical usage is as follow:
//
//   f, err := rootio.Open("ntup.root")
//   obj, err := f.Get("tree")
//   tree := obj.(*rootio.Tree)
//   fmt.Printf("entries= %v\n", t.Entries())
package rootio // import "go-hep.org/x/hep/rootio"
<commit_msg>rootio: update package doc, reference rootio examples<commit_after>// Copyright 2017 The go-hep Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package rootio provides a pure-go read-access to ROOT files.
// rootio might, with time, provide write-access too.
//
// A typical usage is as follows:
//
//   f, err := rootio.Open("ntup.root")
//   if err != nil {
//       log.Fatal(err)
//   }
//   defer f.Close()
//
//   obj, ok := f.Get("tree")
//   if !ok {
//       log.Fatalf("no object called %q in file %s", "tree", f.Name())
//   }
//   tree := obj.(rootio.Tree)
//   fmt.Printf("entries= %v\n", tree.Entries())
//
// More complete examples on how to iterate over the content of a Tree can
// be found in the examples attached to rootio.TreeScanner and rootio.Scanner:
// https://godoc.org/go-hep.org/x/hep/rootio#pkg-examples
//
// Another possibility is to look at:
// https://godoc.org/go-hep.org/x/hep/rootio/cmd/root-ls,
// a command that inspects the content of ROOT files.
package rootio // import "go-hep.org/x/hep/rootio"
<|endoftext|>


<commit_before>package rpc

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"

	rpcJSON "github.com/gorilla/rpc/json"
)

type (
	// Client is a simple JSON-RPC over HTTP client used by the agent.
	Client struct {
		URL string
	}
)

// NewClient create a new client.  This only communicates with 127.0.0.1
func NewClient(port uint, path string) (*Client, error) {
	if path == "" {
		path = RPCPath
	}
	c := &Client{
		//XXX: this seems wrong
		URL: fmt.Sprintf("http://127.0.0.1:%d%s", port, path),
	}
	return c, nil
}

// Do calls an RPC method
func (c *Client) Do(method string, request interface{}, response interface{}) error {
	data, err := rpcJSON.EncodeClientRequest(method, request)
	if err != nil {
		return err
	}
	resp, err := http.Post(c.URL, "application/json", bytes.NewReader(data))
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		buf := new(bytes.Buffer)
		if _, err := buf.ReadFrom(resp.Body); err != nil {
			return err
		}
		return errors.New(buf.String())
	}

	err = rpcJSON.DecodeClientResponse(resp.Body, &response)
	if err != nil {
		return err
	}
	return nil
}

// DoRaw calls a service and proxies the response
func (c *Client) DoRaw(request interface{}, rw http.ResponseWriter) {
	data, err := json.Marshal(request)
	if err != nil {
		http.Error(rw, err.Error(), 500)
		return
	}
	resp, err := http.Post(c.URL, "application/json", bytes.NewReader(data))
	if err != nil {
		http.Error(rw, err.Error(), 500)
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		buf := new(bytes.Buffer)
		if _, err := buf.ReadFrom(resp.Body); err != nil {
			http.Error(rw, err.Error(), 500)
			return
		}
		http.Error(rw, buf.String(), resp.StatusCode)
		return
	}
	rw.Header().Set("Content-Type", "application/octet-stream")
	_, err = io.Copy(rw, resp.Body)
	if err != nil {
		http.Error(rw, err.Error(), 500)
	}
	return
}
<commit_msg>Use http.StatusBadRequest and don't `new` the bytes.Buffer<commit_after>package rpc

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"

	rpcJSON "github.com/gorilla/rpc/json"
)

type (
	// Client is a simple JSON-RPC over HTTP client used by the agent.
	Client struct {
		URL string
	}
)

// NewClient create a new client.  This only communicates with 127.0.0.1
func NewClient(port uint, path string) (*Client, error) {
	if path == "" {
		path = RPCPath
	}
	c := &Client{
		//XXX: this seems wrong
		URL: fmt.Sprintf("http://127.0.0.1:%d%s", port, path),
	}
	return c, nil
}

// Do calls an RPC method
func (c *Client) Do(method string, request interface{}, response interface{}) error {
	data, err := rpcJSON.EncodeClientRequest(method, request)
	if err != nil {
		return err
	}
	resp, err := http.Post(c.URL, "application/json", bytes.NewReader(data))
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode >= http.StatusBadRequest {
		var buf bytes.Buffer
		if _, err := buf.ReadFrom(resp.Body); err != nil {
			return err
		}
		return errors.New(buf.String())
	}

	err = rpcJSON.DecodeClientResponse(resp.Body, &response)
	if err != nil {
		return err
	}
	return nil
}

// DoRaw calls a service and proxies the response
func (c *Client) DoRaw(request interface{}, rw http.ResponseWriter) {
	data, err := json.Marshal(request)
	if err != nil {
		http.Error(rw, err.Error(), 500)
		return
	}
	resp, err := http.Post(c.URL, "application/json", bytes.NewReader(data))
	if err != nil {
		http.Error(rw, err.Error(), 500)
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		buf := new(bytes.Buffer)
		if _, err := buf.ReadFrom(resp.Body); err != nil {
			http.Error(rw, err.Error(), 500)
			return
		}
		http.Error(rw, buf.String(), resp.StatusCode)
		return
	}
	rw.Header().Set("Content-Type", "application/octet-stream")
	_, err = io.Copy(rw, resp.Body)
	if err != nil {
		http.Error(rw, err.Error(), 500)
	}
	return
}
<|endoftext|>


<commit_before>// Copyright 2014 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package app

import (
	"errors"
	"github.com/tsuru/tsuru/db"
	"github.com/tsuru/tsuru/quota"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

func reserveUnits(app *App, quantity int) error {
	app, err := checkAppLimit(app.Name, quantity)
	if err != nil {
		return err
	}
	conn, err := db.Conn()
	if err != nil {
		return err
	}
	defer conn.Close()
	err = conn.Apps().Update(
		bson.M{"name": app.Name, "quota.inuse": app.Quota.InUse},
		bson.M{"$inc": bson.M{"quota.inuse": quantity}},
	)
	for err == mgo.ErrNotFound {
		app, err = checkAppLimit(app.Name, quantity)
		if err != nil {
			return err
		}
		err = conn.Apps().Update(
			bson.M{"name": app.Name, "quota.inuse": app.Quota.InUse},
			bson.M{"$inc": bson.M{"quota.inuse": quantity}},
		)
	}
	return err
}

func checkAppLimit(name string, quantity int) (*App, error) {
	app, err := GetByName(name)
	if err != nil {
		return nil, err
	}
	if app.Quota.Limit > -1 && app.Quota.InUse+quantity > app.Quota.Limit {
		return nil, &quota.QuotaExceededError{
			Available: uint(app.Quota.Limit - app.Quota.InUse),
			Requested: uint(quantity),
		}
	}
	return app, nil
}

func releaseUnits(app *App, quantity int) error {
	app, err := checkAppUsage(app.Name, quantity)
	if err != nil {
		return err
	}
	conn, err := db.Conn()
	if err != nil {
		return err
	}
	defer conn.Close()
	err = conn.Apps().Update(
		bson.M{"name": app.Name, "quota.inuse": app.Quota.InUse},
		bson.M{"$inc": bson.M{"quota.inuse": -1 * quantity}},
	)
	for err == mgo.ErrNotFound {
		app, err = checkAppUsage(app.Name, quantity)
		if err != nil {
			return err
		}
		err = conn.Apps().Update(
			bson.M{"name": app.Name, "quota.inuse": app.Quota.InUse},
			bson.M{"$inc": bson.M{"quota.inuse": -1 * quantity}},
		)
	}
	return err
}

func checkAppUsage(name string, quantity int) (*App, error) {
	app, err := GetByName(name)
	if err != nil {
		return nil, err
	}
	if app.Quota.InUse-quantity < 0 {
		return nil, errors.New("Not enough reserved units")
	}
	return app, nil
}

// ChangeQuota redefines the limit of the app. The new limit must be bigger
// than or equal to the current number of units in the app. The new limit maybe
// smaller than 0, which mean that the app should have an unlimited number of
// units.
func ChangeQuota(app *App, limit int) error {
	conn, err := db.Conn()
	if err != nil {
		return err
	}
	defer conn.Close()
	if limit < 0 {
		limit = -1
	} else if limit < app.Quota.InUse {
		return errors.New("new limit is lesser than the current allocated value")
	}
	return conn.Apps().Update(
		bson.M{"name": app.Name},
		bson.M{"$set": bson.M{"quota.limit": limit}},
	)
}
<commit_msg>app: move some code in the quota handling<commit_after>// Copyright 2014 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package app

import (
	"errors"
	"github.com/tsuru/tsuru/db"
	"github.com/tsuru/tsuru/quota"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

func reserveUnits(app *App, quantity int) error {
	app, err := checkAppLimit(app.Name, quantity)
	if err != nil {
		return err
	}
	conn, err := db.Conn()
	if err != nil {
		return err
	}
	defer conn.Close()
	err = conn.Apps().Update(
		bson.M{"name": app.Name, "quota.inuse": app.Quota.InUse},
		bson.M{"$inc": bson.M{"quota.inuse": quantity}},
	)
	for err == mgo.ErrNotFound {
		app, err = checkAppLimit(app.Name, quantity)
		if err != nil {
			return err
		}
		err = conn.Apps().Update(
			bson.M{"name": app.Name, "quota.inuse": app.Quota.InUse},
			bson.M{"$inc": bson.M{"quota.inuse": quantity}},
		)
	}
	return err
}

func checkAppLimit(name string, quantity int) (*App, error) {
	app, err := GetByName(name)
	if err != nil {
		return nil, err
	}
	if app.Quota.Limit > -1 && app.Quota.InUse+quantity > app.Quota.Limit {
		return nil, &quota.QuotaExceededError{
			Available: uint(app.Quota.Limit - app.Quota.InUse),
			Requested: uint(quantity),
		}
	}
	return app, nil
}

func releaseUnits(app *App, quantity int) error {
	app, err := checkAppUsage(app.Name, quantity)
	if err != nil {
		return err
	}
	conn, err := db.Conn()
	if err != nil {
		return err
	}
	defer conn.Close()
	err = conn.Apps().Update(
		bson.M{"name": app.Name, "quota.inuse": app.Quota.InUse},
		bson.M{"$inc": bson.M{"quota.inuse": -1 * quantity}},
	)
	for err == mgo.ErrNotFound {
		app, err = checkAppUsage(app.Name, quantity)
		if err != nil {
			return err
		}
		err = conn.Apps().Update(
			bson.M{"name": app.Name, "quota.inuse": app.Quota.InUse},
			bson.M{"$inc": bson.M{"quota.inuse": -1 * quantity}},
		)
	}
	return err
}

func checkAppUsage(name string, quantity int) (*App, error) {
	app, err := GetByName(name)
	if err != nil {
		return nil, err
	}
	if app.Quota.InUse-quantity < 0 {
		return nil, errors.New("Not enough reserved units")
	}
	return app, nil
}

// ChangeQuota redefines the limit of the app. The new limit must be bigger
// than or equal to the current number of units in the app. The new limit maybe
// smaller than 0, which mean that the app should have an unlimited number of
// units.
func ChangeQuota(app *App, limit int) error {
	if limit < 0 {
		limit = -1
	} else if limit < app.Quota.InUse {
		return errors.New("new limit is lesser than the current allocated value")
	}
	conn, err := db.Conn()
	if err != nil {
		return err
	}
	defer conn.Close()
	return conn.Apps().Update(
		bson.M{"name": app.Name},
		bson.M{"$set": bson.M{"quota.limit": limit}},
	)
}
<|endoftext|>


<commit_before>package kubetest

import (
	"context"
	"errors"
	"fmt"
	"testing"
	"time"

	"github.com/golang/protobuf/ptypes/empty"
	. "github.com/onsi/gomega"
	"github.com/sirupsen/logrus"
	v1 "k8s.io/api/core/v1"

	"github.com/networkservicemesh/networkservicemesh/controlplane/pkg/apis/crossconnect"
	"github.com/networkservicemesh/networkservicemesh/controlplane/pkg/apis/local/connection"
	"github.com/networkservicemesh/networkservicemesh/pkg/tools"
	"github.com/networkservicemesh/networkservicemesh/test/kubetest/pods"
)

const (
	defaultEventTimeout = 10 * time.Second
)

// MonitorClient is shorter name for crossconnect.MonitorCrossConnect_MonitorCrossConnectsClient
type MonitorClient crossconnect.MonitorCrossConnect_MonitorCrossConnectsClient

// EventChecker abstracts checker of particular events from channel
type EventChecker interface {
	Check(eventCh <-chan *crossconnect.CrossConnectEvent) error
}

// SingleEventChecker checks single event in channel
type SingleEventChecker struct {
	Timeout   time.Duration
	EventType crossconnect.CrossConnectEventType
	Empty     bool
	SrcUp     bool
	DstUp     bool
}

// Check implements method from EventChecker interface
func (e *SingleEventChecker) Check(eventCh <-chan *crossconnect.CrossConnectEvent) error {
	if e.Timeout == 0 {
		e.Timeout = defaultEventTimeout
	}

	select {
	case actual, ok := <-eventCh:
		if !ok {
			return errors.New("end of channel")
		}
		logrus.Infof("Accept new event, type - %v", actual.GetType())

		if actual.GetType() != e.EventType {
			return fmt.Errorf("event type %v expected to be %v", actual.GetType(), e.EventType)
		}

		if actual.GetType() == crossconnect.CrossConnectEventType_DELETE {
			// we don't care about state of connections since event is DELETE
			return nil
		}

		if e.Empty != (len(actual.GetCrossConnects()) == 0) {
			return fmt.Errorf("expected event with 1 cross-connect, actual - %v", len(actual.GetCrossConnects()))
		}

		for _, xcon := range actual.GetCrossConnects() {
			logrus.Info(XconToString(xcon))
			if err := checkXcon(xcon, e.SrcUp, e.DstUp); err != nil {
				return err
			}
		}
	case <-time.After(e.Timeout):
		return fmt.Errorf("no event during %v seconds, type %v", e.Timeout, e.EventType)
	}

	return nil
}

// MultipleEventChecker checks subsequence of events in channel
type MultipleEventChecker struct {
	Events []EventChecker
}

// Check implements method from EventChecker interface
func (e *MultipleEventChecker) Check(eventCh <-chan *crossconnect.CrossConnectEvent) error {
	if len(e.Events) == 0 {
		return errors.New("events array can't be empty")
	}

	for _, event := range e.Events {
		if err := event.Check(eventCh); err != nil {
			return err
		}
	}

	return nil
}

// OrEventChecker checks that one of checker - Event1 or Event2 successfully finishes
type OrEventChecker struct {
	Event1 EventChecker
	Event2 EventChecker
}

// Check implements method from EventChecker interface
func (e *OrEventChecker) Check(eventCh <-chan *crossconnect.CrossConnectEvent) error {
	copyCh := make(chan *crossconnect.CrossConnectEvent)
	var buffer []*crossconnect.CrossConnectEvent
	go func() {
		for {
			event := <-eventCh
			buffer = append(buffer, event)
			copyCh <- event
		}
	}()

	err := e.Event1.Check(copyCh)
	if err == nil {
		return nil
	}
	logrus.Infof("the first condition is false: %v, checking the second...", err)

	copyCh2 := make(chan *crossconnect.CrossConnectEvent)
	go func() {
		for _, event := range buffer {
			copyCh2 <- event
		}
		event := <-eventCh
		copyCh2 <- event
	}()

	if err := e.Event2.Check(copyCh2); err != nil {
		return err
	}

	return nil
}

// CrossConnectClientAt returns channel of CrossConnectEvents from passed nsmgr pod
func CrossConnectClientAt(k8s *K8s, pod *v1.Pod) (<-chan *crossconnect.CrossConnectEvent, func()) {
	fwd, err := k8s.NewPortForwarder(pod, 6001)
	k8s.g.Expect(err).To(BeNil())

	err = fwd.Start()
	k8s.g.Expect(err).To(BeNil())

	client, closeClient, cancel := CreateCrossConnectClient(k8s, fmt.Sprintf("localhost:%d", fwd.ListenPort))

	stopCh := make(chan struct{})

	closeFunc := func() {
		logrus.Infof("CLOSE")
		close(stopCh)
		closeClient()
		fwd.Stop()
	}

	return getEventCh(client, cancel, stopCh), closeFunc
}

// XconProxyMonitor deploys proxy monitor to node and returns channel of events from it
func XconProxyMonitor(k8s *K8s, conf *NodeConf, suffix string) (<-chan *crossconnect.CrossConnectEvent, func()) {
	address := fmt.Sprintf("%s:5001", conf.Nsmd.Status.PodIP)

	xconProxy := k8s.CreatePod(pods.TestCommonPod(
		fmt.Sprintf("xcon-proxy-monitor-%s", suffix),
		[]string{"/bin/proxy-xcon-monitor", fmt.Sprintf("-address=%s", address)},
		conf.Node,
		map[string]string{}))

	eventCh, closeFunc := CrossConnectClientAt(k8s, xconProxy)
	return eventCh, func() {
		k8s.DeletePods(xconProxy)
		closeFunc()
	}
}

// NewEventChecker starts goroutine that read events from actualCh and
// compare it with SingleEventChecker passed to expectedFunc
func NewEventChecker(t *testing.T, actualCh <-chan *crossconnect.CrossConnectEvent) (expectedFunc func(EventChecker), waitFunc func()) {
	expectedCh := make(chan EventChecker, 10)
	waitCh := make(chan struct{})

	go checkEventsCh(t, actualCh, expectedCh, waitCh)

	expectedFunc = func(d EventChecker) {
		expectedCh <- d
	}

	waitFunc = func() {
		close(expectedCh)
		<-waitCh
	}

	return
}

func checkEventsCh(t *testing.T, actualCh <-chan *crossconnect.CrossConnectEvent,
	expectedCh <-chan EventChecker, waitCh chan struct{}) {
	defer close(waitCh)

	for checker := range expectedCh {
		if err := checker.Check(actualCh); err != nil {
			t.Error(err)
			return
		}
	}
}

func checkXcon(actual *crossconnect.CrossConnect, srcUp, dstUp bool) error {
	if src := actual.GetLocalSource(); src != nil && (srcUp != (src.GetState().String() == "UP")) {
		return xconStateError(actual, "SRC_UP", srcUp)
	}
	if src := actual.GetRemoteSource(); src != nil && (srcUp != (src.GetState().String() == "UP")) {
		return xconStateError(actual, "SRC_UP", srcUp)
	}
	if dst := actual.GetLocalDestination(); dst != nil && dstUp != (dst.GetState().String() == "UP") {
		return xconStateError(actual, "DST_UP", dstUp)
	}
	if dst := actual.GetRemoteDestination(); dst != nil && dstUp != (dst.GetState().String() == "UP") {
		return xconStateError(actual, "DST_UP", dstUp)
	}
	return nil
}

func xconStateError(xcon *crossconnect.CrossConnect, side string, expected bool) error {
	return fmt.Errorf("%s, expected %s state to be %v", XconToString(xcon), side, expected)
}

func getEventCh(mc MonitorClient, cf context.CancelFunc, stopCh <-chan struct{}) <-chan *crossconnect.CrossConnectEvent {
	eventCh := make(chan *crossconnect.CrossConnectEvent)

	go func() {
		for {
			select {
			case <-mc.Context().Done():
				logrus.Error("Context done")
				close(eventCh)
				return
			default:
				event, err := mc.Recv()
				if err != nil {
					logrus.Errorf("Recv: %v:", err)
					continue
				}
				eventCh <- event
			}
		}
	}()

	go func() {
		<-stopCh
		cf()
	}()

	return eventCh
}

// CreateCrossConnectClient returns CrossConnectMonitorClient to passed address
func CreateCrossConnectClient(k8s *K8s, address string) (MonitorClient, func(), context.CancelFunc) {
	var err error
	logrus.Infof("Starting CrossConnections Monitor on %s", address)
	conn, err := tools.DialTCP(address)
	if err != nil {
		k8s.g.Expect(err).To(BeNil())
		return nil, nil, nil
	}

	monitorClient := crossconnect.NewMonitorCrossConnectClient(conn)
	ctx, cancel := context.WithCancel(context.Background())
	stream, err := monitorClient.MonitorCrossConnects(ctx, &empty.Empty{})
	if err != nil {
		k8s.g.Expect(err).To(BeNil())
		cancel()
		return nil, nil, nil
	}

	closeFunc := func() {
		if err := conn.Close(); err != nil {
			logrus.Errorf("Closing the stream with: %v", err)
		}
	}
	return stream, closeFunc, cancel
}

// XconToString converts CrossConnect to string
func XconToString(xcon *crossconnect.CrossConnect) string {
	return fmt.Sprintf("%s - %s", srcConnToString(xcon), dstConnToString(xcon))
}

func srcConnToString(xcon *crossconnect.CrossConnect) string {
	var ip string
	var state string
	var distance string

	if ls := xcon.GetLocalSource(); ls != nil {
		ip = ls.GetContext().GetIpContext().GetSrcIpAddr()
		state = ls.GetState().String()
		distance = "local"
	} else {
		ip = xcon.GetRemoteSource().GetContext().GetIpContext().GetSrcIpAddr()
		state = xcon.GetRemoteSource().GetState().String()
		distance = "remote"

	}

	return fmt.Sprintf("[SRC:%s:%s:%s]", distance, ip, state)
}

func dstConnToString(xcon *crossconnect.CrossConnect) string {
	var ip string
	var state string
	var distance string
	var endpoint string

	if ls := xcon.GetLocalDestination(); ls != nil {
		ip = ls.GetContext().GetIpContext().GetDstIpAddr()
		state = ls.GetState().String()
		distance = "local"
		endpoint = ls.GetMechanism().GetParameters()[connection.WorkspaceNSEName]
	} else {
		ip = xcon.GetRemoteDestination().GetContext().GetIpContext().GetDstIpAddr()
		state = xcon.GetRemoteDestination().GetState().String()
		distance = "remote"
		endpoint = xcon.GetRemoteDestination().GetMechanism().GetParameters()[connection.WorkspaceNSEName]

	}

	return fmt.Sprintf("[DST:%s:%s:%s:%s]", endpoint, distance, ip, state)
}

// CollectXcons takes n crossconencts from event channel
func CollectXcons(ch <-chan *crossconnect.CrossConnectEvent,
	n int, timeout time.Duration) (map[string]*crossconnect.CrossConnect, error) {
	rv := map[string]*crossconnect.CrossConnect{}
	for {
		select {
		case event, ok := <-ch:
			if !ok && len(rv) < n {
				return nil, errors.New("reached end of CrossConnectEvent channel")
			}
			for id, xcon := range event.GetCrossConnects() {
				rv[id] = xcon
			}
			if len(rv) == n {
				return rv, nil
			}
		case <-time.After(timeout):
			return nil, fmt.Errorf("no events during %v", timeout)
		}
	}
}
<commit_msg>fix bug (#1601)<commit_after>package kubetest

import (
	"context"
	"errors"
	"fmt"
	"sync"
	"testing"
	"time"

	"github.com/golang/protobuf/ptypes/empty"
	. "github.com/onsi/gomega"
	"github.com/sirupsen/logrus"
	v1 "k8s.io/api/core/v1"

	"github.com/networkservicemesh/networkservicemesh/controlplane/pkg/apis/crossconnect"
	"github.com/networkservicemesh/networkservicemesh/controlplane/pkg/apis/local/connection"
	"github.com/networkservicemesh/networkservicemesh/pkg/tools"
	"github.com/networkservicemesh/networkservicemesh/test/kubetest/pods"
)

const (
	defaultEventTimeout = 10 * time.Second
)

// MonitorClient is shorter name for crossconnect.MonitorCrossConnect_MonitorCrossConnectsClient
type MonitorClient crossconnect.MonitorCrossConnect_MonitorCrossConnectsClient

// EventChecker abstracts checker of particular events from channel
type EventChecker interface {
	Check(eventCh <-chan *crossconnect.CrossConnectEvent) error
}

// SingleEventChecker checks single event in channel
type SingleEventChecker struct {
	Timeout   time.Duration
	EventType crossconnect.CrossConnectEventType
	Empty     bool
	SrcUp     bool
	DstUp     bool
}

// Check implements method from EventChecker interface
func (e *SingleEventChecker) Check(eventCh <-chan *crossconnect.CrossConnectEvent) error {
	if e.Timeout == 0 {
		e.Timeout = defaultEventTimeout
	}

	select {
	case actual, ok := <-eventCh:
		if !ok {
			return errors.New("end of channel")
		}
		logrus.Infof("Accept new event, type - %v", actual.GetType())

		if actual.GetType() != e.EventType {
			return fmt.Errorf("event type %v expected to be %v", actual.GetType(), e.EventType)
		}

		if actual.GetType() == crossconnect.CrossConnectEventType_DELETE {
			// we don't care about state of connections since event is DELETE
			return nil
		}

		if e.Empty != (len(actual.GetCrossConnects()) == 0) {
			return fmt.Errorf("expected event with 1 cross-connect, actual - %v", len(actual.GetCrossConnects()))
		}

		for _, xcon := range actual.GetCrossConnects() {
			logrus.Info(XconToString(xcon))
			if err := checkXcon(xcon, e.SrcUp, e.DstUp); err != nil {
				return err
			}
		}
	case <-time.After(e.Timeout):
		return fmt.Errorf("no event during %v seconds, type %v", e.Timeout, e.EventType)
	}

	return nil
}

// MultipleEventChecker checks subsequence of events in channel
type MultipleEventChecker struct {
	Events []EventChecker
}

// Check implements method from EventChecker interface
func (e *MultipleEventChecker) Check(eventCh <-chan *crossconnect.CrossConnectEvent) error {
	if len(e.Events) == 0 {
		return errors.New("events array can't be empty")
	}

	for _, event := range e.Events {
		if err := event.Check(eventCh); err != nil {
			return err
		}
	}

	return nil
}

// OrEventChecker checks that one of checker - Event1 or Event2 successfully finishes
type OrEventChecker struct {
	Event1 EventChecker
	Event2 EventChecker
}

// Check implements method from EventChecker interface
func (e *OrEventChecker) Check(eventCh <-chan *crossconnect.CrossConnectEvent) error {
	m := sync.Mutex{}
	copyCh := make(chan *crossconnect.CrossConnectEvent)
	var buffer []*crossconnect.CrossConnectEvent
	go func() {
		for {
			m.Lock()
			event := <-eventCh
			buffer = append(buffer, event)
			m.Unlock()
			copyCh <- event

		}
	}()

	err := e.Event1.Check(copyCh)
	if err == nil {
		return nil
	}
	logrus.Infof("the first condition is false: %v, checking the second...", err)

	copyCh2 := make(chan *crossconnect.CrossConnectEvent)
	go func() {
		m.Lock()
		for _, event := range buffer {
			copyCh2 <- event
		}
		event := <-eventCh
		m.Unlock()
		copyCh2 <- event
	}()

	if err := e.Event2.Check(copyCh2); err != nil {
		return err
	}

	return nil
}

// CrossConnectClientAt returns channel of CrossConnectEvents from passed nsmgr pod
func CrossConnectClientAt(k8s *K8s, pod *v1.Pod) (<-chan *crossconnect.CrossConnectEvent, func()) {
	fwd, err := k8s.NewPortForwarder(pod, 6001)
	k8s.g.Expect(err).To(BeNil())

	err = fwd.Start()
	k8s.g.Expect(err).To(BeNil())

	client, closeClient, cancel := CreateCrossConnectClient(k8s, fmt.Sprintf("localhost:%d", fwd.ListenPort))

	stopCh := make(chan struct{})

	closeFunc := func() {
		logrus.Infof("CLOSE")
		close(stopCh)
		closeClient()
		fwd.Stop()
	}

	return getEventCh(client, cancel, stopCh), closeFunc
}

// XconProxyMonitor deploys proxy monitor to node and returns channel of events from it
func XconProxyMonitor(k8s *K8s, conf *NodeConf, suffix string) (<-chan *crossconnect.CrossConnectEvent, func()) {
	address := fmt.Sprintf("%s:5001", conf.Nsmd.Status.PodIP)

	xconProxy := k8s.CreatePod(pods.TestCommonPod(
		fmt.Sprintf("xcon-proxy-monitor-%s", suffix),
		[]string{"/bin/proxy-xcon-monitor", fmt.Sprintf("-address=%s", address)},
		conf.Node,
		map[string]string{}))

	eventCh, closeFunc := CrossConnectClientAt(k8s, xconProxy)
	return eventCh, func() {
		k8s.DeletePods(xconProxy)
		closeFunc()
	}
}

// NewEventChecker starts goroutine that read events from actualCh and
// compare it with SingleEventChecker passed to expectedFunc
func NewEventChecker(t *testing.T, actualCh <-chan *crossconnect.CrossConnectEvent) (expectedFunc func(EventChecker), waitFunc func()) {
	expectedCh := make(chan EventChecker, 10)
	waitCh := make(chan struct{})

	go checkEventsCh(t, actualCh, expectedCh, waitCh)

	expectedFunc = func(d EventChecker) {
		expectedCh <- d
	}

	waitFunc = func() {
		close(expectedCh)
		<-waitCh
	}

	return
}

func checkEventsCh(t *testing.T, actualCh <-chan *crossconnect.CrossConnectEvent,
	expectedCh <-chan EventChecker, waitCh chan struct{}) {
	defer close(waitCh)

	for checker := range expectedCh {
		if err := checker.Check(actualCh); err != nil {
			t.Error(err)
			return
		}
	}
}

func checkXcon(actual *crossconnect.CrossConnect, srcUp, dstUp bool) error {
	if src := actual.GetLocalSource(); src != nil && (srcUp != (src.GetState().String() == "UP")) {
		return xconStateError(actual, "SRC_UP", srcUp)
	}
	if src := actual.GetRemoteSource(); src != nil && (srcUp != (src.GetState().String() == "UP")) {
		return xconStateError(actual, "SRC_UP", srcUp)
	}
	if dst := actual.GetLocalDestination(); dst != nil && dstUp != (dst.GetState().String() == "UP") {
		return xconStateError(actual, "DST_UP", dstUp)
	}
	if dst := actual.GetRemoteDestination(); dst != nil && dstUp != (dst.GetState().String() == "UP") {
		return xconStateError(actual, "DST_UP", dstUp)
	}
	return nil
}

func xconStateError(xcon *crossconnect.CrossConnect, side string, expected bool) error {
	return fmt.Errorf("%s, expected %s state to be %v", XconToString(xcon), side, expected)
}

func getEventCh(mc MonitorClient, cf context.CancelFunc, stopCh <-chan struct{}) <-chan *crossconnect.CrossConnectEvent {
	eventCh := make(chan *crossconnect.CrossConnectEvent)

	go func() {
		for {
			select {
			case <-mc.Context().Done():
				logrus.Error("Context done")
				close(eventCh)
				return
			default:
				event, err := mc.Recv()
				if err != nil {
					logrus.Errorf("Recv: %v:", err)
					continue
				}
				eventCh <- event
			}
		}
	}()

	go func() {
		<-stopCh
		cf()
	}()

	return eventCh
}

// CreateCrossConnectClient returns CrossConnectMonitorClient to passed address
func CreateCrossConnectClient(k8s *K8s, address string) (MonitorClient, func(), context.CancelFunc) {
	var err error
	logrus.Infof("Starting CrossConnections Monitor on %s", address)
	conn, err := tools.DialTCP(address)
	if err != nil {
		k8s.g.Expect(err).To(BeNil())
		return nil, nil, nil
	}

	monitorClient := crossconnect.NewMonitorCrossConnectClient(conn)
	ctx, cancel := context.WithCancel(context.Background())
	stream, err := monitorClient.MonitorCrossConnects(ctx, &empty.Empty{})
	if err != nil {
		k8s.g.Expect(err).To(BeNil())
		cancel()
		return nil, nil, nil
	}

	closeFunc := func() {
		if err := conn.Close(); err != nil {
			logrus.Errorf("Closing the stream with: %v", err)
		}
	}
	return stream, closeFunc, cancel
}

// XconToString converts CrossConnect to string
func XconToString(xcon *crossconnect.CrossConnect) string {
	return fmt.Sprintf("%s - %s", srcConnToString(xcon), dstConnToString(xcon))
}

func srcConnToString(xcon *crossconnect.CrossConnect) string {
	var ip string
	var state string
	var distance string

	if ls := xcon.GetLocalSource(); ls != nil {
		ip = ls.GetContext().GetIpContext().GetSrcIpAddr()
		state = ls.GetState().String()
		distance = "local"
	} else {
		ip = xcon.GetRemoteSource().GetContext().GetIpContext().GetSrcIpAddr()
		state = xcon.GetRemoteSource().GetState().String()
		distance = "remote"

	}

	return fmt.Sprintf("[SRC:%s:%s:%s]", distance, ip, state)
}

func dstConnToString(xcon *crossconnect.CrossConnect) string {
	var ip string
	var state string
	var distance string
	var endpoint string

	if ls := xcon.GetLocalDestination(); ls != nil {
		ip = ls.GetContext().GetIpContext().GetDstIpAddr()
		state = ls.GetState().String()
		distance = "local"
		endpoint = ls.GetMechanism().GetParameters()[connection.WorkspaceNSEName]
	} else {
		ip = xcon.GetRemoteDestination().GetContext().GetIpContext().GetDstIpAddr()
		state = xcon.GetRemoteDestination().GetState().String()
		distance = "remote"
		endpoint = xcon.GetRemoteDestination().GetMechanism().GetParameters()[connection.WorkspaceNSEName]

	}

	return fmt.Sprintf("[DST:%s:%s:%s:%s]", endpoint, distance, ip, state)
}

// CollectXcons takes n crossconencts from event channel
func CollectXcons(ch <-chan *crossconnect.CrossConnectEvent,
	n int, timeout time.Duration) (map[string]*crossconnect.CrossConnect, error) {
	rv := map[string]*crossconnect.CrossConnect{}
	for {
		select {
		case event, ok := <-ch:
			if !ok && len(rv) < n {
				return nil, errors.New("reached end of CrossConnectEvent channel")
			}
			for id, xcon := range event.GetCrossConnects() {
				rv[id] = xcon
			}
			if len(rv) == n {
				return rv, nil
			}
		case <-time.After(timeout):
			return nil, fmt.Errorf("no events during %v", timeout)
		}
	}
}
<|endoftext|>


<commit_before>package fastly

import (
	"encoding/json"
	"fmt"
	"net/http"
	"sort"
)

type ACLEntryConfig config

type ACLEntry struct {
	// Non-writable
	ServiceID string `json:"service_id"`
	ID        string `json:"id"`
	ACLID     string `json:"acl_id"`

	// writable
	IP      string      `json:"ip"`
	Subnet  uint8       `json:"subnet"`
	Comment string      `json:"comment"`
	Negated Compatibool `json:"negated"`
}

// aclEntriesByName is a sortable list of aclEntries.
type aclEntriesByIP []*ACLEntry

// Len, Swap, and Less implement the sortable interface.
func (s aclEntriesByIP) Len() int      { return len(s) }
func (s aclEntriesByIP) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s aclEntriesByIP) Less(i, j int) bool {
	return s[i].IP < s[j].IP
}

// List aclEntries for a specific ACL and service.
func (c *ACLEntryConfig) List(serviceID, aclID string) ([]*ACLEntry, *http.Response, error) {
	u := fmt.Sprintf("/service/%s/acl/%s/entries", serviceID, aclID)

	req, err := c.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	aclEntries := new([]*ACLEntry)
	resp, err := c.client.Do(req, aclEntries)
	if err != nil {
		return nil, resp, err
	}

	sort.Stable(aclEntriesByIP(*aclEntries))

	return *aclEntries, resp, nil
}

// Get fetches a specific aclEntry by entryID.
func (c *ACLEntryConfig) Get(serviceID, aclID, entryID string) (*ACLEntry, *http.Response, error) {
	u := fmt.Sprintf("/service/%s/acl/%s/entry/%s", serviceID, aclID, entryID)

	req, err := c.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	aclEntry := new(ACLEntry)
	resp, err := c.client.Do(req, aclEntry)
	if err != nil {
		return nil, resp, err
	}
	return aclEntry, resp, nil
}

// Create a new aclEntry.
func (c *ACLEntryConfig) Create(serviceID, aclID string, aclEntry *ACLEntry) (*ACLEntry, *http.Response, error) {
	u := fmt.Sprintf("/service/%s/acl/%s/entry", serviceID, aclID)

	req, err := c.client.NewJSONRequest("POST", u, aclEntry)
	if err != nil {
		return nil, nil, err
	}

	b := new(ACLEntry)
	resp, err := c.client.Do(req, b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, nil
}

// Update a aclEntry
func (c *ACLEntryConfig) Update(serviceID, aclID, entryID string, aclEntry *ACLEntry) (*ACLEntry, *http.Response, error) {
	u := fmt.Sprintf("/service/%s/acl/%s/entry/%s", serviceID, aclID, entryID)

	req, err := c.client.NewJSONRequest("PATCH", u, aclEntry)
	if err != nil {
		return nil, nil, err
	}

	b := new(ACLEntry)
	resp, err := c.client.Do(req, b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, nil
}

// Delete a aclEntry
func (c *ACLEntryConfig) Delete(serviceID, aclID, entryID string) (*http.Response, error) {
	u := fmt.Sprintf("/service/%s/acl/%s/entry/%s", serviceID, aclID, entryID)

	req, err := c.client.NewRequest("DELETE", u, nil)
	if err != nil {
		return nil, err
	}

	resp, err := c.client.Do(req, nil)
	if err != nil {
		return resp, err
	}

	return resp, nil
}

type ACLEntryBatchUpdate struct {
	Entries []ACLEntryUpdate `json:"entries"`
}

type ACLEntryUpdate struct {
	Operation BatchOperation `json:"op,omitempty"`
	ID        string         `json:"id,omitempty"`
	IP        string         `json:"ip,omitempty"`
	Subnet    string         `json:"subnet,omitempty"`
}

func (c *ACLEntryConfig) BatchUpdate(serviceID, aclID string, entries []ACLEntryUpdate) (*http.Response, error) {
	u := fmt.Sprintf("/service/%s/acl/%s/entries", serviceID, aclID)

	var update ACLEntryBatchUpdate
	update.Entries = entries
	data, _ := json.Marshal(update)
	fmt.Println(string(data))
	req, err := c.client.NewJSONRequest("PATCH", u, update)
	if err != nil {
		return nil, err
	}

	resp, err := c.client.Do(req, nil)
	if err != nil {
		return resp, err
	}

	return resp, nil
}
<commit_msg>Don't print the data we send with BatchUpdate().<commit_after>package fastly

import (
	"fmt"
	"net/http"
	"sort"
)

type ACLEntryConfig config

type ACLEntry struct {
	// Non-writable
	ServiceID string `json:"service_id"`
	ID        string `json:"id"`
	ACLID     string `json:"acl_id"`

	// writable
	IP      string      `json:"ip"`
	Subnet  uint8       `json:"subnet"`
	Comment string      `json:"comment"`
	Negated Compatibool `json:"negated"`
}

// aclEntriesByName is a sortable list of aclEntries.
type aclEntriesByIP []*ACLEntry

// Len, Swap, and Less implement the sortable interface.
func (s aclEntriesByIP) Len() int      { return len(s) }
func (s aclEntriesByIP) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s aclEntriesByIP) Less(i, j int) bool {
	return s[i].IP < s[j].IP
}

// List aclEntries for a specific ACL and service.
func (c *ACLEntryConfig) List(serviceID, aclID string) ([]*ACLEntry, *http.Response, error) {
	u := fmt.Sprintf("/service/%s/acl/%s/entries", serviceID, aclID)

	req, err := c.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	aclEntries := new([]*ACLEntry)
	resp, err := c.client.Do(req, aclEntries)
	if err != nil {
		return nil, resp, err
	}

	sort.Stable(aclEntriesByIP(*aclEntries))

	return *aclEntries, resp, nil
}

// Get fetches a specific aclEntry by entryID.
func (c *ACLEntryConfig) Get(serviceID, aclID, entryID string) (*ACLEntry, *http.Response, error) {
	u := fmt.Sprintf("/service/%s/acl/%s/entry/%s", serviceID, aclID, entryID)

	req, err := c.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	aclEntry := new(ACLEntry)
	resp, err := c.client.Do(req, aclEntry)
	if err != nil {
		return nil, resp, err
	}
	return aclEntry, resp, nil
}

// Create a new aclEntry.
func (c *ACLEntryConfig) Create(serviceID, aclID string, aclEntry *ACLEntry) (*ACLEntry, *http.Response, error) {
	u := fmt.Sprintf("/service/%s/acl/%s/entry", serviceID, aclID)

	req, err := c.client.NewJSONRequest("POST", u, aclEntry)
	if err != nil {
		return nil, nil, err
	}

	b := new(ACLEntry)
	resp, err := c.client.Do(req, b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, nil
}

// Update a aclEntry
func (c *ACLEntryConfig) Update(serviceID, aclID, entryID string, aclEntry *ACLEntry) (*ACLEntry, *http.Response, error) {
	u := fmt.Sprintf("/service/%s/acl/%s/entry/%s", serviceID, aclID, entryID)

	req, err := c.client.NewJSONRequest("PATCH", u, aclEntry)
	if err != nil {
		return nil, nil, err
	}

	b := new(ACLEntry)
	resp, err := c.client.Do(req, b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, nil
}

// Delete a aclEntry
func (c *ACLEntryConfig) Delete(serviceID, aclID, entryID string) (*http.Response, error) {
	u := fmt.Sprintf("/service/%s/acl/%s/entry/%s", serviceID, aclID, entryID)

	req, err := c.client.NewRequest("DELETE", u, nil)
	if err != nil {
		return nil, err
	}

	resp, err := c.client.Do(req, nil)
	if err != nil {
		return resp, err
	}

	return resp, nil
}

type ACLEntryBatchUpdate struct {
	Entries []ACLEntryUpdate `json:"entries"`
}

type ACLEntryUpdate struct {
	Operation BatchOperation `json:"op,omitempty"`
	ID        string         `json:"id,omitempty"`
	IP        string         `json:"ip,omitempty"`
	Subnet    string         `json:"subnet,omitempty"`
}

func (c *ACLEntryConfig) BatchUpdate(serviceID, aclID string, entries []ACLEntryUpdate) (*http.Response, error) {
	u := fmt.Sprintf("/service/%s/acl/%s/entries", serviceID, aclID)

	var update ACLEntryBatchUpdate
	update.Entries = entries
	req, err := c.client.NewJSONRequest("PATCH", u, update)
	if err != nil {
		return nil, err
	}

	resp, err := c.client.Do(req, nil)
	if err != nil {
		return resp, err
	}

	return resp, nil
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// 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
//
//     https://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.

// [START trace_setup_go_quickstart]

// Sample trace_quickstart traces incoming and outgoing requests.
package main

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

	"contrib.go.opencensus.io/exporter/stackdriver"
	"contrib.go.opencensus.io/exporter/stackdriver/propagation"
	"go.opencensus.io/plugin/ochttp"
	"go.opencensus.io/trace"
)

func main() {
	// Create and register a OpenCensus Stackdriver Trace exporter.
	exporter, err := stackdriver.NewExporter(stackdriver.Options{
		ProjectID: os.Getenv("GOOGLE_CLOUD_PROJECT"),
	})
	if err != nil {
		log.Fatal(err)
	}
	trace.RegisterExporter(exporter)

	// By default, traces will be sampled relatively rarely. To change the
	// sampling frequency for your entire program, call ApplyConfig. Use a
	// ProbabilitySampler to sample a subset of traces, or use AlwaysSample to
	// collect a trace on every run.
	//
	// Be careful about using trace.AlwaysSample in a production application
	// with significant traffic: a new trace will be started and exported for
	// every request.
	trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})

	client := &http.Client{
		Transport: &ochttp.Transport{
			// Use Google Cloud propagation format.
			Propagation: &propagation.HTTPFormat{},
		},
	}

	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		req, _ := http.NewRequest("GET", "https://www.google.com", nil)

		// The trace ID from the incoming request will be
		// propagated to the outgoing request.
		req = req.WithContext(r.Context())

		// The outgoing request will be traced with r's trace ID.
		resp, err := client.Do(req)
		if err != nil {
			log.Fatal(err)
		}
		// Because we don't read the resp.Body, need to manually call Close().
		resp.Body.Close()
	})
	http.Handle("/foo", handler)

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	log.Printf("Listening on port %s", port)
	if err := http.ListenAndServe(":"+port, nil); err != nil {
		log.Fatal(err)
	}
}

// [END trace_setup_go_quickstart]
<commit_msg>trace/trace_quickstart: use Google Cloud propagation in server (#1417)<commit_after>// Copyright 2019 Google LLC
//
// 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
//
//     https://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.

// [START trace_setup_go_quickstart]

// Sample trace_quickstart traces incoming and outgoing requests.
package main

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

	"contrib.go.opencensus.io/exporter/stackdriver"
	"contrib.go.opencensus.io/exporter/stackdriver/propagation"
	"go.opencensus.io/plugin/ochttp"
	"go.opencensus.io/trace"
)

func main() {
	// Create and register a OpenCensus Stackdriver Trace exporter.
	exporter, err := stackdriver.NewExporter(stackdriver.Options{
		ProjectID: os.Getenv("GOOGLE_CLOUD_PROJECT"),
	})
	if err != nil {
		log.Fatal(err)
	}
	trace.RegisterExporter(exporter)

	// By default, traces will be sampled relatively rarely. To change the
	// sampling frequency for your entire program, call ApplyConfig. Use a
	// ProbabilitySampler to sample a subset of traces, or use AlwaysSample to
	// collect a trace on every run.
	//
	// Be careful about using trace.AlwaysSample in a production application
	// with significant traffic: a new trace will be started and exported for
	// every request.
	trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})

	client := &http.Client{
		Transport: &ochttp.Transport{
			// Use Google Cloud propagation format.
			Propagation: &propagation.HTTPFormat{},
		},
	}

	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		req, _ := http.NewRequest("GET", "https://www.google.com", nil)

		// The trace ID from the incoming request will be
		// propagated to the outgoing request.
		req = req.WithContext(r.Context())

		// The outgoing request will be traced with r's trace ID.
		resp, err := client.Do(req)
		if err != nil {
			log.Fatal(err)
		}
		// Because we don't read the resp.Body, need to manually call Close().
		resp.Body.Close()
	})
	http.Handle("/foo", handler)

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	log.Printf("Listening on port %s", port)

	// Use an ochttp.Handler in order to instrument OpenCensus for incoming
	// requests.
	httpHandler := &ochttp.Handler{
		// Use the Google Cloud propagation format.
		Propagation: &propagation.HTTPFormat{},
	}
	if err := http.ListenAndServe(":"+port, httpHandler); err != nil {
		log.Fatal(err)
	}
}

// [END trace_setup_go_quickstart]
<|endoftext|>


<commit_before>package app

import (
	"github.com/tpjg/goriakpbc"
	"log"
	"time"
)

type Topic struct {
	// store a CRDT in riak for the topic configuration including subscribers
	Name     string
	Config   *riak.RDtMap
	riakPool RiakPool
	queues   Queues
}

type Topics struct {
	// global topic configuration, should contain list of all active topics
	Config *riak.RDtMap
	// topic map
	TopicMap map[string]*Topic
	riakPool RiakPool
	queues   Queues
}

func InitTopics(cfg Config, riakPool RiakPool, queues Queues) Topics {
	client := riakPool.GetConn()
	defer riakPool.PutConn(client)
	bucket, err := client.NewBucketType("maps", "config")
	if err != nil {
		log.Println(err)
	}
	config, err := bucket.FetchMap("topicsConfig")
	if err != nil {
		log.Println(err)
	}
	if config.FetchSet("topics") == nil {
		topicSet := config.AddSet("topics")
		//there's a bug in the protobufs client/cant have an empty set
		topicSet.Add([]byte("default_topic"))
		err = config.Store()
	}
	if err != nil {
		log.Println(err)
	}
	topics := Topics{
		Config:   config,
		riakPool: riakPool,
		queues:   queues,
		TopicMap: make(map[string]*Topic),
	}
	go topics.syncConfig(cfg)
	return topics
}

func (topics Topics) InitTopic(name string) {
	client := topics.riakPool.GetConn()
	defer topics.riakPool.PutConn(client)
	bucket, _ := client.NewBucketType("maps", "config")
	config, _ := bucket.FetchMap(name)

	topic := new(Topic)
	topic.Config = config
	topic.Name = name
	topic.riakPool = topics.riakPool
	topic.queues = topics.queues
	topics.TopicMap[name] = topic

	// Add the queue to the riak store
	topics.Config.FetchSet("topics").Add([]byte(name))
	topics.Config.Store()

}

//Broadcast the message to all listening queues and return the acked writes
func (topic *Topic) Broadcast(cfg Config, message string) map[string]string {
	queueWrites := make(map[string]string)
	for _, queue := range topic.Config.FetchSet("queues").GetValue() {
		//check if we've initialized this queue yet
		var present bool
		_, present = topic.queues.QueueMap[string(queue)]
		if present != true {
			topic.queues.InitQueue(cfg, string(queue))
		}
		uuid := topic.queues.QueueMap[string(queue)].Put(cfg, message)
		queueWrites[string(queue)] = uuid
	}
	return queueWrites
}

func (topic *Topic) AddQueue(name string) {

	client := topic.riakPool.GetConn()
	defer topic.riakPool.PutConn(client)

	bucket, err := client.NewBucketType("maps", "config")
	topic.Config, err = bucket.FetchMap(topic.Name)

	queueSet := topic.Config.AddSet("queues")
	queueSet.Add([]byte(name))
	topic.Config.Store()
	topic.Config, err = bucket.FetchMap(topic.Name)
	if err != nil {
		log.Println(err)
	}
}

func (topic *Topic) DeleteQueue(name string) {
	client := topic.riakPool.GetConn()
	defer topic.riakPool.PutConn(client)

	bucket, _ := client.NewBucketType("maps", "config")
	topic.Config, _ = bucket.FetchMap(topic.Name)

	topic.Config.FetchSet("queues").Remove([]byte(name))
	topic.Config.Store()
	topic.Config, _ = bucket.FetchMap(topic.Name)
}

func (topic *Topic) ListQueues() []string {
	list := make([]string, 0, 10)
	queueList := topic.Config.FetchSet("queues")
	if queueList != nil {
		for _, queueName := range queueList.GetValue() {
			list = append(list, string(queueName))
		}
	}
	return list
}

func (topics Topics) DeleteTopic(name string) bool {
	client := topics.riakPool.GetConn()
	defer topics.riakPool.PutConn(client)
	bucket, err := client.NewBucketType("maps", "config")
	topics.Config, err = bucket.FetchMap("topicsConfig")
	topics.Config.FetchSet("topics").Remove([]byte(name))
	err = topics.Config.Store()
	topics.Config, err = bucket.FetchMap("topicsConfig")
	topics.TopicMap[name].Delete()
	delete(topics.TopicMap, name)
	if err != nil {
		log.Println(err)
		return false
	} else {
		return true
	}
}
func (topic *Topic) Delete() {
	client := topic.riakPool.GetConn()
	defer topic.riakPool.PutConn(client)

	bucket, _ := client.NewBucketType("maps", "config")
	topic.Config, _ = bucket.FetchMap(topic.Name)
	topic.Config.Destroy()
}

//helpers
//TODO move error handling for empty config in riak to initializer
func (topics Topics) syncConfig(cfg Config) {
	for {
		log.Println("syncing with Riak")
		//refresh the topic RDtMap
		client := topics.riakPool.GetConn()
		bucket, err := client.NewBucketType("maps", "config")
		if err != nil {
			log.Println(err)
		}
		//fetch the map ignore error for event that map doesn't exist
		//TODO make these keys configurable?
		//Question is this thread safe...?
		topics.Config, err = bucket.FetchMap("topicsConfig")
		if err != nil {
			log.Println(err)
		}
		//iterate the map and add or remove topics that need to be destroyed
		topicSlice := topics.Config.FetchSet("topics").GetValue()
		if topicSlice == nil {
			//bail if there aren't any topics
			//but not before sleeping
			topics.riakPool.PutConn(client)
			time.Sleep(cfg.Core.SyncConfigInterval * time.Second)
			continue
		}
		//Is there a better way to do this?

		//iterate over the topics in riak and add the missing ones
		topicsToKeep := make(map[string]bool)
		for _, topic := range topicSlice {
			var present bool
			_, present = topics.TopicMap[string(topic)]
			if present != true {
				topics.InitTopic(string(topic))
			}
			topicsToKeep[string(topic)] = true

		}
		//iterate over the topics in topics.TopicMap and delete the ones no longer used
		for topic, _ := range topics.TopicMap {
			var present bool
			_, present = topicsToKeep[topic]
			if present != true {
				delete(topics.TopicMap, topic)
			}
		}

		//sync all topics with riak

		for _, topic := range topics.TopicMap {
			topic.syncConfig()
		}
		//sleep for the configured interval
		topics.riakPool.PutConn(client)
		time.Sleep(cfg.Core.SyncConfigInterval * time.Millisecond)

	}
}

func (topic Topic) syncConfig() {
	//refresh the topic RDtMap
	client := topic.riakPool.GetConn()
	defer topic.riakPool.PutConn(client)
	bucket, _ := client.NewBucketType("maps", "config")
	topic.Config, _ = bucket.FetchMap(topic.Name)
}
<commit_msg>fixing tabs<commit_after>package app

import (
  "github.com/tpjg/goriakpbc"
  "log"
  "time"
)

type Topic struct {
  // store a CRDT in riak for the topic configuration including subscribers
  Name     string
  Config   *riak.RDtMap
  riakPool RiakPool
  queues   Queues
}

type Topics struct {
  // global topic configuration, should contain list of all active topics
  Config *riak.RDtMap
  // topic map
  TopicMap map[string]*Topic
  riakPool RiakPool
  queues   Queues
}

func InitTopics(cfg Config, riakPool RiakPool, queues Queues) Topics {
  client := riakPool.GetConn()
  defer riakPool.PutConn(client)
  bucket, err := client.NewBucketType("maps", "config")
  if err != nil {
    log.Println(err)
  }
  config, err := bucket.FetchMap("topicsConfig")
  if err != nil {
    log.Println(err)
  }
  if config.FetchSet("topics") == nil {
    topicSet := config.AddSet("topics")
    //there's a bug in the protobufs client/cant have an empty set
    topicSet.Add([]byte("default_topic"))
    err = config.Store()
  }
  if err != nil {
    log.Println(err)
  }
  topics := Topics{
    Config:   config,
    riakPool: riakPool,
    queues:   queues,
    TopicMap: make(map[string]*Topic),
  }
  go topics.syncConfig(cfg)
  return topics
}

func (topics Topics) InitTopic(name string) {
  client := topics.riakPool.GetConn()
  defer topics.riakPool.PutConn(client)
  bucket, _ := client.NewBucketType("maps", "config")
  config, _ := bucket.FetchMap(name)

  topic := new(Topic)
  topic.Config = config
  topic.Name = name
  topic.riakPool = topics.riakPool
  topic.queues = topics.queues
  topics.TopicMap[name] = topic

  // Add the queue to the riak store
  topics.Config.FetchSet("topics").Add([]byte(name))
  topics.Config.Store()

}

//Broadcast the message to all listening queues and return the acked writes
func (topic *Topic) Broadcast(cfg Config, message string) map[string]string {
  queueWrites := make(map[string]string)
  for _, queue := range topic.Config.FetchSet("queues").GetValue() {
    //check if we've initialized this queue yet
    var present bool
    _, present = topic.queues.QueueMap[string(queue)]
    if present != true {
      topic.queues.InitQueue(cfg, string(queue))
    }
    uuid := topic.queues.QueueMap[string(queue)].Put(cfg, message)
    queueWrites[string(queue)] = uuid
  }
  return queueWrites
}

func (topic *Topic) AddQueue(name string) {

  client := topic.riakPool.GetConn()
  defer topic.riakPool.PutConn(client)

  bucket, err := client.NewBucketType("maps", "config")
  topic.Config, err = bucket.FetchMap(topic.Name)

  queueSet := topic.Config.AddSet("queues")
  queueSet.Add([]byte(name))
  topic.Config.Store()
  topic.Config, err = bucket.FetchMap(topic.Name)
  if err != nil {
    log.Println(err)
  }
}

func (topic *Topic) DeleteQueue(name string) {
  client := topic.riakPool.GetConn()
  defer topic.riakPool.PutConn(client)

  bucket, _ := client.NewBucketType("maps", "config")
  topic.Config, _ = bucket.FetchMap(topic.Name)

  topic.Config.FetchSet("queues").Remove([]byte(name))
  topic.Config.Store()
  topic.Config, _ = bucket.FetchMap(topic.Name)
}

func (topic *Topic) ListQueues() []string {
  list := make([]string, 0, 10)
  queueList := topic.Config.FetchSet("queues")
  if queueList != nil {
    for _, queueName := range queueList.GetValue() {
      list = append(list, string(queueName))
    }
  }
  return list
}

func (topics Topics) DeleteTopic(name string) bool {
  client := topics.riakPool.GetConn()
  defer topics.riakPool.PutConn(client)
  bucket, err := client.NewBucketType("maps", "config")
  topics.Config, err = bucket.FetchMap("topicsConfig")
  topics.Config.FetchSet("topics").Remove([]byte(name))
  err = topics.Config.Store()
  topics.Config, err = bucket.FetchMap("topicsConfig")
  topics.TopicMap[name].Delete()
  delete(topics.TopicMap, name)
  if err != nil {
    log.Println(err)
    return false
  } else {
    return true
  }
}
func (topic *Topic) Delete() {
  client := topic.riakPool.GetConn()
  defer topic.riakPool.PutConn(client)

  bucket, _ := client.NewBucketType("maps", "config")
  topic.Config, _ = bucket.FetchMap(topic.Name)
  topic.Config.Destroy()
}

//helpers
//TODO move error handling for empty config in riak to initializer
func (topics Topics) syncConfig(cfg Config) {
  for {
    log.Println("syncing with Riak")
    //refresh the topic RDtMap
    client := topics.riakPool.GetConn()
    bucket, err := client.NewBucketType("maps", "config")
    if err != nil {
      log.Println(err)
    }
    //fetch the map ignore error for event that map doesn't exist
    //TODO make these keys configurable?
    //Question is this thread safe...?
    topics.Config, err = bucket.FetchMap("topicsConfig")
    if err != nil {
      log.Println(err)
    }
    //iterate the map and add or remove topics that need to be destroyed
    topicSlice := topics.Config.FetchSet("topics").GetValue()
    if topicSlice == nil {
      //bail if there aren't any topics
      //but not before sleeping
      topics.riakPool.PutConn(client)
      time.Sleep(cfg.Core.SyncConfigInterval * time.Second)
      continue
    }
    //Is there a better way to do this?

    //iterate over the topics in riak and add the missing ones
    topicsToKeep := make(map[string]bool)
    for _, topic := range topicSlice {
      var present bool
      _, present = topics.TopicMap[string(topic)]
      if present != true {
        topics.InitTopic(string(topic))
      }
      topicsToKeep[string(topic)] = true

    }
    //iterate over the topics in topics.TopicMap and delete the ones no longer used
    for topic, _ := range topics.TopicMap {
      var present bool
      _, present = topicsToKeep[topic]
      if present != true {
        delete(topics.TopicMap, topic)
      }
    }

    //sync all topics with riak

    for _, topic := range topics.TopicMap {
      topic.syncConfig()
    }
    //sleep for the configured interval
    topics.riakPool.PutConn(client)
    time.Sleep(cfg.Core.SyncConfigInterval * time.Millisecond)

  }
}

func (topic Topic) syncConfig() {
  //refresh the topic RDtMap
  client := topic.riakPool.GetConn()
  defer topic.riakPool.PutConn(client)
  bucket, _ := client.NewBucketType("maps", "config")
  topic.Config, _ = bucket.FetchMap(topic.Name)
}
<|endoftext|>


<commit_before>// Copyright (C) 2014 Miquel Sabaté Solà <mikisabate@gmail.com>
// This file is licensed under the MIT license.
// See the LICENSE file.

package app

import (
	"bytes"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"path"
	"time"

	mpath "github.com/mssola/go-utils/path"
)

// TODO: we can do something nicer with these structs...

type Options struct {
	Id       string
	Name     string
	LoggedIn bool
	Error    bool
	JS       bool
	Stats    *Statistics
}

type ExtendedHome struct {
	Players  []*ExtendedPlayer
	Values   []int
	LoggedIn bool
	JS       bool
}

type Home struct {
	Players  []Player
	LoggedIn bool
	JS       bool
}

const (
	layout   = "application/layout"
	viewsDir = "views"
	viewsExt = "tpl"
)

func view(name string) string {
	base := mpath.FindRoot("vendrell", ".")
	return path.Join(base, viewsDir, name+"."+viewsExt)
}

func render(res http.ResponseWriter, name string, data interface{}) {
	b, _ := ioutil.ReadFile(view(layout))
	t, _ := template.New("l").Funcs(LayoutHelpers(name, data)).Parse(string(b))
	t.Execute(res, data)
}

func LayoutHelpers(name string, data interface{}) template.FuncMap {
	return template.FuncMap{
		"yield": func() template.HTML {
			var buffer bytes.Buffer

			b, _ := ioutil.ReadFile(view(name))
			t := template.New(name).Funcs(newViewHelpers())
			t, _ = t.Parse(string(b))
			t.Execute(&buffer, data)
			return template.HTML(buffer.String())
		},
	}
}

func newViewHelpers() template.FuncMap {
	return template.FuncMap{
		"fmtDate": fmtDate,
		"inc": func(n int) int {
			return n + 1
		},
	}
}

// Returns the view helpers for this application.
// TODO: do not export.
func ViewHelpers() []template.FuncMap {
	return []template.FuncMap{
		{
			"fmtDate": fmtDate,
			"inc": func(n int) int {
				return n + 1
			},
		},
	}
}

// Returns a date formatted in an nicer way.
func fmtDate(t time.Time) string {
	return fmt.Sprintf("%02d/%02d/%04d", t.Day(), t.Month(), t.Year())
}
<commit_msg>app: added some panics for the views.<commit_after>// Copyright (C) 2014 Miquel Sabaté Solà <mikisabate@gmail.com>
// This file is licensed under the MIT license.
// See the LICENSE file.

package app

import (
	"bytes"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"path"
	"time"

	mpath "github.com/mssola/go-utils/path"
)

// TODO: we can do something nicer with these structs...

type Options struct {
	Id       string
	Name     string
	LoggedIn bool
	Error    bool
	JS       bool
	Stats    *Statistics
}

type ExtendedHome struct {
	Players  []*ExtendedPlayer
	Values   []int
	LoggedIn bool
	JS       bool
}

type Home struct {
	Players  []Player
	LoggedIn bool
	JS       bool
}

const (
	layout   = "application/layout"
	viewsDir = "views"
	viewsExt = "tpl"
)

func view(name string) string {
	base := mpath.FindRoot("vendrell", ".")
	return path.Join(base, viewsDir, name+"."+viewsExt)
}

func render(res http.ResponseWriter, name string, data interface{}) {
	b, e := ioutil.ReadFile(view(layout))
	if e != nil {
		panic("Could not read layout file!")
	}
	t, e := template.New("l").Funcs(LayoutHelpers(name, data)).Parse(string(b))
	if e != nil {
		panic("Could not parse layout file!")
	}
	t.Execute(res, data)
}

func LayoutHelpers(name string, data interface{}) template.FuncMap {
	return template.FuncMap{
		"yield": func() template.HTML {
			var buffer bytes.Buffer

			b, e := ioutil.ReadFile(view(name))
			if e != nil {
				panic(fmt.Sprintf("Could not read: %v", name))
			}
			t := template.New(name).Funcs(newViewHelpers())
			t, e = t.Parse(string(b))
			if e != nil {
				panic(fmt.Sprintf("Could not parse: %v", name))
			}
			t.Execute(&buffer, data)
			return template.HTML(buffer.String())
		},
	}
}

func newViewHelpers() template.FuncMap {
	return template.FuncMap{
		"fmtDate": fmtDate,
		"inc": func(n int) int {
			return n + 1
		},
	}
}

// Returns the view helpers for this application.
// TODO: do not export.
func ViewHelpers() []template.FuncMap {
	return []template.FuncMap{
		{
			"fmtDate": fmtDate,
			"inc": func(n int) int {
				return n + 1
			},
		},
	}
}

// Returns a date formatted in an nicer way.
func fmtDate(t time.Time) string {
	return fmt.Sprintf("%02d/%02d/%04d", t.Day(), t.Month(), t.Year())
}
<|endoftext|>