<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 thread form
type threadForm struct {
	Title   string `form:"title" binding:"required"`
	Comment string `form:"comment" binding:"required"`
	Ib      uint   `form:"ib" binding:"required"`
}

// ThreadController handles the creation of new threads
func ThreadController(c *gin.Context) {
	var err error
	var tf threadForm
	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).SetMeta("ThreadController.ContentLength")
		return
	}

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

	err = c.Bind(&tf)
	if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInvalidParam))
		c.Error(err).SetMeta("ThreadController.Bind")
		return
	}

	// Set parameters to ThreadModel
	m := models.ThreadModel{
		Uid:     userdata.Id,
		Ip:      c.ClientIP(),
		Title:   tf.Title,
		Comment: tf.Comment,
		Ib:      tf.Ib,
	}

	image := u.ImageType{}

	// Check if theres a file
	image.File, image.Header, err = req.FormFile("file")
	if err == http.ErrMissingFile {
		c.JSON(http.StatusBadRequest, gin.H{"error_message": e.ErrNoImage.Error()})
		c.Error(e.ErrNoImage).SetMeta("ThreadController.FormFile")
		return
	}

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

	// 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).SetMeta("ThreadController.CheckComment")
		return
	}

	// set the ib for duplicate checking
	image.Ib = m.Ib

	// 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).SetMeta("ThreadController.SaveImage")
		return
	}

	m.MD5 = image.MD5
	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).SetMeta("ThreadController.Post")
		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)

	err = cache.Delete(index_key, directory_key)
	if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInternalError))
		c.Error(err).SetMeta("ThreadController.cache.Delete")
		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).SetMeta("ThreadController.redirect")
		return
	}

	c.Redirect(303, redirect)

	audit := audit.Audit{
		User:   userdata.Id,
		Ib:     m.Ib,
		Ip:     m.Ip,
		Action: audit.AuditNewThread,
		Info:   fmt.Sprintf("%s", m.Title),
	}

	// submit audit
	err = audit.Submit()
	if err != nil {
		c.Error(err).SetMeta("ThreadController.audit.Submit")
	}

	return

}
<commit_msg>shared mutex<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 thread form
type threadForm struct {
	Title   string `form:"title" binding:"required"`
	Comment string `form:"comment" binding:"required"`
	Ib      uint   `form:"ib" binding:"required"`
}

// ThreadController handles the creation of new threads
func ThreadController(c *gin.Context) {
	var err error
	var tf threadForm
	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).SetMeta("ThreadController.ContentLength")
		return
	}

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

	err = c.Bind(&tf)
	if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInvalidParam))
		c.Error(err).SetMeta("ThreadController.Bind")
		return
	}

	// Set parameters to ThreadModel
	m := models.ThreadModel{
		Uid:     userdata.Id,
		Ip:      c.ClientIP(),
		Title:   tf.Title,
		Comment: tf.Comment,
		Ib:      tf.Ib,
	}

	image := u.ImageType{}

	// Check if theres a file
	image.File, image.Header, err = req.FormFile("file")
	if err == http.ErrMissingFile {
		c.JSON(http.StatusBadRequest, gin.H{"error_message": e.ErrNoImage.Error()})
		c.Error(e.ErrNoImage).SetMeta("ThreadController.FormFile")
		return
	}

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

	// 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).SetMeta("ThreadController.CheckComment")
		return
	}

	// set the ib for duplicate checking
	image.Ib = m.Ib

	// 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).SetMeta("ThreadController.SaveImage")
		return
	}

	m.MD5 = image.MD5
	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).SetMeta("ThreadController.Post")
		return
	}

	// Initialize cache handle
	cache := redis.RedisCache

	// Delete redis stuff
	index_key := redis.RedisKeyIndex["index"]
	index_key.SetKey(fmt.Sprintf("%d", m.Ib))
	err = index_key.Delete()
	if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInternalError))
		c.Error(err).SetMeta("ThreadController.cache.Delete")
		return
	}

	directory_key := fmt.Sprintf("%s:%d", "directory", m.Ib)

	err = cache.Delete(index_key, directory_key)
	if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInternalError))
		c.Error(err).SetMeta("ThreadController.cache.Delete")
		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).SetMeta("ThreadController.redirect")
		return
	}

	c.Redirect(303, redirect)

	audit := audit.Audit{
		User:   userdata.Id,
		Ib:     m.Ib,
		Ip:     m.Ip,
		Action: audit.AuditNewThread,
		Info:   fmt.Sprintf("%s", m.Title),
	}

	// submit audit
	err = audit.Submit()
	if err != nil {
		c.Error(err).SetMeta("ThreadController.audit.Submit")
	}

	return

}
<|endoftext|>


<commit_before>package inmem

import (
	"context"
	"fmt"
	"reflect"
	"sort"
	"testing"

	radix "github.com/armon/go-radix"
	log "github.com/hashicorp/go-hclog"
	"github.com/hashicorp/vault/helper/logging"
	"github.com/hashicorp/vault/physical"
)

type faultyPseudo struct {
	underlying  InmemBackend
	faultyPaths map[string]struct{}
}

func (f *faultyPseudo) Get(ctx context.Context, key string) (*physical.Entry, error) {
	return f.underlying.Get(context.Background(), key)
}

func (f *faultyPseudo) Put(ctx context.Context, entry *physical.Entry) error {
	return f.underlying.Put(context.Background(), entry)
}

func (f *faultyPseudo) Delete(ctx context.Context, key string) error {
	return f.underlying.Delete(context.Background(), key)
}

func (f *faultyPseudo) GetInternal(ctx context.Context, key string) (*physical.Entry, error) {
	if _, ok := f.faultyPaths[key]; ok {
		return nil, fmt.Errorf("fault")
	}
	return f.underlying.GetInternal(context.Background(), key)
}

func (f *faultyPseudo) PutInternal(ctx context.Context, entry *physical.Entry) error {
	if _, ok := f.faultyPaths[entry.Key]; ok {
		return fmt.Errorf("fault")
	}
	return f.underlying.PutInternal(context.Background(), entry)
}

func (f *faultyPseudo) DeleteInternal(ctx context.Context, key string) error {
	if _, ok := f.faultyPaths[key]; ok {
		return fmt.Errorf("fault")
	}
	return f.underlying.DeleteInternal(context.Background(), key)
}

func (f *faultyPseudo) List(ctx context.Context, prefix string) ([]string, error) {
	return f.underlying.List(context.Background(), prefix)
}

func (f *faultyPseudo) Transaction(ctx context.Context, txns []*physical.TxnEntry) error {
	f.underlying.permitPool.Acquire()
	defer f.underlying.permitPool.Release()

	f.underlying.Lock()
	defer f.underlying.Unlock()

	return physical.GenericTransactionHandler(ctx, f, txns)
}

func newFaultyPseudo(logger log.Logger, faultyPaths []string) *faultyPseudo {
	out := &faultyPseudo{
		underlying: InmemBackend{
			root:       radix.New(),
			permitPool: physical.NewPermitPool(1),
			logger:     logger.Named("storage.inmembackend"),
		},
		faultyPaths: make(map[string]struct{}, len(faultyPaths)),
	}
	for _, v := range faultyPaths {
		out.faultyPaths[v] = struct{}{}
	}
	return out
}

func TestPseudo_Basic(t *testing.T) {
	logger := logging.NewVaultLogger(log.Debug)
	p := newFaultyPseudo(logger, nil)
	physical.ExerciseBackend(t, p)
	physical.ExerciseBackend_ListPrefix(t, p)
}

func TestPseudo_SuccessfulTransaction(t *testing.T) {
	logger := logging.NewVaultLogger(log.Debug)
	p := newFaultyPseudo(logger, nil)

	physical.ExerciseTransactionalBackend(t, p)
}

func TestPseudo_FailedTransaction(t *testing.T) {
	logger := logging.NewVaultLogger(log.Debug)
	p := newFaultyPseudo(logger, []string{"zip"})

	txns := physical.SetupTestingTransactions(t, p)
	if err := p.Transaction(context.Background(), txns); err == nil {
		t.Fatal("expected error during transaction")
	}

	keys, err := p.List(context.Background(), "")
	if err != nil {
		t.Fatal(err)
	}

	expected := []string{"foo", "zip", "deleteme", "deleteme2"}

	sort.Strings(keys)
	sort.Strings(expected)
	if !reflect.DeepEqual(keys, expected) {
		t.Fatalf("mismatch: expected\n%#v\ngot\n%#v\n", expected, keys)
	}

	entry, err := p.Get(context.Background(), "foo")
	if err != nil {
		t.Fatal(err)
	}
	if entry == nil {
		t.Fatal("got nil entry")
	}
	if entry.Value == nil {
		t.Fatal("got nil value")
	}
	if string(entry.Value) != "bar" {
		t.Fatal("values did not rollback correctly")
	}

	entry, err = p.Get(context.Background(), "zip")
	if err != nil {
		t.Fatal(err)
	}
	if entry == nil {
		t.Fatal("got nil entry")
	}
	if entry.Value == nil {
		t.Fatal("got nil value")
	}
	if string(entry.Value) != "zap" {
		t.Fatal("values did not rollback correctly")
	}
}
<commit_msg>Fix another test error<commit_after>package inmem

import (
	"context"
	"fmt"
	"reflect"
	"sort"
	"testing"

	radix "github.com/armon/go-radix"
	log "github.com/hashicorp/go-hclog"
	"github.com/hashicorp/vault/helper/logging"
	"github.com/hashicorp/vault/physical"
)

type faultyPseudo struct {
	underlying  InmemBackend
	faultyPaths map[string]struct{}
}

func (f *faultyPseudo) Get(ctx context.Context, key string) (*physical.Entry, error) {
	return f.underlying.Get(context.Background(), key)
}

func (f *faultyPseudo) Put(ctx context.Context, entry *physical.Entry) error {
	return f.underlying.Put(context.Background(), entry)
}

func (f *faultyPseudo) Delete(ctx context.Context, key string) error {
	return f.underlying.Delete(context.Background(), key)
}

func (f *faultyPseudo) GetInternal(ctx context.Context, key string) (*physical.Entry, error) {
	if _, ok := f.faultyPaths[key]; ok {
		return nil, fmt.Errorf("fault")
	}
	return f.underlying.GetInternal(context.Background(), key)
}

func (f *faultyPseudo) PutInternal(ctx context.Context, entry *physical.Entry) error {
	if _, ok := f.faultyPaths[entry.Key]; ok {
		return fmt.Errorf("fault")
	}
	return f.underlying.PutInternal(context.Background(), entry)
}

func (f *faultyPseudo) DeleteInternal(ctx context.Context, key string) error {
	if _, ok := f.faultyPaths[key]; ok {
		return fmt.Errorf("fault")
	}
	return f.underlying.DeleteInternal(context.Background(), key)
}

func (f *faultyPseudo) List(ctx context.Context, prefix string) ([]string, error) {
	return f.underlying.List(context.Background(), prefix)
}

func (f *faultyPseudo) Transaction(ctx context.Context, txns []*physical.TxnEntry) error {
	f.underlying.permitPool.Acquire()
	defer f.underlying.permitPool.Release()

	f.underlying.Lock()
	defer f.underlying.Unlock()

	return physical.GenericTransactionHandler(ctx, f, txns)
}

func newFaultyPseudo(logger log.Logger, faultyPaths []string) *faultyPseudo {
	out := &faultyPseudo{
		underlying: InmemBackend{
			root:       radix.New(),
			permitPool: physical.NewPermitPool(1),
			logger:     logger.Named("storage.inmembackend"),
			failGet:    new(uint32),
			failPut:    new(uint32),
			failDelete: new(uint32),
			failList:   new(uint32),
		},
		faultyPaths: make(map[string]struct{}, len(faultyPaths)),
	}
	for _, v := range faultyPaths {
		out.faultyPaths[v] = struct{}{}
	}
	return out
}

func TestPseudo_Basic(t *testing.T) {
	logger := logging.NewVaultLogger(log.Debug)
	p := newFaultyPseudo(logger, nil)
	physical.ExerciseBackend(t, p)
	physical.ExerciseBackend_ListPrefix(t, p)
}

func TestPseudo_SuccessfulTransaction(t *testing.T) {
	logger := logging.NewVaultLogger(log.Debug)
	p := newFaultyPseudo(logger, nil)

	physical.ExerciseTransactionalBackend(t, p)
}

