<commit_before>// Copyright 2016 Florian Pigorsch. All rights reserved.
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// Package staticmaps renders static map images from OSM tiles with markers, paths, and filled areas.
package sm

import (
	"errors"
	"image"
	"image/draw"
	"math"

	"github.com/fogleman/gg"
	"github.com/golang/geo/s2"
)

// Context holds all information about the map image that is to be rendered
type Context struct {
	width  int
	height int

	hasZoom bool
	zoom    int

	hasCenter bool
	center    s2.LatLng

	markers []*Marker
	paths   []*Path
	areas   []*Area

	tileProvider *TileProvider
}

// NewContext creates a new instance of Context
func NewContext() *Context {
	t := new(Context)
	t.width = 512
	t.height = 512
	t.hasZoom = false
	t.hasCenter = false
	t.tileProvider = NewTileProviderMapQuest()
	return t
}

// SetTileProvider sets the TileProvider to be used
func (m *Context) SetTileProvider(t *TileProvider) {
	m.tileProvider = t
}

// SetSize sets the size of the generated image
func (m *Context) SetSize(width, height int) {
	m.width = width
	m.height = height
}

// SetZoom sets the zoom level
func (m *Context) SetZoom(zoom int) {
	m.zoom = zoom
	m.hasZoom = true
}

// SetCenter sets the center coordinates
func (m *Context) SetCenter(center s2.LatLng) {
	m.center = center
	m.hasCenter = true
}

// AddMarker adds a marker to the Context
func (m *Context) AddMarker(marker *Marker) {
	m.markers = append(m.markers, marker)
}

// ClearMarkers removes all markers from the Context
func (m *Context) ClearMarkers() {
	m.markers = nil
}

// AddPath adds a path to the Context
func (m *Context) AddPath(path *Path) {
	m.paths = append(m.paths, path)
}

// ClearPaths removes all paths from the Context
func (m *Context) ClearPaths() {
	m.paths = nil
}

// AddArea adds an area to the Context
func (m *Context) AddArea(area *Area) {
	m.areas = append(m.areas, area)
}

// ClearAreas removes all areas from the Context
func (m *Context) ClearAreas() {
	m.areas = nil
}

func (m *Context) determineBounds() s2.Rect {
	r := s2.EmptyRect()
	for _, marker := range m.markers {
		r = r.Union(marker.bounds())
	}
	for _, path := range m.paths {
		r = r.Union(path.bounds())
	}
	for _, area := range m.areas {
		r = r.Union(area.bounds())
	}
	return r
}

func (m *Context) determineExtraMarginPixels() float64 {
	p := 0.0
	for _, marker := range m.markers {
		if pp := marker.extraMarginPixels(); pp > p {
			p = pp
		}
	}
	for _, path := range m.paths {
		if pp := path.extraMarginPixels(); pp > p {
			p = pp
		}
	}
	for _, area := range m.areas {
		if pp := area.extraMarginPixels(); pp > p {
			p = pp
		}
	}
	return p
}

func (m *Context) determineZoom(bounds s2.Rect, center s2.LatLng) int {
	b := bounds.AddPoint(center)
	if b.IsEmpty() || b.IsPoint() {
		return 15
	}

	tileSize := m.tileProvider.TileSize
	margin := 4.0 + m.determineExtraMarginPixels()
	w := (float64(m.width) - 2.0*margin) / float64(tileSize)
	h := (float64(m.height) - 2.0*margin) / float64(tileSize)
	minX := (b.Lo().Lng.Degrees() + 180.0) / 360.0
	maxX := (b.Hi().Lng.Degrees() + 180.0) / 360.0
	minY := (1.0 - math.Log(math.Tan(b.Lo().Lat.Radians())+(1.0/math.Cos(b.Lo().Lat.Radians())))/math.Pi) / 2.0
	maxY := (1.0 - math.Log(math.Tan(b.Hi().Lat.Radians())+(1.0/math.Cos(b.Hi().Lat.Radians())))/math.Pi) / 2.0
	dx := math.Abs(maxX - minX)
	dy := math.Abs(maxY - minY)

	zoom := 1
	for zoom < 30 {
		tiles := float64(uint(1) << uint(zoom))
		if dx*tiles > w || dy*tiles > h {
			return zoom - 1
		}
		zoom = zoom + 1
	}

	return 15
}

type transformer struct {
	zoom               int
	tileSize           int
	pWidth, pHeight    int
	pCenterX, pCenterY int
	tCountX, tCountY   int
	tCenterX, tCenterY float64
	tOriginX, tOriginY int
}

func newTransformer(width int, height int, zoom int, llCenter s2.LatLng, tileSize int) *transformer {
	t := new(transformer)
	t.zoom = zoom
	t.tileSize = tileSize
	t.tCenterX, t.tCenterY = t.ll2t(llCenter)

	ww := float64(width) / float64(tileSize)
	hh := float64(height) / float64(tileSize)

	t.tOriginX = int(math.Floor(t.tCenterX - 0.5*ww))
	t.tOriginY = int(math.Floor(t.tCenterY - 0.5*hh))

	t.tCountX = 1 + int(math.Floor(t.tCenterX+0.5*ww)) - t.tOriginX
	t.tCountY = 1 + int(math.Floor(t.tCenterY+0.5*hh)) - t.tOriginY

	t.pWidth = t.tCountX * tileSize
	t.pHeight = t.tCountY * tileSize

	t.pCenterX = int((t.tCenterX - float64(t.tOriginX)) * float64(tileSize))
	t.pCenterY = int((t.tCenterY - float64(t.tOriginY)) * float64(tileSize))

	return t
}

func (t *transformer) ll2t(ll s2.LatLng) (float64, float64) {
	tiles := math.Exp2(float64(t.zoom))
	x := tiles * (ll.Lng.Degrees() + 180.0) / 360.0
	y := tiles * (1 - math.Log(math.Tan(ll.Lat.Radians())+(1.0/math.Cos(ll.Lat.Radians())))/math.Pi) / 2.0
	return x, y
}

func (t *transformer) ll2p(ll s2.LatLng) (float64, float64) {
	x, y := t.ll2t(ll)
	x = float64(t.pCenterX) + (x-t.tCenterX)*float64(t.tileSize)
	y = float64(t.pCenterY) + (y-t.tCenterY)*float64(t.tileSize)
	return x, y
}

// Render actually renders the map image including all map objects (markers, paths, areas)
func (m *Context) Render() (image.Image, error) {
	bounds := m.determineBounds()

	center := m.center
	if !m.hasCenter {
		if bounds.IsEmpty() {
			return nil, errors.New("No center coordinates specified, cannot determine center from markers")
		}
		center = bounds.Center()
	}

	zoom := m.zoom
	if !m.hasZoom {
		zoom = m.determineZoom(bounds, center)
	}

	tileSize := m.tileProvider.TileSize
	trans := newTransformer(m.width, m.height, zoom, center, tileSize)
	img := image.NewRGBA(image.Rect(0, 0, trans.pWidth, trans.pHeight))
	gc := gg.NewContextForRGBA(img)

	// fetch and draw tiles to img
	t := NewTileFetcher(m.tileProvider)
	for xx := 0; xx < trans.tCountX; xx++ {
		x := trans.tOriginX + xx
		if x < 0 {
			x = x + (1 << uint(zoom))
		}
		for yy := 0; yy < trans.tCountY; yy++ {
			y := trans.tOriginY + yy
			if tileImg, err := t.Fetch(zoom, x, y); err == nil {
				gc.DrawImage(tileImg, xx*tileSize, yy*tileSize)
			}
		}
	}

	// draw map objects
	for _, area := range m.areas {
		area.draw(gc, trans)
	}
	for _, path := range m.paths {
		path.draw(gc, trans)
	}
	for _, marker := range m.markers {
		marker.draw(gc, trans)
	}

	// crop image
	croppedImg := image.NewRGBA(image.Rect(0, 0, int(m.width), int(m.height)))
	draw.Draw(croppedImg, image.Rect(0, 0, int(m.width), int(m.height)),
		img, image.Point{trans.pCenterX - int(m.width)/2, trans.pCenterY - int(m.height)/2},
		draw.Src)

	// draw attribution
	_, textHeight := gc.MeasureString(m.tileProvider.Attribution)
	boxHeight := textHeight + 4.0
	gc = gg.NewContextForRGBA(croppedImg)
	gc.SetRGBA(0.0, 0.0, 0.0, 0.5)
	gc.DrawRectangle(0.0, float64(m.height)-boxHeight, float64(m.width), boxHeight)
	gc.Fill()
	gc.SetRGBA(1.0, 1.0, 1.0, 0.75)
	gc.DrawString(m.tileProvider.Attribution, 4.0, float64(m.height)-4.0)

	return croppedImg, nil
}
<commit_msg>renamed package from 'staticmaps' to 'sm'<commit_after>// Copyright 2016 Florian Pigorsch. All rights reserved.
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// Package sm renders static map images from OSM tiles with markers, paths, and filled areas.
package sm

import (
	"errors"
	"image"
	"image/draw"
	"math"

	"github.com/fogleman/gg"
	"github.com/golang/geo/s2"
)

// Context holds all information about the map image that is to be rendered
type Context struct {
	width  int
	height int

	hasZoom bool
	zoom    int

	hasCenter bool
	center    s2.LatLng

	markers []*Marker
	paths   []*Path
	areas   []*Area

	tileProvider *TileProvider
}

// NewContext creates a new instance of Context
func NewContext() *Context {
	t := new(Context)
	t.width = 512
	t.height = 512
	t.hasZoom = false
	t.hasCenter = false
	t.tileProvider = NewTileProviderMapQuest()
	return t
}

// SetTileProvider sets the TileProvider to be used
func (m *Context) SetTileProvider(t *TileProvider) {
	m.tileProvider = t
}

// SetSize sets the size of the generated image
func (m *Context) SetSize(width, height int) {
	m.width = width
	m.height = height
}

// SetZoom sets the zoom level
func (m *Context) SetZoom(zoom int) {
	m.zoom = zoom
	m.hasZoom = true
}

// SetCenter sets the center coordinates
func (m *Context) SetCenter(center s2.LatLng) {
	m.center = center
	m.hasCenter = true
}

// AddMarker adds a marker to the Context
func (m *Context) AddMarker(marker *Marker) {
	m.markers = append(m.markers, marker)
}

// ClearMarkers removes all markers from the Context
func (m *Context) ClearMarkers() {
	m.markers = nil
}

// AddPath adds a path to the Context
func (m *Context) AddPath(path *Path) {
	m.paths = append(m.paths, path)
}

// ClearPaths removes all paths from the Context
func (m *Context) ClearPaths() {
	m.paths = nil
}

// AddArea adds an area to the Context
func (m *Context) AddArea(area *Area) {
	m.areas = append(m.areas, area)
}

// ClearAreas removes all areas from the Context
func (m *Context) ClearAreas() {
	m.areas = nil
}

func (m *Context) determineBounds() s2.Rect {
	r := s2.EmptyRect()
	for _, marker := range m.markers {
		r = r.Union(marker.bounds())
	}
	for _, path := range m.paths {
		r = r.Union(path.bounds())
	}
	for _, area := range m.areas {
		r = r.Union(area.bounds())
	}
	return r
}

func (m *Context) determineExtraMarginPixels() float64 {
	p := 0.0
	for _, marker := range m.markers {
		if pp := marker.extraMarginPixels(); pp > p {
			p = pp
		}
	}
	for _, path := range m.paths {
		if pp := path.extraMarginPixels(); pp > p {
			p = pp
		}
	}
	for _, area := range m.areas {
		if pp := area.extraMarginPixels(); pp > p {
			p = pp
		}
	}
	return p
}

func (m *Context) determineZoom(bounds s2.Rect, center s2.LatLng) int {
	b := bounds.AddPoint(center)
	if b.IsEmpty() || b.IsPoint() {
		return 15
	}

	tileSize := m.tileProvider.TileSize
	margin := 4.0 + m.determineExtraMarginPixels()
	w := (float64(m.width) - 2.0*margin) / float64(tileSize)
	h := (float64(m.height) - 2.0*margin) / float64(tileSize)
	minX := (b.Lo().Lng.Degrees() + 180.0) / 360.0
	maxX := (b.Hi().Lng.Degrees() + 180.0) / 360.0
	minY := (1.0 - math.Log(math.Tan(b.Lo().Lat.Radians())+(1.0/math.Cos(b.Lo().Lat.Radians())))/math.Pi) / 2.0
	maxY := (1.0 - math.Log(math.Tan(b.Hi().Lat.Radians())+(1.0/math.Cos(b.Hi().Lat.Radians())))/math.Pi) / 2.0
	dx := math.Abs(maxX - minX)
	dy := math.Abs(maxY - minY)

	zoom := 1
	for zoom < 30 {
		tiles := float64(uint(1) << uint(zoom))
		if dx*tiles > w || dy*tiles > h {
			return zoom - 1
		}
		zoom = zoom + 1
	}

	return 15
}

type transformer struct {
	zoom               int
	tileSize           int
	pWidth, pHeight    int
	pCenterX, pCenterY int
	tCountX, tCountY   int
	tCenterX, tCenterY float64
	tOriginX, tOriginY int
}

func newTransformer(width int, height int, zoom int, llCenter s2.LatLng, tileSize int) *transformer {
	t := new(transformer)
	t.zoom = zoom
	t.tileSize = tileSize
	t.tCenterX, t.tCenterY = t.ll2t(llCenter)

	ww := float64(width) / float64(tileSize)
	hh := float64(height) / float64(tileSize)

	t.tOriginX = int(math.Floor(t.tCenterX - 0.5*ww))
	t.tOriginY = int(math.Floor(t.tCenterY - 0.5*hh))

	t.tCountX = 1 + int(math.Floor(t.tCenterX+0.5*ww)) - t.tOriginX
	t.tCountY = 1 + int(math.Floor(t.tCenterY+0.5*hh)) - t.tOriginY

	t.pWidth = t.tCountX * tileSize
	t.pHeight = t.tCountY * tileSize

	t.pCenterX = int((t.tCenterX - float64(t.tOriginX)) * float64(tileSize))
	t.pCenterY = int((t.tCenterY - float64(t.tOriginY)) * float64(tileSize))

	return t
}

func (t *transformer) ll2t(ll s2.LatLng) (float64, float64) {
	tiles := math.Exp2(float64(t.zoom))
	x := tiles * (ll.Lng.Degrees() + 180.0) / 360.0
	y := tiles * (1 - math.Log(math.Tan(ll.Lat.Radians())+(1.0/math.Cos(ll.Lat.Radians())))/math.Pi) / 2.0
	return x, y
}

func (t *transformer) ll2p(ll s2.LatLng) (float64, float64) {
	x, y := t.ll2t(ll)
	x = float64(t.pCenterX) + (x-t.tCenterX)*float64(t.tileSize)
	y = float64(t.pCenterY) + (y-t.tCenterY)*float64(t.tileSize)
	return x, y
}

// Render actually renders the map image including all map objects (markers, paths, areas)
func (m *Context) Render() (image.Image, error) {
	bounds := m.determineBounds()

	center := m.center
	if !m.hasCenter {
		if bounds.IsEmpty() {
			return nil, errors.New("No center coordinates specified, cannot determine center from markers")
		}
		center = bounds.Center()
	}

	zoom := m.zoom
	if !m.hasZoom {
		zoom = m.determineZoom(bounds, center)
	}

	tileSize := m.tileProvider.TileSize
	trans := newTransformer(m.width, m.height, zoom, center, tileSize)
	img := image.NewRGBA(image.Rect(0, 0, trans.pWidth, trans.pHeight))
	gc := gg.NewContextForRGBA(img)

	// fetch and draw tiles to img
	t := NewTileFetcher(m.tileProvider)
	for xx := 0; xx < trans.tCountX; xx++ {
		x := trans.tOriginX + xx
		if x < 0 {
			x = x + (1 << uint(zoom))
		}
		for yy := 0; yy < trans.tCountY; yy++ {
			y := trans.tOriginY + yy
			if tileImg, err := t.Fetch(zoom, x, y); err == nil {
				gc.DrawImage(tileImg, xx*tileSize, yy*tileSize)
			}
		}
	}

	// draw map objects
	for _, area := range m.areas {
		area.draw(gc, trans)
	}
	for _, path := range m.paths {
		path.draw(gc, trans)
	}
	for _, marker := range m.markers {
		marker.draw(gc, trans)
	}

	// crop image
	croppedImg := image.NewRGBA(image.Rect(0, 0, int(m.width), int(m.height)))
	draw.Draw(croppedImg, image.Rect(0, 0, int(m.width), int(m.height)),
		img, image.Point{trans.pCenterX - int(m.width)/2, trans.pCenterY - int(m.height)/2},
		draw.Src)

	// draw attribution
	_, textHeight := gc.MeasureString(m.tileProvider.Attribution)
	boxHeight := textHeight + 4.0
	gc = gg.NewContextForRGBA(croppedImg)
	gc.SetRGBA(0.0, 0.0, 0.0, 0.5)
	gc.DrawRectangle(0.0, float64(m.height)-boxHeight, float64(m.width), boxHeight)
	gc.Fill()
	gc.SetRGBA(1.0, 1.0, 1.0, 0.75)
	gc.DrawString(m.tileProvider.Attribution, 4.0, float64(m.height)-4.0)

	return croppedImg, nil
}
<|endoftext|>


<commit_before>package rados

/*
#cgo LDFLAGS: -lrados
#include "stdlib.h"
#include "rados/librados.h"
*/
import "C"

import (
    "fmt"
    "unsafe"
)

// Context represents a RADOS IO context for the pool Pool.
type Context struct {
    Pool string
    ctx  C.rados_ioctx_t
}

// NewContext creates a new RADOS IO context for a given pool, which used to
// do IO operations. The pool must exist (see Rados.PoolCreate()).
func (r *Rados) NewContext(pool string) (*Context, error) {
    if r.rados == nil {
        return nil, fmt.Errorf("RADOS not connected")
    }

    cpool := C.CString(pool)
    defer C.free(unsafe.Pointer(cpool))

    c := &Context{Pool: pool}

    if cerr := C.rados_ioctx_create(r.rados, cpool, &c.ctx); cerr < 0 {
        return nil, fmt.Errorf("RADOS new ioctx for pool %s: %s",
            strerror(cerr))
    }

    return c, nil
}

// Release this RADOS IO context.
//
// TODO: track all uncompleted async operations before calling
// rados_ioctx_destroy(), because it doesn't do that itself.
func (c *Context) Release() error {
    C.rados_ioctx_destroy(c.ctx)

    return nil
}
<commit_msg>Implement PoolStat(). Untested.<commit_after>package rados

/*
#cgo LDFLAGS: -lrados
#include "stdlib.h"
#include "rados/librados.h"
*/
import "C"

import (
    "fmt"
    "unsafe"
)

// Context represents a RADOS IO context for the pool Pool.
type Context struct {
    Pool string
    ctx  C.rados_ioctx_t
}

// NewContext creates a new RADOS IO context for a given pool, which used to
// do IO operations. The pool must exist (see Rados.PoolCreate()).
func (r *Rados) NewContext(pool string) (*Context, error) {
    if r.rados == nil {
        return nil, fmt.Errorf("RADOS not connected")
    }

    cpool := C.CString(pool)
    defer C.free(unsafe.Pointer(cpool))

    c := &Context{Pool: pool}

    if cerr := C.rados_ioctx_create(r.rados, cpool, &c.ctx); cerr < 0 {
        return nil, fmt.Errorf("RADOS new ioctx for pool %s: %s",
            strerror(cerr))
    }

    return c, nil
}

// Release this RADOS IO context.
//
// TODO: track all uncompleted async operations before calling
// rados_ioctx_destroy(), because it doesn't do that itself.
func (c *Context) Release() error {
    C.rados_ioctx_destroy(c.ctx)

    return nil
}

// PoolInfo provides usage information about a pool
type PoolInfo struct {
    BytesUsed                  uint64
    KBytesUsed                 uint64
    NumObjects                 uint64
    NumObjectClones            uint64
    NumObjectCopies            uint64
    NumObjectsMissingOnPrimary uint64
    NumObjectsUnfound          uint64
    NumObjectsDegraded         uint64
    BytesRead                  uint64
    BytesWritten               uint64
    KBytesRead                 uint64
    KBytesWritten              uint64
}

// PoolStat retrieves the current usage for pool referenced by the
// given context and returns them in the PoolInfo structure.
func (c *Context) PoolStat() (*PoolInfo, error) {
    var pstat C.struct_rados_pool_stat_t

    if cerr := C.rados_ioctx_pool_stat(c.ctx, &pstat); cerr < 0 {
        return nil, fmt.Errorf("RADOS pool stat: %s", strerror(cerr))
    }

    info := &PoolInfo{
        BytesUsed:                  uint64(pstat.num_bytes),
        KBytesUsed:                 uint64(pstat.num_kb),
        NumObjects:                 uint64(pstat.num_objects),
        NumObjectClones:            uint64(pstat.num_object_clones),
        NumObjectCopies:            uint64(pstat.num_object_copies),
        NumObjectsMissingOnPrimary: uint64(pstat.num_objects_missing_on_primary),
        NumObjectsUnfound:          uint64(pstat.num_objects_unfound),
        NumObjectsDegraded:         uint64(pstat.num_objects_degraded),
        BytesRead:                  uint64(pstat.num_rd),
        BytesWritten:               uint64(pstat.num_wr),
        KBytesRead:                 uint64(pstat.num_rd_kb),
        KBytesWritten:              uint64(pstat.num_wr_kb),
    }

    return info, nil
}
<|endoftext|>


<commit_before>package tchannel

import (
	"time"

	"golang.org/x/net/context"
)

type contextKey int

const (
	contextKeyUnknown contextKey = iota
	contextKeyTracing
	contextKeyCall
)

// IncomingCall exposes properties for incoming calls through the context.
type IncomingCall interface {
	// CallerName returns the caller name from the CallerName transport header.
	CallerName() string
}

// NewContext returns a new root context used to make TChannel requests.
func NewContext(timeout time.Duration) (context.Context, context.CancelFunc) {
	tctx, cancel := context.WithTimeout(context.Background(), timeout)
	ctx := context.WithValue(tctx, contextKeyTracing, NewRootSpan())
	return ctx, cancel
}

// WrapContextForTest returns a copy of the given Context that is associated with the call.
// This should be used in units test only.
func WrapContextForTest(ctx context.Context, call IncomingCall) context.Context {
	return context.WithValue(ctx, contextKeyCall, call)
}

// newIncomingContext creates a new context for an incoming call with the given span.
func newIncomingContext(call IncomingCall, timeout time.Duration, span *Span) (context.Context, context.CancelFunc) {
	tctx, cancel := context.WithTimeout(context.Background(), timeout)
	ctx := context.WithValue(tctx, contextKeyTracing, span)
	ctx = context.WithValue(ctx, contextKeyCall, call)
	return ctx, cancel
}

// CurrentCall returns the current incoming call, or nil if this is not an incoming call context.
func CurrentCall(ctx context.Context) IncomingCall {
	return CurrentCallWithKey(ctx, contextKeyCall)
}

// CurrentCall given a key, returns the current incoming call, or nil if this is not an incoming call context.
func CurrentCallWithKey(ctx context.Context, key interface{}) IncomingCall {
	if v := ctx.Value(key); v != nil {
		return v.(IncomingCall)
	}
	return nil
}
<commit_msg>Fix a bad merge<commit_after>package tchannel

import (
	"time"

	"golang.org/x/net/context"
)

type contextKey int

const (
	contextKeyUnknown contextKey = iota
	contextKeyTracing
	contextKeyCall
)

// IncomingCall exposes properties for incoming calls through the context.
type IncomingCall interface {
	// CallerName returns the caller name from the CallerName transport header.
	CallerName() string
}

// NewContext returns a new root context used to make TChannel requests.
func NewContext(timeout time.Duration) (context.Context, context.CancelFunc) {
	tctx, cancel := context.WithTimeout(context.Background(), timeout)
	ctx := context.WithValue(tctx, contextKeyTracing, NewRootSpan())
	return ctx, cancel
}

// WrapContextForTest returns a copy of the given Context that is associated with the call.
// This should be used in units test only.
func WrapContextForTest(ctx context.Context, call IncomingCall) context.Context {
	return context.WithValue(ctx, contextKeyCall, call)
}

// newIncomingContext creates a new context for an incoming call with the given span.
func newIncomingContext(call IncomingCall, timeout time.Duration, span *Span) (context.Context, context.CancelFunc) {
	tctx, cancel := context.WithTimeout(context.Background(), timeout)
	ctx := context.WithValue(tctx, contextKeyTracing, span)
	ctx = context.WithValue(ctx, contextKeyCall, call)
	return ctx, cancel
}

// CurrentCall returns the current incoming call, or nil if this is not an incoming call context.
func CurrentCall(ctx context.Context) IncomingCall {
	if v := ctx.Value(contextKeyCall); v != nil {
		return v.(IncomingCall)
	}
	return nil
}
<|endoftext|>


<commit_before>package automation

import (
	"fmt"
	"log"
	"sort"
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"google.golang.org/api/recommender/v1"
)

type MockService struct {
	zones                                 []string
	mutex                                 sync.Mutex
	numberOfTimesListRecommendationsCalls int
	zonesCalled                           []string
	numConcurrentCalls                    int
}

func (s *MockService) listZoneRecommendations(project string, location string, recommenderID string) []*recommender.GoogleCloudRecommenderV1Recommendation {
	s.mutex.Lock()
	s.numberOfTimesListRecommendationsCalls++
	s.zonesCalled = append(s.zonesCalled, location)
	s.mutex.Unlock()
	return []*recommender.GoogleCloudRecommenderV1Recommendation{}
}

func (s *MockService) listZonesNames(project string) []string {
	return s.zones
}