func TestPseudo_FailedTransaction(t *testing.T) {
	logger := logging.NewVaultLogger(log.Debug)
	p := newFaultyPseudo(logger, []string{"zip"})

	txns := physical.SetupTestingTransactions(t, p)
	if err := p.Transaction(context.Background(), txns); err == nil {
		t.Fatal("expected error during transaction")
	}

	keys, err := p.List(context.Background(), "")
	if err != nil {
		t.Fatal(err)
	}

	expected := []string{"foo", "zip", "deleteme", "deleteme2"}

	sort.Strings(keys)
	sort.Strings(expected)
	if !reflect.DeepEqual(keys, expected) {
		t.Fatalf("mismatch: expected\n%#v\ngot\n%#v\n", expected, keys)
	}

	entry, err := p.Get(context.Background(), "foo")
	if err != nil {
		t.Fatal(err)
	}
	if entry == nil {
		t.Fatal("got nil entry")
	}
	if entry.Value == nil {
		t.Fatal("got nil value")
	}
	if string(entry.Value) != "bar" {
		t.Fatal("values did not rollback correctly")
	}

	entry, err = p.Get(context.Background(), "zip")
	if err != nil {
		t.Fatal(err)
	}
	if entry == nil {
		t.Fatal("got nil entry")
	}
	if entry.Value == nil {
		t.Fatal("got nil value")
	}
	if string(entry.Value) != "zap" {
		t.Fatal("values did not rollback correctly")
	}
}
<|endoftext|>


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

package v1

import (
	"errors"
	"fmt"
	"path"
	"sort"
	"strings"
	// TODO(nmittler): Remove this
	_ "github.com/golang/glog"

	meshconfig "istio.io/api/mesh/v1alpha1"
	routing "istio.io/api/routing/v1alpha1"
	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pkg/log"
)

func buildIngressListeners(mesh *meshconfig.MeshConfig, instances []*model.ServiceInstance, discovery model.ServiceDiscovery,
	config model.IstioConfigStore,
	ingress model.Node) Listeners {

	opts := buildHTTPListenerOpts{
		mesh:             mesh,
		node:             ingress,
		instances:        instances,
		routeConfig:      nil,
		ip:               WildcardAddress,
		port:             80,
		rds:              "80",
		useRemoteAddress: true,
		direction:        EgressTraceOperation,
		outboundListener: false,
		store:            config,
	}

	listeners := Listeners{buildHTTPListener(opts)}

	// lack of SNI in Envoy implies that TLS secrets are attached to listeners
	// therefore, we should first check that TLS endpoint is needed before shipping TLS listener
	_, secret := buildIngressRoutes(mesh, ingress, instances, discovery, config)
	if secret != "" {
		opts.port = 443
		opts.rds = "443"
		listener := buildHTTPListener(opts)
		listener.SSLContext = &SSLContext{
			CertChainFile:  path.Join(model.IngressCertsPath, model.IngressCertFilename),
			PrivateKeyFile: path.Join(model.IngressCertsPath, model.IngressKeyFilename),
			ALPNProtocols:  strings.Join(ListenersALPNProtocols, ","),
		}
		listeners = append(listeners, listener)
	}

	return listeners
}

func buildIngressRoutes(mesh *meshconfig.MeshConfig, sidecar model.Node,
	instances []*model.ServiceInstance,
	discovery model.ServiceDiscovery,
	config model.IstioConfigStore) (HTTPRouteConfigs, string) {
	// build vhosts
	vhosts := make(map[string][]*HTTPRoute)
	vhostsTLS := make(map[string][]*HTTPRoute)
	tlsAll := ""

	rules, _ := config.List(model.IngressRule.Type, model.NamespaceAll)
	for _, rule := range rules {
		routes, tls, err := buildIngressRoute(mesh, sidecar, instances, rule, discovery, config)
		if err != nil {
			log.Warnf("Error constructing Envoy route from ingress rule: %v", err)
			continue
		}

		host := "*"
		ingress := rule.Spec.(*routing.IngressRule)
		if ingress.Match != nil && ingress.Match.Request != nil {
			if authority, ok := ingress.Match.Request.Headers[model.HeaderAuthority]; ok {
				switch match := authority.GetMatchType().(type) {
				case *routing.StringMatch_Exact:
					host = match.Exact
				default:
					log.Warnf("Unsupported match type for authority condition %T, falling back to %q", match, host)
					continue
				}
			}
		}
		if tls != "" {
			vhostsTLS[host] = append(vhostsTLS[host], routes...)
			if tlsAll == "" {
				tlsAll = tls
			} else if tlsAll != tls {
				log.Warnf("Multiple secrets detected %s and %s", tls, tlsAll)
				if tls < tlsAll {
					tlsAll = tls
				}
			}
		} else {
			vhosts[host] = append(vhosts[host], routes...)
		}
	}

	// normalize config
	rc := &HTTPRouteConfig{VirtualHosts: make([]*VirtualHost, 0)}
	for host, routes := range vhosts {
		sort.Sort(RoutesByPath(routes))
		rc.VirtualHosts = append(rc.VirtualHosts, &VirtualHost{
			Name:    host,
			Domains: buildIngressVhostDomains(host, 80),
			Routes:  routes,
		})
	}

	rcTLS := &HTTPRouteConfig{VirtualHosts: make([]*VirtualHost, 0)}
	for host, routes := range vhostsTLS {
		sort.Sort(RoutesByPath(routes))
		rcTLS.VirtualHosts = append(rcTLS.VirtualHosts, &VirtualHost{
			Name:    host,
			Domains: buildIngressVhostDomains(host, 443),
			Routes:  routes,
		})
	}

	configs := HTTPRouteConfigs{80: rc, 443: rcTLS}
	return configs.normalize(), tlsAll
}

// buildIngressVhostDomains returns an array of domain strings with the port attached
func buildIngressVhostDomains(vhost string, port int) []string {
	domains := make([]string, 0)
	domains = append(domains, vhost)

	if vhost != "*" {
		domains = append(domains, fmt.Sprintf("%s:%d", vhost, port))
	}

	return domains
}

// buildIngressRoute translates an ingress rule to an Envoy route
func buildIngressRoute(mesh *meshconfig.MeshConfig, sidecar model.Node,
	instances []*model.ServiceInstance, rule model.Config,
	discovery model.ServiceDiscovery,
	config model.IstioConfigStore) ([]*HTTPRoute, string, error) {
	ingress := rule.Spec.(*routing.IngressRule)
	destination := model.ResolveHostname(rule.ConfigMeta, ingress.Destination)
	service, err := discovery.GetService(destination)
	if err != nil {
		return nil, "", err
	}
	if service == nil {
		return nil, "", fmt.Errorf("cannot find service %q", destination)
	}
	tls := ingress.TlsSecret
	servicePort, err := extractPort(service, ingress)
	if err != nil {
		return nil, "", err
	}
	if !servicePort.Protocol.IsHTTP() {
		return nil, "", fmt.Errorf("unsupported protocol %q for %q", servicePort.Protocol, service.Hostname)
	}

	// unfold the rules for the destination port
	routes := buildDestinationHTTPRoutes(sidecar, service, servicePort, instances, config, buildOutboundCluster)

	// filter by path, prefix from the ingress
	ingressRoute := buildHTTPRouteMatch(ingress.Match)

	// TODO: not handling header match in ingress apart from uri and authority (uri must not be regex)
	if len(ingressRoute.Headers) > 0 {
		if len(ingressRoute.Headers) > 1 || ingressRoute.Headers[0].Name != headerAuthority {
			return nil, "", errors.New("header matches in ingress rule not supported")
		}
	}

	out := make([]*HTTPRoute, 0)
	for _, route := range routes {
		// enable mixer check on the route
		if mesh.MixerAddress != "" {
			route.OpaqueConfig = buildMixerOpaqueConfig(!mesh.DisablePolicyChecks, true, service.Hostname)
		}

		if applied := route.CombinePathPrefix(ingressRoute.Path, ingressRoute.Prefix); applied != nil {
			out = append(out, applied)
		}
	}

	return out, tls, nil
}