func (s *MockService) getNumConcurrentCalls() int {
	return s.numConcurrentCalls
}

func TestListRecommendations(t *testing.T) {
	for numConcurrentCalls := 1; numConcurrentCalls <= 5; numConcurrentCalls++ {
		zones := []string{"zone1", "zone2", "zone3"}
		mock := &MockService{zones: zones, numConcurrentCalls: numConcurrentCalls}
		result := ListRecommendations(mock, "", "")

		assert.Equal(t, 0, len(result), "No recommedations expected")
		assert.Equal(t, len(zones), mock.numberOfTimesListRecommendationsCalls, "Wrong number of listZoneRecommendaions calls")
		assert.ElementsMatch(t, mock.zones, mock.zonesCalled, "listZoneRecommendations was called for different zones")
	}
}

type SleepingService struct {
	zones                                 []string
	numberOfTimesListRecommendationsCalls int
	requestsDurations                     []time.Duration
	numConcurrentCalls                    int
}

func (s *SleepingService) listZoneRecommendations(project string, location string, recommenderID string) []*recommender.GoogleCloudRecommenderV1Recommendation {
	d, err := time.ParseDuration(location)
	if err != nil {
		log.Fatal(err)
	}
	time.Sleep(d)
	s.requestsDurations = append(s.requestsDurations, d)
	return []*recommender.GoogleCloudRecommenderV1Recommendation{}
}

func (s *SleepingService) listZonesNames(project string) []string {
	return s.zones
}

func (s *SleepingService) getNumConcurrentCalls() int {
	return s.numConcurrentCalls
}

func TestConcurrency(t *testing.T) {
	durations := []time.Duration{time.Second, time.Millisecond * 50}
	zones := []string{}
	for _, duration := range durations {
		zones = append(zones, fmt.Sprint(duration))
	}
	mock := &SleepingService{zones: zones, numConcurrentCalls: 2}
	result := ListRecommendations(mock, "", "")

	assert.Equal(t, 0, len(result), "No recommedations expected")
	assert.Equal(t, len(zones), len(mock.requestsDurations), "Wrong number of calls to listZoneRecommendations")
	assert.True(t, sort.SliceIsSorted(mock.requestsDurations, func(i, j int) bool {
		return mock.requestsDurations[i] < mock.requestsDurations[j]
	}), "Faster goroutine should have finished first")
}

type BenchmarkService struct {
	numConcurrentCalls int
}

func (s *BenchmarkService) listZoneRecommendations(project string, location string, recommenderID string) []*recommender.GoogleCloudRecommenderV1Recommendation {
	time.Sleep(time.Second)
	return []*recommender.GoogleCloudRecommenderV1Recommendation{}
}

func (s *BenchmarkService) listZonesNames(project string) []string {
	zones := []string{}
	for i := 0; i < 100; i++ {
		zones = append(zones, fmt.Sprintf("zone %d", i))
	}
	return zones
}

func (s *BenchmarkService) getNumConcurrentCalls() int {
	return s.numConcurrentCalls
}

func BenchmarkGoroutines(b *testing.B) {
	for _, numConcurrentCalls := range []int{4, 8, 16, 32, 64} {
		b.Run(fmt.Sprintf("%d goroutines:", numConcurrentCalls), func(b *testing.B) {
			s := &BenchmarkService{numConcurrentCalls}
			ListRecommendations(s, "", "")
		})
	}
}
<commit_msg>delete testConcurrency test<commit_after>package automation

import (
	"fmt"
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"google.golang.org/api/recommender/v1"
)

type MockService struct {
	zones                                 []string
	mutex                                 sync.Mutex
	numberOfTimesListRecommendationsCalls int
	zonesCalled                           []string
}

func (s *MockService) ListRecommendations(project string, location string, recommenderID string) []*recommender.GoogleCloudRecommenderV1Recommendation {
	s.mutex.Lock()
	s.numberOfTimesListRecommendationsCalls++
	s.zonesCalled = append(s.zonesCalled, location)
	s.mutex.Unlock()
	return []*recommender.GoogleCloudRecommenderV1Recommendation{}
}

func (s *MockService) ListZonesNames(project string) []string {
	return s.zones
}

func TestListRecommendations(t *testing.T) {
	for numConcurrentCalls := 0; numConcurrentCalls <= 5; numConcurrentCalls++ {
		zones := []string{"zone1", "zone2", "zone3"}
		mock := &MockService{zones: zones}
		result := ListRecommendations(mock, "", "", numConcurrentCalls)

		assert.Equal(t, 0, len(result), "No recommedations expected")
		assert.Equal(t, len(zones), mock.numberOfTimesListRecommendationsCalls, "Wrong number of listZoneRecommendaions calls")
		assert.ElementsMatch(t, mock.zones, mock.zonesCalled, "listZoneRecommendations was called for different zones")
	}
}

type BenchmarkService struct {
}

func (s *BenchmarkService) ListRecommendations(project string, location string, recommenderID string) []*recommender.GoogleCloudRecommenderV1Recommendation {
	time.Sleep(time.Second)
	return []*recommender.GoogleCloudRecommenderV1Recommendation{}
}

func (s *BenchmarkService) ListZonesNames(project string) []string {
	zones := []string{}
	for i := 0; i < 100; i++ {
		zones = append(zones, fmt.Sprintf("zone %d", i))
	}
	return zones
}