// extractPort extracts the destination service port from the given destination,
func extractPort(svc *model.Service, ingress *routing.IngressRule) (*model.Port, error) {
	switch p := ingress.GetDestinationServicePort().(type) {
	case *routing.IngressRule_DestinationPort:
		num := p.DestinationPort
		port, exists := svc.Ports.GetByPort(int(num))
		if !exists {
			return nil, fmt.Errorf("cannot find port %d in %q", num, svc.Hostname)
		}
		return port, nil
	case *routing.IngressRule_DestinationPortName:
		name := p.DestinationPortName
		port, exists := svc.Ports.Get(name)
		if !exists {
			return nil, fmt.Errorf("cannot find port %q in %q", name, svc.Hostname)
		}
		return port, nil
	}
	return nil, errors.New("unrecognized destination port")
}
<commit_msg>ignore catch all route if other routes exist for ingress (#3218)<commit_after>// Copyright 2017 Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package v1

import (
	"errors"
	"fmt"
	"path"
	"sort"
	"strings"
	// TODO(nmittler): Remove this
	_ "github.com/golang/glog"

	meshconfig "istio.io/api/mesh/v1alpha1"
	routing "istio.io/api/routing/v1alpha1"
	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pkg/log"
)

func buildIngressListeners(mesh *meshconfig.MeshConfig, instances []*model.ServiceInstance, discovery model.ServiceDiscovery,
	config model.IstioConfigStore,
	ingress model.Node) Listeners {

	opts := buildHTTPListenerOpts{
		mesh:             mesh,
		node:             ingress,
		instances:        instances,
		routeConfig:      nil,
		ip:               WildcardAddress,
		port:             80,
		rds:              "80",
		useRemoteAddress: true,
		direction:        EgressTraceOperation,
		outboundListener: false,
		store:            config,
	}

	listeners := Listeners{buildHTTPListener(opts)}

	// lack of SNI in Envoy implies that TLS secrets are attached to listeners
	// therefore, we should first check that TLS endpoint is needed before shipping TLS listener
	_, secret := buildIngressRoutes(mesh, ingress, instances, discovery, config)
	if secret != "" {
		opts.port = 443
		opts.rds = "443"
		listener := buildHTTPListener(opts)
		listener.SSLContext = &SSLContext{
			CertChainFile:  path.Join(model.IngressCertsPath, model.IngressCertFilename),
			PrivateKeyFile: path.Join(model.IngressCertsPath, model.IngressKeyFilename),
			ALPNProtocols:  strings.Join(ListenersALPNProtocols, ","),
		}
		listeners = append(listeners, listener)
	}

	return listeners
}

func buildIngressRoutes(mesh *meshconfig.MeshConfig, sidecar model.Node,
	instances []*model.ServiceInstance,
	discovery model.ServiceDiscovery,
	config model.IstioConfigStore) (HTTPRouteConfigs, string) {
	// build vhosts
	vhosts := make(map[string][]*HTTPRoute)
	vhostsTLS := make(map[string][]*HTTPRoute)
	tlsAll := ""

	rules, _ := config.List(model.IngressRule.Type, model.NamespaceAll)
	for _, rule := range rules {
		routes, tls, err := buildIngressRoute(mesh, sidecar, instances, rule, discovery, config)
		if err != nil {
			log.Warnf("Error constructing Envoy route from ingress rule: %v", err)
			continue
		}

		host := "*"
		ingress := rule.Spec.(*routing.IngressRule)
		if ingress.Match != nil && ingress.Match.Request != nil {
			if authority, ok := ingress.Match.Request.Headers[model.HeaderAuthority]; ok {
				switch match := authority.GetMatchType().(type) {
				case *routing.StringMatch_Exact:
					host = match.Exact
				default:
					log.Warnf("Unsupported match type for authority condition %T, falling back to %q", match, host)
					continue
				}
			}
		}
		if tls != "" {
			vhostsTLS[host] = append(vhostsTLS[host], routes...)
			if tlsAll == "" {
				tlsAll = tls
			} else if tlsAll != tls {
				log.Warnf("Multiple secrets detected %s and %s", tls, tlsAll)
				if tls < tlsAll {
					tlsAll = tls
				}
			}
		} else {
			vhosts[host] = append(vhosts[host], routes...)
		}
	}

	// normalize config
	rc := &HTTPRouteConfig{VirtualHosts: make([]*VirtualHost, 0)}
	for host, routes := range vhosts {
		sort.Sort(RoutesByPath(routes))
		rc.VirtualHosts = append(rc.VirtualHosts, &VirtualHost{
			Name:    host,
			Domains: buildIngressVhostDomains(host, 80),
			Routes:  routes,
		})
	}

	rcTLS := &HTTPRouteConfig{VirtualHosts: make([]*VirtualHost, 0)}
	for host, routes := range vhostsTLS {
		sort.Sort(RoutesByPath(routes))
		rcTLS.VirtualHosts = append(rcTLS.VirtualHosts, &VirtualHost{
			Name:    host,
			Domains: buildIngressVhostDomains(host, 443),
			Routes:  routes,
		})
	}

	configs := HTTPRouteConfigs{80: rc, 443: rcTLS}
	return configs.normalize(), tlsAll
}

// buildIngressVhostDomains returns an array of domain strings with the port attached
func buildIngressVhostDomains(vhost string, port int) []string {
	domains := make([]string, 0)
	domains = append(domains, vhost)

	if vhost != "*" {
		domains = append(domains, fmt.Sprintf("%s:%d", vhost, port))
	}

	return domains
}

// buildIngressRoute translates an ingress rule to an Envoy route
func buildIngressRoute(mesh *meshconfig.MeshConfig, sidecar model.Node,
	instances []*model.ServiceInstance, rule model.Config,
	discovery model.ServiceDiscovery,
	config model.IstioConfigStore) ([]*HTTPRoute, string, error) {
	ingress := rule.Spec.(*routing.IngressRule)
	destination := model.ResolveHostname(rule.ConfigMeta, ingress.Destination)
	service, err := discovery.GetService(destination)
	if err != nil {
		return nil, "", err
	}
	if service == nil {
		return nil, "", fmt.Errorf("cannot find service %q", destination)
	}
	tls := ingress.TlsSecret
	servicePort, err := extractPort(service, ingress)
	if err != nil {
		return nil, "", err
	}
	if !servicePort.Protocol.IsHTTP() {
		return nil, "", fmt.Errorf("unsupported protocol %q for %q", servicePort.Protocol, service.Hostname)
	}

	// unfold the rules for the destination port
	routes := buildDestinationHTTPRoutes(sidecar, service, servicePort, instances, config, buildOutboundCluster)

	// filter by path, prefix from the ingress
	ingressRoute := buildHTTPRouteMatch(ingress.Match)

	// TODO: not handling header match in ingress apart from uri and authority (uri must not be regex)
	if len(ingressRoute.Headers) > 0 {
		if len(ingressRoute.Headers) > 1 || ingressRoute.Headers[0].Name != headerAuthority {
			return nil, "", errors.New("header matches in ingress rule not supported")
		}
	}

	out := make([]*HTTPRoute, 0)
	for _, route := range routes {
		// See https://github.com/istio/istio/issues/3067. When a route has a catchAll route in addition to
		// others, combining with ingress results in ome non deterministic rendering of routes inside Envoy
		// route block, wherein a prefix match occurs first before another route with same
		// prefix match+prefix rewrite. A quick fix is to disable combining with the catchAll route if there
		// are other routes. A long term fix is to stop combining routes from two different configuration sources.
		if route.CatchAll() && len(routes) > 1 {
			continue
		}

		// enable mixer check on the route
		if mesh.MixerAddress != "" {
			route.OpaqueConfig = buildMixerOpaqueConfig(!mesh.DisablePolicyChecks, true, service.Hostname)
		}

		if applied := route.CombinePathPrefix(ingressRoute.Path, ingressRoute.Prefix); applied != nil {
			out = append(out, applied)
		}
	}

	return out, tls, nil
}

// extractPort extracts the destination service port from the given destination,
func extractPort(svc *model.Service, ingress *routing.IngressRule) (*model.Port, error) {
	switch p := ingress.GetDestinationServicePort().(type) {
	case *routing.IngressRule_DestinationPort:
		num := p.DestinationPort
		port, exists := svc.Ports.GetByPort(int(num))
		if !exists {
			return nil, fmt.Errorf("cannot find port %d in %q", num, svc.Hostname)
		}
		return port, nil
	case *routing.IngressRule_DestinationPortName:
		name := p.DestinationPortName
		port, exists := svc.Ports.Get(name)
		if !exists {
			return nil, fmt.Errorf("cannot find port %q in %q", name, svc.Hostname)
		}
		return port, nil
	}
	return nil, errors.New("unrecognized destination port")
}
<|endoftext|>


<commit_before>package handler

import (
	"context"
	"encoding/json"

	"cloud.google.com/go/pubsub"
	"github.com/checkr/flagr/pkg/config"
	"github.com/checkr/flagr/swagger_gen/models"
	"google.golang.org/api/option"

	"github.com/sirupsen/logrus"
)

type pubsubRecorder struct {
	enabled  bool
	producer *pubsub.Client
	topic    *pubsub.Topic
}

// NewPubsubRecorder creates a new Pubsub recorder
var NewPubsubRecorder = func() DataRecorder {
	client, err := pubsub.NewClient(
		context.Background(),
		config.Config.RecorderPubsubProjectID,
		option.WithCredentialsFile(config.Config.RecorderPubsubKeyFile),
	)
	if err != nil {
		logrus.WithField("pubsub_error", err).Fatal("error getting pubsub client")
	}

	return &pubsubRecorder{
		producer: client,
		topic:    client.Topic(config.Config.RecorderPubsubTopicName),
		enabled:  config.Config.RecorderEnabled,
	}
}

func (p *pubsubRecorder) AsyncRecord(r *models.EvalResult) {
	if !p.enabled {
		return
	}

	pr := &pubsubEvalResult{
		EvalResult: r,
	}

	payload, err := pr.Payload()
	if err != nil {
		logrus.WithField("pubsub_error", err).Error("error marshaling")
	}

	messageFrame := pubsubMessageFrame{
		Payload:   string(payload),
		Encrypted: false,
	}

	message, err := messageFrame.encode()
	if err != nil {
		logrus.WithField("pubsub_error", err).Error("error marshaling")
	}

	ctx := context.Background()
	res := p.topic.Publish(ctx, &pubsub.Message{Data: message})
	if config.Config.RecorderPubsubVerbose {
		go func() {
			ctx, cancel := context.WithTimeout(ctx, config.Config.RecorderPubsubVerboseCancel)
			defer cancel()
			id, err := res.Get(ctx)
			if err != nil {
				logrus.WithFields(logrus.Fields{"pubsub_error": err, "id": id}).Error("error pushing to pubsub")
			}
		}()
	}
}

type pubsubEvalResult struct {
	*models.EvalResult
}

type pubsubMessageFrame struct {
	Payload   string `json:"payload"`
	Encrypted bool   `json:"encrypted"`
}

func (pmf *pubsubMessageFrame) encode() ([]byte, error) {
	return json.MarshalIndent(pmf, "", "  ")
}

// Payload marshals the EvalResult
func (r *pubsubEvalResult) Payload() ([]byte, error) {
	return r.EvalResult.MarshalBinary()
}
<commit_msg>remove fatal for now<commit_after>package handler

import (
	"context"
	"encoding/json"

	"cloud.google.com/go/pubsub"
	"github.com/checkr/flagr/pkg/config"
	"github.com/checkr/flagr/swagger_gen/models"
	"google.golang.org/api/option"

	"github.com/sirupsen/logrus"
)

type pubsubRecorder struct {
	enabled  bool
	producer *pubsub.Client
	topic    *pubsub.Topic
}

// NewPubsubRecorder creates a new Pubsub recorder
var NewPubsubRecorder = func() DataRecorder {
	client, err := pubsub.NewClient(
		context.Background(),
		config.Config.RecorderPubsubProjectID,
		option.WithCredentialsFile(config.Config.RecorderPubsubKeyFile),
	)
	if err != nil {
		// TODO: use Fatal again after fixing the test expecting to not panic.
		// logrus.WithField("pubsub_error", err).Fatal("error getting pubsub client")
		logrus.WithField("pubsub_error", err).Error("error getting pubsub client")
	}

	return &pubsubRecorder{
		producer: client,
		topic:    client.Topic(config.Config.RecorderPubsubTopicName),
		enabled:  config.Config.RecorderEnabled,
	}
}

func (p *pubsubRecorder) AsyncRecord(r *models.EvalResult) {
	if !p.enabled {
		return
	}

	pr := &pubsubEvalResult{
		EvalResult: r,
	}

	payload, err := pr.Payload()
	if err != nil {
		logrus.WithField("pubsub_error", err).Error("error marshaling")
	}

	messageFrame := pubsubMessageFrame{
		Payload:   string(payload),
		Encrypted: false,
	}

	message, err := messageFrame.encode()
	if err != nil {
		logrus.WithField("pubsub_error", err).Error("error marshaling")
	}

	ctx := context.Background()
	res := p.topic.Publish(ctx, &pubsub.Message{Data: message})
	if config.Config.RecorderPubsubVerbose {
		go func() {
			ctx, cancel := context.WithTimeout(ctx, config.Config.RecorderPubsubVerboseCancel)
			defer cancel()
			id, err := res.Get(ctx)
			if err != nil {
				logrus.WithFields(logrus.Fields{"pubsub_error": err, "id": id}).Error("error pushing to pubsub")
			}
		}()
	}
}

type pubsubEvalResult struct {
	*models.EvalResult
}

type pubsubMessageFrame struct {
	Payload   string `json:"payload"`
	Encrypted bool   `json:"encrypted"`
}

func (pmf *pubsubMessageFrame) encode() ([]byte, error) {
	return json.MarshalIndent(pmf, "", "  ")
}

// Payload marshals the EvalResult
func (r *pubsubEvalResult) Payload() ([]byte, error) {
	return r.EvalResult.MarshalBinary()
}
<|endoftext|>


<commit_before>/*
Copyright (C) 2016 Red Hat, Inc.

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

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

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

package registration

import (
	"fmt"

	"github.com/docker/machine/libmachine/host"
	"github.com/docker/machine/libmachine/log"
	"github.com/docker/machine/libmachine/provision"
	"github.com/pkg/errors"
)

type RegistrationParameters struct {
	Username string
	Password string
}

// Register host VM
func RegisterHostVM(host *host.Host, param *RegistrationParameters) error {
	commander := provision.GenericSSHCommander{Driver: host.Driver}
	registrator, supportRegistration, err := DetectRegistrator(commander)
	if !supportRegistration {
		log.Debug("Distribution doesn't support registration")
	}

	if err != nil && err != ErrDetectionFailed {
		return err
	}

	if registrator != nil {
		fmt.Println("Registering machine using subscription-manager")
		if param.Username == "" || param.Password == "" {
			return errors.New("This virutal machine requires registration. " +
				"Credentials must either be passed via the environment variables " +
				"MINISHIFT_USERNAME and MINISHIFT_PASSWORD " +
				" or the --username and --password flags\n")
		}

		if err := registrator.Register(param); err != nil {
			return err
		}
	}
	return nil
}

// Unregister host VM
func UnregisterHostVM(host *host.Host, param *RegistrationParameters) error {
	commander := provision.GenericSSHCommander{Driver: host.Driver}
	registrator, supportUnregistration, err := DetectRegistrator(commander)

	if !supportUnregistration {
		log.Debug("Distribution doesn't support unregistration")
	}

	if err != nil && err != ErrDetectionFailed {
		return err
	}

	if registrator != nil {
		fmt.Println("Unregistering machine")
		if err := registrator.Unregister(param); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>Issue #516 Fixing typo in utils.go<commit_after>/*
Copyright (C) 2016 Red Hat, Inc.

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

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

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

package registration

import (
	"fmt"

	"github.com/docker/machine/libmachine/host"
	"github.com/docker/machine/libmachine/log"
	"github.com/docker/machine/libmachine/provision"
	"github.com/pkg/errors"
)

type RegistrationParameters struct {
	Username string
	Password string
}

// Register host VM
func RegisterHostVM(host *host.Host, param *RegistrationParameters) error {
	commander := provision.GenericSSHCommander{Driver: host.Driver}
	registrator, supportRegistration, err := DetectRegistrator(commander)
	if !supportRegistration {
		log.Debug("Distribution doesn't support registration")
	}

	if err != nil && err != ErrDetectionFailed {
		return err
	}

	if registrator != nil {
		fmt.Println("Registering machine using subscription-manager")
		if param.Username == "" || param.Password == "" {
			return errors.New("This virtual machine requires registration. " +
				"Credentials must either be passed via the environment variables " +
				"MINISHIFT_USERNAME and MINISHIFT_PASSWORD " +
				" or the --username and --password flags\n")
		}

		if err := registrator.Register(param); err != nil {
			return err
		}
	}
	return nil
}

// Unregister host VM
func UnregisterHostVM(host *host.Host, param *RegistrationParameters) error {
	commander := provision.GenericSSHCommander{Driver: host.Driver}
	registrator, supportUnregistration, err := DetectRegistrator(commander)

	if !supportUnregistration {
		log.Debug("Distribution doesn't support unregistration")
	}

	if err != nil && err != ErrDetectionFailed {
		return err
	}

	if registrator != nil {
		fmt.Println("Unregistering machine")
		if err := registrator.Unregister(param); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


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

package httpserver

import (
	"log"
	"net/http"
	"time"
)

type HttpServerConfig struct {
	Address   string
	TLS       bool
	CertFile  string
	KeyFile   string
	Websocket bool // implement it - TODO
}

type HttpServer struct{}

func Start(handler http.Handler, config HttpServerConfig) (chan<- string, <-chan error, *HttpServer) {
	ctrlChannel := make(chan string)
	errorChannel := make(chan error)
	server := HttpServer{}
	go start(ctrlChannel, errorChannel, handler, config, &server)
	return ctrlChannel, errorChannel, &server
}

func start(ctrlChannel <-chan string, errorChannel chan<- error,
	router http.Handler, config HttpServerConfig, server *HttpServer) {
	var err error

	// Minio server config
	httpServer := &http.Server{
		Addr:           config.Address,
		Handler:        router,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}
	log.Println("Starting HTTP Server on:", config.Address)

	if config.TLS {
		httpServer.TLSConfig = getDefaultTLSConfig()
		err = httpServer.ListenAndServeTLS(config.CertFile, config.KeyFile)
	} else {
		err = httpServer.ListenAndServe()
	}
	errorChannel <- err
	close(errorChannel)
}
<commit_msg>Remove HTTP server timeouts write and read.<commit_after>/*
 * Mini Object Storage, (C) 2014 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package httpserver

import (
	"log"
	"net/http"
	//	"time"
)

type HttpServerConfig struct {
	Address   string
	TLS       bool
	CertFile  string
	KeyFile   string
	Websocket bool // implement it - TODO
}

type HttpServer struct{}

func Start(handler http.Handler, config HttpServerConfig) (chan<- string, <-chan error, *HttpServer) {
	ctrlChannel := make(chan string)
	errorChannel := make(chan error)
	server := HttpServer{}
	go start(ctrlChannel, errorChannel, handler, config, &server)
	return ctrlChannel, errorChannel, &server
}

func start(ctrlChannel <-chan string, errorChannel chan<- error,
	router http.Handler, config HttpServerConfig, server *HttpServer) {
	var err error

	// Minio server config
	httpServer := &http.Server{
		Addr:    config.Address,
		Handler: router,
		// TODO add this later with a proper timer thread
		//		ReadTimeout:    20 * time.Second,
		//		WriteTimeout:   20 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}
	log.Println("Starting HTTP Server on:", config.Address)

	if config.TLS {
		httpServer.TLSConfig = getDefaultTLSConfig()
		err = httpServer.ListenAndServeTLS(config.CertFile, config.KeyFile)
	} else {
		err = httpServer.ListenAndServe()
	}
	errorChannel <- err
	close(errorChannel)
}
<|endoftext|>


<commit_before>package rendering

import (
	"context"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"time"
)

var netTransport = &http.Transport{
	Proxy: http.ProxyFromEnvironment,
	Dial: (&net.Dialer{
		Timeout: 30 * time.Second,
	}).Dial,
	TLSHandshakeTimeout: 5 * time.Second,
}

var netClient = &http.Client{
	Transport: netTransport,
}

func (rs *RenderingService) renderViaHttp(ctx context.Context, opts Opts) (*RenderResult, error) {
	filePath, err := rs.getFilePathForNewImage()
	if err != nil {
		return nil, err
	}

	rendererUrl, err := url.Parse(rs.Cfg.RendererUrl)
	if err != nil {
		return nil, err
	}

	renderKey, err := rs.getRenderKey(opts.OrgId, opts.UserId, opts.OrgRole)
	if err != nil {
		return nil, err
	}

	queryParams := rendererUrl.Query()
	queryParams.Add("url", rs.getURL(opts.Path))
	queryParams.Add("renderKey", renderKey)
	queryParams.Add("width", strconv.Itoa(opts.Width))
	queryParams.Add("height", strconv.Itoa(opts.Height))
	queryParams.Add("domain", rs.domain)
	queryParams.Add("timezone", isoTimeOffsetToPosixTz(opts.Timezone))
	queryParams.Add("encoding", opts.Encoding)
	queryParams.Add("timeout", strconv.Itoa(int(opts.Timeout.Seconds())))
	rendererUrl.RawQuery = queryParams.Encode()

	req, err := http.NewRequest("GET", rendererUrl.String(), nil)
	if err != nil {
		return nil, err
	}

	reqContext, cancel := context.WithTimeout(ctx, opts.Timeout+time.Second*2)
	defer cancel()

	req = req.WithContext(reqContext)

	// make request to renderer server
	resp, err := netClient.Do(req)
	if err != nil {
		rs.log.Error("Failed to send request to remote rendering service.", "error", err)
		return nil, fmt.Errorf("Failed to send request to remote rendering service. %s", err)
	}

	// save response to file
	defer resp.Body.Close()

	// check for timeout first
	if reqContext.Err() == context.DeadlineExceeded {
		rs.log.Info("Rendering timed out")
		return nil, ErrTimeout
	}

	// if we didn't get a 200 response, something went wrong.
	if resp.StatusCode != http.StatusOK {
		rs.log.Error("Remote rendering request failed", "error", resp.Status)
		return nil, fmt.Errorf("Remote rendering request failed. %d: %s", resp.StatusCode, resp.Status)
	}

	out, err := os.Create(filePath)
	if err != nil {
		return nil, err
	}
	defer out.Close()
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		// check that we didn't timeout while receiving the response.
		if reqContext.Err() == context.DeadlineExceeded {
			rs.log.Info("Rendering timed out")
			return nil, ErrTimeout
		}
		rs.log.Error("Remote rendering request failed", "error", err)
		return nil, fmt.Errorf("Remote rendering request failed.  %s", err)
	}

	return &RenderResult{FilePath: filePath}, err
}
<commit_msg>Renderer: Add user-agent to rendering plugin requests (#20956)<commit_after>package rendering

import (
	"context"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"time"

	"github.com/grafana/grafana/pkg/setting"
)

var netTransport = &http.Transport{
	Proxy: http.ProxyFromEnvironment,
	Dial: (&net.Dialer{
		Timeout: 30 * time.Second,
	}).Dial,
	TLSHandshakeTimeout: 5 * time.Second,
}

var netClient = &http.Client{
	Transport: netTransport,
}

func (rs *RenderingService) renderViaHttp(ctx context.Context, opts Opts) (*RenderResult, error) {
	filePath, err := rs.getFilePathForNewImage()
	if err != nil {
		return nil, err
	}

	rendererUrl, err := url.Parse(rs.Cfg.RendererUrl)
	if err != nil {
		return nil, err
	}

	renderKey, err := rs.getRenderKey(opts.OrgId, opts.UserId, opts.OrgRole)
	if err != nil {
		return nil, err
	}

	queryParams := rendererUrl.Query()
	queryParams.Add("url", rs.getURL(opts.Path))
	queryParams.Add("renderKey", renderKey)
	queryParams.Add("width", strconv.Itoa(opts.Width))
	queryParams.Add("height", strconv.Itoa(opts.Height))
	queryParams.Add("domain", rs.domain)
	queryParams.Add("timezone", isoTimeOffsetToPosixTz(opts.Timezone))
	queryParams.Add("encoding", opts.Encoding)
	queryParams.Add("timeout", strconv.Itoa(int(opts.Timeout.Seconds())))
	rendererUrl.RawQuery = queryParams.Encode()

	req, err := http.NewRequest("GET", rendererUrl.String(), nil)
	if err != nil {
		return nil, err
	}

	req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))

	reqContext, cancel := context.WithTimeout(ctx, opts.Timeout+time.Second*2)
	defer cancel()

	req = req.WithContext(reqContext)

	// make request to renderer server
	resp, err := netClient.Do(req)
	if err != nil {
		rs.log.Error("Failed to send request to remote rendering service.", "error", err)
		return nil, fmt.Errorf("Failed to send request to remote rendering service. %s", err)
	}

	// save response to file
	defer resp.Body.Close()

	// check for timeout first
	if reqContext.Err() == context.DeadlineExceeded {
		rs.log.Info("Rendering timed out")
		return nil, ErrTimeout
	}

	// if we didn't get a 200 response, something went wrong.
	if resp.StatusCode != http.StatusOK {
		rs.log.Error("Remote rendering request failed", "error", resp.Status)
		return nil, fmt.Errorf("Remote rendering request failed. %d: %s", resp.StatusCode, resp.Status)
	}

	out, err := os.Create(filePath)
	if err != nil {
		return nil, err
	}
	defer out.Close()
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		// check that we didn't timeout while receiving the response.
		if reqContext.Err() == context.DeadlineExceeded {
			rs.log.Info("Rendering timed out")
			return nil, ErrTimeout
		}
		rs.log.Error("Remote rendering request failed", "error", err)
		return nil, fmt.Errorf("Remote rendering request failed.  %s", err)
	}

	return &RenderResult{FilePath: filePath}, err
}
<|endoftext|>


<commit_before>//go:build integration
// +build integration

package sqlstore

import (
	"context"
	"fmt"
	"testing"

	"github.com/grafana/grafana/pkg/models"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestStatsDataAccess(t *testing.T) {
	sqlStore := InitTestDB(t)
	populateDB(t, sqlStore)

	t.Run("Get system stats should not results in error", func(t *testing.T) {
		query := models.GetSystemStatsQuery{}
		err := GetSystemStats(&query)
		require.NoError(t, err)
		assert.Equal(t, int64(3), query.Result.Users)
		assert.Equal(t, 0, query.Result.Editors)
		assert.Equal(t, 0, query.Result.Viewers)
		assert.Equal(t, 3, query.Result.Admins)
		assert.Equal(t, int64(0), query.Result.LibraryPanels)
		assert.Equal(t, int64(0), query.Result.LibraryVariables)
	})

	t.Run("Get system user count stats should not results in error", func(t *testing.T) {
		query := models.GetSystemUserCountStatsQuery{}
		err := GetSystemUserCountStats(context.Background(), &query)
		assert.NoError(t, err)
	})

	t.Run("Get datasource stats should not results in error", func(t *testing.T) {
		query := models.GetDataSourceStatsQuery{}
		err := GetDataSourceStats(&query)
		assert.NoError(t, err)
	})

	t.Run("Get datasource access stats should not results in error", func(t *testing.T) {
		query := models.GetDataSourceAccessStatsQuery{}
		err := GetDataSourceAccessStats(&query)
		assert.NoError(t, err)
	})

	t.Run("Get alert notifier stats should not results in error", func(t *testing.T) {
		query := models.GetAlertNotifierUsageStatsQuery{}
		err := GetAlertNotifiersUsageStats(context.Background(), &query)
		assert.NoError(t, err)
	})

	t.Run("Get admin stats should not result in error", func(t *testing.T) {
		query := models.GetAdminStatsQuery{}
		err := GetAdminStats(&query)
		assert.NoError(t, err)
	})
}

func populateDB(t *testing.T, sqlStore *SQLStore) {
	t.Helper()

	users := make([]models.User, 3)
	for i := range users {
		cmd := models.CreateUserCommand{
			Email:   fmt.Sprintf("usertest%v@test.com", i),
			Name:    fmt.Sprintf("user name %v", i),
			Login:   fmt.Sprintf("user_test_%v_login", i),
			OrgName: fmt.Sprintf("Org #%v", i),
		}
		user, err := sqlStore.CreateUser(context.Background(), cmd)
		require.NoError(t, err)
		users[i] = *user
	}

	// get 1st user's organisation
	getOrgByIdQuery := &models.GetOrgByIdQuery{Id: users[0].OrgId}
	err := GetOrgById(getOrgByIdQuery)
	require.NoError(t, err)
	org := getOrgByIdQuery.Result

	// add 2nd user as editor
	cmd := &models.AddOrgUserCommand{
		OrgId:  org.Id,
		UserId: users[1].Id,
		Role:   models.ROLE_EDITOR,
	}
	err = AddOrgUser(cmd)
	require.NoError(t, err)

	// add 3rd user as viewer
	cmd = &models.AddOrgUserCommand{
		OrgId:  org.Id,
		UserId: users[2].Id,
		Role:   models.ROLE_VIEWER,
	}
	err = AddOrgUser(cmd)
	require.NoError(t, err)

	// get 2nd user's organisation
	getOrgByIdQuery = &models.GetOrgByIdQuery{Id: users[1].OrgId}
	err = GetOrgById(getOrgByIdQuery)
	require.NoError(t, err)
	org = getOrgByIdQuery.Result

	// add 1st user as admin
	cmd = &models.AddOrgUserCommand{
		OrgId:  org.Id,
		UserId: users[0].Id,
		Role:   models.ROLE_ADMIN,
	}
	err = AddOrgUser(cmd)
	require.NoError(t, err)

	// update 1st user last seen at
	updateUserLastSeenAtCmd := &models.UpdateUserLastSeenAtCommand{
		UserId: users[0].Id,
	}
	err = UpdateUserLastSeenAt(updateUserLastSeenAtCmd)
	require.NoError(t, err)

	// force renewal of user stats
	err = updateUserRoleCountsIfNecessary(context.Background(), true)
	require.NoError(t, err)
}
<commit_msg>Cast fields to int64 (#39179)<commit_after>//go:build integration
// +build integration

package sqlstore

import (
	"context"
	"fmt"
	"testing"

	"github.com/grafana/grafana/pkg/models"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestStatsDataAccess(t *testing.T) {
	sqlStore := InitTestDB(t)
	populateDB(t, sqlStore)

	t.Run("Get system stats should not results in error", func(t *testing.T) {
		query := models.GetSystemStatsQuery{}
		err := GetSystemStats(&query)
		require.NoError(t, err)
		assert.Equal(t, int64(3), query.Result.Users)
		assert.Equal(t, int64(0), query.Result.Editors)
		assert.Equal(t, int64(0), query.Result.Viewers)
		assert.Equal(t, int64(3), query.Result.Admins)
		assert.Equal(t, int64(0), query.Result.LibraryPanels)
		assert.Equal(t, int64(0), query.Result.LibraryVariables)
	})

	t.Run("Get system user count stats should not results in error", func(t *testing.T) {
		query := models.GetSystemUserCountStatsQuery{}
		err := GetSystemUserCountStats(context.Background(), &query)
		assert.NoError(t, err)
	})

	t.Run("Get datasource stats should not results in error", func(t *testing.T) {
		query := models.GetDataSourceStatsQuery{}
		err := GetDataSourceStats(&query)
		assert.NoError(t, err)
	})

	t.Run("Get datasource access stats should not results in error", func(t *testing.T) {
		query := models.GetDataSourceAccessStatsQuery{}
		err := GetDataSourceAccessStats(&query)
		assert.NoError(t, err)
	})

	t.Run("Get alert notifier stats should not results in error", func(t *testing.T) {
		query := models.GetAlertNotifierUsageStatsQuery{}
		err := GetAlertNotifiersUsageStats(context.Background(), &query)
		assert.NoError(t, err)
	})

	t.Run("Get admin stats should not result in error", func(t *testing.T) {
		query := models.GetAdminStatsQuery{}
		err := GetAdminStats(&query)
		assert.NoError(t, err)
	})
}

func populateDB(t *testing.T, sqlStore *SQLStore) {
	t.Helper()

	users := make([]models.User, 3)
	for i := range users {
		cmd := models.CreateUserCommand{
			Email:   fmt.Sprintf("usertest%v@test.com", i),
			Name:    fmt.Sprintf("user name %v", i),
			Login:   fmt.Sprintf("user_test_%v_login", i),
			OrgName: fmt.Sprintf("Org #%v", i),
		}
		user, err := sqlStore.CreateUser(context.Background(), cmd)
		require.NoError(t, err)
		users[i] = *user
	}

	// get 1st user's organisation
	getOrgByIdQuery := &models.GetOrgByIdQuery{Id: users[0].OrgId}
	err := GetOrgById(getOrgByIdQuery)
	require.NoError(t, err)
	org := getOrgByIdQuery.Result

	// add 2nd user as editor
	cmd := &models.AddOrgUserCommand{
		OrgId:  org.Id,
		UserId: users[1].Id,
		Role:   models.ROLE_EDITOR,
	}
	err = AddOrgUser(cmd)
	require.NoError(t, err)

	// add 3rd user as viewer
	cmd = &models.AddOrgUserCommand{
		OrgId:  org.Id,
		UserId: users[2].Id,
		Role:   models.ROLE_VIEWER,
	}
	err = AddOrgUser(cmd)
	require.NoError(t, err)

	// get 2nd user's organisation
	getOrgByIdQuery = &models.GetOrgByIdQuery{Id: users[1].OrgId}
	err = GetOrgById(getOrgByIdQuery)
	require.NoError(t, err)
	org = getOrgByIdQuery.Result

	// add 1st user as admin
	cmd = &models.AddOrgUserCommand{
		OrgId:  org.Id,
		UserId: users[0].Id,
		Role:   models.ROLE_ADMIN,
	}
	err = AddOrgUser(cmd)
	require.NoError(t, err)

	// update 1st user last seen at
	updateUserLastSeenAtCmd := &models.UpdateUserLastSeenAtCommand{
		UserId: users[0].Id,
	}
	err = UpdateUserLastSeenAt(updateUserLastSeenAtCmd)
	require.NoError(t, err)

	// force renewal of user stats
	err = updateUserRoleCountsIfNecessary(context.Background(), true)
	require.NoError(t, err)
}
<|endoftext|>


<commit_before>package entry

import (
	"time"

	"github.com/SeerUK/tid/pkg/tracking"
	"github.com/eidolon/console"
	"github.com/eidolon/console/parameters"
)

// CreateCommand creates a command to add timesheet entries.
func CreateCommand(factory tracking.Factory) *console.Command {
	var duration time.Duration
	var note string
	var started time.Time

	configure := func(def *console.Definition) {
		def.AddArgument(console.ArgumentDefinition{
			Value: parameters.NewDurationValue(&duration),
			Spec:  "DURATION",
			Desc:  "How long did you spend on what you want to add?",
		})

		def.AddArgument(console.ArgumentDefinition{
			Value: parameters.NewStringValue(&note),
			Spec:  "NOTE",
			Desc:  "What were you working on?",
		})

		def.AddOption(console.OptionDefinition{
			Value: parameters.NewDateValue(&started),
			Spec:  "-d, --date=DATE",
			Desc:  "When did you start working?",
		})
	}

	execute := func(input *console.Input, output *console.Output) error {
		facade := factory.BuildEntryFacade()

		entry, err := facade.Create(started, duration, note)
		if err != nil {
			return err
		}

		output.Printf("Added entry '%s' (%s)\n", entry.Note, entry.ShortHash())

		return nil
	}

	return &console.Command{
		Name:        "create",
		Description: "Create a new timesheet entry.",
		Configure:   configure,
		Execute:     execute,
	}
}
<commit_msg>Fixed create note creating entries on today by default.<commit_after>package entry

import (
	"time"

	"github.com/SeerUK/tid/pkg/tracking"
	"github.com/eidolon/console"
	"github.com/eidolon/console/parameters"
)

// CreateCommand creates a command to add timesheet entries.
func CreateCommand(factory tracking.Factory) *console.Command {
	var duration time.Duration
	var note string
	var started = time.Now()

	configure := func(def *console.Definition) {
		def.AddArgument(console.ArgumentDefinition{
			Value: parameters.NewDurationValue(&duration),
			Spec:  "DURATION",
			Desc:  "How long did you spend on what you want to add?",
		})

		def.AddArgument(console.ArgumentDefinition{
			Value: parameters.NewStringValue(&note),
			Spec:  "NOTE",
			Desc:  "What were you working on?",
		})

		def.AddOption(console.OptionDefinition{
			Value: parameters.NewDateValue(&started),
			Spec:  "-d, --date=DATE",
			Desc:  "When did you start working? (Default: today)",
		})
	}

	execute := func(input *console.Input, output *console.Output) error {
		facade := factory.BuildEntryFacade()

		entry, err := facade.Create(started, duration, note)
		if err != nil {
			return err
		}

		output.Printf("Added entry '%s' (%s)\n", entry.Note, entry.ShortHash())

		return nil
	}

	return &console.Command{
		Name:        "create",
		Description: "Create a new timesheet entry.",
		Configure:   configure,
		Execute:     execute,
	}
}
<|endoftext|>


<commit_before>package rep

import (
	"encoding/json"
	"errors"
	"net/url"
	"strconv"

	"github.com/cloudfoundry-incubator/bbs/models"
	"github.com/cloudfoundry-incubator/executor"
)

const (
	LifecycleTag  = "lifecycle"
	ResultFileTag = "result-file"
	DomainTag     = "domain"

	TaskLifecycle = "task"
	LRPLifecycle  = "lrp"

	ProcessGuidTag  = "process-guid"
	InstanceGuidTag = "instance-guid"
	ProcessIndexTag = "process-index"
)

var (
	ErrContainerMissingTags = errors.New("container is missing tags")
	ErrInvalidProcessIndex  = errors.New("container does not have a valid process index")
)

func ActualLRPKeyFromTags(tags executor.Tags) (*models.ActualLRPKey, error) {
	if tags == nil {
		return &models.ActualLRPKey{}, ErrContainerMissingTags
	}

	processIndex, err := strconv.Atoi(tags[ProcessIndexTag])
	if err != nil {
		return &models.ActualLRPKey{}, ErrInvalidProcessIndex
	}

	actualLRPKey := models.NewActualLRPKey(
		tags[ProcessGuidTag],
		int32(processIndex),
		tags[DomainTag],
	)

	err = actualLRPKey.Validate()
	if err != nil {
		return &models.ActualLRPKey{}, err
	}

	return &actualLRPKey, nil
}

func ActualLRPInstanceKeyFromContainer(container executor.Container, cellID string) (*models.ActualLRPInstanceKey, error) {
	if container.Tags == nil {
		return &models.ActualLRPInstanceKey{}, ErrContainerMissingTags
	}

	actualLRPInstanceKey := models.NewActualLRPInstanceKey(
		container.Tags[InstanceGuidTag],
		cellID,
	)

	err := actualLRPInstanceKey.Validate()
	if err != nil {
		return &models.ActualLRPInstanceKey{}, err
	}

	return &actualLRPInstanceKey, nil
}

func ActualLRPNetInfoFromContainer(container executor.Container) (*models.ActualLRPNetInfo, error) {
	ports := []*models.PortMapping{}
	for _, portMapping := range container.Ports {
		ports = append(ports, models.NewPortMapping(uint32(portMapping.HostPort), uint32(portMapping.ContainerPort)))
	}

	actualLRPNetInfo := models.NewActualLRPNetInfo(container.ExternalIP, ports...)

	err := actualLRPNetInfo.Validate()
	if err != nil {
		return nil, err
	}

	return &actualLRPNetInfo, nil
}

func LRPContainerGuid(processGuid, instanceGuid string) string {
	return processGuid + "-" + instanceGuid
}

func NewRunRequestFromDesiredLRP(
	containerGuid string,
	desiredLRP *models.DesiredLRP,
	lrpKey *models.ActualLRPKey,
	lrpInstanceKey *models.ActualLRPInstanceKey,
) (executor.RunRequest, error) {
	diskScope, err := diskScopeForRootFS(desiredLRP.RootFs)
	if err != nil {
		return executor.RunRequest{}, err
	}

	mounts, err := convertVolumeMounts(desiredLRP.VolumeMounts)
	if err != nil {
		return executor.RunRequest{}, err
	}

	runInfo := executor.RunInfo{
		CPUWeight: uint(desiredLRP.CpuWeight),
		DiskScope: diskScope,
		Ports:     ConvertPortMappings(desiredLRP.Ports),
		LogConfig: executor.LogConfig{
			Guid:       desiredLRP.LogGuid,
			Index:      int(lrpKey.Index),
			SourceName: desiredLRP.LogSource,
		},

		MetricsConfig: executor.MetricsConfig{
			Guid:  desiredLRP.MetricsGuid,
			Index: int(lrpKey.Index),
		},
		StartTimeout:       uint(desiredLRP.StartTimeout),
		Privileged:         desiredLRP.Privileged,
		CachedDependencies: ConvertCachedDependencies(desiredLRP.CachedDependencies),
		Setup:              desiredLRP.Setup,
		Action:             desiredLRP.Action,
		Monitor:            desiredLRP.Monitor,
		EgressRules:        desiredLRP.EgressRules,
		Env: append([]executor.EnvironmentVariable{
			{Name: "INSTANCE_GUID", Value: lrpInstanceKey.InstanceGuid},
			{Name: "INSTANCE_INDEX", Value: strconv.Itoa(int(lrpKey.Index))},
			{Name: "CF_INSTANCE_GUID", Value: lrpInstanceKey.InstanceGuid},
			{Name: "CF_INSTANCE_INDEX", Value: strconv.Itoa(int(lrpKey.Index))},
		}, executor.EnvironmentVariablesFromModel(desiredLRP.EnvironmentVariables)...),
		TrustedSystemCertificatesPath: desiredLRP.TrustedSystemCertificatesPath,
		VolumeMounts:                  mounts,
		Network:                       convertNetwork(desiredLRP.Network),
	}
	tags := executor.Tags{}
	return executor.NewRunRequest(containerGuid, &runInfo, tags), nil
}

func NewRunRequestFromTask(task *models.Task) (executor.RunRequest, error) {
	diskScope, err := diskScopeForRootFS(task.RootFs)
	if err != nil {
		return executor.RunRequest{}, err
	}

	mounts, err := convertVolumeMounts(task.VolumeMounts)
	if err != nil {
		return executor.RunRequest{}, err
	}

	tags := executor.Tags{
		ResultFileTag: task.ResultFile,
	}
	runInfo := executor.RunInfo{
		DiskScope:  diskScope,
		CPUWeight:  uint(task.CpuWeight),
		Privileged: task.Privileged,
		LogConfig: executor.LogConfig{
			Guid:       task.LogGuid,
			SourceName: task.LogSource,
		},
		MetricsConfig: executor.MetricsConfig{
			Guid: task.MetricsGuid,
		},
		CachedDependencies:            ConvertCachedDependencies(task.CachedDependencies),
		Action:                        task.Action,
		Env:                           executor.EnvironmentVariablesFromModel(task.EnvironmentVariables),
		EgressRules:                   task.EgressRules,
		TrustedSystemCertificatesPath: task.TrustedSystemCertificatesPath,
		VolumeMounts:                  mounts,
		Network:                       convertNetwork(task.Network),
	}
	return executor.NewRunRequest(task.TaskGuid, &runInfo, tags), nil
}

func ConvertCachedDependencies(modelDeps []*models.CachedDependency) []executor.CachedDependency {
	execDeps := make([]executor.CachedDependency, len(modelDeps))
	for i := range modelDeps {
		execDeps[i] = ConvertCachedDependency(modelDeps[i])
	}
	return execDeps
}

func ConvertCachedDependency(modelDep *models.CachedDependency) executor.CachedDependency {
	return executor.CachedDependency{
		Name:      modelDep.Name,
		From:      modelDep.From,
		To:        modelDep.To,
		CacheKey:  modelDep.CacheKey,
		LogSource: modelDep.LogSource,
	}
}

func convertVolumeMounts(volumeMounts []*models.VolumeMount) ([]executor.VolumeMount, error) {
	execMnts := make([]executor.VolumeMount, len(volumeMounts))
	for i := range volumeMounts {
		var err error
		execMnts[i], err = convertVolumeMount(volumeMounts[i])
		if err != nil {
			return nil, err
		}
	}
	return execMnts, nil
}

func convertVolumeMount(volumeMnt *models.VolumeMount) (executor.VolumeMount, error) {
	var config map[string]interface{}
	if len(volumeMnt.Config) > 0 {
		err := json.Unmarshal(volumeMnt.Config, &config)
		if err != nil {
			return executor.VolumeMount{}, err
		}
	}

	return executor.VolumeMount{
		Driver:        volumeMnt.Driver,
		VolumeId:      volumeMnt.VolumeId,
		ContainerPath: volumeMnt.ContainerPath,
		Mode:          executor.BindMountMode(volumeMnt.Mode),
		Config:        config,
	}, nil
}

func convertNetwork(network *models.Network) *executor.Network {
	if network == nil {
		return nil
	}

	return &executor.Network{
		Properties: network.Properties,
	}
}

func ConvertPortMappings(containerPorts []uint32) []executor.PortMapping {
	out := []executor.PortMapping{}
	for _, port := range containerPorts {
		out = append(out, executor.PortMapping{
			ContainerPort: uint16(port),
		})
	}

	return out
}

func diskScopeForRootFS(rootFS string) (executor.DiskLimitScope, error) {
	preloaded := false

	url, err := url.Parse(rootFS)
	if err != nil {
		return executor.TotalDiskLimit, err
	}

	if url.Scheme == models.PreloadedRootFSScheme {
		preloaded = true
	}

	if preloaded {
		return executor.ExclusiveDiskLimit, nil
	}
	return executor.TotalDiskLimit, nil
}
<commit_msg>'Use instanceGuid as the container handler<commit_after>package rep

import (
	"encoding/json"
	"errors"
	"net/url"
	"strconv"

	"github.com/cloudfoundry-incubator/bbs/models"
	"github.com/cloudfoundry-incubator/executor"
)

const (
	LifecycleTag  = "lifecycle"
	ResultFileTag = "result-file"
	DomainTag     = "domain"

	TaskLifecycle = "task"
	LRPLifecycle  = "lrp"

	ProcessGuidTag  = "process-guid"
	InstanceGuidTag = "instance-guid"
	ProcessIndexTag = "process-index"
)

var (
	ErrContainerMissingTags = errors.New("container is missing tags")
	ErrInvalidProcessIndex  = errors.New("container does not have a valid process index")
)

func ActualLRPKeyFromTags(tags executor.Tags) (*models.ActualLRPKey, error) {
	if tags == nil {
		return &models.ActualLRPKey{}, ErrContainerMissingTags
	}

	processIndex, err := strconv.Atoi(tags[ProcessIndexTag])
	if err != nil {
		return &models.ActualLRPKey{}, ErrInvalidProcessIndex
	}

	actualLRPKey := models.NewActualLRPKey(
		tags[ProcessGuidTag],
		int32(processIndex),
		tags[DomainTag],
	)

	err = actualLRPKey.Validate()
	if err != nil {
		return &models.ActualLRPKey{}, err
	}

	return &actualLRPKey, nil
}

func ActualLRPInstanceKeyFromContainer(container executor.Container, cellID string) (*models.ActualLRPInstanceKey, error) {
	if container.Tags == nil {
		return &models.ActualLRPInstanceKey{}, ErrContainerMissingTags
	}

	actualLRPInstanceKey := models.NewActualLRPInstanceKey(
		container.Tags[InstanceGuidTag],
		cellID,
	)

	err := actualLRPInstanceKey.Validate()
	if err != nil {
		return &models.ActualLRPInstanceKey{}, err
	}

	return &actualLRPInstanceKey, nil
}

func ActualLRPNetInfoFromContainer(container executor.Container) (*models.ActualLRPNetInfo, error) {
	ports := []*models.PortMapping{}
	for _, portMapping := range container.Ports {
		ports = append(ports, models.NewPortMapping(uint32(portMapping.HostPort), uint32(portMapping.ContainerPort)))
	}

	actualLRPNetInfo := models.NewActualLRPNetInfo(container.ExternalIP, ports...)

	err := actualLRPNetInfo.Validate()
	if err != nil {
		return nil, err
	}

	return &actualLRPNetInfo, nil
}

func LRPContainerGuid(processGuid, instanceGuid string) string {
	return instanceGuid
}

func NewRunRequestFromDesiredLRP(
	containerGuid string,
	desiredLRP *models.DesiredLRP,
	lrpKey *models.ActualLRPKey,
	lrpInstanceKey *models.ActualLRPInstanceKey,
) (executor.RunRequest, error) {
	diskScope, err := diskScopeForRootFS(desiredLRP.RootFs)
	if err != nil {
		return executor.RunRequest{}, err
	}

	mounts, err := convertVolumeMounts(desiredLRP.VolumeMounts)
	if err != nil {
		return executor.RunRequest{}, err
	}

	runInfo := executor.RunInfo{
		CPUWeight: uint(desiredLRP.CpuWeight),
		DiskScope: diskScope,
		Ports:     ConvertPortMappings(desiredLRP.Ports),
		LogConfig: executor.LogConfig{
			Guid:       desiredLRP.LogGuid,
			Index:      int(lrpKey.Index),
			SourceName: desiredLRP.LogSource,
		},

		MetricsConfig: executor.MetricsConfig{
			Guid:  desiredLRP.MetricsGuid,
			Index: int(lrpKey.Index),
		},
		StartTimeout:       uint(desiredLRP.StartTimeout),
		Privileged:         desiredLRP.Privileged,
		CachedDependencies: ConvertCachedDependencies(desiredLRP.CachedDependencies),
		Setup:              desiredLRP.Setup,
		Action:             desiredLRP.Action,
		Monitor:            desiredLRP.Monitor,
		EgressRules:        desiredLRP.EgressRules,
		Env: append([]executor.EnvironmentVariable{
			{Name: "INSTANCE_GUID", Value: lrpInstanceKey.InstanceGuid},
			{Name: "INSTANCE_INDEX", Value: strconv.Itoa(int(lrpKey.Index))},
			{Name: "CF_INSTANCE_GUID", Value: lrpInstanceKey.InstanceGuid},
			{Name: "CF_INSTANCE_INDEX", Value: strconv.Itoa(int(lrpKey.Index))},
		}, executor.EnvironmentVariablesFromModel(desiredLRP.EnvironmentVariables)...),
		TrustedSystemCertificatesPath: desiredLRP.TrustedSystemCertificatesPath,
		VolumeMounts:                  mounts,
		Network:                       convertNetwork(desiredLRP.Network),
	}
	tags := executor.Tags{}
	return executor.NewRunRequest(containerGuid, &runInfo, tags), nil
}

func NewRunRequestFromTask(task *models.Task) (executor.RunRequest, error) {
	diskScope, err := diskScopeForRootFS(task.RootFs)
	if err != nil {
		return executor.RunRequest{}, err
	}

	mounts, err := convertVolumeMounts(task.VolumeMounts)
	if err != nil {
		return executor.RunRequest{}, err
	}

	tags := executor.Tags{
		ResultFileTag: task.ResultFile,
	}
	runInfo := executor.RunInfo{
		DiskScope:  diskScope,
		CPUWeight:  uint(task.CpuWeight),
		Privileged: task.Privileged,
		LogConfig: executor.LogConfig{
			Guid:       task.LogGuid,
			SourceName: task.LogSource,
		},
		MetricsConfig: executor.MetricsConfig{
			Guid: task.MetricsGuid,
		},
		CachedDependencies:            ConvertCachedDependencies(task.CachedDependencies),
		Action:                        task.Action,
		Env:                           executor.EnvironmentVariablesFromModel(task.EnvironmentVariables),
		EgressRules:                   task.EgressRules,
		TrustedSystemCertificatesPath: task.TrustedSystemCertificatesPath,
		VolumeMounts:                  mounts,
		Network:                       convertNetwork(task.Network),
	}
	return executor.NewRunRequest(task.TaskGuid, &runInfo, tags), nil
}

func ConvertCachedDependencies(modelDeps []*models.CachedDependency) []executor.CachedDependency {
	execDeps := make([]executor.CachedDependency, len(modelDeps))
	for i := range modelDeps {
		execDeps[i] = ConvertCachedDependency(modelDeps[i])
	}
	return execDeps
}

func ConvertCachedDependency(modelDep *models.CachedDependency) executor.CachedDependency {
	return executor.CachedDependency{
		Name:      modelDep.Name,
		From:      modelDep.From,
		To:        modelDep.To,
		CacheKey:  modelDep.CacheKey,
		LogSource: modelDep.LogSource,
	}
}

func convertVolumeMounts(volumeMounts []*models.VolumeMount) ([]executor.VolumeMount, error) {
	execMnts := make([]executor.VolumeMount, len(volumeMounts))
	for i := range volumeMounts {
		var err error
		execMnts[i], err = convertVolumeMount(volumeMounts[i])
		if err != nil {
			return nil, err
		}
	}
	return execMnts, nil
}

func convertVolumeMount(volumeMnt *models.VolumeMount) (executor.VolumeMount, error) {
	var config map[string]interface{}
	if len(volumeMnt.Config) > 0 {
		err := json.Unmarshal(volumeMnt.Config, &config)
		if err != nil {
			return executor.VolumeMount{}, err
		}
	}

	return executor.VolumeMount{
		Driver:        volumeMnt.Driver,
		VolumeId:      volumeMnt.VolumeId,
		ContainerPath: volumeMnt.ContainerPath,
		Mode:          executor.BindMountMode(volumeMnt.Mode),
		Config:        config,
	}, nil
}

func convertNetwork(network *models.Network) *executor.Network {
	if network == nil {
		return nil
	}

	return &executor.Network{
		Properties: network.Properties,
	}
}

func ConvertPortMappings(containerPorts []uint32) []executor.PortMapping {
	out := []executor.PortMapping{}
	for _, port := range containerPorts {
		out = append(out, executor.PortMapping{
			ContainerPort: uint16(port),
		})
	}

	return out
}

func diskScopeForRootFS(rootFS string) (executor.DiskLimitScope, error) {
	preloaded := false

	url, err := url.Parse(rootFS)
	if err != nil {
		return executor.TotalDiskLimit, err
	}

	if url.Scheme == models.PreloadedRootFSScheme {
		preloaded = true
	}

	if preloaded {
		return executor.ExclusiveDiskLimit, nil
	}
	return executor.TotalDiskLimit, nil
}
<|endoftext|>


<commit_before>package self_test

import (
	"crypto/tls"
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net"
	"sync"
	"sync/atomic"

	quic "github.com/lucas-clemente/quic-go"
	"github.com/lucas-clemente/quic-go/integrationtests/tools/testserver"
	"github.com/lucas-clemente/quic-go/internal/testdata"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Stream Cancelations", func() {
	const numStreams = 80

	Context("canceling the read side", func() {
		var server quic.Listener

		runServer := func() <-chan int32 {
			numCanceledStreamsChan := make(chan int32)
			var err error
			server, err = quic.ListenAddr("localhost:0", testdata.GetTLSConfig(), nil)
			Expect(err).ToNot(HaveOccurred())

			var canceledCounter int32
			go func() {
				defer GinkgoRecover()
				var wg sync.WaitGroup
				wg.Add(numStreams)
				sess, err := server.Accept()
				Expect(err).ToNot(HaveOccurred())
				for i := 0; i < numStreams; i++ {
					go func() {
						defer GinkgoRecover()
						defer wg.Done()
						str, err := sess.OpenUniStreamSync()
						Expect(err).ToNot(HaveOccurred())
						if _, err = str.Write(testserver.PRData); err != nil {
							Expect(err).To(MatchError(fmt.Sprintf("Stream %d was reset with error code %d", str.StreamID(), str.StreamID())))
							atomic.AddInt32(&canceledCounter, 1)
							return
						}
						Expect(str.Close()).To(Succeed())
					}()
				}
				wg.Wait()
				numCanceledStreamsChan <- atomic.LoadInt32(&canceledCounter)
			}()
			return numCanceledStreamsChan
		}

		AfterEach(func() {
			Expect(server.Close()).To(Succeed())
		})

		It("downloads when the client immediately cancels most streams", func() {
			serverCanceledCounterChan := runServer()
			sess, err := quic.DialAddr(
				fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
				&tls.Config{RootCAs: testdata.GetRootCA()},
				&quic.Config{MaxIncomingUniStreams: numStreams / 2},
			)
			Expect(err).ToNot(HaveOccurred())

			var canceledCounter int32
			var wg sync.WaitGroup
			wg.Add(numStreams)
			for i := 0; i < numStreams; i++ {
				go func() {
					defer GinkgoRecover()
					defer wg.Done()
					str, err := sess.AcceptUniStream()
					Expect(err).ToNot(HaveOccurred())
					// cancel around 2/3 of the streams
					if rand.Int31()%3 != 0 {
						atomic.AddInt32(&canceledCounter, 1)
						Expect(str.CancelRead(quic.ErrorCode(str.StreamID()))).To(Succeed())
						return
					}
					data, err := ioutil.ReadAll(str)
					Expect(err).ToNot(HaveOccurred())
					Expect(data).To(Equal(testserver.PRData))
				}()
			}
			wg.Wait()

			var serverCanceledCounter int32
			Eventually(serverCanceledCounterChan).Should(Receive(&serverCanceledCounter))
			Expect(sess.Close()).To(Succeed())

			clientCanceledCounter := atomic.LoadInt32(&canceledCounter)
			// The server will only count a stream as being reset if learns about the cancelation before it finished writing all data.
			Expect(clientCanceledCounter).To(BeNumerically(">=", serverCanceledCounter))
			fmt.Fprintf(GinkgoWriter, "Canceled reading on %d of %d streams.\n", clientCanceledCounter, numStreams)
			Expect(clientCanceledCounter).To(BeNumerically(">", numStreams/10))
			Expect(numStreams - clientCanceledCounter).To(BeNumerically(">", numStreams/10))
		})

		It("downloads when the client cancels streams after reading from them for a bit", func() {
			serverCanceledCounterChan := runServer()

			sess, err := quic.DialAddr(
				fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
				&tls.Config{RootCAs: testdata.GetRootCA()},
				&quic.Config{MaxIncomingUniStreams: numStreams / 2},
			)
			Expect(err).ToNot(HaveOccurred())

			var canceledCounter int32
			var wg sync.WaitGroup
			wg.Add(numStreams)
			for i := 0; i < numStreams; i++ {
				go func() {
					defer GinkgoRecover()
					defer wg.Done()
					str, err := sess.AcceptUniStream()
					Expect(err).ToNot(HaveOccurred())
					// only read some data from about 1/3 of the streams
					if rand.Int31()%3 != 0 {
						length := int(rand.Int31n(int32(len(testserver.PRData) - 1)))
						data, err := ioutil.ReadAll(io.LimitReader(str, int64(length)))
						Expect(err).ToNot(HaveOccurred())
						Expect(str.CancelRead(quic.ErrorCode(str.StreamID()))).To(Succeed())
						Expect(data).To(Equal(testserver.PRData[:length]))
						atomic.AddInt32(&canceledCounter, 1)
						return
					}
					data, err := ioutil.ReadAll(str)
					Expect(err).ToNot(HaveOccurred())
					Expect(data).To(Equal(testserver.PRData))
				}()
			}
			wg.Wait()

			var serverCanceledCounter int32
			Eventually(serverCanceledCounterChan).Should(Receive(&serverCanceledCounter))
			Expect(sess.Close()).To(Succeed())

			clientCanceledCounter := atomic.LoadInt32(&canceledCounter)
			// The server will only count a stream as being reset if learns about the cancelation before it finished writing all data.
			Expect(clientCanceledCounter).To(BeNumerically(">=", serverCanceledCounter))
			fmt.Fprintf(GinkgoWriter, "Canceled reading on %d of %d streams.\n", clientCanceledCounter, numStreams)
			Expect(clientCanceledCounter).To(BeNumerically(">", numStreams/10))
			Expect(numStreams - clientCanceledCounter).To(BeNumerically(">", numStreams/10))
		})
	})

	Context("canceling the write side", func() {
		runClient := func(server quic.Listener) int32 /* number of canceled streams */ {
			sess, err := quic.DialAddr(
				fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
				&tls.Config{RootCAs: testdata.GetRootCA()},
				&quic.Config{MaxIncomingUniStreams: numStreams / 2},
			)
			Expect(err).ToNot(HaveOccurred())

			var wg sync.WaitGroup
			var counter int32
			wg.Add(numStreams)
			for i := 0; i < numStreams; i++ {
				go func() {
					defer GinkgoRecover()
					defer wg.Done()
					str, err := sess.AcceptUniStream()
					Expect(err).ToNot(HaveOccurred())
					data, err := ioutil.ReadAll(str)
					if err != nil {
						atomic.AddInt32(&counter, 1)
						Expect(err).To(MatchError(fmt.Sprintf("Stream %d was reset with error code %d", str.StreamID(), str.StreamID())))
						return
					}
					Expect(data).To(Equal(testserver.PRData))
				}()
			}
			wg.Wait()

			streamCount := atomic.LoadInt32(&counter)
			fmt.Fprintf(GinkgoWriter, "Canceled writing on %d of %d streams\n", streamCount, numStreams)
			Expect(streamCount).To(BeNumerically(">", numStreams/10))
			Expect(numStreams - streamCount).To(BeNumerically(">", numStreams/10))
			Expect(sess.Close()).To(Succeed())
			Expect(server.Close()).To(Succeed())
			return streamCount
		}

		It("downloads when the server cancels some streams immediately", func() {
			server, err := quic.ListenAddr("localhost:0", testdata.GetTLSConfig(), nil)
			Expect(err).ToNot(HaveOccurred())

			var canceledCounter int32
			go func() {
				defer GinkgoRecover()
				sess, err := server.Accept()
				Expect(err).ToNot(HaveOccurred())
				for i := 0; i < numStreams; i++ {
					go func() {
						defer GinkgoRecover()
						str, err := sess.OpenUniStreamSync()
						Expect(err).ToNot(HaveOccurred())
						// cancel about 2/3 of the streams
						if rand.Int31()%3 != 0 {
							Expect(str.CancelWrite(quic.ErrorCode(str.StreamID()))).To(Succeed())
							atomic.AddInt32(&canceledCounter, 1)
							return
						}
						_, err = str.Write(testserver.PRData)
						Expect(err).ToNot(HaveOccurred())
						Expect(str.Close()).To(Succeed())
					}()
				}
			}()

			clientCanceledStreams := runClient(server)
			Expect(clientCanceledStreams).To(Equal(atomic.LoadInt32(&canceledCounter)))
		})

		It("downloads when the server cancels some streams after sending some data", func() {
			server, err := quic.ListenAddr("localhost:0", testdata.GetTLSConfig(), nil)
			Expect(err).ToNot(HaveOccurred())

			var canceledCounter int32
			go func() {
				defer GinkgoRecover()
				sess, err := server.Accept()
				Expect(err).ToNot(HaveOccurred())
				for i := 0; i < numStreams; i++ {
					go func() {
						defer GinkgoRecover()
						str, err := sess.OpenUniStreamSync()
						Expect(err).ToNot(HaveOccurred())
						// only write some data from about 1/3 of the streams, then cancel
						if rand.Int31()%3 != 0 {
							length := int(rand.Int31n(int32(len(testserver.PRData) - 1)))
							_, err = str.Write(testserver.PRData[:length])
							Expect(err).ToNot(HaveOccurred())
							Expect(str.CancelWrite(quic.ErrorCode(str.StreamID()))).To(Succeed())
							atomic.AddInt32(&canceledCounter, 1)
							return
						}
						_, err = str.Write(testserver.PRData)
						Expect(err).ToNot(HaveOccurred())
						Expect(str.Close()).To(Succeed())
					}()
				}
			}()

			clientCanceledStreams := runClient(server)
			Expect(clientCanceledStreams).To(Equal(atomic.LoadInt32(&canceledCounter)))
		})
	})
})
<commit_msg>add an integration tests where both sides of the stream are canceled<commit_after>package self_test

import (
	"crypto/tls"
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net"
	"sync"
	"sync/atomic"

	quic "github.com/lucas-clemente/quic-go"
	"github.com/lucas-clemente/quic-go/integrationtests/tools/testserver"
	"github.com/lucas-clemente/quic-go/internal/testdata"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Stream Cancelations", func() {
	const numStreams = 80

	Context("canceling the read side", func() {
		var server quic.Listener

		runServer := func() <-chan int32 {
			numCanceledStreamsChan := make(chan int32)
			var err error
			server, err = quic.ListenAddr("localhost:0", testdata.GetTLSConfig(), nil)
			Expect(err).ToNot(HaveOccurred())

			var canceledCounter int32
			go func() {
				defer GinkgoRecover()
				var wg sync.WaitGroup
				wg.Add(numStreams)
				sess, err := server.Accept()
				Expect(err).ToNot(HaveOccurred())
				for i := 0; i < numStreams; i++ {
					go func() {
						defer GinkgoRecover()
						defer wg.Done()
						str, err := sess.OpenUniStreamSync()
						Expect(err).ToNot(HaveOccurred())
						if _, err = str.Write(testserver.PRData); err != nil {
							Expect(err).To(MatchError(fmt.Sprintf("Stream %d was reset with error code %d", str.StreamID(), str.StreamID())))
							atomic.AddInt32(&canceledCounter, 1)
							return
						}
						Expect(str.Close()).To(Succeed())
					}()
				}
				wg.Wait()
				numCanceledStreamsChan <- atomic.LoadInt32(&canceledCounter)
			}()
			return numCanceledStreamsChan
		}

		AfterEach(func() {
			Expect(server.Close()).To(Succeed())
		})

		It("downloads when the client immediately cancels most streams", func() {
			serverCanceledCounterChan := runServer()
			sess, err := quic.DialAddr(
				fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
				&tls.Config{RootCAs: testdata.GetRootCA()},
				&quic.Config{MaxIncomingUniStreams: numStreams / 2},
			)
			Expect(err).ToNot(HaveOccurred())

			var canceledCounter int32
			var wg sync.WaitGroup
			wg.Add(numStreams)
			for i := 0; i < numStreams; i++ {
				go func() {
					defer GinkgoRecover()
					defer wg.Done()
					str, err := sess.AcceptUniStream()
					Expect(err).ToNot(HaveOccurred())
					// cancel around 2/3 of the streams
					if rand.Int31()%3 != 0 {
						atomic.AddInt32(&canceledCounter, 1)
						Expect(str.CancelRead(quic.ErrorCode(str.StreamID()))).To(Succeed())
						return
					}
					data, err := ioutil.ReadAll(str)
					Expect(err).ToNot(HaveOccurred())
					Expect(data).To(Equal(testserver.PRData))
				}()
			}
			wg.Wait()

			var serverCanceledCounter int32
			Eventually(serverCanceledCounterChan).Should(Receive(&serverCanceledCounter))
			Expect(sess.Close()).To(Succeed())

			clientCanceledCounter := atomic.LoadInt32(&canceledCounter)
			// The server will only count a stream as being reset if learns about the cancelation before it finished writing all data.
			Expect(clientCanceledCounter).To(BeNumerically(">=", serverCanceledCounter))
			fmt.Fprintf(GinkgoWriter, "Canceled reading on %d of %d streams.\n", clientCanceledCounter, numStreams)
			Expect(clientCanceledCounter).To(BeNumerically(">", numStreams/10))
			Expect(numStreams - clientCanceledCounter).To(BeNumerically(">", numStreams/10))
		})

		It("downloads when the client cancels streams after reading from them for a bit", func() {
			serverCanceledCounterChan := runServer()

			sess, err := quic.DialAddr(
				fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
				&tls.Config{RootCAs: testdata.GetRootCA()},
				&quic.Config{MaxIncomingUniStreams: numStreams / 2},
			)
			Expect(err).ToNot(HaveOccurred())

			var canceledCounter int32
			var wg sync.WaitGroup
			wg.Add(numStreams)
			for i := 0; i < numStreams; i++ {
				go func() {
					defer GinkgoRecover()
					defer wg.Done()
					str, err := sess.AcceptUniStream()
					Expect(err).ToNot(HaveOccurred())
					// only read some data from about 1/3 of the streams
					if rand.Int31()%3 != 0 {
						length := int(rand.Int31n(int32(len(testserver.PRData) - 1)))
						data, err := ioutil.ReadAll(io.LimitReader(str, int64(length)))
						Expect(err).ToNot(HaveOccurred())
						Expect(str.CancelRead(quic.ErrorCode(str.StreamID()))).To(Succeed())
						Expect(data).To(Equal(testserver.PRData[:length]))
						atomic.AddInt32(&canceledCounter, 1)
						return
					}
					data, err := ioutil.ReadAll(str)
					Expect(err).ToNot(HaveOccurred())
					Expect(data).To(Equal(testserver.PRData))
				}()
			}
			wg.Wait()

			var serverCanceledCounter int32
			Eventually(serverCanceledCounterChan).Should(Receive(&serverCanceledCounter))
			Expect(sess.Close()).To(Succeed())

			clientCanceledCounter := atomic.LoadInt32(&canceledCounter)
			// The server will only count a stream as being reset if learns about the cancelation before it finished writing all data.
			Expect(clientCanceledCounter).To(BeNumerically(">=", serverCanceledCounter))
			fmt.Fprintf(GinkgoWriter, "Canceled reading on %d of %d streams.\n", clientCanceledCounter, numStreams)
			Expect(clientCanceledCounter).To(BeNumerically(">", numStreams/10))
			Expect(numStreams - clientCanceledCounter).To(BeNumerically(">", numStreams/10))
		})
	})

	Context("canceling the write side", func() {
		runClient := func(server quic.Listener) int32 /* number of canceled streams */ {
			sess, err := quic.DialAddr(
				fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
				&tls.Config{RootCAs: testdata.GetRootCA()},
				&quic.Config{MaxIncomingUniStreams: numStreams / 2},
			)
			Expect(err).ToNot(HaveOccurred())

			var wg sync.WaitGroup
			var counter int32
			wg.Add(numStreams)
			for i := 0; i < numStreams; i++ {
				go func() {
					defer GinkgoRecover()
					defer wg.Done()
					str, err := sess.AcceptUniStream()
					Expect(err).ToNot(HaveOccurred())
					data, err := ioutil.ReadAll(str)
					if err != nil {
						atomic.AddInt32(&counter, 1)
						Expect(err).To(MatchError(fmt.Sprintf("Stream %d was reset with error code %d", str.StreamID(), str.StreamID())))
						return
					}
					Expect(data).To(Equal(testserver.PRData))
				}()
			}
			wg.Wait()

			streamCount := atomic.LoadInt32(&counter)
			fmt.Fprintf(GinkgoWriter, "Canceled writing on %d of %d streams\n", streamCount, numStreams)
			Expect(streamCount).To(BeNumerically(">", numStreams/10))
			Expect(numStreams - streamCount).To(BeNumerically(">", numStreams/10))
			Expect(sess.Close()).To(Succeed())
			Expect(server.Close()).To(Succeed())
			return streamCount
		}

		It("downloads when the server cancels some streams immediately", func() {
			server, err := quic.ListenAddr("localhost:0", testdata.GetTLSConfig(), nil)
			Expect(err).ToNot(HaveOccurred())

			var canceledCounter int32
			go func() {
				defer GinkgoRecover()
				sess, err := server.Accept()
				Expect(err).ToNot(HaveOccurred())
				for i := 0; i < numStreams; i++ {
					go func() {
						defer GinkgoRecover()
						str, err := sess.OpenUniStreamSync()
						Expect(err).ToNot(HaveOccurred())
						// cancel about 2/3 of the streams
						if rand.Int31()%3 != 0 {
							Expect(str.CancelWrite(quic.ErrorCode(str.StreamID()))).To(Succeed())
							atomic.AddInt32(&canceledCounter, 1)
							return
						}
						_, err = str.Write(testserver.PRData)
						Expect(err).ToNot(HaveOccurred())
						Expect(str.Close()).To(Succeed())
					}()
				}
			}()

			clientCanceledStreams := runClient(server)
			Expect(clientCanceledStreams).To(Equal(atomic.LoadInt32(&canceledCounter)))
		})

		It("downloads when the server cancels some streams after sending some data", func() {
			server, err := quic.ListenAddr("localhost:0", testdata.GetTLSConfig(), nil)
			Expect(err).ToNot(HaveOccurred())

			var canceledCounter int32
			go func() {
				defer GinkgoRecover()
				sess, err := server.Accept()
				Expect(err).ToNot(HaveOccurred())
				for i := 0; i < numStreams; i++ {
					go func() {
						defer GinkgoRecover()
						str, err := sess.OpenUniStreamSync()
						Expect(err).ToNot(HaveOccurred())
						// only write some data from about 1/3 of the streams, then cancel
						if rand.Int31()%3 != 0 {
							length := int(rand.Int31n(int32(len(testserver.PRData) - 1)))
							_, err = str.Write(testserver.PRData[:length])
							Expect(err).ToNot(HaveOccurred())
							Expect(str.CancelWrite(quic.ErrorCode(str.StreamID()))).To(Succeed())
							atomic.AddInt32(&canceledCounter, 1)
							return
						}
						_, err = str.Write(testserver.PRData)
						Expect(err).ToNot(HaveOccurred())
						Expect(str.Close()).To(Succeed())
					}()
				}
			}()

			clientCanceledStreams := runClient(server)
			Expect(clientCanceledStreams).To(Equal(atomic.LoadInt32(&canceledCounter)))
		})
	})

	Context("canceling both read and write side", func() {
		It("downloads data when both sides cancel streams immediately", func() {
			server, err := quic.ListenAddr("localhost:0", testdata.GetTLSConfig(), nil)
			Expect(err).ToNot(HaveOccurred())

			done := make(chan struct{})
			go func() {
				defer GinkgoRecover()
				var wg sync.WaitGroup
				wg.Add(numStreams)
				sess, err := server.Accept()
				Expect(err).ToNot(HaveOccurred())
				for i := 0; i < numStreams; i++ {
					go func() {
						defer GinkgoRecover()
						defer wg.Done()
						str, err := sess.OpenUniStreamSync()
						Expect(err).ToNot(HaveOccurred())
						// cancel about half of the streams
						if rand.Int31()%2 == 0 {
							Expect(str.CancelWrite(quic.ErrorCode(str.StreamID()))).To(Succeed())
							return
						}
						if _, err = str.Write(testserver.PRData); err != nil {
							Expect(err).To(MatchError(fmt.Sprintf("Stream %d was reset with error code %d", str.StreamID(), str.StreamID())))
							return
						}
						Expect(str.Close()).To(Succeed())
					}()
				}
				wg.Wait()
				close(done)
			}()

			sess, err := quic.DialAddr(
				fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
				&tls.Config{RootCAs: testdata.GetRootCA()},
				&quic.Config{MaxIncomingUniStreams: numStreams / 2},
			)
			Expect(err).ToNot(HaveOccurred())

			var wg sync.WaitGroup
			var counter int32
			wg.Add(numStreams)
			for i := 0; i < numStreams; i++ {
				go func() {
					defer GinkgoRecover()
					defer wg.Done()
					str, err := sess.AcceptUniStream()
					Expect(err).ToNot(HaveOccurred())
					// cancel around half of the streams
					if rand.Int31()%2 == 0 {
						Expect(str.CancelRead(quic.ErrorCode(str.StreamID()))).To(Succeed())
						return
					}
					data, err := ioutil.ReadAll(str)
					if err != nil {
						Expect(err).To(MatchError(fmt.Sprintf("Stream %d was reset with error code %d", str.StreamID(), str.StreamID())))
						return
					}
					atomic.AddInt32(&counter, 1)
					Expect(data).To(Equal(testserver.PRData))
				}()
			}
			wg.Wait()

			count := atomic.LoadInt32(&counter)
			Expect(count).To(BeNumerically(">", numStreams/15))
			fmt.Fprintf(GinkgoWriter, "Successfully read from %d of %d streams.\n", count, numStreams)

			Expect(sess.Close()).To(Succeed())
			Eventually(done).Should(BeClosed())
			Expect(server.Close()).To(Succeed())
		})

		It("downloads data when both sides cancel streams after a while", func() {
			server, err := quic.ListenAddr("localhost:0", testdata.GetTLSConfig(), nil)
			Expect(err).ToNot(HaveOccurred())

			done := make(chan struct{})
			go func() {
				defer GinkgoRecover()
				var wg sync.WaitGroup
				wg.Add(numStreams)
				sess, err := server.Accept()
				Expect(err).ToNot(HaveOccurred())
				for i := 0; i < numStreams; i++ {
					go func() {
						defer GinkgoRecover()
						defer wg.Done()
						str, err := sess.OpenUniStreamSync()
						Expect(err).ToNot(HaveOccurred())
						// cancel about half of the streams
						length := len(testserver.PRData)
						if rand.Int31()%2 == 0 {
							length = int(rand.Int31n(int32(len(testserver.PRData) - 1)))
						}
						if _, err = str.Write(testserver.PRData[:length]); err != nil {
							Expect(err).To(MatchError(fmt.Sprintf("Stream %d was reset with error code %d", str.StreamID(), str.StreamID())))
							return
						}
						if length < len(testserver.PRData) {
							Expect(str.CancelWrite(quic.ErrorCode(str.StreamID()))).To(Succeed())
						} else {
							Expect(str.Close()).To(Succeed())
						}
					}()
				}
				wg.Wait()
				close(done)
			}()

			sess, err := quic.DialAddr(
				fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
				&tls.Config{RootCAs: testdata.GetRootCA()},
				&quic.Config{MaxIncomingUniStreams: numStreams / 2},
			)
			Expect(err).ToNot(HaveOccurred())

			var wg sync.WaitGroup
			var counter int32
			wg.Add(numStreams)
			for i := 0; i < numStreams; i++ {
				go func() {
					defer GinkgoRecover()
					defer wg.Done()

					str, err := sess.AcceptUniStream()
					Expect(err).ToNot(HaveOccurred())

					r := io.Reader(str)
					length := len(testserver.PRData)
					// cancel around half of the streams
					if rand.Int31()%2 == 0 {
						length = int(rand.Int31n(int32(len(testserver.PRData) - 1)))
						r = io.LimitReader(str, int64(length))
					}
					data, err := ioutil.ReadAll(r)
					if err != nil {
						Expect(err).To(MatchError(fmt.Sprintf("Stream %d was reset with error code %d", str.StreamID(), str.StreamID())))
						return
					}
					Expect(data).To(Equal(testserver.PRData[:length]))
					if length < len(testserver.PRData) {
						Expect(str.CancelRead(quic.ErrorCode(str.StreamID()))).To(Succeed())
						return
					}

					atomic.AddInt32(&counter, 1)
					Expect(data).To(Equal(testserver.PRData))
				}()
			}
			wg.Wait()

			count := atomic.LoadInt32(&counter)
			Expect(count).To(BeNumerically(">", numStreams/15))
			fmt.Fprintf(GinkgoWriter, "Successfully read from %d of %d streams.\n", count, numStreams)

			Expect(sess.Close()).To(Succeed())
			Eventually(done).Should(BeClosed())
			Expect(server.Close()).To(Succeed())
		})
	})
})
<|endoftext|>