func BenchmarkGoroutines(b *testing.B) {
	for _, numConcurrentCalls := range []int{4, 8, 16, 32, 64} {
		b.Run(fmt.Sprintf("%d goroutines:", numConcurrentCalls), func(b *testing.B) {
			s := &BenchmarkService{}
			ListRecommendations(s, "", "", numConcurrentCalls)
		})
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package azure

import (
	"io"
	"io/ioutil"

	"k8s.io/kubernetes/pkg/cloudprovider"

	"github.com/Azure/azure-sdk-for-go/arm/compute"
	"github.com/Azure/azure-sdk-for-go/arm/network"
	"github.com/Azure/azure-sdk-for-go/arm/storage"
	"github.com/Azure/go-autorest/autorest/azure"
	"github.com/ghodss/yaml"
)

// CloudProviderName is the value used for the --cloud-provider flag
const CloudProviderName = "azure"

// Config holds the configuration parsed from the --cloud-config flag
type Config struct {
	Cloud                      string `json:"cloud" yaml:"cloud"`
	TenantID                   string `json:"tenantId" yaml:"tenantId"`
	SubscriptionID             string `json:"subscriptionId" yaml:"subscriptionId"`
	ResourceGroup              string `json:"resourceGroup" yaml:"resourceGroup"`
	Location                   string `json:"location" yaml:"location"`
	VnetName                   string `json:"vnetName" yaml:"vnetName"`
	SubnetName                 string `json:"subnetName" yaml:"subnetName"`
	SecurityGroupName          string `json:"securityGroupName" yaml:"securityGroupName"`
	RouteTableName             string `json:"routeTableName" yaml:"routeTableName"`
	PrimaryAvailabilitySetName string `json:"primaryAvailabilitySetName" yaml:"primaryAvailabilitySetName"`

	AADClientID     string `json:"aadClientId" yaml:"aadClientId"`
	AADClientSecret string `json:"aadClientSecret" yaml:"aadClientSecret"`
	AADTenantID     string `json:"aadTenantId" yaml:"aadTenantId"`
}

// Cloud holds the config and clients
type Cloud struct {
	Config
	Environment             azure.Environment
	RoutesClient            network.RoutesClient
	SubnetsClient           network.SubnetsClient
	InterfacesClient        network.InterfacesClient
	RouteTablesClient       network.RouteTablesClient
	LoadBalancerClient      network.LoadBalancersClient
	PublicIPAddressesClient network.PublicIPAddressesClient
	SecurityGroupsClient    network.SecurityGroupsClient
	VirtualMachinesClient   compute.VirtualMachinesClient
	StorageAccountClient    storage.AccountsClient
}

func init() {
	cloudprovider.RegisterCloudProvider(CloudProviderName, NewCloud)
}

// NewCloud returns a Cloud with initialized clients
func NewCloud(configReader io.Reader) (cloudprovider.Interface, error) {
	var az Cloud

	configContents, err := ioutil.ReadAll(configReader)
	if err != nil {
		return nil, err
	}
	err = yaml.Unmarshal(configContents, &az)
	if err != nil {
		return nil, err
	}

	if az.Cloud == "" {
		az.Environment = azure.PublicCloud
	} else {
		az.Environment, err = azure.EnvironmentFromName(az.Cloud)
		if err != nil {
			return nil, err
		}
	}

	oauthConfig, err := az.Environment.OAuthConfigForTenant(az.TenantID)
	if err != nil {
		return nil, err
	}

	servicePrincipalToken, err := azure.NewServicePrincipalToken(
		*oauthConfig,
		az.AADClientID,
		az.AADClientSecret,
		az.Environment.ServiceManagementEndpoint)
	if err != nil {
		return nil, err
	}

	az.SubnetsClient = network.NewSubnetsClient(az.SubscriptionID)
	az.SubnetsClient.BaseURI = az.Environment.ResourceManagerEndpoint
	az.SubnetsClient.Authorizer = servicePrincipalToken

	az.RouteTablesClient = network.NewRouteTablesClient(az.SubscriptionID)
	az.RouteTablesClient.BaseURI = az.Environment.ResourceManagerEndpoint
	az.RouteTablesClient.Authorizer = servicePrincipalToken

	az.RoutesClient = network.NewRoutesClient(az.SubscriptionID)
	az.RoutesClient.BaseURI = az.Environment.ResourceManagerEndpoint
	az.RoutesClient.Authorizer = servicePrincipalToken

	az.InterfacesClient = network.NewInterfacesClient(az.SubscriptionID)
	az.InterfacesClient.BaseURI = az.Environment.ResourceManagerEndpoint
	az.InterfacesClient.Authorizer = servicePrincipalToken

	az.LoadBalancerClient = network.NewLoadBalancersClient(az.SubscriptionID)
	az.LoadBalancerClient.BaseURI = az.Environment.ResourceManagerEndpoint
	az.LoadBalancerClient.Authorizer = servicePrincipalToken

	az.VirtualMachinesClient = compute.NewVirtualMachinesClient(az.SubscriptionID)
	az.VirtualMachinesClient.BaseURI = az.Environment.ResourceManagerEndpoint
	az.VirtualMachinesClient.Authorizer = servicePrincipalToken

	az.PublicIPAddressesClient = network.NewPublicIPAddressesClient(az.SubscriptionID)
	az.PublicIPAddressesClient.BaseURI = az.Environment.ResourceManagerEndpoint
	az.PublicIPAddressesClient.Authorizer = servicePrincipalToken

	az.SecurityGroupsClient = network.NewSecurityGroupsClient(az.SubscriptionID)
	az.SecurityGroupsClient.BaseURI = az.Environment.ResourceManagerEndpoint
	az.SecurityGroupsClient.Authorizer = servicePrincipalToken

	az.StorageAccountClient = storage.NewAccountsClientWithBaseURI(az.Environment.ResourceManagerEndpoint, az.SubscriptionID)
	az.StorageAccountClient.Authorizer = servicePrincipalToken
	return &az, nil
}

// LoadBalancer returns a balancer interface. Also returns true if the interface is supported, false otherwise.
func (az *Cloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
	return az, true
}

// Instances returns an instances interface. Also returns true if the interface is supported, false otherwise.
func (az *Cloud) Instances() (cloudprovider.Instances, bool) {
	return az, true
}

// Zones returns a zones interface. Also returns true if the interface is supported, false otherwise.
func (az *Cloud) Zones() (cloudprovider.Zones, bool) {
	return az, true
}

// Clusters returns a clusters interface.  Also returns true if the interface is supported, false otherwise.
func (az *Cloud) Clusters() (cloudprovider.Clusters, bool) {
	return nil, false
}

// Routes returns a routes interface along with whether the interface is supported.
func (az *Cloud) Routes() (cloudprovider.Routes, bool) {
	return az, true
}

// ScrubDNS provides an opportunity for cloud-provider-specific code to process DNS settings for pods.
func (az *Cloud) ScrubDNS(nameservers, searches []string) (nsOut, srchOut []string) {
	return nameservers, searches
}

// ProviderName returns the cloud provider ID.
func (az *Cloud) ProviderName() string {
	return CloudProviderName
}
<commit_msg>Set custom PollingDelay of 5 seconds for Azure VirtualMachinesClient<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package azure

import (
	"io"
	"io/ioutil"

	"k8s.io/kubernetes/pkg/cloudprovider"

	"github.com/Azure/azure-sdk-for-go/arm/compute"
	"github.com/Azure/azure-sdk-for-go/arm/network"
	"github.com/Azure/azure-sdk-for-go/arm/storage"
	"github.com/Azure/go-autorest/autorest/azure"
	"github.com/ghodss/yaml"
	"time"
)

// CloudProviderName is the value used for the --cloud-provider flag
const CloudProviderName = "azure"

// Config holds the configuration parsed from the --cloud-config flag
type Config struct {
	Cloud                      string `json:"cloud" yaml:"cloud"`
	TenantID                   string `json:"tenantId" yaml:"tenantId"`
	SubscriptionID             string `json:"subscriptionId" yaml:"subscriptionId"`
	ResourceGroup              string `json:"resourceGroup" yaml:"resourceGroup"`
	Location                   string `json:"location" yaml:"location"`
	VnetName                   string `json:"vnetName" yaml:"vnetName"`
	SubnetName                 string `json:"subnetName" yaml:"subnetName"`
	SecurityGroupName          string `json:"securityGroupName" yaml:"securityGroupName"`
	RouteTableName             string `json:"routeTableName" yaml:"routeTableName"`
	PrimaryAvailabilitySetName string `json:"primaryAvailabilitySetName" yaml:"primaryAvailabilitySetName"`

	AADClientID     string `json:"aadClientId" yaml:"aadClientId"`
	AADClientSecret string `json:"aadClientSecret" yaml:"aadClientSecret"`
	AADTenantID     string `json:"aadTenantId" yaml:"aadTenantId"`
}

// Cloud holds the config and clients
type Cloud struct {
	Config
	Environment             azure.Environment
	RoutesClient            network.RoutesClient
	SubnetsClient           network.SubnetsClient
	InterfacesClient        network.InterfacesClient
	RouteTablesClient       network.RouteTablesClient
	LoadBalancerClient      network.LoadBalancersClient
	PublicIPAddressesClient network.PublicIPAddressesClient
	SecurityGroupsClient    network.SecurityGroupsClient
	VirtualMachinesClient   compute.VirtualMachinesClient
	StorageAccountClient    storage.AccountsClient
}

func init() {
	cloudprovider.RegisterCloudProvider(CloudProviderName, NewCloud)
}

// NewCloud returns a Cloud with initialized clients
func NewCloud(configReader io.Reader) (cloudprovider.Interface, error) {
	var az Cloud

	configContents, err := ioutil.ReadAll(configReader)
	if err != nil {
		return nil, err
	}
	err = yaml.Unmarshal(configContents, &az)
	if err != nil {
		return nil, err
	}

	if az.Cloud == "" {
		az.Environment = azure.PublicCloud
	} else {
		az.Environment, err = azure.EnvironmentFromName(az.Cloud)
		if err != nil {
			return nil, err
		}
	}

	oauthConfig, err := az.Environment.OAuthConfigForTenant(az.TenantID)
	if err != nil {
		return nil, err
	}

	servicePrincipalToken, err := azure.NewServicePrincipalToken(
		*oauthConfig,
		az.AADClientID,
		az.AADClientSecret,
		az.Environment.ServiceManagementEndpoint)
	if err != nil {
		return nil, err
	}

	az.SubnetsClient = network.NewSubnetsClient(az.SubscriptionID)
	az.SubnetsClient.BaseURI = az.Environment.ResourceManagerEndpoint
	az.SubnetsClient.Authorizer = servicePrincipalToken

	az.RouteTablesClient = network.NewRouteTablesClient(az.SubscriptionID)
	az.RouteTablesClient.BaseURI = az.Environment.ResourceManagerEndpoint
	az.RouteTablesClient.Authorizer = servicePrincipalToken

	az.RoutesClient = network.NewRoutesClient(az.SubscriptionID)
	az.RoutesClient.BaseURI = az.Environment.ResourceManagerEndpoint
	az.RoutesClient.Authorizer = servicePrincipalToken

	az.InterfacesClient = network.NewInterfacesClient(az.SubscriptionID)
	az.InterfacesClient.BaseURI = az.Environment.ResourceManagerEndpoint
	az.InterfacesClient.Authorizer = servicePrincipalToken

	az.LoadBalancerClient = network.NewLoadBalancersClient(az.SubscriptionID)
	az.LoadBalancerClient.BaseURI = az.Environment.ResourceManagerEndpoint
	az.LoadBalancerClient.Authorizer = servicePrincipalToken

	az.VirtualMachinesClient = compute.NewVirtualMachinesClient(az.SubscriptionID)
	az.VirtualMachinesClient.BaseURI = az.Environment.ResourceManagerEndpoint
	az.VirtualMachinesClient.Authorizer = servicePrincipalToken
	az.VirtualMachinesClient.PollingDelay = 5 * time.Second

	az.PublicIPAddressesClient = network.NewPublicIPAddressesClient(az.SubscriptionID)
	az.PublicIPAddressesClient.BaseURI = az.Environment.ResourceManagerEndpoint
	az.PublicIPAddressesClient.Authorizer = servicePrincipalToken

	az.SecurityGroupsClient = network.NewSecurityGroupsClient(az.SubscriptionID)
	az.SecurityGroupsClient.BaseURI = az.Environment.ResourceManagerEndpoint
	az.SecurityGroupsClient.Authorizer = servicePrincipalToken

	az.StorageAccountClient = storage.NewAccountsClientWithBaseURI(az.Environment.ResourceManagerEndpoint, az.SubscriptionID)
	az.StorageAccountClient.Authorizer = servicePrincipalToken
	return &az, nil
}

// LoadBalancer returns a balancer interface. Also returns true if the interface is supported, false otherwise.
func (az *Cloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
	return az, true
}

// Instances returns an instances interface. Also returns true if the interface is supported, false otherwise.
func (az *Cloud) Instances() (cloudprovider.Instances, bool) {
	return az, true
}

// Zones returns a zones interface. Also returns true if the interface is supported, false otherwise.
func (az *Cloud) Zones() (cloudprovider.Zones, bool) {
	return az, true
}

// Clusters returns a clusters interface.  Also returns true if the interface is supported, false otherwise.
func (az *Cloud) Clusters() (cloudprovider.Clusters, bool) {
	return nil, false
}

// Routes returns a routes interface along with whether the interface is supported.
func (az *Cloud) Routes() (cloudprovider.Routes, bool) {
	return az, true
}

// ScrubDNS provides an opportunity for cloud-provider-specific code to process DNS settings for pods.
func (az *Cloud) ScrubDNS(nameservers, searches []string) (nsOut, srchOut []string) {
	return nameservers, searches
}

// ProviderName returns the cloud provider ID.
func (az *Cloud) ProviderName() string {
	return CloudProviderName
}
<|endoftext|>


<commit_before>package utils

import (
	"fmt"
	"net"
	"net/url"
	"strings"
	"time"

	"github.com/pkg/errors"

	"github.com/rancher/types/apis/management.cattle.io/v3"

	loggingconfig "github.com/rancher/rancher/pkg/controllers/user/logging/config"
)

type WrapLogging struct {
	CurrentTarget string
	WrapSyslog
	WrapSplunk
	WrapElasticsearch
	WrapKafka
}

type WrapClusterLogging struct {
	v3.ClusterLoggingSpec
	WrapEmbedded
	WrapLogging
}

type WrapProjectLogging struct {
	v3.ProjectLoggingSpec
	GrepNamespace string
	WrapLogging
}

type WrapEmbedded struct {
	DateFormat string
}

type WrapElasticsearch struct {
	DateFormat string
	Host       string
	Scheme     string
}

type WrapSplunk struct {
	Server string
	Scheme string
}

type WrapKafka struct {
	Brokers   string
	Zookeeper string
}

type WrapSyslog struct {
	Host string
	Port string
}

func (w *WrapClusterLogging) Validate() error {
	_, _, err := GetWrapConfig(w.ElasticsearchConfig, w.SplunkConfig, w.SyslogConfig, w.KafkaConfig, w.EmbeddedConfig)
	return err
}

func (w *WrapProjectLogging) Validate() error {
	_, _, err := GetWrapConfig(w.ElasticsearchConfig, w.SplunkConfig, w.SyslogConfig, w.KafkaConfig, nil)
	return err
}

func ToWrapClusterLogging(clusterLogging v3.ClusterLoggingSpec) (*WrapClusterLogging, error) {
	wp := WrapClusterLogging{
		ClusterLoggingSpec: clusterLogging,
	}

	wrapLogging, wem, err := GetWrapConfig(clusterLogging.ElasticsearchConfig, clusterLogging.SplunkConfig, clusterLogging.SyslogConfig, clusterLogging.KafkaConfig, clusterLogging.EmbeddedConfig)
	if err != nil {
		return nil, err
	}
	wp.WrapLogging = wrapLogging
	wp.WrapEmbedded = wem

	return &wp, nil
}

func ToWrapProjectLogging(grepNamespace string, projectLogging v3.ProjectLoggingSpec) (*WrapProjectLogging, error) {
	wp := WrapProjectLogging{
		ProjectLoggingSpec: projectLogging,
		GrepNamespace:      grepNamespace,
	}

	wrapLogging, _, err := GetWrapConfig(projectLogging.ElasticsearchConfig, projectLogging.SplunkConfig, projectLogging.SyslogConfig, projectLogging.KafkaConfig, nil)

	if err != nil {
		return nil, err
	}
	wp.WrapLogging = wrapLogging
	return &wp, nil
}

func GetWrapConfig(es *v3.ElasticsearchConfig, sp *v3.SplunkConfig, sl *v3.SyslogConfig, kf *v3.KafkaConfig, em *v3.EmbeddedConfig) (wrapLogging WrapLogging, wem WrapEmbedded, err error) {
	if es != nil {
		var h, s string
		h, s, err = parseEndpoint(es.Endpoint)
		if err != nil {
			return
		}
		err = testReachable("tcp", h)
		if err != nil {
			return
		}
		wrapLogging.WrapElasticsearch = WrapElasticsearch{
			Host:       h,
			Scheme:     s,
			DateFormat: getDateFormat(es.DateFormat),
		}
		wrapLogging.CurrentTarget = loggingconfig.Elasticsearch
	}

	if sp != nil {
		var h, s string
		h, s, err = parseEndpoint(sp.Endpoint)
		if err != nil {
			return
		}
		err = testReachable("tcp", h)
		if err != nil {
			return
		}
		wrapLogging.WrapSplunk = WrapSplunk{
			Server: h,
			Scheme: s,
		}
		wrapLogging.CurrentTarget = loggingconfig.Splunk
	}

	if sl != nil {
		err = testReachable(sl.Protocol, sl.Endpoint)
		if err != nil {
			return
		}
		var host, port string
		host, port, err = net.SplitHostPort(sl.Endpoint)
		if err != nil {
			return
		}
		wrapLogging.WrapSyslog = WrapSyslog{
			Host: host,
			Port: port,
		}
		wrapLogging.CurrentTarget = loggingconfig.Syslog
	}

	if kf != nil {
		if len(kf.BrokerEndpoints) == 0 && kf.ZookeeperEndpoint == "" {
			err = errors.New("one of the kafka endpoint must be set")
			return
		}
		if len(kf.BrokerEndpoints) != 0 {
			var bs []string
			var h string
			for _, v := range kf.BrokerEndpoints {
				h, _, err = parseEndpoint(v)
				if err != nil {
					return
				}
				err = testReachable("tcp", h)
				if err != nil {
					return
				}
				bs = append(bs, h)
			}
			wrapLogging.WrapKafka = WrapKafka{
				Brokers: strings.Join(bs, ","),
			}
		} else {
			if kf.ZookeeperEndpoint != "" {
				var h string
				if h, _, err = parseEndpoint(kf.ZookeeperEndpoint); err != nil {
					return
				}
				err = testReachable("tcp", h)
				if err != nil {
					return
				}
				wrapLogging.WrapKafka = WrapKafka{
					Zookeeper: h,
				}
			}
		}
		wrapLogging.CurrentTarget = loggingconfig.Kafka
	}

	if em != nil {
		if em.LimitsCPU != 0 && em.LimitsCPU < em.RequestsCPU {
			err = fmt.Errorf("limits cpu %d is less than request cpu %d", em.LimitsCPU, em.RequestsCPU)
			return
		}
		if em.LimitsMemery != 0 && em.LimitsMemery < em.RequestsMemery {
			err = fmt.Errorf("limits memory %d is less than request memory %d", em.LimitsMemery, em.RequestsMemery)
			return
		}
		wem = WrapEmbedded{
			DateFormat: getDateFormat(em.DateFormat),
		}
		wrapLogging.CurrentTarget = loggingconfig.Embedded
	}
	return
}

func parseEndpoint(endpoint string) (host string, scheme string, err error) {
	u, err := url.ParseRequestURI(endpoint)
	if err != nil {
		return "", "", errors.Wrapf(err, "invalid endpoint %s", endpoint)
	}

	if u.Host == "" || u.Scheme == "" {
		return "", "", fmt.Errorf("invalid endpoint %s, empty host or schema", endpoint)
	}

	return u.Host, u.Scheme, nil
}

func getDateFormat(dateformat string) string {
	ToRealMap := map[string]string{
		"YYYY.MM.DD": "%Y.%m.%d",
		"YYYY.MM":    "%Y.%m.",
		"YYYY":       "%Y.",
	}
	if _, ok := ToRealMap[dateformat]; ok {
		return ToRealMap[dateformat]
	}
	return "%Y.%m.%d"
}

func testReachable(network string, url string) error {
	timeout := time.Duration(10 * time.Second)
	conn, err := net.DialTimeout(network, url, timeout)
	if err != nil {
		return fmt.Errorf("url %s unreachable, error: %v", url, err)
	}
	conn.Close()
	return nil
}

func GetClusterTarget(spec v3.ClusterLoggingSpec) string {
	if spec.EmbeddedConfig != nil {
		return "embedded"
	} else if spec.ElasticsearchConfig != nil {
		return "elasticsearch"
	} else if spec.SplunkConfig != nil {
		return "splunk"
	} else if spec.KafkaConfig != nil {
		return "kafka"
	} else if spec.SyslogConfig != nil {
		return "syslog"
	}
	return "none"
}

func GetProjectTarget(spec v3.ProjectLoggingSpec) string {
	if spec.ElasticsearchConfig != nil {
		return "elasticsearch"
	} else if spec.SplunkConfig != nil {
		return "splunk"
	} else if spec.KafkaConfig != nil {
		return "kafka"
	} else if spec.SyslogConfig != nil {
		return "syslog"
	}
	return "none"
}
<commit_msg>fix elasticsearch format<commit_after>package utils

import (
	"fmt"
	"net"
	"net/url"
	"strings"
	"time"

	"github.com/pkg/errors"

	"github.com/rancher/types/apis/management.cattle.io/v3"

	loggingconfig "github.com/rancher/rancher/pkg/controllers/user/logging/config"
)

type WrapLogging struct {
	CurrentTarget string
	WrapSyslog
	WrapSplunk
	WrapElasticsearch
	WrapKafka
}

type WrapClusterLogging struct {
	v3.ClusterLoggingSpec
	WrapEmbedded
	WrapLogging
}

type WrapProjectLogging struct {
	v3.ProjectLoggingSpec
	GrepNamespace string
	WrapLogging
}

type WrapEmbedded struct {
	DateFormat string
}

type WrapElasticsearch struct {
	DateFormat string
	Host       string
	Scheme     string
}

type WrapSplunk struct {
	Server string
	Scheme string
}

type WrapKafka struct {
	Brokers   string
	Zookeeper string
}

type WrapSyslog struct {
	Host string
	Port string
}

func (w *WrapClusterLogging) Validate() error {
	_, _, err := GetWrapConfig(w.ElasticsearchConfig, w.SplunkConfig, w.SyslogConfig, w.KafkaConfig, w.EmbeddedConfig)
	return err
}

func (w *WrapProjectLogging) Validate() error {
	_, _, err := GetWrapConfig(w.ElasticsearchConfig, w.SplunkConfig, w.SyslogConfig, w.KafkaConfig, nil)
	return err
}

func ToWrapClusterLogging(clusterLogging v3.ClusterLoggingSpec) (*WrapClusterLogging, error) {
	wp := WrapClusterLogging{
		ClusterLoggingSpec: clusterLogging,
	}

	wrapLogging, wem, err := GetWrapConfig(clusterLogging.ElasticsearchConfig, clusterLogging.SplunkConfig, clusterLogging.SyslogConfig, clusterLogging.KafkaConfig, clusterLogging.EmbeddedConfig)
	if err != nil {
		return nil, err
	}
	wp.WrapLogging = wrapLogging
	wp.WrapEmbedded = wem

	return &wp, nil
}

func ToWrapProjectLogging(grepNamespace string, projectLogging v3.ProjectLoggingSpec) (*WrapProjectLogging, error) {
	wp := WrapProjectLogging{
		ProjectLoggingSpec: projectLogging,
		GrepNamespace:      grepNamespace,
	}

	wrapLogging, _, err := GetWrapConfig(projectLogging.ElasticsearchConfig, projectLogging.SplunkConfig, projectLogging.SyslogConfig, projectLogging.KafkaConfig, nil)

	if err != nil {
		return nil, err
	}
	wp.WrapLogging = wrapLogging
	return &wp, nil
}

func GetWrapConfig(es *v3.ElasticsearchConfig, sp *v3.SplunkConfig, sl *v3.SyslogConfig, kf *v3.KafkaConfig, em *v3.EmbeddedConfig) (wrapLogging WrapLogging, wem WrapEmbedded, err error) {
	if es != nil {
		var h, s string
		h, s, err = parseEndpoint(es.Endpoint)
		if err != nil {
			return
		}
		err = testReachable("tcp", h)
		if err != nil {
			return
		}
		wrapLogging.WrapElasticsearch = WrapElasticsearch{
			Host:       h,
			Scheme:     s,
			DateFormat: getDateFormat(es.DateFormat),
		}
		wrapLogging.CurrentTarget = loggingconfig.Elasticsearch
	}

	if sp != nil {
		var h, s string
		h, s, err = parseEndpoint(sp.Endpoint)
		if err != nil {
			return
		}
		err = testReachable("tcp", h)
		if err != nil {
			return
		}
		wrapLogging.WrapSplunk = WrapSplunk{
			Server: h,
			Scheme: s,
		}
		wrapLogging.CurrentTarget = loggingconfig.Splunk
	}

	if sl != nil {
		err = testReachable(sl.Protocol, sl.Endpoint)
		if err != nil {
			return
		}
		var host, port string
		host, port, err = net.SplitHostPort(sl.Endpoint)
		if err != nil {
			return
		}
		wrapLogging.WrapSyslog = WrapSyslog{
			Host: host,
			Port: port,
		}
		wrapLogging.CurrentTarget = loggingconfig.Syslog
	}

	if kf != nil {
		if len(kf.BrokerEndpoints) == 0 && kf.ZookeeperEndpoint == "" {
			err = errors.New("one of the kafka endpoint must be set")
			return
		}
		if len(kf.BrokerEndpoints) != 0 {
			var bs []string
			var h string
			for _, v := range kf.BrokerEndpoints {
				h, _, err = parseEndpoint(v)
				if err != nil {
					return
				}
				err = testReachable("tcp", h)
				if err != nil {
					return
				}
				bs = append(bs, h)
			}
			wrapLogging.WrapKafka = WrapKafka{
				Brokers: strings.Join(bs, ","),
			}
		} else {
			if kf.ZookeeperEndpoint != "" {
				var h string
				if h, _, err = parseEndpoint(kf.ZookeeperEndpoint); err != nil {
					return
				}
				err = testReachable("tcp", h)
				if err != nil {
					return
				}
				wrapLogging.WrapKafka = WrapKafka{
					Zookeeper: h,
				}
			}
		}
		wrapLogging.CurrentTarget = loggingconfig.Kafka
	}

	if em != nil {
		if em.LimitsCPU != 0 && em.LimitsCPU < em.RequestsCPU {
			err = fmt.Errorf("limits cpu %d is less than request cpu %d", em.LimitsCPU, em.RequestsCPU)
			return
		}
		if em.LimitsMemery != 0 && em.LimitsMemery < em.RequestsMemery {
			err = fmt.Errorf("limits memory %d is less than request memory %d", em.LimitsMemery, em.RequestsMemery)
			return
		}
		wem = WrapEmbedded{
			DateFormat: getDateFormat(em.DateFormat),
		}
		wrapLogging.CurrentTarget = loggingconfig.Embedded
	}
	return
}

func parseEndpoint(endpoint string) (host string, scheme string, err error) {
	u, err := url.ParseRequestURI(endpoint)
	if err != nil {
		return "", "", errors.Wrapf(err, "invalid endpoint %s", endpoint)
	}

	if u.Host == "" || u.Scheme == "" {
		return "", "", fmt.Errorf("invalid endpoint %s, empty host or schema", endpoint)
	}

	return u.Host, u.Scheme, nil
}

func getDateFormat(dateformat string) string {
	ToRealMap := map[string]string{
		"YYYY-MM-DD": "%Y-%m-%d",
		"YYYY-MM":    "%Y-%m",
		"YYYY":       "%Y",
	}
	if _, ok := ToRealMap[dateformat]; ok {
		return ToRealMap[dateformat]
	}
	return "%Y-%m-%d"
}

func testReachable(network string, url string) error {
	timeout := time.Duration(10 * time.Second)
	conn, err := net.DialTimeout(network, url, timeout)
	if err != nil {
		return fmt.Errorf("url %s unreachable, error: %v", url, err)
	}
	conn.Close()
	return nil
}

func GetClusterTarget(spec v3.ClusterLoggingSpec) string {
	if spec.EmbeddedConfig != nil {
		return "embedded"
	} else if spec.ElasticsearchConfig != nil {
		return "elasticsearch"
	} else if spec.SplunkConfig != nil {
		return "splunk"
	} else if spec.KafkaConfig != nil {
		return "kafka"
	} else if spec.SyslogConfig != nil {
		return "syslog"
	}
	return "none"
}

func GetProjectTarget(spec v3.ProjectLoggingSpec) string {
	if spec.ElasticsearchConfig != nil {
		return "elasticsearch"
	} else if spec.SplunkConfig != nil {
		return "splunk"
	} else if spec.KafkaConfig != nil {
		return "kafka"
	} else if spec.SyslogConfig != nil {
		return "syslog"
	}
	return "none"
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package cmd

import (
	"bytes"
	"io"
	"reflect"
	"testing"

	"k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/rest/fake"
	"k8s.io/kubernetes/pkg/apis/rbac"
	cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
)

type testClusterRolePrinter struct {
	CachedClusterRole *rbac.ClusterRole
}

func (t *testClusterRolePrinter) PrintObj(obj runtime.Object, out io.Writer) error {
	t.CachedClusterRole = obj.(*rbac.ClusterRole)
	return nil
}

func (t *testClusterRolePrinter) AfterPrint(output io.Writer, res string) error {
	return nil
}

func (t *testClusterRolePrinter) HandledResources() []string {
	return []string{}
}

func TestCreateClusterRole(t *testing.T) {
	clusterRoleName := "my-cluster-role"

	f, tf, _, _ := cmdtesting.NewAPIFactory()
	printer := &testClusterRolePrinter{}
	tf.Printer = printer
	tf.Namespace = "test"
	tf.Client = &fake.RESTClient{}
	tf.ClientConfig = defaultClientConfig()

	tests := map[string]struct {
		verbs               string
		resources           string
		resourceNames       string
		expectedClusterRole *rbac.ClusterRole
	}{
		"test-duplicate-resources": {
			verbs:     "get,watch,list",
			resources: "pods,pods",
			expectedClusterRole: &rbac.ClusterRole{
				ObjectMeta: v1.ObjectMeta{
					Name: clusterRoleName,
				},
				Rules: []rbac.PolicyRule{
					{
						Verbs:         []string{"get", "watch", "list"},
						Resources:     []string{"pods"},
						APIGroups:     []string{""},
						ResourceNames: []string{},
					},
				},
			},
		},
		"test-valid-case-with-multiple-apigroups": {
			verbs:     "get,watch,list",
			resources: "pods,deployments.extensions",
			expectedClusterRole: &rbac.ClusterRole{
				ObjectMeta: v1.ObjectMeta{
					Name: clusterRoleName,
				},
				Rules: []rbac.PolicyRule{
					{
						Verbs:         []string{"get", "watch", "list"},
						Resources:     []string{"pods"},
						APIGroups:     []string{""},
						ResourceNames: []string{},
					},
					{
						Verbs:         []string{"get", "watch", "list"},
						Resources:     []string{"deployments"},
						APIGroups:     []string{"extensions"},
						ResourceNames: []string{},
					},
				},
			},
		},
	}

	for name, test := range tests {
		buf := bytes.NewBuffer([]byte{})
		cmd := NewCmdCreateClusterRole(f, buf)
		cmd.Flags().Set("dry-run", "true")
		cmd.Flags().Set("output", "object")
		cmd.Flags().Set("verb", test.verbs)
		cmd.Flags().Set("resource", test.resources)
		if test.resourceNames != "" {
			cmd.Flags().Set("resource-name", test.resourceNames)
		}
		cmd.Run(cmd, []string{clusterRoleName})
		if !reflect.DeepEqual(test.expectedClusterRole, printer.CachedClusterRole) {
			t.Errorf("%s:\nexpected:\n%#v\nsaw:\n%#v", name, test.expectedClusterRole, printer.CachedClusterRole)
		}
	}
}
<commit_msg>add some unit tests for "kubectl create clusterrole"<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package cmd

import (
	"bytes"
	"io"
	"reflect"
	"testing"

	"k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/rest/fake"
	"k8s.io/kubernetes/pkg/apis/rbac"
	cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
)

type testClusterRolePrinter struct {
	CachedClusterRole *rbac.ClusterRole
}

func (t *testClusterRolePrinter) PrintObj(obj runtime.Object, out io.Writer) error {
	t.CachedClusterRole = obj.(*rbac.ClusterRole)
	return nil
}

func (t *testClusterRolePrinter) AfterPrint(output io.Writer, res string) error {
	return nil
}

func (t *testClusterRolePrinter) HandledResources() []string {
	return []string{}
}

func TestCreateClusterRole(t *testing.T) {
	clusterRoleName := "my-cluster-role"

	f, tf, _, _ := cmdtesting.NewAPIFactory()
	printer := &testClusterRolePrinter{}
	tf.Printer = printer
	tf.Namespace = "test"
	tf.Client = &fake.RESTClient{}
	tf.ClientConfig = defaultClientConfig()

	tests := map[string]struct {
		verbs               string
		resources           string
		nonResourceURL      string
		resourceNames       string
		expectedClusterRole *rbac.ClusterRole
	}{
		"test-duplicate-resources": {
			verbs:     "get,watch,list",
			resources: "pods,pods",
			expectedClusterRole: &rbac.ClusterRole{
				ObjectMeta: v1.ObjectMeta{
					Name: clusterRoleName,
				},
				Rules: []rbac.PolicyRule{
					{
						Verbs:         []string{"get", "watch", "list"},
						Resources:     []string{"pods"},
						APIGroups:     []string{""},
						ResourceNames: []string{},
					},
				},
			},
		},
		"test-valid-case-with-multiple-apigroups": {
			verbs:     "get,watch,list",
			resources: "pods,deployments.extensions",
			expectedClusterRole: &rbac.ClusterRole{
				ObjectMeta: v1.ObjectMeta{
					Name: clusterRoleName,
				},
				Rules: []rbac.PolicyRule{
					{
						Verbs:         []string{"get", "watch", "list"},
						Resources:     []string{"pods"},
						APIGroups:     []string{""},
						ResourceNames: []string{},
					},
					{
						Verbs:         []string{"get", "watch", "list"},
						Resources:     []string{"deployments"},
						APIGroups:     []string{"extensions"},
						ResourceNames: []string{},
					},
				},
			},
		},
		"test-non-resource-url": {
			verbs:          "get",
			nonResourceURL: "/logs/,/healthz",
			expectedClusterRole: &rbac.ClusterRole{
				ObjectMeta: v1.ObjectMeta{
					Name: clusterRoleName,
				},
				Rules: []rbac.PolicyRule{
					{
						Verbs:           []string{"get"},
						NonResourceURLs: []string{"/logs/", "/healthz"},
					},
				},
			},
		},
		"test-resource-and-non-resource-url": {
			verbs:          "get",
			nonResourceURL: "/logs/,/healthz",
			resources:      "pods",
			expectedClusterRole: &rbac.ClusterRole{
				ObjectMeta: v1.ObjectMeta{
					Name: clusterRoleName,
				},
				Rules: []rbac.PolicyRule{
					{
						Verbs:         []string{"get"},
						Resources:     []string{"pods"},
						APIGroups:     []string{""},
						ResourceNames: []string{},
					},
					{
						Verbs:           []string{"get"},
						NonResourceURLs: []string{"/logs/", "/healthz"},
					},
				},
			},
		},
	}

	for name, test := range tests {
		buf := bytes.NewBuffer([]byte{})
		cmd := NewCmdCreateClusterRole(f, buf)
		cmd.Flags().Set("dry-run", "true")
		cmd.Flags().Set("output", "object")
		cmd.Flags().Set("verb", test.verbs)
		cmd.Flags().Set("resource", test.resources)
		cmd.Flags().Set("non-resource-url", test.nonResourceURL)
		if test.resourceNames != "" {
			cmd.Flags().Set("resource-name", test.resourceNames)
		}
		cmd.Run(cmd, []string{clusterRoleName})
		if !reflect.DeepEqual(test.expectedClusterRole, printer.CachedClusterRole) {
			t.Errorf("%s:\nexpected:\n%#v\nsaw:\n%#v", name, test.expectedClusterRole, printer.CachedClusterRole)
		}
	}
}

func TestClusterRoleValidate(t *testing.T) {
	f, tf, _, _ := cmdtesting.NewAPIFactory()
	tf.Printer = &testPrinter{}
	tf.Namespace = "test"

	tests := map[string]struct {
		clusterRoleOptions *CreateClusterRoleOptions
		expectErr          bool
	}{
		"test-missing-name": {
			clusterRoleOptions: &CreateClusterRoleOptions{
				CreateRoleOptions: &CreateRoleOptions{},
			},
			expectErr: true,
		},
		"test-missing-verb": {
			clusterRoleOptions: &CreateClusterRoleOptions{
				CreateRoleOptions: &CreateRoleOptions{
					Name: "my-clusterrole",
				},
			},
			expectErr: true,
		},
		"test-missing-resource": {
			clusterRoleOptions: &CreateClusterRoleOptions{
				CreateRoleOptions: &CreateRoleOptions{
					Name:  "my-clusterrole",
					Verbs: []string{"get"},
				},
			},
			expectErr: true,
		},
		"test-missing-resource-existing-apigroup": {
			clusterRoleOptions: &CreateClusterRoleOptions{
				CreateRoleOptions: &CreateRoleOptions{
					Name:  "my-clusterrole",
					Verbs: []string{"get"},
					Resources: []ResourceOptions{
						{
							Group: "extensions",
						},
					},
				},
			},
			expectErr: true,
		},
		"test-missing-resource-existing-subresource": {
			clusterRoleOptions: &CreateClusterRoleOptions{
				CreateRoleOptions: &CreateRoleOptions{
					Name:  "my-clusterrole",
					Verbs: []string{"get"},
					Resources: []ResourceOptions{
						{
							SubResource: "scale",
						},
					},
				},
			},
			expectErr: true,
		},
		"test-invalid-verb": {
			clusterRoleOptions: &CreateClusterRoleOptions{
				CreateRoleOptions: &CreateRoleOptions{
					Name:  "my-clusterrole",
					Verbs: []string{"invalid-verb"},
					Resources: []ResourceOptions{
						{
							Resource: "pods",
						},
					},
				},
			},
			expectErr: true,
		},
		"test-nonresource-verb": {
			clusterRoleOptions: &CreateClusterRoleOptions{
				CreateRoleOptions: &CreateRoleOptions{
					Name:  "my-clusterrole",
					Verbs: []string{"post"},
					Resources: []ResourceOptions{
						{
							Resource: "pods",
						},
					},
				},
			},
			expectErr: true,
		},
		"test-special-verb": {
			clusterRoleOptions: &CreateClusterRoleOptions{
				CreateRoleOptions: &CreateRoleOptions{
					Name:  "my-clusterrole",
					Verbs: []string{"use"},
					Resources: []ResourceOptions{
						{
							Resource: "pods",
						},
					},
				},
			},
			expectErr: true,
		},
		"test-mix-verbs": {
			clusterRoleOptions: &CreateClusterRoleOptions{
				CreateRoleOptions: &CreateRoleOptions{
					Name:  "my-clusterrole",
					Verbs: []string{"impersonate", "use"},
					Resources: []ResourceOptions{
						{
							Resource:    "userextras",
							SubResource: "scopes",
						},
					},
				},
			},
			expectErr: true,
		},
		"test-special-verb-with-wrong-apigroup": {
			clusterRoleOptions: &CreateClusterRoleOptions{
				CreateRoleOptions: &CreateRoleOptions{
					Name:  "my-clusterrole",
					Verbs: []string{"impersonate"},
					Resources: []ResourceOptions{
						{
							Resource:    "userextras",
							SubResource: "scopes",
							Group:       "extensions",
						},
					},
				},
			},
			expectErr: true,
		},
		"test-invalid-resource": {
			clusterRoleOptions: &CreateClusterRoleOptions{
				CreateRoleOptions: &CreateRoleOptions{
					Name:  "my-clusterrole",
					Verbs: []string{"get"},
					Resources: []ResourceOptions{
						{
							Resource: "invalid-resource",
						},
					},
				},
			},
			expectErr: true,
		},
		"test-resource-name-with-multiple-resources": {
			clusterRoleOptions: &CreateClusterRoleOptions{
				CreateRoleOptions: &CreateRoleOptions{
					Name:  "my-clusterrole",
					Verbs: []string{"get"},
					Resources: []ResourceOptions{
						{
							Resource: "pods",
						},
						{
							Resource: "deployments",
							Group:    "extensions",
						},
					},
				},
			},
			expectErr: false,
		},
		"test-valid-case": {
			clusterRoleOptions: &CreateClusterRoleOptions{
				CreateRoleOptions: &CreateRoleOptions{
					Name:  "role-binder",
					Verbs: []string{"get", "list", "bind"},
					Resources: []ResourceOptions{
						{
							Resource: "roles",
							Group:    "rbac.authorization.k8s.io",
						},
					},
				},
			},
			expectErr: false,
		},
		"test-valid-case-with-subresource": {
			clusterRoleOptions: &CreateClusterRoleOptions{
				CreateRoleOptions: &CreateRoleOptions{
					Name:  "my-clusterrole",
					Verbs: []string{"get", "list"},
					Resources: []ResourceOptions{
						{
							Resource:    "replicasets",
							SubResource: "scale",
						},
					},
				},
			},
			expectErr: false,
		},
		"test-valid-case-with-additional-resource": {
			clusterRoleOptions: &CreateClusterRoleOptions{
				CreateRoleOptions: &CreateRoleOptions{
					Name:  "my-clusterrole",
					Verbs: []string{"impersonate"},
					Resources: []ResourceOptions{
						{
							Resource:    "userextras",
							SubResource: "scopes",
							Group:       "authentication.k8s.io",
						},
					},
				},
			},
			expectErr: false,
		},
		"test-invalid-verb-for-non-resource-url": {
			clusterRoleOptions: &CreateClusterRoleOptions{
				CreateRoleOptions: &CreateRoleOptions{
					Name:  "my-clusterrole",
					Verbs: []string{"create"},
				},
				NonResourceURLs: []string{"/logs/"},
			},
			expectErr: true,
		},
		"test-resource-and-non-resource-url-specified-together": {
			clusterRoleOptions: &CreateClusterRoleOptions{
				CreateRoleOptions: &CreateRoleOptions{
					Name:  "my-clusterrole",
					Verbs: []string{"get"},
					Resources: []ResourceOptions{
						{
							Resource:    "replicasets",
							SubResource: "scale",
						},
					},
				},
				NonResourceURLs: []string{"/logs/"},
			},
			expectErr: false,
		},
	}

	for name, test := range tests {
		test.clusterRoleOptions.Mapper, _ = f.Object()
		err := test.clusterRoleOptions.Validate()
		if test.expectErr && err == nil {
			t.Errorf("%s: expect error happens, but validate passes.", name)
		}
		if !test.expectErr && err != nil {
			t.Errorf("%s: unexpected error: %v", name, err)
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Knative 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 mttrigger

import (
	"context"
	"errors"
	"fmt"

	"go.uber.org/zap"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/equality"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/dynamic"
	corev1listers "k8s.io/client-go/listers/core/v1"

	"knative.dev/eventing/pkg/apis/eventing"
	eventingv1 "knative.dev/eventing/pkg/apis/eventing/v1"
	messagingv1 "knative.dev/eventing/pkg/apis/messaging/v1"
	clientset "knative.dev/eventing/pkg/client/clientset/versioned"
	eventinglisters "knative.dev/eventing/pkg/client/listers/eventing/v1"
	messaginglisters "knative.dev/eventing/pkg/client/listers/messaging/v1"
	"knative.dev/eventing/pkg/duck"
	"knative.dev/eventing/pkg/reconciler/mtbroker/resources"
	"knative.dev/eventing/pkg/reconciler/names"
	"knative.dev/eventing/pkg/reconciler/sugar/trigger/path"
	"knative.dev/eventing/pkg/utils"
	"knative.dev/pkg/apis"
	duckv1 "knative.dev/pkg/apis/duck/v1"
	"knative.dev/pkg/controller"
	"knative.dev/pkg/logging"
	pkgreconciler "knative.dev/pkg/reconciler"
	"knative.dev/pkg/resolver"
	"knative.dev/pkg/system"
)

var brokerGVK = eventingv1.SchemeGroupVersion.WithKind("Broker")

const (
	// Name of the corev1.Events emitted from the Trigger reconciliation process.
	subscriptionDeleteFailed = "SubscriptionDeleteFailed"
	subscriptionCreateFailed = "SubscriptionCreateFailed"
	subscriptionGetFailed    = "SubscriptionGetFailed"
	subscriptionDeleted      = "SubscriptionDeleted"
)

type Reconciler struct {
	eventingClientSet clientset.Interface
	dynamicClientSet  dynamic.Interface

	// listers index properties about resources
	subscriptionLister messaginglisters.SubscriptionLister
	brokerLister       eventinglisters.BrokerLister
	triggerLister      eventinglisters.TriggerLister
	configmapLister    corev1listers.ConfigMapLister

	// Dynamic tracker to track KResources. In particular, it tracks the dependency between Triggers and Sources.
	kresourceTracker duck.ListableTracker

	// Dynamic tracker to track AddressableTypes. In particular, it tracks Trigger subscribers.
	uriResolver *resolver.URIResolver
	impl        *controller.Impl
}

func (r *Reconciler) ReconcileKind(ctx context.Context, t *eventingv1.Trigger) pkgreconciler.Event {
	logging.FromContext(ctx).Infow("Reconciling", zap.Any("Trigger", t))
	t.Status.InitializeConditions()

	if t.DeletionTimestamp != nil {
		// Everything is cleaned up by the garbage collector.
		return nil
	}

	b, err := r.brokerLister.Brokers(t.Namespace).Get(t.Spec.Broker)
	if err != nil {
		if apierrs.IsNotFound(err) {
			logging.FromContext(ctx).Errorw(fmt.Sprintf("Trigger %s/%s has no broker %q", t.Namespace, t.Name, t.Spec.Broker))
			t.Status.MarkBrokerFailed("BrokerDoesNotExist", "Broker %q does not exist", t.Spec.Broker)
			// Ok to return nil here. Once the Broker comes available, or Trigger changes, we get requeued.
			return nil
		} else {
			t.Status.MarkBrokerFailed("FailedToGetBroker", "Failed to get broker %q : %s", t.Spec.Broker, err)
			return err
		}
	}

	// If it's not my brokerclass, ignore
	if b.Annotations[eventing.BrokerClassKey] != eventing.MTChannelBrokerClassValue {
		logging.FromContext(ctx).Infow("Ignoring trigger %s/%s", t.Namespace, t.Name)
		return nil
	}
	t.Status.PropagateBrokerCondition(b.Status.GetTopLevelCondition())

	// If Broker is not ready, we're done, but once it becomes ready, we'll get requeued.
	if !b.Status.IsReady() {
		logging.FromContext(ctx).Errorw("Broker is not ready", zap.Any("Broker", b))
		return nil
	}

	brokerTrigger, err := getBrokerChannelRef(ctx, b)
	if err != nil {
		t.Status.MarkBrokerFailed("MissingBrokerChannel", "Failed to get broker %q annotations: %s", t.Spec.Broker, err)
		return fmt.Errorf("failed to find Broker's Trigger channel: %s", err)
	}
	if t.Spec.Subscriber.Ref != nil {
		// To call URIFromDestination(dest apisv1alpha1.Destination, parent interface{}), dest.Ref must have a Namespace
		// We will use the Namespace of Trigger as the Namespace of dest.Ref
		t.Spec.Subscriber.Ref.Namespace = t.GetNamespace()
	}

	subscriberURI, err := r.uriResolver.URIFromDestinationV1(t.Spec.Subscriber, b)
	if err != nil {
		logging.FromContext(ctx).Errorw("Unable to get the Subscriber's URI", zap.Error(err))
		t.Status.MarkSubscriberResolvedFailed("Unable to get the Subscriber's URI", "%v", err)
		t.Status.SubscriberURI = nil
		return err
	}
	t.Status.SubscriberURI = subscriberURI
	t.Status.MarkSubscriberResolvedSucceeded()

	sub, err := r.subscribeToBrokerChannel(ctx, b, t, brokerTrigger)
	if err != nil {
		logging.FromContext(ctx).Errorw("Unable to Subscribe", zap.Error(err))
		t.Status.MarkNotSubscribed("NotSubscribed", "%v", err)
		return err
	}
	t.Status.PropagateSubscriptionCondition(sub.Status.GetTopLevelCondition())

	if err := r.checkDependencyAnnotation(ctx, t, b); err != nil {
		return err
	}

	return nil
}

// subscribeToBrokerChannel subscribes service 'svc' to the Broker's channels.
func (r *Reconciler) subscribeToBrokerChannel(ctx context.Context, b *eventingv1.Broker, t *eventingv1.Trigger, brokerTrigger *corev1.ObjectReference) (*messagingv1.Subscription, error) {
	recorder := controller.GetEventRecorder(ctx)
	uri := &apis.URL{
		Scheme: "http",
		Host:   names.ServiceHostName("broker-filter", system.Namespace()),
		Path:   path.Generate(t),
	}
	// Note that we have to hard code the brokerGKV stuff as sometimes typemeta is not
	// filled in. So instead of b.TypeMeta.Kind and b.TypeMeta.APIVersion, we have to
	// do it this way.
	brokerObjRef := &corev1.ObjectReference{
		Kind:       brokerGVK.Kind,
		APIVersion: brokerGVK.GroupVersion().String(),
		Name:       b.Name,
		Namespace:  b.Namespace,
	}
	expected := resources.NewSubscription(t, brokerTrigger, brokerObjRef, uri, b.Spec.Delivery)

	sub, err := r.subscriptionLister.Subscriptions(t.Namespace).Get(expected.Name)
	// If the resource doesn't exist, we'll create it.
	if apierrs.IsNotFound(err) {
		// Issue #2842: Subscription name uses kmeta.ChildName. If a subscription by the previous name pattern is found, it should
		// be deleted. This might cause temporary downtime.
		if deprecatedName := utils.GenerateFixedName(t, fmt.Sprintf("%s-%s", t.Spec.Broker, t.Name)); deprecatedName != expected.Name {
			if err := r.eventingClientSet.MessagingV1().Subscriptions(t.Namespace).Delete(deprecatedName, &metav1.DeleteOptions{}); err != nil && !apierrs.IsNotFound(err) {
				return nil, fmt.Errorf("error deleting deprecated named subscription: %v", err)
			}
			controller.GetEventRecorder(ctx).Eventf(t, corev1.EventTypeNormal, subscriptionDeleted, "Deprecated subscription removed: \"%s/%s\"", t.Namespace, deprecatedName)
		}

		logging.FromContext(ctx).Info("Creating subscription")
		sub, err = r.eventingClientSet.MessagingV1().Subscriptions(t.Namespace).Create(expected)
		if err != nil {
			return nil, err
		}
		return sub, nil
	} else if err != nil {
		logging.FromContext(ctx).Errorw("Failed to get subscription", zap.Error(err))
		recorder.Eventf(t, corev1.EventTypeWarning, subscriptionGetFailed, "Getting the Trigger's Subscription failed: %v", err)
		return nil, err
	} else if !metav1.IsControlledBy(sub, t) {
		t.Status.MarkNotSubscribed("SubscriptionNotOwnedByTrigger", "trigger %q does not own subscription %q", t.Name, sub.Name)
		return nil, fmt.Errorf("trigger %q does not own subscription %q", t.Name, sub.Name)
	} else if sub, err = r.reconcileSubscription(ctx, t, expected, sub); err != nil {
		logging.FromContext(ctx).Errorw("Failed to reconcile subscription", zap.Error(err))
		return sub, err
	}

	return sub, nil
}

func (r *Reconciler) reconcileSubscription(ctx context.Context, t *eventingv1.Trigger, expected, actual *messagingv1.Subscription) (*messagingv1.Subscription, error) {
	// Update Subscription if it has changed.
	if equality.Semantic.DeepDerivative(expected.Spec, actual.Spec) {
		return actual, nil
	}
	recorder := controller.GetEventRecorder(ctx)
	logging.FromContext(ctx).Infow("Differing Subscription", zap.Any("expected", expected.Spec), zap.Any("actual", actual.Spec))

	// Given that spec.channel is immutable, we cannot just update the Subscription. We delete
	// it and re-create it instead.
	logging.FromContext(ctx).Infow("Deleting subscription", zap.String("namespace", actual.Namespace), zap.String("name", actual.Name))
	err := r.eventingClientSet.MessagingV1().Subscriptions(t.Namespace).Delete(actual.Name, &metav1.DeleteOptions{})
	if err != nil {
		logging.FromContext(ctx).Info("Cannot delete subscription", zap.Error(err))
		recorder.Eventf(t, corev1.EventTypeWarning, subscriptionDeleteFailed, "Delete Trigger's subscription failed: %v", err)
		return nil, err
	}
	logging.FromContext(ctx).Info("Creating subscription")
	newSub, err := r.eventingClientSet.MessagingV1().Subscriptions(t.Namespace).Create(expected)
	if err != nil {
		logging.FromContext(ctx).Infow("Cannot create subscription", zap.Error(err))
		recorder.Eventf(t, corev1.EventTypeWarning, subscriptionCreateFailed, "Create Trigger's subscription failed: %v", err)
		return nil, err
	}
	return newSub, nil
}

func (r *Reconciler) checkDependencyAnnotation(ctx context.Context, t *eventingv1.Trigger, b *eventingv1.Broker) error {
	if dependencyAnnotation, ok := t.GetAnnotations()[eventingv1.DependencyAnnotation]; ok {
		dependencyObjRef, err := eventingv1.GetObjRefFromDependencyAnnotation(dependencyAnnotation)
		if err != nil {
			t.Status.MarkDependencyFailed("ReferenceError", "Unable to unmarshal objectReference from dependency annotation of trigger: %v", err)
			return fmt.Errorf("getting object ref from dependency annotation %q: %v", dependencyAnnotation, err)
		}
		trackKResource := r.kresourceTracker.TrackInNamespace(b)
		// Trigger and its dependent source are in the same namespace, we already did the validation in the webhook.
		if err := trackKResource(dependencyObjRef); err != nil {
			return fmt.Errorf("tracking dependency: %v", err)
		}
		if err := r.propagateDependencyReadiness(ctx, t, dependencyObjRef); err != nil {
			return fmt.Errorf("propagating dependency readiness: %v", err)
		}
	} else {
		t.Status.MarkDependencySucceeded()
	}
	return nil
}

func (r *Reconciler) propagateDependencyReadiness(ctx context.Context, t *eventingv1.Trigger, dependencyObjRef corev1.ObjectReference) error {
	lister, err := r.kresourceTracker.ListerFor(dependencyObjRef)
	if err != nil {
		t.Status.MarkDependencyUnknown("ListerDoesNotExist", "Failed to retrieve lister: %v", err)
		return fmt.Errorf("retrieving lister: %v", err)
	}
	dependencyObj, err := lister.ByNamespace(t.GetNamespace()).Get(dependencyObjRef.Name)
	if err != nil {
		if apierrs.IsNotFound(err) {
			t.Status.MarkDependencyFailed("DependencyDoesNotExist", "Dependency does not exist: %v", err)
		} else {
			t.Status.MarkDependencyUnknown("DependencyGetFailed", "Failed to get dependency: %v", err)
		}
		return fmt.Errorf("getting the dependency: %v", err)
	}
	dependency := dependencyObj.(*duckv1.KResource)

	// The dependency hasn't yet reconciled our latest changes to
	// its desired state, so its conditions are outdated.
	if dependency.GetGeneration() != dependency.Status.ObservedGeneration {
		logging.FromContext(ctx).Infow("The ObjectMeta Generation of dependency is not equal to the observedGeneration of status",
			zap.Any("objectMetaGeneration", dependency.GetGeneration()),
			zap.Any("statusObservedGeneration", dependency.Status.ObservedGeneration))
		t.Status.MarkDependencyUnknown("GenerationNotEqual", "The dependency's metadata.generation, %q, is not equal to its status.observedGeneration, %q.", dependency.GetGeneration(), dependency.Status.ObservedGeneration)
		return nil
	}
	t.Status.PropagateDependencyStatus(dependency)
	return nil
}

func getBrokerChannelRef(ctx context.Context, b *eventingv1.Broker) (*corev1.ObjectReference, error) {
	if b.Status.Annotations != nil {
		ref := &corev1.ObjectReference{
			Kind:       b.Status.Annotations[eventing.BrokerChannelKindStatusAnnotationKey],
			APIVersion: b.Status.Annotations[eventing.BrokerChannelAPIVersionStatusAnnotationKey],
			Name:       b.Status.Annotations[eventing.BrokerChannelNameStatusAnnotationKey],
			Namespace:  b.Namespace,
		}
		if ref.Kind != "" && ref.APIVersion != "" && ref.Name != "" && ref.Namespace != "" {
			return ref, nil
		}
	}
	return nil, errors.New("Broker.Status.Annotations nil or missing values")
}
<commit_msg>fix log message (#4030)<commit_after>/*
Copyright 2020 The Knative 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 mttrigger

import (
	"context"
	"errors"
	"fmt"

	"go.uber.org/zap"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/equality"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/dynamic"
	corev1listers "k8s.io/client-go/listers/core/v1"

	"knative.dev/eventing/pkg/apis/eventing"
	eventingv1 "knative.dev/eventing/pkg/apis/eventing/v1"
	messagingv1 "knative.dev/eventing/pkg/apis/messaging/v1"
	clientset "knative.dev/eventing/pkg/client/clientset/versioned"
	eventinglisters "knative.dev/eventing/pkg/client/listers/eventing/v1"
	messaginglisters "knative.dev/eventing/pkg/client/listers/messaging/v1"
	"knative.dev/eventing/pkg/duck"
	"knative.dev/eventing/pkg/reconciler/mtbroker/resources"
	"knative.dev/eventing/pkg/reconciler/names"
	"knative.dev/eventing/pkg/reconciler/sugar/trigger/path"
	"knative.dev/eventing/pkg/utils"
	"knative.dev/pkg/apis"
	duckv1 "knative.dev/pkg/apis/duck/v1"
	"knative.dev/pkg/controller"
	"knative.dev/pkg/logging"
	pkgreconciler "knative.dev/pkg/reconciler"
	"knative.dev/pkg/resolver"
	"knative.dev/pkg/system"
)

var brokerGVK = eventingv1.SchemeGroupVersion.WithKind("Broker")

const (
	// Name of the corev1.Events emitted from the Trigger reconciliation process.
	subscriptionDeleteFailed = "SubscriptionDeleteFailed"
	subscriptionCreateFailed = "SubscriptionCreateFailed"
	subscriptionGetFailed    = "SubscriptionGetFailed"
	subscriptionDeleted      = "SubscriptionDeleted"
)

type Reconciler struct {
	eventingClientSet clientset.Interface
	dynamicClientSet  dynamic.Interface

	// listers index properties about resources
	subscriptionLister messaginglisters.SubscriptionLister
	brokerLister       eventinglisters.BrokerLister
	triggerLister      eventinglisters.TriggerLister
	configmapLister    corev1listers.ConfigMapLister

	// Dynamic tracker to track KResources. In particular, it tracks the dependency between Triggers and Sources.
	kresourceTracker duck.ListableTracker

	// Dynamic tracker to track AddressableTypes. In particular, it tracks Trigger subscribers.
	uriResolver *resolver.URIResolver
	impl        *controller.Impl
}

func (r *Reconciler) ReconcileKind(ctx context.Context, t *eventingv1.Trigger) pkgreconciler.Event {
	logging.FromContext(ctx).Infow("Reconciling", zap.Any("Trigger", t))
	t.Status.InitializeConditions()

	if t.DeletionTimestamp != nil {
		// Everything is cleaned up by the garbage collector.
		return nil
	}

	b, err := r.brokerLister.Brokers(t.Namespace).Get(t.Spec.Broker)
	if err != nil {
		if apierrs.IsNotFound(err) {
			logging.FromContext(ctx).Errorw(fmt.Sprintf("Trigger %s/%s has no broker %q", t.Namespace, t.Name, t.Spec.Broker))
			t.Status.MarkBrokerFailed("BrokerDoesNotExist", "Broker %q does not exist", t.Spec.Broker)
			// Ok to return nil here. Once the Broker comes available, or Trigger changes, we get requeued.
			return nil
		} else {
			t.Status.MarkBrokerFailed("FailedToGetBroker", "Failed to get broker %q : %s", t.Spec.Broker, err)
			return err
		}
	}

	// If it's not my brokerclass, ignore
	if b.Annotations[eventing.BrokerClassKey] != eventing.MTChannelBrokerClassValue {
		logging.FromContext(ctx).Infof("Ignoring trigger %s/%s", t.Namespace, t.Name)
		return nil
	}
	t.Status.PropagateBrokerCondition(b.Status.GetTopLevelCondition())

	// If Broker is not ready, we're done, but once it becomes ready, we'll get requeued.
	if !b.Status.IsReady() {
		logging.FromContext(ctx).Errorw("Broker is not ready", zap.Any("Broker", b))
		return nil
	}

	brokerTrigger, err := getBrokerChannelRef(ctx, b)
	if err != nil {
		t.Status.MarkBrokerFailed("MissingBrokerChannel", "Failed to get broker %q annotations: %s", t.Spec.Broker, err)
		return fmt.Errorf("failed to find Broker's Trigger channel: %s", err)
	}
	if t.Spec.Subscriber.Ref != nil {
		// To call URIFromDestination(dest apisv1alpha1.Destination, parent interface{}), dest.Ref must have a Namespace
		// We will use the Namespace of Trigger as the Namespace of dest.Ref
		t.Spec.Subscriber.Ref.Namespace = t.GetNamespace()
	}

	subscriberURI, err := r.uriResolver.URIFromDestinationV1(t.Spec.Subscriber, b)
	if err != nil {
		logging.FromContext(ctx).Errorw("Unable to get the Subscriber's URI", zap.Error(err))
		t.Status.MarkSubscriberResolvedFailed("Unable to get the Subscriber's URI", "%v", err)
		t.Status.SubscriberURI = nil
		return err
	}
	t.Status.SubscriberURI = subscriberURI
	t.Status.MarkSubscriberResolvedSucceeded()

	sub, err := r.subscribeToBrokerChannel(ctx, b, t, brokerTrigger)
	if err != nil {
		logging.FromContext(ctx).Errorw("Unable to Subscribe", zap.Error(err))
		t.Status.MarkNotSubscribed("NotSubscribed", "%v", err)
		return err
	}
	t.Status.PropagateSubscriptionCondition(sub.Status.GetTopLevelCondition())

	if err := r.checkDependencyAnnotation(ctx, t, b); err != nil {
		return err
	}

	return nil
}

// subscribeToBrokerChannel subscribes service 'svc' to the Broker's channels.
func (r *Reconciler) subscribeToBrokerChannel(ctx context.Context, b *eventingv1.Broker, t *eventingv1.Trigger, brokerTrigger *corev1.ObjectReference) (*messagingv1.Subscription, error) {
	recorder := controller.GetEventRecorder(ctx)
	uri := &apis.URL{
		Scheme: "http",
		Host:   names.ServiceHostName("broker-filter", system.Namespace()),
		Path:   path.Generate(t),
	}
	// Note that we have to hard code the brokerGKV stuff as sometimes typemeta is not
	// filled in. So instead of b.TypeMeta.Kind and b.TypeMeta.APIVersion, we have to
	// do it this way.
	brokerObjRef := &corev1.ObjectReference{
		Kind:       brokerGVK.Kind,
		APIVersion: brokerGVK.GroupVersion().String(),
		Name:       b.Name,
		Namespace:  b.Namespace,
	}
	expected := resources.NewSubscription(t, brokerTrigger, brokerObjRef, uri, b.Spec.Delivery)

	sub, err := r.subscriptionLister.Subscriptions(t.Namespace).Get(expected.Name)
	// If the resource doesn't exist, we'll create it.
	if apierrs.IsNotFound(err) {
		// Issue #2842: Subscription name uses kmeta.ChildName. If a subscription by the previous name pattern is found, it should
		// be deleted. This might cause temporary downtime.
		if deprecatedName := utils.GenerateFixedName(t, fmt.Sprintf("%s-%s", t.Spec.Broker, t.Name)); deprecatedName != expected.Name {
			if err := r.eventingClientSet.MessagingV1().Subscriptions(t.Namespace).Delete(deprecatedName, &metav1.DeleteOptions{}); err != nil && !apierrs.IsNotFound(err) {
				return nil, fmt.Errorf("error deleting deprecated named subscription: %v", err)
			}
			controller.GetEventRecorder(ctx).Eventf(t, corev1.EventTypeNormal, subscriptionDeleted, "Deprecated subscription removed: \"%s/%s\"", t.Namespace, deprecatedName)
		}

		logging.FromContext(ctx).Info("Creating subscription")
		sub, err = r.eventingClientSet.MessagingV1().Subscriptions(t.Namespace).Create(expected)
		if err != nil {
			return nil, err
		}
		return sub, nil
	} else if err != nil {
		logging.FromContext(ctx).Errorw("Failed to get subscription", zap.Error(err))
		recorder.Eventf(t, corev1.EventTypeWarning, subscriptionGetFailed, "Getting the Trigger's Subscription failed: %v", err)
		return nil, err
	} else if !metav1.IsControlledBy(sub, t) {
		t.Status.MarkNotSubscribed("SubscriptionNotOwnedByTrigger", "trigger %q does not own subscription %q", t.Name, sub.Name)
		return nil, fmt.Errorf("trigger %q does not own subscription %q", t.Name, sub.Name)
	} else if sub, err = r.reconcileSubscription(ctx, t, expected, sub); err != nil {
		logging.FromContext(ctx).Errorw("Failed to reconcile subscription", zap.Error(err))
		return sub, err
	}

	return sub, nil
}

func (r *Reconciler) reconcileSubscription(ctx context.Context, t *eventingv1.Trigger, expected, actual *messagingv1.Subscription) (*messagingv1.Subscription, error) {
	// Update Subscription if it has changed.
	if equality.Semantic.DeepDerivative(expected.Spec, actual.Spec) {
		return actual, nil
	}
	recorder := controller.GetEventRecorder(ctx)
	logging.FromContext(ctx).Infow("Differing Subscription", zap.Any("expected", expected.Spec), zap.Any("actual", actual.Spec))

	// Given that spec.channel is immutable, we cannot just update the Subscription. We delete
	// it and re-create it instead.
	logging.FromContext(ctx).Infow("Deleting subscription", zap.String("namespace", actual.Namespace), zap.String("name", actual.Name))
	err := r.eventingClientSet.MessagingV1().Subscriptions(t.Namespace).Delete(actual.Name, &metav1.DeleteOptions{})
	if err != nil {
		logging.FromContext(ctx).Info("Cannot delete subscription", zap.Error(err))
		recorder.Eventf(t, corev1.EventTypeWarning, subscriptionDeleteFailed, "Delete Trigger's subscription failed: %v", err)
		return nil, err
	}
	logging.FromContext(ctx).Info("Creating subscription")
	newSub, err := r.eventingClientSet.MessagingV1().Subscriptions(t.Namespace).Create(expected)
	if err != nil {
		logging.FromContext(ctx).Infow("Cannot create subscription", zap.Error(err))
		recorder.Eventf(t, corev1.EventTypeWarning, subscriptionCreateFailed, "Create Trigger's subscription failed: %v", err)
		return nil, err
	}
	return newSub, nil
}

func (r *Reconciler) checkDependencyAnnotation(ctx context.Context, t *eventingv1.Trigger, b *eventingv1.Broker) error {
	if dependencyAnnotation, ok := t.GetAnnotations()[eventingv1.DependencyAnnotation]; ok {
		dependencyObjRef, err := eventingv1.GetObjRefFromDependencyAnnotation(dependencyAnnotation)
		if err != nil {
			t.Status.MarkDependencyFailed("ReferenceError", "Unable to unmarshal objectReference from dependency annotation of trigger: %v", err)
			return fmt.Errorf("getting object ref from dependency annotation %q: %v", dependencyAnnotation, err)
		}
		trackKResource := r.kresourceTracker.TrackInNamespace(b)
		// Trigger and its dependent source are in the same namespace, we already did the validation in the webhook.
		if err := trackKResource(dependencyObjRef); err != nil {
			return fmt.Errorf("tracking dependency: %v", err)
		}
		if err := r.propagateDependencyReadiness(ctx, t, dependencyObjRef); err != nil {
			return fmt.Errorf("propagating dependency readiness: %v", err)
		}
	} else {
		t.Status.MarkDependencySucceeded()
	}
	return nil
}

func (r *Reconciler) propagateDependencyReadiness(ctx context.Context, t *eventingv1.Trigger, dependencyObjRef corev1.ObjectReference) error {
	lister, err := r.kresourceTracker.ListerFor(dependencyObjRef)
	if err != nil {
		t.Status.MarkDependencyUnknown("ListerDoesNotExist", "Failed to retrieve lister: %v", err)
		return fmt.Errorf("retrieving lister: %v", err)
	}
	dependencyObj, err := lister.ByNamespace(t.GetNamespace()).Get(dependencyObjRef.Name)
	if err != nil {
		if apierrs.IsNotFound(err) {
			t.Status.MarkDependencyFailed("DependencyDoesNotExist", "Dependency does not exist: %v", err)
		} else {
			t.Status.MarkDependencyUnknown("DependencyGetFailed", "Failed to get dependency: %v", err)
		}
		return fmt.Errorf("getting the dependency: %v", err)
	}
	dependency := dependencyObj.(*duckv1.KResource)

	// The dependency hasn't yet reconciled our latest changes to
	// its desired state, so its conditions are outdated.
	if dependency.GetGeneration() != dependency.Status.ObservedGeneration {
		logging.FromContext(ctx).Infow("The ObjectMeta Generation of dependency is not equal to the observedGeneration of status",
			zap.Any("objectMetaGeneration", dependency.GetGeneration()),
			zap.Any("statusObservedGeneration", dependency.Status.ObservedGeneration))
		t.Status.MarkDependencyUnknown("GenerationNotEqual", "The dependency's metadata.generation, %q, is not equal to its status.observedGeneration, %q.", dependency.GetGeneration(), dependency.Status.ObservedGeneration)
		return nil
	}
	t.Status.PropagateDependencyStatus(dependency)
	return nil
}

func getBrokerChannelRef(ctx context.Context, b *eventingv1.Broker) (*corev1.ObjectReference, error) {
	if b.Status.Annotations != nil {
		ref := &corev1.ObjectReference{
			Kind:       b.Status.Annotations[eventing.BrokerChannelKindStatusAnnotationKey],
			APIVersion: b.Status.Annotations[eventing.BrokerChannelAPIVersionStatusAnnotationKey],
			Name:       b.Status.Annotations[eventing.BrokerChannelNameStatusAnnotationKey],
			Namespace:  b.Namespace,
		}
		if ref.Kind != "" && ref.APIVersion != "" && ref.Name != "" && ref.Namespace != "" {
			return ref, nil
		}
	}
	return nil, errors.New("Broker.Status.Annotations nil or missing values")
}
<|endoftext|>